1// Copyright (c) 2016 Google Inc. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15#include "gtest/gtest.h" 16#include "source/table.h" 17#include "spirv-tools/libspirv.h" 18 19namespace spvtools { 20namespace { 21 22// TODO(antiagainst): Use public C API for setting the consumer once exists. 23#ifndef SPIRV_TOOLS_SHAREDLIB 24void SetContextMessageConsumer(spv_context context, MessageConsumer consumer) { 25 spvtools::SetContextMessageConsumer(context, consumer); 26} 27#else 28void SetContextMessageConsumer(spv_context, MessageConsumer) {} 29#endif 30 31// The default consumer is a null std::function. 32TEST(CInterface, DefaultConsumerNullDiagnosticForValidInput) { 33 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); 34 const char input_text[] = 35 "OpCapability Shader\n" 36 "OpCapability Linkage\n" 37 "OpMemoryModel Logical GLSL450"; 38 39 spv_binary binary = nullptr; 40 EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, 41 sizeof(input_text), &binary, nullptr)); 42 43 { 44 // Sadly the compiler don't allow me to feed binary directly to 45 // spvValidate(). 46 spv_const_binary_t b{binary->code, binary->wordCount}; 47 EXPECT_EQ(SPV_SUCCESS, spvValidate(context, &b, nullptr)); 48 } 49 50 spv_text text = nullptr; 51 EXPECT_EQ(SPV_SUCCESS, spvBinaryToText(context, binary->code, 52 binary->wordCount, 0, &text, nullptr)); 53 54 spvTextDestroy(text); 55 spvBinaryDestroy(binary); 56 spvContextDestroy(context); 57} 58 59// The default consumer is a null std::function. 60TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidAssembling) { 61 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); 62 const char input_text[] = "%1 = OpName"; 63 64 spv_binary binary = nullptr; 65 EXPECT_EQ(SPV_ERROR_INVALID_TEXT, 66 spvTextToBinary(context, input_text, sizeof(input_text), &binary, 67 nullptr)); 68 spvBinaryDestroy(binary); 69 spvContextDestroy(context); 70} 71 72// The default consumer is a null std::function. 73TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidDiassembling) { 74 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); 75 const char input_text[] = "OpNop"; 76 77 spv_binary binary = nullptr; 78 ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, 79 sizeof(input_text), &binary, nullptr)); 80 // Change OpNop to an invalid (wordcount|opcode) word. 81 binary->code[binary->wordCount - 1] = 0xffffffff; 82 83 spv_text text = nullptr; 84 EXPECT_EQ(SPV_ERROR_INVALID_BINARY, 85 spvBinaryToText(context, binary->code, binary->wordCount, 0, &text, 86 nullptr)); 87 88 spvTextDestroy(text); 89 spvBinaryDestroy(binary); 90 spvContextDestroy(context); 91} 92 93// The default consumer is a null std::function. 94TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidValidating) { 95 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); 96 const char input_text[] = "OpNop"; 97 98 spv_binary binary = nullptr; 99 ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, 100 sizeof(input_text), &binary, nullptr)); 101 102 spv_const_binary_t b{binary->code, binary->wordCount}; 103 EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, nullptr)); 104 105 spvBinaryDestroy(binary); 106 spvContextDestroy(context); 107} 108 109TEST(CInterface, SpecifyConsumerNullDiagnosticForAssembling) { 110 const char input_text[] = " OpName\n"; 111 112 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); 113 int invocation = 0; 114 SetContextMessageConsumer( 115 context, 116 [&invocation](spv_message_level_t level, const char* source, 117 const spv_position_t& position, const char* message) { 118 ++invocation; 119 EXPECT_EQ(SPV_MSG_ERROR, level); 120 // The error happens at scanning the beginning of second line. 121 EXPECT_STREQ("input", source); 122 EXPECT_EQ(1u, position.line); 123 EXPECT_EQ(0u, position.column); 124 EXPECT_EQ(12u, position.index); 125 EXPECT_STREQ( 126 "Expected operand for OpName instruction, but found the end of the " 127 "stream.", 128 message); 129 }); 130 131 spv_binary binary = nullptr; 132 EXPECT_EQ(SPV_ERROR_INVALID_TEXT, 133 spvTextToBinary(context, input_text, sizeof(input_text), &binary, 134 nullptr)); 135#ifndef SPIRV_TOOLS_SHAREDLIB 136 EXPECT_EQ(1, invocation); 137#endif 138 spvBinaryDestroy(binary); 139 spvContextDestroy(context); 140} 141 142TEST(CInterface, SpecifyConsumerNullDiagnosticForDisassembling) { 143 const char input_text[] = "OpNop"; 144 145 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); 146 int invocation = 0; 147 SetContextMessageConsumer( 148 context, 149 [&invocation](spv_message_level_t level, const char* source, 150 const spv_position_t& position, const char* message) { 151 ++invocation; 152 EXPECT_EQ(SPV_MSG_ERROR, level); 153 EXPECT_STREQ("input", source); 154 EXPECT_EQ(0u, position.line); 155 EXPECT_EQ(0u, position.column); 156 EXPECT_EQ(1u, position.index); 157 EXPECT_STREQ("Invalid opcode: 65535", message); 158 }); 159 160 spv_binary binary = nullptr; 161 ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, 162 sizeof(input_text), &binary, nullptr)); 163 // Change OpNop to an invalid (wordcount|opcode) word. 164 binary->code[binary->wordCount - 1] = 0xffffffff; 165 166 spv_text text = nullptr; 167 EXPECT_EQ(SPV_ERROR_INVALID_BINARY, 168 spvBinaryToText(context, binary->code, binary->wordCount, 0, &text, 169 nullptr)); 170#ifndef SPIRV_TOOLS_SHAREDLIB 171 EXPECT_EQ(1, invocation); 172#endif 173 174 spvTextDestroy(text); 175 spvBinaryDestroy(binary); 176 spvContextDestroy(context); 177} 178 179TEST(CInterface, SpecifyConsumerNullDiagnosticForValidating) { 180 const char input_text[] = "OpNop"; 181 182 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); 183 int invocation = 0; 184 SetContextMessageConsumer( 185 context, 186 [&invocation](spv_message_level_t level, const char* source, 187 const spv_position_t& position, const char* message) { 188 ++invocation; 189 EXPECT_EQ(SPV_MSG_ERROR, level); 190 EXPECT_STREQ("input", source); 191 EXPECT_EQ(0u, position.line); 192 EXPECT_EQ(0u, position.column); 193 // TODO(antiagainst): what validation reports is not a word offset here. 194 // It is inconsistent with diassembler. Should be fixed. 195 EXPECT_EQ(1u, position.index); 196 EXPECT_STREQ( 197 "Nop cannot appear before the memory model instruction\n" 198 " OpNop\n", 199 message); 200 }); 201 202 spv_binary binary = nullptr; 203 ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, 204 sizeof(input_text), &binary, nullptr)); 205 206 spv_const_binary_t b{binary->code, binary->wordCount}; 207 EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, nullptr)); 208#ifndef SPIRV_TOOLS_SHAREDLIB 209 EXPECT_EQ(1, invocation); 210#endif 211 212 spvBinaryDestroy(binary); 213 spvContextDestroy(context); 214} 215 216// When having both a consumer and an diagnostic object, the diagnostic object 217// should take priority. 218TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForAssembling) { 219 const char input_text[] = " OpName"; 220 221 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); 222 int invocation = 0; 223 SetContextMessageConsumer( 224 context, 225 [&invocation](spv_message_level_t, const char*, const spv_position_t&, 226 const char*) { ++invocation; }); 227 228 spv_binary binary = nullptr; 229 spv_diagnostic diagnostic = nullptr; 230 EXPECT_EQ(SPV_ERROR_INVALID_TEXT, 231 spvTextToBinary(context, input_text, sizeof(input_text), &binary, 232 &diagnostic)); 233 EXPECT_EQ(0, invocation); // Consumer should not be invoked at all. 234 EXPECT_STREQ( 235 "Expected operand for OpName instruction, but found the end of the " 236 "stream.", 237 diagnostic->error); 238 239 spvDiagnosticDestroy(diagnostic); 240 spvBinaryDestroy(binary); 241 spvContextDestroy(context); 242} 243 244TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForDisassembling) { 245 const char input_text[] = "OpNop"; 246 247 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); 248 int invocation = 0; 249 SetContextMessageConsumer( 250 context, 251 [&invocation](spv_message_level_t, const char*, const spv_position_t&, 252 const char*) { ++invocation; }); 253 254 spv_binary binary = nullptr; 255 ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, 256 sizeof(input_text), &binary, nullptr)); 257 // Change OpNop to an invalid (wordcount|opcode) word. 258 binary->code[binary->wordCount - 1] = 0xffffffff; 259 260 spv_diagnostic diagnostic = nullptr; 261 spv_text text = nullptr; 262 EXPECT_EQ(SPV_ERROR_INVALID_BINARY, 263 spvBinaryToText(context, binary->code, binary->wordCount, 0, &text, 264 &diagnostic)); 265 266 EXPECT_EQ(0, invocation); // Consumer should not be invoked at all. 267 EXPECT_STREQ("Invalid opcode: 65535", diagnostic->error); 268 269 spvTextDestroy(text); 270 spvDiagnosticDestroy(diagnostic); 271 spvBinaryDestroy(binary); 272 spvContextDestroy(context); 273} 274 275TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForValidating) { 276 const char input_text[] = "OpNop"; 277 278 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); 279 int invocation = 0; 280 SetContextMessageConsumer( 281 context, 282 [&invocation](spv_message_level_t, const char*, const spv_position_t&, 283 const char*) { ++invocation; }); 284 285 spv_binary binary = nullptr; 286 ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, 287 sizeof(input_text), &binary, nullptr)); 288 289 spv_diagnostic diagnostic = nullptr; 290 spv_const_binary_t b{binary->code, binary->wordCount}; 291 EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, &diagnostic)); 292 293 EXPECT_EQ(0, invocation); // Consumer should not be invoked at all. 294 EXPECT_STREQ( 295 "Nop cannot appear before the memory model instruction\n" 296 " OpNop\n", 297 diagnostic->error); 298 299 spvDiagnosticDestroy(diagnostic); 300 spvBinaryDestroy(binary); 301 spvContextDestroy(context); 302} 303 304} // namespace 305} // namespace spvtools 306