1 // Copyright 2019 The Amber Authors.
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 parseried.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "gtest/gtest.h"
16 #include "src/amberscript/parser.h"
17 #include "src/shader_data.h"
18 
19 namespace amber {
20 namespace amberscript {
21 
22 using AmberScriptParserTest = testing::Test;
23 
TEST_F(AmberScriptParserTest,ShaderPassThrough)24 TEST_F(AmberScriptParserTest, ShaderPassThrough) {
25   std::string in = "SHADER vertex my_shader1 PASSTHROUGH";
26 
27   Parser parser;
28   Result r = parser.Parse(in);
29   ASSERT_TRUE(r.IsSuccess()) << r.Error();
30 
31   auto script = parser.GetScript();
32   const auto& shaders = script->GetShaders();
33   ASSERT_EQ(1U, shaders.size());
34 
35   const auto* shader = shaders[0].get();
36   EXPECT_EQ("my_shader1", shader->GetName());
37   EXPECT_EQ(kShaderTypeVertex, shader->GetType());
38   EXPECT_EQ(kShaderFormatSpirvAsm, shader->GetFormat());
39   EXPECT_EQ(kPassThroughShader, shader->GetData());
40 }
41 
TEST_F(AmberScriptParserTest,ShaderInvalidShaderTypeToken)42 TEST_F(AmberScriptParserTest, ShaderInvalidShaderTypeToken) {
43   std::string in = "SHADER 1234 my_shader PASSTHROUGH";
44 
45   Parser parser;
46   Result r = parser.Parse(in);
47   ASSERT_FALSE(r.IsSuccess());
48   EXPECT_EQ("1: invalid token when looking for shader type", r.Error());
49 }
50 
TEST_F(AmberScriptParserTest,ShaderInvalidShaderNameToken)51 TEST_F(AmberScriptParserTest, ShaderInvalidShaderNameToken) {
52   std::string in = "SHADER vertex 12345 PASSTHROUGH";
53 
54   Parser parser;
55   Result r = parser.Parse(in);
56   ASSERT_FALSE(r.IsSuccess());
57   EXPECT_EQ("1: invalid token when looking for shader name", r.Error());
58 }
59 
TEST_F(AmberScriptParserTest,ShaderInvalidShaderFormatToken)60 TEST_F(AmberScriptParserTest, ShaderInvalidShaderFormatToken) {
61   std::string in = "SHADER vertex my_shader 1234";
62 
63   Parser parser;
64   Result r = parser.Parse(in);
65   ASSERT_FALSE(r.IsSuccess());
66   EXPECT_EQ("1: invalid token when looking for shader format", r.Error());
67 }
68 
69 struct NameData {
70   const char* name;
71 };
72 
73 using AmberScriptParserShaderPassThroughTest = testing::TestWithParam<NameData>;
TEST_P(AmberScriptParserShaderPassThroughTest,ShaderPassThroughWithoutVertex)74 TEST_P(AmberScriptParserShaderPassThroughTest, ShaderPassThroughWithoutVertex) {
75   auto test_data = GetParam();
76 
77   std::string in =
78       "SHADER " + std::string(test_data.name) + " my_shader PASSTHROUGH";
79 
80   Parser parser;
81   Result r = parser.Parse(in);
82   ASSERT_FALSE(r.IsSuccess());
83   EXPECT_EQ(
84       "1: invalid shader type for PASSTHROUGH. Only vertex PASSTHROUGH "
85       "allowed",
86       r.Error());
87 }
88 INSTANTIATE_TEST_SUITE_P(
89     AmberScriptParserShaderPassThroughTests,
90     AmberScriptParserShaderPassThroughTest,
91     testing::Values(NameData{"fragment"},
92                     NameData{"geometry"},
93                     NameData{"tessellation_evaluation"},
94                     NameData{"tessellation_control"},
95                     NameData{"compute"},
96                     NameData{"multi"}));  // NOLINT(whitespace/parens)
97 
TEST_F(AmberScriptParserTest,ShaderPassThroughUnknownShaderType)98 TEST_F(AmberScriptParserTest, ShaderPassThroughUnknownShaderType) {
99   std::string in = "SHADER UNKNOWN my_shader PASSTHROUGH";
100 
101   Parser parser;
102   Result r = parser.Parse(in);
103   ASSERT_FALSE(r.IsSuccess());
104   EXPECT_EQ("1: unknown shader type: UNKNOWN", r.Error());
105 }
106 
TEST_F(AmberScriptParserTest,ShaderPassThroughMissingName)107 TEST_F(AmberScriptParserTest, ShaderPassThroughMissingName) {
108   std::string in = "SHADER vertex PASSTHROUGH";
109 
110   Parser parser;
111   Result r = parser.Parse(in);
112   ASSERT_FALSE(r.IsSuccess());
113   EXPECT_EQ("1: invalid token when looking for shader format", r.Error());
114 }
115 
TEST_F(AmberScriptParserTest,ShaderPassThroughExtraParameters)116 TEST_F(AmberScriptParserTest, ShaderPassThroughExtraParameters) {
117   std::string in = "SHADER vertex my_shader PASSTHROUGH INVALID";
118 
119   Parser parser;
120   Result r = parser.Parse(in);
121   ASSERT_FALSE(r.IsSuccess());
122   EXPECT_EQ("1: extra parameters after SHADER PASSTHROUGH: INVALID", r.Error());
123 }
124 
TEST_F(AmberScriptParserTest,Shader)125 TEST_F(AmberScriptParserTest, Shader) {
126   std::string shader_result = R"(
127 # Shader has a comment in it.
128 void main() {
129   gl_FragColor = vec3(2, 3, 4);
130 }
131 )";
132 
133   std::string in = R"(#!amber
134 SHADER geometry shader_name GLSL
135 )" + shader_result +
136                    "END";
137 
138   Parser parser;
139   Result r = parser.Parse(in);
140   ASSERT_TRUE(r.IsSuccess()) << r.Error();
141 
142   auto script = parser.GetScript();
143   const auto& shaders = script->GetShaders();
144   ASSERT_EQ(1U, shaders.size());
145 
146   const auto* shader = shaders[0].get();
147   EXPECT_EQ("shader_name", shader->GetName());
148   EXPECT_EQ(kShaderTypeGeometry, shader->GetType());
149   EXPECT_EQ(kShaderFormatGlsl, shader->GetFormat());
150   EXPECT_EQ(shader_result, shader->GetData());
151 }
152 
TEST_F(AmberScriptParserTest,ShaderInvalidFormat)153 TEST_F(AmberScriptParserTest, ShaderInvalidFormat) {
154   std::string in = R"(#!amber
155 SHADER geometry shader_name INVALID
156 # Shader has a comment in it.
157 void main() {
158   gl_FragColor = vec3(2, 3, 4);
159 }
160 END)";
161 
162   Parser parser;
163   Result r = parser.Parse(in);
164   ASSERT_FALSE(r.IsSuccess());
165   EXPECT_EQ("2: unknown shader format: INVALID", r.Error());
166 }
167 
TEST_F(AmberScriptParserTest,ShaderMissingFormat)168 TEST_F(AmberScriptParserTest, ShaderMissingFormat) {
169   std::string in = R"(#!amber
170 SHADER geometry shader_name
171 # Shader has a comment in it.
172 void main() {
173   gl_FragColor = vec3(2, 3, 4);
174 }
175 END)";
176 
177   Parser parser;
178   Result r = parser.Parse(in);
179   ASSERT_FALSE(r.IsSuccess());
180   EXPECT_EQ("3: invalid token when looking for shader format", r.Error());
181 }
182 
TEST_F(AmberScriptParserTest,ShaderEmpty)183 TEST_F(AmberScriptParserTest, ShaderEmpty) {
184   std::string in = R"(#!amber
185 SHADER geometry shader_name GLSL
186 END)";
187 
188   Parser parser;
189   Result r = parser.Parse(in);
190   ASSERT_FALSE(r.IsSuccess());
191   EXPECT_EQ("3: SHADER must not be empty", r.Error());
192 }
193 
TEST_F(AmberScriptParserTest,ShaderMissingName)194 TEST_F(AmberScriptParserTest, ShaderMissingName) {
195   std::string in = R"(#!amber
196 SHADER geometry GLSL
197 # Shader has a comment in it.
198 void main() {
199   gl_FragColor = vec3(2, 3, 4);
200 }
201 END)";
202 
203   Parser parser;
204   Result r = parser.Parse(in);
205   ASSERT_FALSE(r.IsSuccess());
206   EXPECT_EQ("3: invalid token when looking for shader format", r.Error());
207 }
208 
TEST_F(AmberScriptParserTest,ShaderMissingEnd)209 TEST_F(AmberScriptParserTest, ShaderMissingEnd) {
210   std::string in = R"(#!amber
211 SHADER geometry shader_name GLSL
212 # Shader has a comment in it.
213 void main() {
214   gl_FragColor = vec3(2, 3, 4);
215 })";
216 
217   Parser parser;
218   Result r = parser.Parse(in);
219   ASSERT_FALSE(r.IsSuccess());
220   EXPECT_EQ("6: SHADER missing END command", r.Error());
221 }
222 
TEST_F(AmberScriptParserTest,ShaderExtraParameter)223 TEST_F(AmberScriptParserTest, ShaderExtraParameter) {
224   std::string in = R"(#!amber
225 SHADER geometry shader_name GLSL INVALID
226 # Shader has a comment in it.
227 void main() {
228   gl_FragColor = vec3(2, 3, 4);
229 }
230 END)";
231 
232   Parser parser;
233   Result r = parser.Parse(in);
234   ASSERT_FALSE(r.IsSuccess());
235   EXPECT_EQ("2: extra parameters after SHADER command: INVALID", r.Error());
236 }
237 
TEST_F(AmberScriptParserTest,ShaderTargetEnv)238 TEST_F(AmberScriptParserTest, ShaderTargetEnv) {
239   std::string in = R"(#!amber
240 SHADER geometry shader_name GLSL TARGET_ENV spv1.4
241 void main() {
242   gl_FragColor = vec3(2, 3, 4);
243 }
244 END)";
245 
246   Parser parser;
247   Result r = parser.Parse(in);
248 
249   ASSERT_TRUE(r.IsSuccess()) << r.Error();
250 
251   auto script = parser.GetScript();
252   const auto& shaders = script->GetShaders();
253   ASSERT_EQ(1U, shaders.size());
254 
255   const auto* shader = shaders[0].get();
256   EXPECT_EQ("spv1.4", shader->GetTargetEnv());
257 }
258 
TEST_F(AmberScriptParserTest,ShaderTargetEnvMissingEnv)259 TEST_F(AmberScriptParserTest, ShaderTargetEnvMissingEnv) {
260   std::string in = R"(#!amber
261 SHADER geometry shader_name GLSL TARGET_ENV
262 void main() {
263   gl_FragColor = vec3(2, 3, 4);
264 }
265 END)";
266 
267   Parser parser;
268   Result r = parser.Parse(in);
269   ASSERT_FALSE(r.IsSuccess());
270   EXPECT_EQ("3: expected target environment after TARGET_ENV", r.Error());
271 }
272 
TEST_F(AmberScriptParserTest,ShaderTargetEnvInvalidEnv)273 TEST_F(AmberScriptParserTest, ShaderTargetEnvInvalidEnv) {
274   std::string in = R"(#!amber
275 SHADER geometry shader_name GLSL TARGET_ENV 12345
276 void main() {
277   gl_FragColor = vec3(2, 3, 4);
278 }
279 END)";
280 
281   Parser parser;
282   Result r = parser.Parse(in);
283   ASSERT_FALSE(r.IsSuccess());
284   EXPECT_EQ("2: expected target environment after TARGET_ENV", r.Error());
285 }
286 
TEST_F(AmberScriptParserTest,ShaderVirtualFile)287 TEST_F(AmberScriptParserTest, ShaderVirtualFile) {
288   std::string in = R"(#!amber
289 VIRTUAL_FILE my_shader.hlsl
290 My shader source
291 END
292 
293 SHADER vertex my_shader HLSL VIRTUAL_FILE my_shader.hlsl
294 )";
295 
296   Parser parser;
297   Result r = parser.Parse(in);
298   ASSERT_EQ(r.Error(), "");
299 
300   auto script = parser.GetScript();
301   auto shader = script->GetShader("my_shader");
302   ASSERT_TRUE(shader != nullptr);
303   auto source = shader->GetData();
304   ASSERT_EQ("My shader source\n", shader->GetData());
305 }
306 
TEST_F(AmberScriptParserTest,VirtualFileDuplicatePath)307 TEST_F(AmberScriptParserTest, VirtualFileDuplicatePath) {
308   std::string in = R"(#!amber
309 VIRTUAL_FILE my.file
310 Blah
311 END
312 
313 VIRTUAL_FILE my.file
314 Blah
315 END
316 )";
317 
318   Parser parser;
319   Result r = parser.Parse(in);
320   ASSERT_EQ(r.Error(), "8: Virtual file 'my.file' already declared");
321 }
322 
TEST_F(AmberScriptParserTest,VirtualFileEmptyPath)323 TEST_F(AmberScriptParserTest, VirtualFileEmptyPath) {
324   std::string in = R"(#!amber
325 VIRTUAL_FILE ""
326 Blah
327 END
328 )";
329 
330   Parser parser;
331   Result r = parser.Parse(in);
332   ASSERT_EQ(r.Error(), "4: Virtual file path was empty");
333 }
334 
335 struct ShaderTypeData {
336   const char* name;
337   ShaderType type;
338 };
339 
340 using AmberScriptParserShaderTypeTest = testing::TestWithParam<ShaderTypeData>;
TEST_P(AmberScriptParserShaderTypeTest,ShaderFormats)341 TEST_P(AmberScriptParserShaderTypeTest, ShaderFormats) {
342   auto test_data = GetParam();
343 
344   std::string shader_result = R"(
345 void main() {
346   gl_FragColor = vec3(2, 3, 4);
347 }
348 )";
349 
350   std::string in = "SHADER " + std::string(test_data.name) +
351                    R"( my_shader GLSL
352 )" + shader_result +
353                    "END";
354 
355   Parser parser;
356   Result r = parser.Parse(in);
357   ASSERT_TRUE(r.IsSuccess()) << r.Error();
358 
359   auto script = parser.GetScript();
360   const auto& shaders = script->GetShaders();
361   ASSERT_EQ(1U, shaders.size());
362 
363   const auto* shader = shaders[0].get();
364   EXPECT_EQ("my_shader", shader->GetName());
365   EXPECT_EQ(test_data.type, shader->GetType());
366   EXPECT_EQ(kShaderFormatGlsl, shader->GetFormat());
367   EXPECT_EQ(shader_result, shader->GetData());
368 }
369 INSTANTIATE_TEST_SUITE_P(
370     AmberScriptParserTestsShaderType,
371     AmberScriptParserShaderTypeTest,
372     testing::Values(
373         ShaderTypeData{"vertex", kShaderTypeVertex},
374         ShaderTypeData{"fragment", kShaderTypeFragment},
375         ShaderTypeData{"geometry", kShaderTypeGeometry},
376         ShaderTypeData{"tessellation_evaluation",
377                        kShaderTypeTessellationEvaluation},
378         ShaderTypeData{"tessellation_control", kShaderTypeTessellationControl},
379         ShaderTypeData{"compute", kShaderTypeCompute},
380         ShaderTypeData{"multi",
381                        kShaderTypeMulti}));  // NOLINT(whitespace/parens)
382 
383 struct ShaderFormatData {
384   const char* name;
385   ShaderFormat format;
386 };
387 
388 using AmberScriptParserShaderFormatTest =
389     testing::TestWithParam<ShaderFormatData>;
TEST_P(AmberScriptParserShaderFormatTest,ShaderFormats)390 TEST_P(AmberScriptParserShaderFormatTest, ShaderFormats) {
391   auto test_data = GetParam();
392 
393   std::string shader_result = R"(void main() {
394   gl_FragColor = vec3(2, 3, 4);
395 }
396 )";
397 
398   std::string in = "SHADER vertex my_shader " + std::string(test_data.name) +
399                    "\n" + shader_result + "END";
400 
401   Parser parser;
402   Result r = parser.Parse(in);
403   ASSERT_TRUE(r.IsSuccess()) << r.Error();
404 
405   auto script = parser.GetScript();
406   const auto& shaders = script->GetShaders();
407   ASSERT_EQ(1U, shaders.size());
408 
409   const auto* shader = shaders[0].get();
410   EXPECT_EQ("my_shader", shader->GetName());
411   EXPECT_EQ(kShaderTypeVertex, shader->GetType());
412   EXPECT_EQ(test_data.format, shader->GetFormat());
413   EXPECT_EQ(shader_result, shader->GetData());
414 }
415 
416 INSTANTIATE_TEST_SUITE_P(
417     AmberScriptParserTestsShaderFormat,
418     AmberScriptParserShaderFormatTest,
419     testing::Values(ShaderFormatData{"GLSL", kShaderFormatGlsl},
420                     ShaderFormatData{"SPIRV-ASM", kShaderFormatSpirvAsm},
421                     ShaderFormatData{
422                         "SPIRV-HEX",
423                         kShaderFormatSpirvHex}));  // NOLINT(whitespace/parens)
424 
TEST_F(AmberScriptParserTest,DuplicateShaderName)425 TEST_F(AmberScriptParserTest, DuplicateShaderName) {
426   std::string in = R"(
427 SHADER vertex my_shader GLSL
428 # shader
429 END
430 SHADER fragment my_shader GLSL
431 # another shader
432 END)";
433 
434   Parser parser;
435   Result r = parser.Parse(in);
436   ASSERT_FALSE(r.IsSuccess());
437   EXPECT_EQ("7: duplicate shader name provided", r.Error());
438 }
439 
TEST_F(AmberScriptParserTest,OpenCLCKernel)440 TEST_F(AmberScriptParserTest, OpenCLCKernel) {
441   std::string in = R"(
442 SHADER compute my_shader OPENCL-C
443 # shader
444 END
445 )";
446 
447   Parser parser;
448   Result r = parser.Parse(in);
449   ASSERT_TRUE(r.IsSuccess());
450 }
451 
TEST_F(AmberScriptParserTest,OpenCLCMultiKernel)452 TEST_F(AmberScriptParserTest, OpenCLCMultiKernel) {
453   std::string in = R"(
454 SHADER multi my_shader OPENCL-C
455 # shader
456 END
457 )";
458 
459   Parser parser;
460   Result r = parser.Parse(in);
461   ASSERT_TRUE(r.IsSuccess());
462 }
463 
TEST_F(AmberScriptParserTest,ShaderDefaultFilePath)464 TEST_F(AmberScriptParserTest, ShaderDefaultFilePath) {
465   std::string in = R"(#!amber
466 SHADER fragment shader_name GLSL
467 void main() {
468   gl_FragColor = vec3(2, 3, 4);
469 }
470 END)";
471 
472   Parser parser;
473   Result r = parser.Parse(in);
474   ASSERT_TRUE(r.IsSuccess()) << r.Error();
475 
476   auto script = parser.GetScript();
477   auto shader = script->GetShader("shader_name");
478   EXPECT_EQ("embedded-shaders/shader_name", shader->GetFilePath());
479 }
480 
TEST_F(AmberScriptParserTest,ShaderVirtualFilePath)481 TEST_F(AmberScriptParserTest, ShaderVirtualFilePath) {
482   std::string in = R"(#!amber
483 VIRTUAL_FILE my_fragment_shader
484 void main() {
485   gl_FragColor = vec3(2, 3, 4);
486 }
487 END
488 
489 SHADER fragment shader_name GLSL VIRTUAL_FILE my_fragment_shader
490 )";
491 
492   Parser parser;
493   Result r = parser.Parse(in);
494   ASSERT_TRUE(r.IsSuccess()) << r.Error();
495 
496   auto script = parser.GetScript();
497   auto shader = script->GetShader("shader_name");
498   EXPECT_EQ("my_fragment_shader", shader->GetFilePath());
499 }
500 
501 }  // namespace amberscript
502 }  // namespace amber
503