| // 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 |