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
19 namespace spvtools {
20 namespace {
21
22 // TODO(antiagainst): Use public C API for setting the consumer once exists.
23 #ifndef SPIRV_TOOLS_SHAREDLIB
SetContextMessageConsumer(spv_context context,MessageConsumer consumer)24 void SetContextMessageConsumer(spv_context context, MessageConsumer consumer) {
25 spvtools::SetContextMessageConsumer(context, consumer);
26 }
27 #else
28 void SetContextMessageConsumer(spv_context, MessageConsumer) {}
29 #endif
30
31 // The default consumer is a null std::function.
TEST(CInterface,DefaultConsumerNullDiagnosticForValidInput)32 TEST(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.
TEST(CInterface,DefaultConsumerNullDiagnosticForInvalidAssembling)60 TEST(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.
TEST(CInterface,DefaultConsumerNullDiagnosticForInvalidDiassembling)73 TEST(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.
TEST(CInterface,DefaultConsumerNullDiagnosticForInvalidValidating)94 TEST(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
TEST(CInterface,SpecifyConsumerNullDiagnosticForAssembling)109 TEST(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 begining 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("Expected operand, found end of stream.", message);
126 });
127
128 spv_binary binary = nullptr;
129 EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
130 spvTextToBinary(context, input_text, sizeof(input_text), &binary,
131 nullptr));
132 #ifndef SPIRV_TOOLS_SHAREDLIB
133 EXPECT_EQ(1, invocation);
134 #endif
135 spvBinaryDestroy(binary);
136 spvContextDestroy(context);
137 }
138
TEST(CInterface,SpecifyConsumerNullDiagnosticForDisassembling)139 TEST(CInterface, SpecifyConsumerNullDiagnosticForDisassembling) {
140 const char input_text[] = "OpNop";
141
142 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
143 int invocation = 0;
144 SetContextMessageConsumer(
145 context,
146 [&invocation](spv_message_level_t level, const char* source,
147 const spv_position_t& position, const char* message) {
148 ++invocation;
149 EXPECT_EQ(SPV_MSG_ERROR, level);
150 EXPECT_STREQ("input", source);
151 EXPECT_EQ(0u, position.line);
152 EXPECT_EQ(0u, position.column);
153 EXPECT_EQ(1u, position.index);
154 EXPECT_STREQ("Invalid opcode: 65535", message);
155 });
156
157 spv_binary binary = nullptr;
158 ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
159 sizeof(input_text), &binary, nullptr));
160 // Change OpNop to an invalid (wordcount|opcode) word.
161 binary->code[binary->wordCount - 1] = 0xffffffff;
162
163 spv_text text = nullptr;
164 EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
165 spvBinaryToText(context, binary->code, binary->wordCount, 0, &text,
166 nullptr));
167 #ifndef SPIRV_TOOLS_SHAREDLIB
168 EXPECT_EQ(1, invocation);
169 #endif
170
171 spvTextDestroy(text);
172 spvBinaryDestroy(binary);
173 spvContextDestroy(context);
174 }
175
TEST(CInterface,SpecifyConsumerNullDiagnosticForValidating)176 TEST(CInterface, SpecifyConsumerNullDiagnosticForValidating) {
177 const char input_text[] = "OpNop";
178
179 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
180 int invocation = 0;
181 SetContextMessageConsumer(
182 context,
183 [&invocation](spv_message_level_t level, const char* source,
184 const spv_position_t& position, const char* message) {
185 ++invocation;
186 EXPECT_EQ(SPV_MSG_ERROR, level);
187 EXPECT_STREQ("input", source);
188 EXPECT_EQ(0u, position.line);
189 EXPECT_EQ(0u, position.column);
190 // TODO(antiagainst): what validation reports is not a word offset here.
191 // It is inconsistent with diassembler. Should be fixed.
192 EXPECT_EQ(1u, position.index);
193 EXPECT_STREQ(
194 "Nop cannot appear before the memory model instruction\n"
195 " OpNop\n",
196 message);
197 });
198
199 spv_binary binary = nullptr;
200 ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
201 sizeof(input_text), &binary, nullptr));
202
203 spv_const_binary_t b{binary->code, binary->wordCount};
204 EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, nullptr));
205 #ifndef SPIRV_TOOLS_SHAREDLIB
206 EXPECT_EQ(1, invocation);
207 #endif
208
209 spvBinaryDestroy(binary);
210 spvContextDestroy(context);
211 }
212
213 // When having both a consumer and an diagnostic object, the diagnostic object
214 // should take priority.
TEST(CInterface,SpecifyConsumerSpecifyDiagnosticForAssembling)215 TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForAssembling) {
216 const char input_text[] = " OpName";
217
218 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
219 int invocation = 0;
220 SetContextMessageConsumer(
221 context,
222 [&invocation](spv_message_level_t, const char*, const spv_position_t&,
223 const char*) { ++invocation; });
224
225 spv_binary binary = nullptr;
226 spv_diagnostic diagnostic = nullptr;
227 EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
228 spvTextToBinary(context, input_text, sizeof(input_text), &binary,
229 &diagnostic));
230 EXPECT_EQ(0, invocation); // Consumer should not be invoked at all.
231 EXPECT_STREQ("Expected operand, found end of stream.", diagnostic->error);
232
233 spvDiagnosticDestroy(diagnostic);
234 spvBinaryDestroy(binary);
235 spvContextDestroy(context);
236 }
237
TEST(CInterface,SpecifyConsumerSpecifyDiagnosticForDisassembling)238 TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForDisassembling) {
239 const char input_text[] = "OpNop";
240
241 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
242 int invocation = 0;
243 SetContextMessageConsumer(
244 context,
245 [&invocation](spv_message_level_t, const char*, const spv_position_t&,
246 const char*) { ++invocation; });
247
248 spv_binary binary = nullptr;
249 ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
250 sizeof(input_text), &binary, nullptr));
251 // Change OpNop to an invalid (wordcount|opcode) word.
252 binary->code[binary->wordCount - 1] = 0xffffffff;
253
254 spv_diagnostic diagnostic = nullptr;
255 spv_text text = nullptr;
256 EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
257 spvBinaryToText(context, binary->code, binary->wordCount, 0, &text,
258 &diagnostic));
259
260 EXPECT_EQ(0, invocation); // Consumer should not be invoked at all.
261 EXPECT_STREQ("Invalid opcode: 65535", diagnostic->error);
262
263 spvTextDestroy(text);
264 spvDiagnosticDestroy(diagnostic);
265 spvBinaryDestroy(binary);
266 spvContextDestroy(context);
267 }
268
TEST(CInterface,SpecifyConsumerSpecifyDiagnosticForValidating)269 TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForValidating) {
270 const char input_text[] = "OpNop";
271
272 auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
273 int invocation = 0;
274 SetContextMessageConsumer(
275 context,
276 [&invocation](spv_message_level_t, const char*, const spv_position_t&,
277 const char*) { ++invocation; });
278
279 spv_binary binary = nullptr;
280 ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
281 sizeof(input_text), &binary, nullptr));
282
283 spv_diagnostic diagnostic = nullptr;
284 spv_const_binary_t b{binary->code, binary->wordCount};
285 EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, &diagnostic));
286
287 EXPECT_EQ(0, invocation); // Consumer should not be invoked at all.
288 EXPECT_STREQ(
289 "Nop cannot appear before the memory model instruction\n"
290 " OpNop\n",
291 diagnostic->error);
292
293 spvDiagnosticDestroy(diagnostic);
294 spvBinaryDestroy(binary);
295 spvContextDestroy(context);
296 }
297
298 } // namespace
299 } // namespace spvtools
300