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 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
TEST(CInterface,SpecifyConsumerNullDiagnosticForDisassembling)142 TEST(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
TEST(CInterface,SpecifyConsumerNullDiagnosticForValidating)179 TEST(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.
TEST(CInterface,SpecifyConsumerSpecifyDiagnosticForAssembling)218 TEST(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
TEST(CInterface,SpecifyConsumerSpecifyDiagnosticForDisassembling)244 TEST(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
TEST(CInterface,SpecifyConsumerSpecifyDiagnosticForValidating)275 TEST(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