|  | // 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. | 
|  |  | 
|  | // Assembler tests for instructions in the "Annotation" section of the | 
|  | // SPIR-V spec. | 
|  |  | 
|  | #include <sstream> | 
|  | #include <string> | 
|  | #include <tuple> | 
|  | #include <vector> | 
|  |  | 
|  | #include "gmock/gmock.h" | 
|  | #include "source/util/string_utils.h" | 
|  | #include "test/test_fixture.h" | 
|  | #include "test/unit_spirv.h" | 
|  |  | 
|  | namespace spvtools { | 
|  | namespace { | 
|  |  | 
|  | using spvtest::EnumCase; | 
|  | using spvtest::MakeInstruction; | 
|  | using utils::MakeVector; | 
|  | using spvtest::TextToBinaryTest; | 
|  | using ::testing::Combine; | 
|  | using ::testing::Eq; | 
|  | using ::testing::Values; | 
|  | using ::testing::ValuesIn; | 
|  |  | 
|  | // Test OpDecorate | 
|  |  | 
|  | using OpDecorateSimpleTest = | 
|  | spvtest::TextToBinaryTestBase<::testing::TestWithParam< | 
|  | std::tuple<spv_target_env, EnumCase<SpvDecoration>>>>; | 
|  |  | 
|  | TEST_P(OpDecorateSimpleTest, AnySimpleDecoration) { | 
|  | // This string should assemble, but should not validate. | 
|  | std::stringstream input; | 
|  | input << "OpDecorate %1 " << std::get<1>(GetParam()).name(); | 
|  | for (auto operand : std::get<1>(GetParam()).operands()) | 
|  | input << " " << operand; | 
|  | input << std::endl; | 
|  | EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())), | 
|  | Eq(MakeInstruction(SpvOpDecorate, | 
|  | {1, uint32_t(std::get<1>(GetParam()).value())}, | 
|  | std::get<1>(GetParam()).operands()))); | 
|  | // Also check disassembly. | 
|  | EXPECT_THAT( | 
|  | EncodeAndDecodeSuccessfully(input.str(), SPV_BINARY_TO_TEXT_OPTION_NONE, | 
|  | std::get<0>(GetParam())), | 
|  | Eq(input.str())); | 
|  | } | 
|  |  | 
|  | // Like above, but parameters to the decoration are IDs. | 
|  | using OpDecorateSimpleIdTest = | 
|  | spvtest::TextToBinaryTestBase<::testing::TestWithParam< | 
|  | std::tuple<spv_target_env, EnumCase<SpvDecoration>>>>; | 
|  |  | 
|  | TEST_P(OpDecorateSimpleIdTest, AnySimpleDecoration) { | 
|  | // This string should assemble, but should not validate. | 
|  | std::stringstream input; | 
|  | input << "OpDecorateId %1 " << std::get<1>(GetParam()).name(); | 
|  | for (auto operand : std::get<1>(GetParam()).operands()) | 
|  | input << " %" << operand; | 
|  | input << std::endl; | 
|  | EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())), | 
|  | Eq(MakeInstruction(SpvOpDecorateId, | 
|  | {1, uint32_t(std::get<1>(GetParam()).value())}, | 
|  | std::get<1>(GetParam()).operands()))); | 
|  | // Also check disassembly. | 
|  | EXPECT_THAT( | 
|  | EncodeAndDecodeSuccessfully(input.str(), SPV_BINARY_TO_TEXT_OPTION_NONE, | 
|  | std::get<0>(GetParam())), | 
|  | Eq(input.str())); | 
|  | } | 
|  |  | 
|  | #define CASE(NAME) SpvDecoration##NAME, #NAME | 
|  | INSTANTIATE_TEST_SUITE_P( | 
|  | TextToBinaryDecorateSimple, OpDecorateSimpleTest, | 
|  | Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), | 
|  | ValuesIn(std::vector<EnumCase<SpvDecoration>>{ | 
|  | // The operand literal values are arbitrarily chosen, | 
|  | // but there are the right number of them. | 
|  | {CASE(RelaxedPrecision), {}}, | 
|  | {CASE(SpecId), {100}}, | 
|  | {CASE(Block), {}}, | 
|  | {CASE(BufferBlock), {}}, | 
|  | {CASE(RowMajor), {}}, | 
|  | {CASE(ColMajor), {}}, | 
|  | {CASE(ArrayStride), {4}}, | 
|  | {CASE(MatrixStride), {16}}, | 
|  | {CASE(GLSLShared), {}}, | 
|  | {CASE(GLSLPacked), {}}, | 
|  | {CASE(CPacked), {}}, | 
|  | // Placeholder line for enum value 12 | 
|  | {CASE(NoPerspective), {}}, | 
|  | {CASE(Flat), {}}, | 
|  | {CASE(Patch), {}}, | 
|  | {CASE(Centroid), {}}, | 
|  | {CASE(Sample), {}}, | 
|  | {CASE(Invariant), {}}, | 
|  | {CASE(Restrict), {}}, | 
|  | {CASE(Aliased), {}}, | 
|  | {CASE(Volatile), {}}, | 
|  | {CASE(Constant), {}}, | 
|  | {CASE(Coherent), {}}, | 
|  | {CASE(NonWritable), {}}, | 
|  | {CASE(NonReadable), {}}, | 
|  | {CASE(Uniform), {}}, | 
|  | {CASE(SaturatedConversion), {}}, | 
|  | {CASE(Stream), {2}}, | 
|  | {CASE(Location), {6}}, | 
|  | {CASE(Component), {3}}, | 
|  | {CASE(Index), {14}}, | 
|  | {CASE(Binding), {19}}, | 
|  | {CASE(DescriptorSet), {7}}, | 
|  | {CASE(Offset), {12}}, | 
|  | {CASE(XfbBuffer), {1}}, | 
|  | {CASE(XfbStride), {8}}, | 
|  | {CASE(NoContraction), {}}, | 
|  | {CASE(InputAttachmentIndex), {102}}, | 
|  | {CASE(Alignment), {16}}, | 
|  | }))); | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleV11, OpDecorateSimpleTest, | 
|  | Combine(Values(SPV_ENV_UNIVERSAL_1_1), | 
|  | Values(EnumCase<SpvDecoration>{ | 
|  | CASE(MaxByteOffset), {128}}))); | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleV14, OpDecorateSimpleTest, | 
|  | Combine(Values(SPV_ENV_UNIVERSAL_1_4), | 
|  | ValuesIn(std::vector<EnumCase<SpvDecoration>>{ | 
|  | {CASE(Uniform), {}}, | 
|  | }))); | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleIdV14, | 
|  | OpDecorateSimpleIdTest, | 
|  | Combine(Values(SPV_ENV_UNIVERSAL_1_4), | 
|  | ValuesIn(std::vector<EnumCase<SpvDecoration>>{ | 
|  | // In 1.4, UniformId decoration takes a | 
|  | // scope Id. | 
|  | {CASE(UniformId), {1}}, | 
|  | }))); | 
|  | #undef CASE | 
|  |  | 
|  | TEST_F(OpDecorateSimpleTest, WrongDecoration) { | 
|  | EXPECT_THAT(CompileFailure("OpDecorate %1 xxyyzz"), | 
|  | Eq("Invalid decoration 'xxyyzz'.")); | 
|  | } | 
|  |  | 
|  | TEST_F(OpDecorateSimpleTest, ExtraOperandsOnDecorationExpectingNone) { | 
|  | EXPECT_THAT(CompileFailure("OpDecorate %1 RelaxedPrecision 99"), | 
|  | Eq("Expected <opcode> or <result-id> at the beginning of an " | 
|  | "instruction, found '99'.")); | 
|  | } | 
|  |  | 
|  | TEST_F(OpDecorateSimpleTest, ExtraOperandsOnDecorationExpectingOne) { | 
|  | EXPECT_THAT(CompileFailure("OpDecorate %1 SpecId 99 100"), | 
|  | Eq("Expected <opcode> or <result-id> at the beginning of an " | 
|  | "instruction, found '100'.")); | 
|  | } | 
|  |  | 
|  | TEST_F(OpDecorateSimpleTest, ExtraOperandsOnDecorationExpectingTwo) { | 
|  | EXPECT_THAT( | 
|  | CompileFailure("OpDecorate %1 LinkageAttributes \"abc\" Import 42"), | 
|  | Eq("Expected <opcode> or <result-id> at the beginning of an " | 
|  | "instruction, found '42'.")); | 
|  | } | 
|  |  | 
|  | // A single test case for an enum decoration. | 
|  | struct DecorateEnumCase { | 
|  | // Place the enum value first, so it's easier to read the binary dumps when | 
|  | // the test fails. | 
|  | uint32_t value;  // The value within the enum, e.g. Position | 
|  | std::string name; | 
|  | uint32_t enum_value;  // Which enum, e.g. BuiltIn | 
|  | std::string enum_name; | 
|  | }; | 
|  |  | 
|  | using OpDecorateEnumTest = | 
|  | spvtest::TextToBinaryTestBase<::testing::TestWithParam<DecorateEnumCase>>; | 
|  |  | 
|  | TEST_P(OpDecorateEnumTest, AnyEnumDecoration) { | 
|  | // This string should assemble, but should not validate. | 
|  | const std::string input = | 
|  | "OpDecorate %1 " + GetParam().enum_name + " " + GetParam().name; | 
|  | EXPECT_THAT(CompiledInstructions(input), | 
|  | Eq(MakeInstruction(SpvOpDecorate, {1, GetParam().enum_value, | 
|  | GetParam().value}))); | 
|  | } | 
|  |  | 
|  | // Test OpDecorate BuiltIn. | 
|  | // clang-format off | 
|  | #define CASE(NAME) \ | 
|  | { SpvBuiltIn##NAME, #NAME, SpvDecorationBuiltIn, "BuiltIn" } | 
|  | INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateBuiltIn, OpDecorateEnumTest, | 
|  | ::testing::ValuesIn(std::vector<DecorateEnumCase>{ | 
|  | CASE(Position), | 
|  | CASE(PointSize), | 
|  | CASE(ClipDistance), | 
|  | CASE(CullDistance), | 
|  | CASE(VertexId), | 
|  | CASE(InstanceId), | 
|  | CASE(PrimitiveId), | 
|  | CASE(InvocationId), | 
|  | CASE(Layer), | 
|  | CASE(ViewportIndex), | 
|  | CASE(TessLevelOuter), | 
|  | CASE(TessLevelInner), | 
|  | CASE(TessCoord), | 
|  | CASE(PatchVertices), | 
|  | CASE(FragCoord), | 
|  | CASE(PointCoord), | 
|  | CASE(FrontFacing), | 
|  | CASE(SampleId), | 
|  | CASE(SamplePosition), | 
|  | CASE(SampleMask), | 
|  | // Value 21 intentionally missing. | 
|  | CASE(FragDepth), | 
|  | CASE(HelperInvocation), | 
|  | CASE(NumWorkgroups), | 
|  | CASE(WorkgroupSize), | 
|  | CASE(WorkgroupId), | 
|  | CASE(LocalInvocationId), | 
|  | CASE(GlobalInvocationId), | 
|  | CASE(LocalInvocationIndex), | 
|  | CASE(WorkDim), | 
|  | CASE(GlobalSize), | 
|  | CASE(EnqueuedWorkgroupSize), | 
|  | CASE(GlobalOffset), | 
|  | CASE(GlobalLinearId), | 
|  | // Value 35 intentionally missing. | 
|  | CASE(SubgroupSize), | 
|  | CASE(SubgroupMaxSize), | 
|  | CASE(NumSubgroups), | 
|  | CASE(NumEnqueuedSubgroups), | 
|  | CASE(SubgroupId), | 
|  | CASE(SubgroupLocalInvocationId), | 
|  | CASE(VertexIndex), | 
|  | CASE(InstanceIndex), | 
|  | })); | 
|  | #undef CASE | 
|  | // clang-format on | 
|  |  | 
|  | TEST_F(OpDecorateEnumTest, WrongBuiltIn) { | 
|  | EXPECT_THAT(CompileFailure("OpDecorate %1 BuiltIn xxyyzz"), | 
|  | Eq("Invalid built-in 'xxyyzz'.")); | 
|  | } | 
|  |  | 
|  | // Test OpDecorate FuncParamAttr | 
|  | // clang-format off | 
|  | #define CASE(NAME) \ | 
|  | { SpvFunctionParameterAttribute##NAME, #NAME, SpvDecorationFuncParamAttr, "FuncParamAttr" } | 
|  | INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateFuncParamAttr, OpDecorateEnumTest, | 
|  | ::testing::ValuesIn(std::vector<DecorateEnumCase>{ | 
|  | CASE(Zext), | 
|  | CASE(Sext), | 
|  | CASE(ByVal), | 
|  | CASE(Sret), | 
|  | CASE(NoAlias), | 
|  | CASE(NoCapture), | 
|  | CASE(NoWrite), | 
|  | CASE(NoReadWrite), | 
|  | })); | 
|  | #undef CASE | 
|  | // clang-format on | 
|  |  | 
|  | TEST_F(OpDecorateEnumTest, WrongFuncParamAttr) { | 
|  | EXPECT_THAT(CompileFailure("OpDecorate %1 FuncParamAttr xxyyzz"), | 
|  | Eq("Invalid function parameter attribute 'xxyyzz'.")); | 
|  | } | 
|  |  | 
|  | // Test OpDecorate FPRoundingMode | 
|  | // clang-format off | 
|  | #define CASE(NAME) \ | 
|  | { SpvFPRoundingMode##NAME, #NAME, SpvDecorationFPRoundingMode, "FPRoundingMode" } | 
|  | INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateFPRoundingMode, OpDecorateEnumTest, | 
|  | ::testing::ValuesIn(std::vector<DecorateEnumCase>{ | 
|  | CASE(RTE), | 
|  | CASE(RTZ), | 
|  | CASE(RTP), | 
|  | CASE(RTN), | 
|  | })); | 
|  | #undef CASE | 
|  | // clang-format on | 
|  |  | 
|  | TEST_F(OpDecorateEnumTest, WrongFPRoundingMode) { | 
|  | EXPECT_THAT(CompileFailure("OpDecorate %1 FPRoundingMode xxyyzz"), | 
|  | Eq("Invalid floating-point rounding mode 'xxyyzz'.")); | 
|  | } | 
|  |  | 
|  | // Test OpDecorate FPFastMathMode. | 
|  | // These can by named enums for the single-bit masks.  However, we don't support | 
|  | // symbolic combinations of the masks.  Rather, they can use !<immediate> | 
|  | // syntax, e.g. !0x3 | 
|  |  | 
|  | // clang-format off | 
|  | #define CASE(ENUM,NAME) \ | 
|  | { SpvFPFastMathMode##ENUM, #NAME, SpvDecorationFPFastMathMode, "FPFastMathMode" } | 
|  | INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateFPFastMathMode, OpDecorateEnumTest, | 
|  | ::testing::ValuesIn(std::vector<DecorateEnumCase>{ | 
|  | CASE(MaskNone, None), | 
|  | CASE(NotNaNMask, NotNaN), | 
|  | CASE(NotInfMask, NotInf), | 
|  | CASE(NSZMask, NSZ), | 
|  | CASE(AllowRecipMask, AllowRecip), | 
|  | CASE(FastMask, Fast), | 
|  | })); | 
|  | #undef CASE | 
|  | // clang-format on | 
|  |  | 
|  | TEST_F(OpDecorateEnumTest, CombinedFPFastMathMask) { | 
|  | // Sample a single combination.  This ensures we've integrated | 
|  | // the instruction parsing logic with spvTextParseMask. | 
|  | const std::string input = "OpDecorate %1 FPFastMathMode NotNaN|NotInf|NSZ"; | 
|  | const uint32_t expected_enum = SpvDecorationFPFastMathMode; | 
|  | const uint32_t expected_mask = SpvFPFastMathModeNotNaNMask | | 
|  | SpvFPFastMathModeNotInfMask | | 
|  | SpvFPFastMathModeNSZMask; | 
|  | EXPECT_THAT( | 
|  | CompiledInstructions(input), | 
|  | Eq(MakeInstruction(SpvOpDecorate, {1, expected_enum, expected_mask}))); | 
|  | } | 
|  |  | 
|  | TEST_F(OpDecorateEnumTest, WrongFPFastMathMode) { | 
|  | EXPECT_THAT( | 
|  | CompileFailure("OpDecorate %1 FPFastMathMode NotNaN|xxyyzz"), | 
|  | Eq("Invalid floating-point fast math mode operand 'NotNaN|xxyyzz'.")); | 
|  | } | 
|  |  | 
|  | // Test OpDecorate Linkage | 
|  |  | 
|  | // A single test case for a linkage | 
|  | struct DecorateLinkageCase { | 
|  | uint32_t linkage_type_value; | 
|  | std::string linkage_type_name; | 
|  | std::string external_name; | 
|  | }; | 
|  |  | 
|  | using OpDecorateLinkageTest = spvtest::TextToBinaryTestBase< | 
|  | ::testing::TestWithParam<DecorateLinkageCase>>; | 
|  |  | 
|  | TEST_P(OpDecorateLinkageTest, AnyLinkageDecoration) { | 
|  | // This string should assemble, but should not validate. | 
|  | const std::string input = "OpDecorate %1 LinkageAttributes \"" + | 
|  | GetParam().external_name + "\" " + | 
|  | GetParam().linkage_type_name; | 
|  | std::vector<uint32_t> expected_operands{1, SpvDecorationLinkageAttributes}; | 
|  | std::vector<uint32_t> encoded_external_name = | 
|  | MakeVector(GetParam().external_name); | 
|  | expected_operands.insert(expected_operands.end(), | 
|  | encoded_external_name.begin(), | 
|  | encoded_external_name.end()); | 
|  | expected_operands.push_back(GetParam().linkage_type_value); | 
|  | EXPECT_THAT(CompiledInstructions(input), | 
|  | Eq(MakeInstruction(SpvOpDecorate, expected_operands))); | 
|  | } | 
|  |  | 
|  | // clang-format off | 
|  | #define CASE(ENUM) SpvLinkageType##ENUM, #ENUM | 
|  | INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateLinkage, OpDecorateLinkageTest, | 
|  | ::testing::ValuesIn(std::vector<DecorateLinkageCase>{ | 
|  | { CASE(Import), "a" }, | 
|  | { CASE(Export), "foo" }, | 
|  | { CASE(Import), "some kind of long name with spaces etc." }, | 
|  | // TODO(dneto): utf-8, escaping, quoting cases. | 
|  | })); | 
|  | #undef CASE | 
|  | // clang-format on | 
|  |  | 
|  | TEST_F(OpDecorateLinkageTest, WrongType) { | 
|  | EXPECT_THAT(CompileFailure("OpDecorate %1 LinkageAttributes \"foo\" xxyyzz"), | 
|  | Eq("Invalid linkage type 'xxyyzz'.")); | 
|  | } | 
|  |  | 
|  | // Test OpGroupMemberDecorate | 
|  |  | 
|  | TEST_F(TextToBinaryTest, GroupMemberDecorateGoodOneTarget) { | 
|  | EXPECT_THAT(CompiledInstructions("OpGroupMemberDecorate %group %id0 42"), | 
|  | Eq(MakeInstruction(SpvOpGroupMemberDecorate, {1, 2, 42}))); | 
|  | } | 
|  |  | 
|  | TEST_F(TextToBinaryTest, GroupMemberDecorateGoodTwoTargets) { | 
|  | EXPECT_THAT( | 
|  | CompiledInstructions("OpGroupMemberDecorate %group %id0 96 %id1 42"), | 
|  | Eq(MakeInstruction(SpvOpGroupMemberDecorate, {1, 2, 96, 3, 42}))); | 
|  | } | 
|  |  | 
|  | TEST_F(TextToBinaryTest, GroupMemberDecorateMissingGroupId) { | 
|  | EXPECT_THAT(CompileFailure("OpGroupMemberDecorate"), | 
|  | Eq("Expected operand, found end of stream.")); | 
|  | } | 
|  |  | 
|  | TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidGroupId) { | 
|  | EXPECT_THAT(CompileFailure("OpGroupMemberDecorate 16"), | 
|  | Eq("Expected id to start with %.")); | 
|  | } | 
|  |  | 
|  | TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidTargetId) { | 
|  | EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group 12"), | 
|  | Eq("Expected id to start with %.")); | 
|  | } | 
|  |  | 
|  | TEST_F(TextToBinaryTest, GroupMemberDecorateMissingTargetMemberNumber) { | 
|  | EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0"), | 
|  | Eq("Expected operand, found end of stream.")); | 
|  | } | 
|  |  | 
|  | TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidTargetMemberNumber) { | 
|  | EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0 %id1"), | 
|  | Eq("Invalid unsigned integer literal: %id1")); | 
|  | } | 
|  |  | 
|  | TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidSecondTargetId) { | 
|  | EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id1 42 12"), | 
|  | Eq("Expected id to start with %.")); | 
|  | } | 
|  |  | 
|  | TEST_F(TextToBinaryTest, GroupMemberDecorateMissingSecondTargetMemberNumber) { | 
|  | EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0 42 %id1"), | 
|  | Eq("Expected operand, found end of stream.")); | 
|  | } | 
|  |  | 
|  | TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidSecondTargetMemberNumber) { | 
|  | EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0 42 %id1 %id2"), | 
|  | Eq("Invalid unsigned integer literal: %id2")); | 
|  | } | 
|  |  | 
|  | // Test OpMemberDecorate | 
|  |  | 
|  | using OpMemberDecorateSimpleTest = | 
|  | spvtest::TextToBinaryTestBase<::testing::TestWithParam< | 
|  | std::tuple<spv_target_env, EnumCase<SpvDecoration>>>>; | 
|  |  | 
|  | TEST_P(OpMemberDecorateSimpleTest, AnySimpleDecoration) { | 
|  | // This string should assemble, but should not validate. | 
|  | std::stringstream input; | 
|  | input << "OpMemberDecorate %1 42 " << std::get<1>(GetParam()).name(); | 
|  | for (auto operand : std::get<1>(GetParam()).operands()) | 
|  | input << " " << operand; | 
|  | input << std::endl; | 
|  | EXPECT_THAT( | 
|  | CompiledInstructions(input.str(), std::get<0>(GetParam())), | 
|  | Eq(MakeInstruction(SpvOpMemberDecorate, | 
|  | {1, 42, uint32_t(std::get<1>(GetParam()).value())}, | 
|  | std::get<1>(GetParam()).operands()))); | 
|  | // Also check disassembly. | 
|  | EXPECT_THAT( | 
|  | EncodeAndDecodeSuccessfully(input.str(), SPV_BINARY_TO_TEXT_OPTION_NONE, | 
|  | std::get<0>(GetParam())), | 
|  | Eq(input.str())); | 
|  | } | 
|  |  | 
|  | #define CASE(NAME) SpvDecoration##NAME, #NAME | 
|  | INSTANTIATE_TEST_SUITE_P( | 
|  | TextToBinaryDecorateSimple, OpMemberDecorateSimpleTest, | 
|  | Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), | 
|  | ValuesIn(std::vector<EnumCase<SpvDecoration>>{ | 
|  | // The operand literal values are arbitrarily chosen, | 
|  | // but there are the right number of them. | 
|  | {CASE(RelaxedPrecision), {}}, | 
|  | {CASE(SpecId), {100}}, | 
|  | {CASE(Block), {}}, | 
|  | {CASE(BufferBlock), {}}, | 
|  | {CASE(RowMajor), {}}, | 
|  | {CASE(ColMajor), {}}, | 
|  | {CASE(ArrayStride), {4}}, | 
|  | {CASE(MatrixStride), {16}}, | 
|  | {CASE(GLSLShared), {}}, | 
|  | {CASE(GLSLPacked), {}}, | 
|  | {CASE(CPacked), {}}, | 
|  | // Placeholder line for enum value 12 | 
|  | {CASE(NoPerspective), {}}, | 
|  | {CASE(Flat), {}}, | 
|  | {CASE(Patch), {}}, | 
|  | {CASE(Centroid), {}}, | 
|  | {CASE(Sample), {}}, | 
|  | {CASE(Invariant), {}}, | 
|  | {CASE(Restrict), {}}, | 
|  | {CASE(Aliased), {}}, | 
|  | {CASE(Volatile), {}}, | 
|  | {CASE(Constant), {}}, | 
|  | {CASE(Coherent), {}}, | 
|  | {CASE(NonWritable), {}}, | 
|  | {CASE(NonReadable), {}}, | 
|  | {CASE(Uniform), {}}, | 
|  | {CASE(SaturatedConversion), {}}, | 
|  | {CASE(Stream), {2}}, | 
|  | {CASE(Location), {6}}, | 
|  | {CASE(Component), {3}}, | 
|  | {CASE(Index), {14}}, | 
|  | {CASE(Binding), {19}}, | 
|  | {CASE(DescriptorSet), {7}}, | 
|  | {CASE(Offset), {12}}, | 
|  | {CASE(XfbBuffer), {1}}, | 
|  | {CASE(XfbStride), {8}}, | 
|  | {CASE(NoContraction), {}}, | 
|  | {CASE(InputAttachmentIndex), {102}}, | 
|  | {CASE(Alignment), {16}}, | 
|  | }))); | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P( | 
|  | TextToBinaryDecorateSimpleV11, OpMemberDecorateSimpleTest, | 
|  | Combine(Values(SPV_ENV_UNIVERSAL_1_1), | 
|  | Values(EnumCase<SpvDecoration>{CASE(MaxByteOffset), {128}}))); | 
|  | #undef CASE | 
|  |  | 
|  | TEST_F(OpMemberDecorateSimpleTest, WrongDecoration) { | 
|  | EXPECT_THAT(CompileFailure("OpMemberDecorate %1 9 xxyyzz"), | 
|  | Eq("Invalid decoration 'xxyyzz'.")); | 
|  | } | 
|  |  | 
|  | TEST_F(OpMemberDecorateSimpleTest, ExtraOperandsOnDecorationExpectingNone) { | 
|  | EXPECT_THAT(CompileFailure("OpMemberDecorate %1 12 RelaxedPrecision 99"), | 
|  | Eq("Expected <opcode> or <result-id> at the beginning of an " | 
|  | "instruction, found '99'.")); | 
|  | } | 
|  |  | 
|  | TEST_F(OpMemberDecorateSimpleTest, ExtraOperandsOnDecorationExpectingOne) { | 
|  | EXPECT_THAT(CompileFailure("OpMemberDecorate %1 0 SpecId 99 100"), | 
|  | Eq("Expected <opcode> or <result-id> at the beginning of an " | 
|  | "instruction, found '100'.")); | 
|  | } | 
|  |  | 
|  | TEST_F(OpMemberDecorateSimpleTest, ExtraOperandsOnDecorationExpectingTwo) { | 
|  | EXPECT_THAT(CompileFailure( | 
|  | "OpMemberDecorate %1 1 LinkageAttributes \"abc\" Import 42"), | 
|  | Eq("Expected <opcode> or <result-id> at the beginning of an " | 
|  | "instruction, found '42'.")); | 
|  | } | 
|  |  | 
|  | // TODO(dneto): OpMemberDecorate cases for decorations with parameters which | 
|  | // are: not just lists of literal numbers. | 
|  |  | 
|  | // TODO(dneto): OpDecorationGroup | 
|  | // TODO(dneto): OpGroupDecorate | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace spvtools |