mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-27 22:00:07 +00:00
8bd75d650e
- Removed dead configuration in CMakeLists.txt. - Used target_compile_options() instead of CMAKE_{C|CXX}_FLAGS. - Turned on warnings on tests. - Fixed various warnings for comparing signed with unsigned values. - Removed dead code exposed by compiler warnings.
398 lines
15 KiB
C++
398 lines
15 KiB
C++
// Copyright (c) 2015 The Khronos Group Inc.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and/or associated documentation files (the
|
|
// "Materials"), to deal in the Materials without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Materials, and to
|
|
// permit persons to whom the Materials are furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Materials.
|
|
//
|
|
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
|
|
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
|
|
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
|
|
// https://www.khronos.org/registry/
|
|
//
|
|
// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
|
|
|
#include "UnitSPIRV.h"
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "TestFixture.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;
|
|
spv_diagnostic diagnostic;
|
|
libspirv::IdType expected_type{test.bitwidth, test.is_signed, type};
|
|
EXPECT_EQ(SPV_SUCCESS,
|
|
libspirv::AssemblyContext(nullptr, &diagnostic)
|
|
.binaryEncodeNumericLiteral(test.text, SPV_ERROR_INVALID_TEXT,
|
|
expected_type, &inst))
|
|
<< diagnostic->error;
|
|
return inst.words;
|
|
}
|
|
|
|
std::string failedEncode(const TextLiteralCase& test,
|
|
libspirv::IdTypeClass type) {
|
|
spv_instruction_t inst;
|
|
spv_diagnostic diagnostic;
|
|
libspirv::IdType expected_type{test.bitwidth, test.is_signed, type};
|
|
EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
|
|
libspirv::AssemblyContext(nullptr, &diagnostic)
|
|
.binaryEncodeNumericLiteral(test.text, SPV_ERROR_INVALID_TEXT,
|
|
expected_type, &inst));
|
|
std::string ret_val;
|
|
if (diagnostic) {
|
|
ret_val = diagnostic->error;
|
|
spvDiagnosticDestroy(diagnostic);
|
|
}
|
|
return ret_val;
|
|
}
|
|
|
|
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
|
|
|
|
// 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
|