| // Copyright (c) 2016 Google 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 "gtest/gtest.h" |
| #include "source/table.h" |
| #include "spirv-tools/libspirv.h" |
| |
| namespace spvtools { |
| namespace { |
| |
| // TODO(antiagainst): Use public C API for setting the consumer once exists. |
| #ifndef SPIRV_TOOLS_SHAREDLIB |
| void SetContextMessageConsumer(spv_context context, MessageConsumer consumer) { |
| spvtools::SetContextMessageConsumer(context, consumer); |
| } |
| #else |
| void SetContextMessageConsumer(spv_context, MessageConsumer) {} |
| #endif |
| |
| // The default consumer is a null std::function. |
| TEST(CInterface, DefaultConsumerNullDiagnosticForValidInput) { |
| auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); |
| const char input_text[] = |
| "OpCapability Shader\n" |
| "OpCapability Linkage\n" |
| "OpMemoryModel Logical GLSL450"; |
| |
| spv_binary binary = nullptr; |
| EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, |
| sizeof(input_text), &binary, nullptr)); |
| |
| { |
| // Sadly the compiler don't allow me to feed binary directly to |
| // spvValidate(). |
| spv_const_binary_t b{binary->code, binary->wordCount}; |
| EXPECT_EQ(SPV_SUCCESS, spvValidate(context, &b, nullptr)); |
| } |
| |
| spv_text text = nullptr; |
| EXPECT_EQ(SPV_SUCCESS, spvBinaryToText(context, binary->code, |
| binary->wordCount, 0, &text, nullptr)); |
| |
| spvTextDestroy(text); |
| spvBinaryDestroy(binary); |
| spvContextDestroy(context); |
| } |
| |
| // The default consumer is a null std::function. |
| TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidAssembling) { |
| auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); |
| const char input_text[] = "%1 = OpName"; |
| |
| spv_binary binary = nullptr; |
| EXPECT_EQ(SPV_ERROR_INVALID_TEXT, |
| spvTextToBinary(context, input_text, sizeof(input_text), &binary, |
| nullptr)); |
| spvBinaryDestroy(binary); |
| spvContextDestroy(context); |
| } |
| |
| // The default consumer is a null std::function. |
| TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidDiassembling) { |
| auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); |
| const char input_text[] = "OpNop"; |
| |
| spv_binary binary = nullptr; |
| ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, |
| sizeof(input_text), &binary, nullptr)); |
| // Change OpNop to an invalid (wordcount|opcode) word. |
| binary->code[binary->wordCount - 1] = 0xffffffff; |
| |
| spv_text text = nullptr; |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, |
| spvBinaryToText(context, binary->code, binary->wordCount, 0, &text, |
| nullptr)); |
| |
| spvTextDestroy(text); |
| spvBinaryDestroy(binary); |
| spvContextDestroy(context); |
| } |
| |
| // The default consumer is a null std::function. |
| TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidValidating) { |
| auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); |
| const char input_text[] = "OpNop"; |
| |
| spv_binary binary = nullptr; |
| ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, |
| sizeof(input_text), &binary, nullptr)); |
| |
| spv_const_binary_t b{binary->code, binary->wordCount}; |
| EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, nullptr)); |
| |
| spvBinaryDestroy(binary); |
| spvContextDestroy(context); |
| } |
| |
| TEST(CInterface, SpecifyConsumerNullDiagnosticForAssembling) { |
| const char input_text[] = " OpName\n"; |
| |
| auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); |
| int invocation = 0; |
| SetContextMessageConsumer( |
| context, |
| [&invocation](spv_message_level_t level, const char* source, |
| const spv_position_t& position, const char* message) { |
| ++invocation; |
| EXPECT_EQ(SPV_MSG_ERROR, level); |
| // The error happens at scanning the beginning of second line. |
| EXPECT_STREQ("input", source); |
| EXPECT_EQ(1u, position.line); |
| EXPECT_EQ(0u, position.column); |
| EXPECT_EQ(12u, position.index); |
| EXPECT_STREQ( |
| "Expected operand for OpName instruction, but found the end of the " |
| "stream.", |
| message); |
| }); |
| |
| spv_binary binary = nullptr; |
| EXPECT_EQ(SPV_ERROR_INVALID_TEXT, |
| spvTextToBinary(context, input_text, sizeof(input_text), &binary, |
| nullptr)); |
| #ifndef SPIRV_TOOLS_SHAREDLIB |
| EXPECT_EQ(1, invocation); |
| #endif |
| spvBinaryDestroy(binary); |
| spvContextDestroy(context); |
| } |
| |
| TEST(CInterface, SpecifyConsumerNullDiagnosticForDisassembling) { |
| const char input_text[] = "OpNop"; |
| |
| auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); |
| int invocation = 0; |
| SetContextMessageConsumer( |
| context, |
| [&invocation](spv_message_level_t level, const char* source, |
| const spv_position_t& position, const char* message) { |
| ++invocation; |
| EXPECT_EQ(SPV_MSG_ERROR, level); |
| EXPECT_STREQ("input", source); |
| EXPECT_EQ(0u, position.line); |
| EXPECT_EQ(0u, position.column); |
| EXPECT_EQ(1u, position.index); |
| EXPECT_STREQ("Invalid opcode: 65535", message); |
| }); |
| |
| spv_binary binary = nullptr; |
| ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, |
| sizeof(input_text), &binary, nullptr)); |
| // Change OpNop to an invalid (wordcount|opcode) word. |
| binary->code[binary->wordCount - 1] = 0xffffffff; |
| |
| spv_text text = nullptr; |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, |
| spvBinaryToText(context, binary->code, binary->wordCount, 0, &text, |
| nullptr)); |
| #ifndef SPIRV_TOOLS_SHAREDLIB |
| EXPECT_EQ(1, invocation); |
| #endif |
| |
| spvTextDestroy(text); |
| spvBinaryDestroy(binary); |
| spvContextDestroy(context); |
| } |
| |
| TEST(CInterface, SpecifyConsumerNullDiagnosticForValidating) { |
| const char input_text[] = "OpNop"; |
| |
| auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); |
| int invocation = 0; |
| SetContextMessageConsumer( |
| context, |
| [&invocation](spv_message_level_t level, const char* source, |
| const spv_position_t& position, const char* message) { |
| ++invocation; |
| EXPECT_EQ(SPV_MSG_ERROR, level); |
| EXPECT_STREQ("input", source); |
| EXPECT_EQ(0u, position.line); |
| EXPECT_EQ(0u, position.column); |
| // TODO(antiagainst): what validation reports is not a word offset here. |
| // It is inconsistent with diassembler. Should be fixed. |
| EXPECT_EQ(1u, position.index); |
| EXPECT_STREQ( |
| "Nop cannot appear before the memory model instruction\n" |
| " OpNop\n", |
| message); |
| }); |
| |
| spv_binary binary = nullptr; |
| ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, |
| sizeof(input_text), &binary, nullptr)); |
| |
| spv_const_binary_t b{binary->code, binary->wordCount}; |
| EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, nullptr)); |
| #ifndef SPIRV_TOOLS_SHAREDLIB |
| EXPECT_EQ(1, invocation); |
| #endif |
| |
| spvBinaryDestroy(binary); |
| spvContextDestroy(context); |
| } |
| |
| // When having both a consumer and an diagnostic object, the diagnostic object |
| // should take priority. |
| TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForAssembling) { |
| const char input_text[] = " OpName"; |
| |
| auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); |
| int invocation = 0; |
| SetContextMessageConsumer( |
| context, |
| [&invocation](spv_message_level_t, const char*, const spv_position_t&, |
| const char*) { ++invocation; }); |
| |
| spv_binary binary = nullptr; |
| spv_diagnostic diagnostic = nullptr; |
| EXPECT_EQ(SPV_ERROR_INVALID_TEXT, |
| spvTextToBinary(context, input_text, sizeof(input_text), &binary, |
| &diagnostic)); |
| EXPECT_EQ(0, invocation); // Consumer should not be invoked at all. |
| EXPECT_STREQ( |
| "Expected operand for OpName instruction, but found the end of the " |
| "stream.", |
| diagnostic->error); |
| |
| spvDiagnosticDestroy(diagnostic); |
| spvBinaryDestroy(binary); |
| spvContextDestroy(context); |
| } |
| |
| TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForDisassembling) { |
| const char input_text[] = "OpNop"; |
| |
| auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); |
| int invocation = 0; |
| SetContextMessageConsumer( |
| context, |
| [&invocation](spv_message_level_t, const char*, const spv_position_t&, |
| const char*) { ++invocation; }); |
| |
| spv_binary binary = nullptr; |
| ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, |
| sizeof(input_text), &binary, nullptr)); |
| // Change OpNop to an invalid (wordcount|opcode) word. |
| binary->code[binary->wordCount - 1] = 0xffffffff; |
| |
| spv_diagnostic diagnostic = nullptr; |
| spv_text text = nullptr; |
| EXPECT_EQ(SPV_ERROR_INVALID_BINARY, |
| spvBinaryToText(context, binary->code, binary->wordCount, 0, &text, |
| &diagnostic)); |
| |
| EXPECT_EQ(0, invocation); // Consumer should not be invoked at all. |
| EXPECT_STREQ("Invalid opcode: 65535", diagnostic->error); |
| |
| spvTextDestroy(text); |
| spvDiagnosticDestroy(diagnostic); |
| spvBinaryDestroy(binary); |
| spvContextDestroy(context); |
| } |
| |
| TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForValidating) { |
| const char input_text[] = "OpNop"; |
| |
| auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); |
| int invocation = 0; |
| SetContextMessageConsumer( |
| context, |
| [&invocation](spv_message_level_t, const char*, const spv_position_t&, |
| const char*) { ++invocation; }); |
| |
| spv_binary binary = nullptr; |
| ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, |
| sizeof(input_text), &binary, nullptr)); |
| |
| spv_diagnostic diagnostic = nullptr; |
| spv_const_binary_t b{binary->code, binary->wordCount}; |
| EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, &diagnostic)); |
| |
| EXPECT_EQ(0, invocation); // Consumer should not be invoked at all. |
| EXPECT_STREQ( |
| "Nop cannot appear before the memory model instruction\n" |
| " OpNop\n", |
| diagnostic->error); |
| |
| spvDiagnosticDestroy(diagnostic); |
| spvBinaryDestroy(binary); |
| spvContextDestroy(context); |
| } |
| |
| } // namespace |
| } // namespace spvtools |