SPIRV-Tools/test/TextLiteral.cpp
Lei Zhang 755f97f534 Add a callback mechanism for communicating messages to callers.
Every time an event happens in the library that the user should be
aware of, the callback will be invoked.

The existing diagnostic mechanism is hijacked internally by a
callback that creates an diagnostic object each time an event
happens.
2016-09-15 12:35:48 -04:00

414 lines
15 KiB
C++

// Copyright (c) 2015-2016 The Khronos Group Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "UnitSPIRV.h"
#include "TestFixture.h"
#include "gmock/gmock.h"
#include "message.h"
#include <string>
using ::testing::Eq;
namespace {
TEST(TextLiteral, GoodI32) {
spv_literal_t l;
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("-0", &l));
EXPECT_EQ(SPV_LITERAL_TYPE_INT_32, l.type);
EXPECT_EQ(0, l.value.i32);
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("-2147483648", &l));
EXPECT_EQ(SPV_LITERAL_TYPE_INT_32, l.type);
EXPECT_EQ((-2147483647L - 1), l.value.i32);
}
TEST(TextLiteral, GoodU32) {
spv_literal_t l;
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("0", &l));
EXPECT_EQ(SPV_LITERAL_TYPE_UINT_32, l.type);
EXPECT_EQ(0, l.value.i32);
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("4294967295", &l));
EXPECT_EQ(SPV_LITERAL_TYPE_UINT_32, l.type);
EXPECT_EQ(4294967295, l.value.u32);
}
TEST(TextLiteral, GoodI64) {
spv_literal_t l;
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("-2147483649", &l));
EXPECT_EQ(SPV_LITERAL_TYPE_INT_64, l.type);
EXPECT_EQ(-2147483649LL, l.value.i64);
}
TEST(TextLiteral, GoodU64) {
spv_literal_t l;
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("4294967296", &l));
EXPECT_EQ(SPV_LITERAL_TYPE_UINT_64, l.type);
EXPECT_EQ(4294967296u, l.value.u64);
}
TEST(TextLiteral, GoodFloat) {
spv_literal_t l;
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("1.0", &l));
EXPECT_EQ(SPV_LITERAL_TYPE_FLOAT_32, l.type);
EXPECT_EQ(1.0, l.value.f);
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("1.5", &l));
EXPECT_EQ(SPV_LITERAL_TYPE_FLOAT_32, l.type);
EXPECT_EQ(1.5, l.value.f);
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("-.25", &l));
EXPECT_EQ(SPV_LITERAL_TYPE_FLOAT_32, l.type);
EXPECT_EQ(-.25, l.value.f);
}
TEST(TextLiteral, BadString) {
spv_literal_t l;
EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("", &l));
EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("-", &l));
EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("--", &l));
EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("1-2", &l));
EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("123a", &l));
EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("12.2.3", &l));
EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("\"", &l));
EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("\"z", &l));
EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("a\"", &l));
}
class GoodStringTest
: public ::testing::TestWithParam<std::pair<const char*, const char*>> {};
TEST_P(GoodStringTest, GoodStrings) {
spv_literal_t l;
ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral(std::get<0>(GetParam()), &l));
EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type);
EXPECT_EQ(std::get<1>(GetParam()), l.str);
}
INSTANTIATE_TEST_CASE_P(
TextLiteral, GoodStringTest,
::testing::ValuesIn(std::vector<std::pair<const char*, const char*>>{
{R"("-")", "-"},
{R"("--")", "--"},
{R"("1-2")", "1-2"},
{R"("123a")", "123a"},
{R"("12.2.3")", "12.2.3"},
{R"("\"")", "\""},
{R"("\\")", "\\"},
{"\"\\foo\nbar\"", "foo\nbar"},
{"\"\\foo\\\nbar\"", "foo\nbar"},
{"\"\xE4\xBA\xB2\"", "\xE4\xBA\xB2"},
{"\"\\\xE4\xBA\xB2\"", "\xE4\xBA\xB2"},
{"\"this \\\" and this \\\\ and \\\xE4\xBA\xB2\"",
"this \" and this \\ and \xE4\xBA\xB2"}}), );
TEST(TextLiteral, StringTooLong) {
spv_literal_t l;
std::string too_long =
std::string("\"") +
std::string(SPV_LIMIT_LITERAL_STRING_BYTES_MAX + 1, 'a') + "\"";
EXPECT_EQ(SPV_ERROR_OUT_OF_MEMORY, spvTextToLiteral(too_long.data(), &l));
}
TEST(TextLiteral, GoodLongString) {
spv_literal_t l;
// The universal limit of 65535 Unicode characters might make this
// fail validation, since SPV_LIMIT_LITERAL_STRING_BYTES_MAX is 4*65535.
// However, as an implementation detail, we'll allow the assembler
// to parse it. Otherwise we'd have to scan the string for valid UTF-8
// characters.
std::string unquoted(SPV_LIMIT_LITERAL_STRING_BYTES_MAX, 'a');
std::string good_long = std::string("\"") + unquoted + "\"";
EXPECT_EQ(SPV_SUCCESS, spvTextToLiteral(good_long.data(), &l));
EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type);
EXPECT_EQ(unquoted.data(), l.str);
}
TEST(TextLiteral, GoodUTF8String) {
const std::string unquoted =
spvtest::MakeLongUTF8String(SPV_LIMIT_LITERAL_STRING_UTF8_CHARS_MAX);
const std::string good_long = std::string("\"") + unquoted + "\"";
spv_literal_t l;
EXPECT_EQ(SPV_SUCCESS, spvTextToLiteral(good_long.data(), &l));
EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type);
EXPECT_EQ(unquoted.data(), l.str);
}
// A test case for parsing literal numbers.
struct TextLiteralCase {
uint32_t bitwidth;
const char* text;
bool is_signed;
bool success;
std::vector<uint32_t> expected_values;
};
using IntegerTest =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<TextLiteralCase>>;
std::vector<uint32_t> successfulEncode(const TextLiteralCase& test,
libspirv::IdTypeClass type) {
spv_instruction_t inst;
std::string message;
auto capture_message = [&message](spvtools::MessageLevel, const char*,
const spv_position_t&,
const char* m) { message = m; };
libspirv::IdType expected_type{test.bitwidth, test.is_signed, type};
EXPECT_EQ(SPV_SUCCESS,
libspirv::AssemblyContext(nullptr, capture_message)
.binaryEncodeNumericLiteral(test.text, SPV_ERROR_INVALID_TEXT,
expected_type, &inst))
<< message;
return inst.words;
}
std::string failedEncode(const TextLiteralCase& test,
libspirv::IdTypeClass type) {
spv_instruction_t inst;
std::string message;
auto capture_message = [&message](spvtools::MessageLevel, const char*,
const spv_position_t&,
const char* m) { message = m; };
libspirv::IdType expected_type{test.bitwidth, test.is_signed, type};
EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
libspirv::AssemblyContext(nullptr, capture_message)
.binaryEncodeNumericLiteral(test.text, SPV_ERROR_INVALID_TEXT,
expected_type, &inst));
return message;
}
TEST_P(IntegerTest, IntegerBounds) {
if (GetParam().success) {
EXPECT_THAT(
successfulEncode(GetParam(), libspirv::IdTypeClass::kScalarIntegerType),
Eq(GetParam().expected_values));
} else {
std::stringstream ss;
ss << "Integer " << GetParam().text << " does not fit in a "
<< GetParam().bitwidth << "-bit "
<< (GetParam().is_signed ? "signed" : "unsigned") << " integer";
EXPECT_THAT(
failedEncode(GetParam(), libspirv::IdTypeClass::kScalarIntegerType),
Eq(ss.str()));
}
}
// Four nicely named methods for making TextLiteralCase values.
// Their names have underscores in some places to make it easier
// to read the table that follows.
TextLiteralCase Make_Ok__Signed(uint32_t bitwidth, const char* text,
std::vector<uint32_t> encoding) {
return TextLiteralCase{bitwidth, text, true, true, encoding};
}
TextLiteralCase Make_Ok__Unsigned(uint32_t bitwidth, const char* text,
std::vector<uint32_t> encoding) {
return TextLiteralCase{bitwidth, text, false, true, encoding};
}
TextLiteralCase Make_Bad_Signed(uint32_t bitwidth, const char* text) {
return TextLiteralCase{bitwidth, text, true, false, {}};
}
TextLiteralCase Make_Bad_Unsigned(uint32_t bitwidth, const char* text) {
return TextLiteralCase{bitwidth, text, false, false, {}};
}
// clang-format off
INSTANTIATE_TEST_CASE_P(
DecimalIntegers, IntegerTest,
::testing::ValuesIn(std::vector<TextLiteralCase>{
// Check max value and overflow value for 1-bit numbers.
Make_Ok__Signed(1, "0", {0}),
Make_Ok__Unsigned(1, "1", {1}),
Make_Bad_Signed(1, "1"),
Make_Bad_Unsigned(1, "2"),
// Check max value and overflow value for 2-bit numbers.
Make_Ok__Signed(2, "1", {1}),
Make_Ok__Unsigned(2, "3", {3}),
Make_Bad_Signed(2, "2"),
Make_Bad_Unsigned(2, "4"),
// Check max negative value and overflow value for signed
// 1- and 2-bit numbers. Signed negative numbers are sign-extended.
Make_Ok__Signed(1, "-0", {uint32_t(0)}),
Make_Ok__Signed(1, "-1", {uint32_t(-1)}),
Make_Ok__Signed(2, "-0", {0}),
Make_Ok__Signed(2, "-1", {uint32_t(-1)}),
Make_Ok__Signed(2, "-2", {uint32_t(-2)}),
Make_Bad_Signed(2, "-3"),
Make_Bad_Unsigned(2, "2224323424242424"),
Make_Ok__Unsigned(16, "65535", {0xFFFF}),
Make_Bad_Unsigned(16, "65536"),
Make_Bad_Signed(16, "65535"),
Make_Ok__Signed(16, "32767", {0x7FFF}),
Make_Ok__Signed(16, "-32768", {0xFFFF8000}),
// Check values around 32-bits in magnitude.
Make_Ok__Unsigned(33, "4294967296", {0, 1}),
Make_Ok__Unsigned(33, "4294967297", {1, 1}),
Make_Bad_Unsigned(33, "8589934592"),
Make_Bad_Signed(33, "4294967296"),
Make_Ok__Signed(33, "-4294967296", {0x0, 0xFFFFFFFF}),
Make_Ok__Unsigned(64, "4294967296", {0, 1}),
Make_Ok__Unsigned(64, "4294967297", {1, 1}),
// Check max value and overflow value for 64-bit numbers.
Make_Ok__Signed(64, "9223372036854775807", {0xffffffff, 0x7fffffff}),
Make_Bad_Signed(64, "9223372036854775808"),
Make_Ok__Unsigned(64, "9223372036854775808", {0x00000000, 0x80000000}),
Make_Ok__Unsigned(64, "18446744073709551615", {0xffffffff, 0xffffffff}),
Make_Ok__Signed(64, "-9223372036854775808", {0x00000000, 0x80000000}),
}),);
// clang-format on
using IntegerLeadingMinusTest =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<TextLiteralCase>>;
TEST_P(IntegerLeadingMinusTest, CantHaveLeadingMinusOnUnsigned) {
EXPECT_FALSE(GetParam().success);
EXPECT_THAT(
failedEncode(GetParam(), libspirv::IdTypeClass::kScalarIntegerType),
Eq("Cannot put a negative number in an unsigned literal"));
}
// clang-format off
INSTANTIATE_TEST_CASE_P(
DecimalAndHexIntegers, IntegerLeadingMinusTest,
::testing::ValuesIn(std::vector<TextLiteralCase>{
// Unsigned numbers never allow a leading minus sign.
Make_Bad_Unsigned(16, "-0"),
Make_Bad_Unsigned(16, "-0x0"),
Make_Bad_Unsigned(16, "-0x1"),
Make_Bad_Unsigned(32, "-0"),
Make_Bad_Unsigned(32, "-0x0"),
Make_Bad_Unsigned(32, "-0x1"),
Make_Bad_Unsigned(64, "-0"),
Make_Bad_Unsigned(64, "-0x0"),
Make_Bad_Unsigned(64, "-0x1"),
}),);
// clang-format off
INSTANTIATE_TEST_CASE_P(
HexIntegers, IntegerTest,
::testing::ValuesIn(std::vector<TextLiteralCase>{
// Check 0x and 0X prefices.
Make_Ok__Signed(16, "0x1234", {0x1234}),
Make_Ok__Signed(16, "0X1234", {0x1234}),
// Check 1-bit numbers
Make_Ok__Signed(1, "0x0", {0}),
Make_Ok__Signed(1, "0x1", {uint32_t(-1)}),
Make_Ok__Unsigned(1, "0x0", {0}),
Make_Ok__Unsigned(1, "0x1", {1}),
Make_Bad_Signed(1, "0x2"),
Make_Bad_Unsigned(1, "0x2"),
// Check 2-bit numbers
Make_Ok__Signed(2, "0x0", {0}),
Make_Ok__Signed(2, "0x1", {1}),
Make_Ok__Signed(2, "0x2", {uint32_t(-2)}),
Make_Ok__Signed(2, "0x3", {uint32_t(-1)}),
Make_Ok__Unsigned(2, "0x0", {0}),
Make_Ok__Unsigned(2, "0x1", {1}),
Make_Ok__Unsigned(2, "0x2", {2}),
Make_Ok__Unsigned(2, "0x3", {3}),
Make_Bad_Signed(2, "0x4"),
Make_Bad_Unsigned(2, "0x4"),
// Check 8-bit numbers
Make_Ok__Signed(8, "0x7f", {0x7f}),
Make_Ok__Signed(8, "0x80", {0xffffff80}),
Make_Ok__Unsigned(8, "0x80", {0x80}),
Make_Ok__Unsigned(8, "0xff", {0xff}),
Make_Bad_Signed(8, "0x100"),
Make_Bad_Unsigned(8, "0x100"),
// Check 16-bit numbers
Make_Ok__Signed(16, "0x7fff", {0x7fff}),
Make_Ok__Signed(16, "0x8000", {0xffff8000}),
Make_Ok__Unsigned(16, "0x8000", {0x8000}),
Make_Ok__Unsigned(16, "0xffff", {0xffff}),
Make_Bad_Signed(16, "0x10000"),
Make_Bad_Unsigned(16, "0x10000"),
// Check 32-bit numbers
Make_Ok__Signed(32, "0x7fffffff", {0x7fffffff}),
Make_Ok__Signed(32, "0x80000000", {0x80000000}),
Make_Ok__Unsigned(32, "0x80000000", {0x80000000}),
Make_Ok__Unsigned(32, "0xffffffff", {0xffffffff}),
Make_Bad_Signed(32, "0x100000000"),
Make_Bad_Unsigned(32, "0x100000000"),
// Check 48-bit numbers
Make_Ok__Unsigned(48, "0x7ffffffff", {0xffffffff, 7}),
Make_Ok__Unsigned(48, "0x800000000", {0, 8}),
Make_Ok__Signed(48, "0x7fffffffffff", {0xffffffff, 0x7fff}),
Make_Ok__Signed(48, "0x800000000000", {0, 0xffff8000}),
Make_Bad_Signed(48, "0x1000000000000"),
Make_Bad_Unsigned(48, "0x1000000000000"),
// Check 64-bit numbers
Make_Ok__Signed(64, "0x7fffffffffffffff", {0xffffffff, 0x7fffffff}),
Make_Ok__Signed(64, "0x8000000000000000", {0x00000000, 0x80000000}),
Make_Ok__Unsigned(64, "0x7fffffffffffffff", {0xffffffff, 0x7fffffff}),
Make_Ok__Unsigned(64, "0x8000000000000000", {0x00000000, 0x80000000}),
}),);
// clang-format on
TEST(OverflowIntegerParse, Decimal) {
std::string signed_input = "-18446744073709551616";
std::string expected_message0 =
"Invalid signed integer literal: " + signed_input;
EXPECT_THAT(failedEncode(Make_Bad_Signed(64, signed_input.c_str()),
libspirv::IdTypeClass::kScalarIntegerType),
Eq(expected_message0));
std::string unsigned_input = "18446744073709551616";
std::string expected_message1 =
"Invalid unsigned integer literal: " + unsigned_input;
EXPECT_THAT(failedEncode(Make_Bad_Unsigned(64, unsigned_input.c_str()),
libspirv::IdTypeClass::kScalarIntegerType),
Eq(expected_message1));
// TODO(dneto): When the given number doesn't have a leading sign,
// we say we're trying to parse an unsigned number, even when the caller
// asked for a signed number. This is kind of weird, but it's an
// artefact of how we do the parsing.
EXPECT_THAT(failedEncode(Make_Bad_Signed(64, unsigned_input.c_str()),
libspirv::IdTypeClass::kScalarIntegerType),
Eq(expected_message1));
}
TEST(OverflowIntegerParse, Hex) {
std::string input = "0x10000000000000000";
std::string expected_message = "Invalid unsigned integer literal: " + input;
EXPECT_THAT(failedEncode(Make_Bad_Signed(64, input.c_str()),
libspirv::IdTypeClass::kScalarIntegerType),
Eq(expected_message));
EXPECT_THAT(failedEncode(Make_Bad_Unsigned(64, input.c_str()),
libspirv::IdTypeClass::kScalarIntegerType),
Eq(expected_message));
}
} // anonymous namespace