• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2016 Google, Inc.
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions
8 // are met:
9 //
10 //    Redistributions of source code must retain the above copyright
11 //    notice, this list of conditions and the following disclaimer.
12 //
13 //    Redistributions in binary form must reproduce the above
14 //    copyright notice, this list of conditions and the following
15 //    disclaimer in the documentation and/or other materials provided
16 //    with the distribution.
17 //
18 //    Neither the name of Google Inc. nor the names of its
19 //    contributors may be used to endorse or promote products derived
20 //    from this software without specific prior written permission.
21 //
22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 // POSSIBILITY OF SUCH DAMAGE.
34 
35 #ifndef GLSLANG_GTESTS_TEST_FIXTURE_H
36 #define GLSLANG_GTESTS_TEST_FIXTURE_H
37 
38 #include <cstdint>
39 #include <fstream>
40 #include <sstream>
41 #include <streambuf>
42 #include <tuple>
43 #include <string>
44 
45 #include <gtest/gtest.h>
46 
47 #include "SPIRV/GlslangToSpv.h"
48 #include "SPIRV/disassemble.h"
49 #include "SPIRV/doc.h"
50 #include "SPIRV/SPVRemapper.h"
51 #include "StandAlone/ResourceLimits.h"
52 #include "glslang/Public/ShaderLang.h"
53 
54 #include "Initializer.h"
55 #include "Settings.h"
56 
57 namespace glslangtest {
58 
59 // This function is used to provide custom test name suffixes based on the
60 // shader source file names. Otherwise, the test name suffixes will just be
61 // numbers, which are not quite obvious.
62 std::string FileNameAsCustomTestSuffix(
63     const ::testing::TestParamInfo<std::string>& info);
64 
65 enum class Source {
66   GLSL,
67   HLSL,
68 };
69 
70 // Enum for shader compilation semantics.
71 enum class Semantics {
72     OpenGL,
73     Vulkan
74 };
75 
76 // Enum for compilation target.
77 enum class Target {
78     AST,
79     Spv,
80     BothASTAndSpv,
81 };
82 
83 EShLanguage GetShaderStage(const std::string& stage);
84 
85 EShMessages DeriveOptions(Source, Semantics, Target);
86 
87 // Reads the content of the file at the given |path|. On success, returns true
88 // and the contents; otherwise, returns false and an empty string.
89 std::pair<bool, std::string> ReadFile(const std::string& path);
90 std::pair<bool, std::vector<std::uint32_t> > ReadSpvBinaryFile(const std::string& path);
91 
92 // Writes the given |contents| into the file at the given |path|. Returns true
93 // on successful output.
94 bool WriteFile(const std::string& path, const std::string& contents);
95 
96 // Returns the suffix of the given |name|.
97 std::string GetSuffix(const std::string& name);
98 
99 // Base class for glslang integration tests. It contains many handy utility-like
100 // methods such as reading shader source files, compiling into AST/SPIR-V, and
101 // comparing with expected outputs.
102 //
103 // To write value-Parameterized tests:
104 //   using ValueParamTest = GlslangTest<::testing::TestWithParam<std::string>>;
105 // To use as normal fixture:
106 //   using FixtureTest = GlslangTest<::testing::Test>;
107 template <typename GT>
108 class GlslangTest : public GT {
109 public:
GlslangTest()110     GlslangTest()
111         : defaultVersion(100),
112           defaultProfile(ENoProfile),
113           forceVersionProfile(false),
114           isForwardCompatible(false) {
115         // Perform validation by default.
116         spirvOptions.validate = true;
117     }
118 
119     // Tries to load the contents from the file at the given |path|. On success,
120     // writes the contents into |contents|. On failure, errors out.
tryLoadFile(const std::string & path,const std::string & tag,std::string * contents)121     void tryLoadFile(const std::string& path, const std::string& tag,
122                      std::string* contents)
123     {
124         bool fileReadOk;
125         std::tie(fileReadOk, *contents) = ReadFile(path);
126         ASSERT_TRUE(fileReadOk) << "Cannot open " << tag << " file: " << path;
127     }
128 
129     // Tries to load the contents from the file at the given |path|. On success,
130     // writes the contents into |contents|. On failure, errors out.
tryLoadSpvFile(const std::string & path,const std::string & tag,std::vector<uint32_t> & contents)131     void tryLoadSpvFile(const std::string& path, const std::string& tag,
132                         std::vector<uint32_t>& contents)
133     {
134         bool fileReadOk;
135         std::tie(fileReadOk, contents) = ReadSpvBinaryFile(path);
136         ASSERT_TRUE(fileReadOk) << "Cannot open " << tag << " file: " << path;
137     }
138 
139     // Checks the equality of |expected| and |real|. If they are not equal,
140     // write |real| to the given file named as |fname| if update mode is on.
141     void checkEqAndUpdateIfRequested(const std::string& expected,
142                                      const std::string& real,
143                                      const std::string& fname,
144                                      const std::string& errorsAndWarnings = "")
145     {
146         // In order to output the message we want under proper circumstances,
147         // we need the following operator<< stuff.
148         EXPECT_EQ(expected, real)
149             << (GlobalTestSettings.updateMode
150                     ? ("Mismatch found and update mode turned on - "
151                        "flushing expected result output.\n")
152                     : "")
153             << "The following warnings/errors occurred:\n"
154             << errorsAndWarnings;
155 
156         // Update the expected output file if requested.
157         // It looks weird to duplicate the comparison between expected_output
158         // and stream.str(). However, if creating a variable for the comparison
159         // result, we cannot have pretty print of the string diff in the above.
160         if (GlobalTestSettings.updateMode && expected != real) {
161             EXPECT_TRUE(WriteFile(fname, real)) << "Flushing failed";
162         }
163     }
164 
165     struct ShaderResult {
166         std::string shaderName;
167         std::string output;
168         std::string error;
169     };
170 
171     // A struct for holding all the information returned by glslang compilation
172     // and linking.
173     struct GlslangResult {
174         std::vector<ShaderResult> shaderResults;
175         std::string linkingOutput;
176         std::string linkingError;
177         bool validationResult;
178         std::string spirvWarningsErrors;
179         std::string spirv;  // Optional SPIR-V disassembly text.
180     };
181 
182     // Compiles and the given source |code| of the given shader |stage| into
183     // the target under the semantics conveyed via |controls|. Returns true
184     // and modifies |shader| on success.
185     bool compile(glslang::TShader* shader, const std::string& code,
186                  const std::string& entryPointName, EShMessages controls,
187                  const TBuiltInResource* resources=nullptr,
188                  const std::string* shaderName=nullptr)
189     {
190         const char* shaderStrings = code.data();
191         const int shaderLengths = static_cast<int>(code.size());
192         const char* shaderNames = nullptr;
193 
194         if ((controls & EShMsgDebugInfo) && shaderName != nullptr) {
195             shaderNames = shaderName->data();
196             shader->setStringsWithLengthsAndNames(
197                     &shaderStrings, &shaderLengths, &shaderNames, 1);
198         } else
199             shader->setStringsWithLengths(&shaderStrings, &shaderLengths, 1);
200         if (!entryPointName.empty()) shader->setEntryPoint(entryPointName.c_str());
201         return shader->parse(
202                 (resources ? resources : &glslang::DefaultTBuiltInResource),
203                 defaultVersion, isForwardCompatible, controls);
204     }
205 
206     // Compiles and links the given source |code| of the given shader
207     // |stage| into the target under the semantics specified via |controls|.
208     // Returns a GlslangResult instance containing all the information generated
209     // during the process. If the target includes SPIR-V, also disassembles
210     // the result and returns disassembly text.
211     GlslangResult compileAndLink(
212             const std::string& shaderName, const std::string& code,
213             const std::string& entryPointName, EShMessages controls,
214             glslang::EShTargetClientVersion clientTargetVersion,
215             glslang::EShTargetLanguageVersion targetLanguageVersion,
216             bool flattenUniformArrays = false,
217             EShTextureSamplerTransformMode texSampTransMode = EShTexSampTransKeep,
218             bool enableOptimizer = false,
219             bool enableDebug = false,
220             bool automap = true)
221     {
222         const EShLanguage stage = GetShaderStage(GetSuffix(shaderName));
223 
224         glslang::TShader shader(stage);
225         if (automap) {
226             shader.setAutoMapLocations(true);
227             shader.setAutoMapBindings(true);
228         }
229         shader.setTextureSamplerTransformMode(texSampTransMode);
230 #ifdef ENABLE_HLSL
231         shader.setFlattenUniformArrays(flattenUniformArrays);
232 #endif
233 
234         if (controls & EShMsgSpvRules) {
235             if (controls & EShMsgVulkanRules) {
236                 shader.setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl
237                                                                : glslang::EShSourceGlsl,
238                                     stage, glslang::EShClientVulkan, 100);
239                 shader.setEnvClient(glslang::EShClientVulkan, clientTargetVersion);
240                 shader.setEnvTarget(glslang::EShTargetSpv, targetLanguageVersion);
241             } else {
242                 shader.setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl
243                                                                : glslang::EShSourceGlsl,
244                                     stage, glslang::EShClientOpenGL, 100);
245                 shader.setEnvClient(glslang::EShClientOpenGL, clientTargetVersion);
246                 shader.setEnvTarget(glslang::EshTargetSpv, glslang::EShTargetSpv_1_0);
247             }
248         }
249 
250         bool success = compile(
251                 &shader, code, entryPointName, controls, nullptr, &shaderName);
252 
253         glslang::TProgram program;
254         program.addShader(&shader);
255         success &= program.link(controls);
256 
257         spv::SpvBuildLogger logger;
258 
259         if (success && (controls & EShMsgSpvRules)) {
260             std::vector<uint32_t> spirv_binary;
261             options().disableOptimizer = !enableOptimizer;
262             options().generateDebugInfo = enableDebug;
263             glslang::GlslangToSpv(*program.getIntermediate(stage),
264                                   spirv_binary, &logger, &options());
265 
266             std::ostringstream disassembly_stream;
267             spv::Parameterize();
268             spv::Disassemble(disassembly_stream, spirv_binary);
269             bool validation_result = !options().validate || logger.getAllMessages().empty();
270             return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
271                     program.getInfoLog(), program.getInfoDebugLog(),
272                     validation_result, logger.getAllMessages(), disassembly_stream.str()};
273         } else {
274             return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
275                     program.getInfoLog(), program.getInfoDebugLog(), true, "", ""};
276         }
277     }
278 
279     // Compiles and links the given source |code| of the given shader
280     // |stage| into the target under the semantics specified via |controls|.
281     // Returns a GlslangResult instance containing all the information generated
282     // during the process. If the target includes SPIR-V, also disassembles
283     // the result and returns disassembly text.
compileLinkIoMap(const std::string shaderName,const std::string & code,const std::string & entryPointName,EShMessages controls,int baseSamplerBinding,int baseTextureBinding,int baseImageBinding,int baseUboBinding,int baseSsboBinding,bool autoMapBindings,bool flattenUniformArrays)284     GlslangResult compileLinkIoMap(
285             const std::string shaderName, const std::string& code,
286             const std::string& entryPointName, EShMessages controls,
287             int baseSamplerBinding,
288             int baseTextureBinding,
289             int baseImageBinding,
290             int baseUboBinding,
291             int baseSsboBinding,
292             bool autoMapBindings,
293             bool flattenUniformArrays)
294     {
295         const EShLanguage stage = GetShaderStage(GetSuffix(shaderName));
296 
297         glslang::TShader shader(stage);
298         shader.setShiftSamplerBinding(baseSamplerBinding);
299         shader.setShiftTextureBinding(baseTextureBinding);
300         shader.setShiftImageBinding(baseImageBinding);
301         shader.setShiftUboBinding(baseUboBinding);
302         shader.setShiftSsboBinding(baseSsboBinding);
303         shader.setAutoMapBindings(autoMapBindings);
304         shader.setAutoMapLocations(true);
305 #ifdef ENABLE_HLSL
306         shader.setFlattenUniformArrays(flattenUniformArrays);
307 #endif
308 
309         bool success = compile(&shader, code, entryPointName, controls);
310 
311         glslang::TProgram program;
312         program.addShader(&shader);
313 
314         success &= program.link(controls);
315 #ifndef GLSLANG_WEB
316         success &= program.mapIO();
317 #endif
318 
319         spv::SpvBuildLogger logger;
320 
321         if (success && (controls & EShMsgSpvRules)) {
322             std::vector<uint32_t> spirv_binary;
323             glslang::GlslangToSpv(*program.getIntermediate(stage),
324                                   spirv_binary, &logger, &options());
325 
326             std::ostringstream disassembly_stream;
327             spv::Parameterize();
328             spv::Disassemble(disassembly_stream, spirv_binary);
329             bool validation_result = !options().validate || logger.getAllMessages().empty();
330             return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
331                     program.getInfoLog(), program.getInfoDebugLog(),
332                     validation_result, logger.getAllMessages(), disassembly_stream.str()};
333         } else {
334             return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
335                     program.getInfoLog(), program.getInfoDebugLog(), true, "", ""};
336         }
337     }
338 
339     // This is like compileAndLink but with remapping of the SPV binary
340     // through spirvbin_t::remap().  While technically this could be merged
341     // with compileAndLink() above (with the remap step optionally being a no-op)
342     // it is given separately here for ease of future extraction.
343     GlslangResult compileLinkRemap(
344             const std::string shaderName, const std::string& code,
345             const std::string& entryPointName, EShMessages controls,
346             const unsigned int remapOptions = spv::spirvbin_t::NONE)
347     {
348         const EShLanguage stage = GetShaderStage(GetSuffix(shaderName));
349 
350         glslang::TShader shader(stage);
351         shader.setAutoMapBindings(true);
352         shader.setAutoMapLocations(true);
353 
354         bool success = compile(&shader, code, entryPointName, controls);
355 
356         glslang::TProgram program;
357         program.addShader(&shader);
358         success &= program.link(controls);
359 
360         spv::SpvBuildLogger logger;
361 
362         if (success && (controls & EShMsgSpvRules)) {
363             std::vector<uint32_t> spirv_binary;
364             glslang::GlslangToSpv(*program.getIntermediate(stage),
365                                   spirv_binary, &logger, &options());
366 
367             spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, remapOptions);
368 
369             std::ostringstream disassembly_stream;
370             spv::Parameterize();
371             spv::Disassemble(disassembly_stream, spirv_binary);
372             bool validation_result = !options().validate || logger.getAllMessages().empty();
373             return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
374                     program.getInfoLog(), program.getInfoDebugLog(),
375                     validation_result, logger.getAllMessages(), disassembly_stream.str()};
376         } else {
377             return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
378                     program.getInfoLog(), program.getInfoDebugLog(), true, "", ""};
379         }
380     }
381 
382     // remap the binary in 'code' with the options in remapOptions
383     GlslangResult remap(
384             const std::string shaderName, const std::vector<uint32_t>& code,
385             EShMessages controls,
386             const unsigned int remapOptions = spv::spirvbin_t::NONE)
387     {
388         if ((controls & EShMsgSpvRules)) {
389             std::vector<uint32_t> spirv_binary(code); // scratch copy
390 
391             spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, remapOptions);
392 
393             std::ostringstream disassembly_stream;
394             spv::Parameterize();
395             spv::Disassemble(disassembly_stream, spirv_binary);
396 
397             return {{{shaderName, "", ""},},
398                     "", "",
399                     true, "", disassembly_stream.str()};
400         } else {
401             return {{{shaderName, "", ""},}, "", "", true, "", ""};
402         }
403     }
404 
outputResultToStream(std::ostringstream * stream,const GlslangResult & result,EShMessages controls)405     void outputResultToStream(std::ostringstream* stream,
406                               const GlslangResult& result,
407                               EShMessages controls)
408     {
409         const auto outputIfNotEmpty = [&stream](const std::string& str) {
410             if (!str.empty()) *stream << str << "\n";
411         };
412 
413         for (const auto& shaderResult : result.shaderResults) {
414             *stream << shaderResult.shaderName << "\n";
415             outputIfNotEmpty(shaderResult.output);
416             outputIfNotEmpty(shaderResult.error);
417         }
418         outputIfNotEmpty(result.linkingOutput);
419         outputIfNotEmpty(result.linkingError);
420         if (!result.validationResult) {
421           *stream << "Validation failed\n";
422         }
423 
424         if (controls & EShMsgSpvRules) {
425             *stream
426                 << (result.spirv.empty()
427                         ? "SPIR-V is not generated for failed compile or link\n"
428                         : result.spirv);
429         }
430     }
431 
432     void loadFileCompileAndCheck(const std::string& testDir,
433                                  const std::string& testName,
434                                  Source source,
435                                  Semantics semantics,
436                                  glslang::EShTargetClientVersion clientTargetVersion,
437                                  glslang::EShTargetLanguageVersion targetLanguageVersion,
438                                  Target target,
439                                  bool automap = true,
440                                  const std::string& entryPointName="",
441                                  const std::string& baseDir="/baseResults/",
442                                  const bool enableOptimizer = false,
443                                  const bool enableDebug = false)
444     {
445         const std::string inputFname = testDir + "/" + testName;
446         const std::string expectedOutputFname =
447             testDir + baseDir + testName + ".out";
448         std::string input, expectedOutput;
449 
450         tryLoadFile(inputFname, "input", &input);
451         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
452 
453         EShMessages controls = DeriveOptions(source, semantics, target);
454         if (enableOptimizer)
455             controls = static_cast<EShMessages>(controls & ~EShMsgHlslLegalization);
456         if (enableDebug)
457             controls = static_cast<EShMessages>(controls | EShMsgDebugInfo);
458         GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion,
459             targetLanguageVersion, false, EShTexSampTransKeep, enableOptimizer, enableDebug, automap);
460 
461         // Generate the hybrid output in the way of glslangValidator.
462         std::ostringstream stream;
463         outputResultToStream(&stream, result, controls);
464 
465         checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
466                                     expectedOutputFname, result.spirvWarningsErrors);
467     }
468 
469     void loadFileCompileAndCheckWithOptions(const std::string &testDir,
470                                             const std::string &testName,
471                                             Source source,
472                                             Semantics semantics,
473                                             glslang::EShTargetClientVersion clientTargetVersion,
474                                             glslang::EShTargetLanguageVersion targetLanguageVersion,
475                                             Target target, bool automap = true, const std::string &entryPointName = "",
476                                             const std::string &baseDir = "/baseResults/",
477                                             const EShMessages additionalOptions = EShMessages::EShMsgDefault)
478     {
479         const std::string inputFname = testDir + "/" + testName;
480         const std::string expectedOutputFname = testDir + baseDir + testName + ".out";
481         std::string input, expectedOutput;
482 
483         tryLoadFile(inputFname, "input", &input);
484         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
485 
486         EShMessages controls = DeriveOptions(source, semantics, target);
487         controls = static_cast<EShMessages>(controls | additionalOptions);
488         GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion,
489             targetLanguageVersion, false, EShTexSampTransKeep, false, automap);
490 
491         // Generate the hybrid output in the way of glslangValidator.
492         std::ostringstream stream;
493         outputResultToStream(&stream, result, controls);
494 
495         checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname);
496     }
497 
498     void loadFileCompileFlattenUniformsAndCheck(const std::string& testDir,
499                                                 const std::string& testName,
500                                                 Source source,
501                                                 Semantics semantics,
502                                                 Target target,
503                                                 const std::string& entryPointName="")
504     {
505         const std::string inputFname = testDir + "/" + testName;
506         const std::string expectedOutputFname =
507             testDir + "/baseResults/" + testName + ".out";
508         std::string input, expectedOutput;
509 
510         tryLoadFile(inputFname, "input", &input);
511         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
512 
513         const EShMessages controls = DeriveOptions(source, semantics, target);
514         GlslangResult result = compileAndLink(testName, input, entryPointName, controls,
515                                               glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, true);
516 
517         // Generate the hybrid output in the way of glslangValidator.
518         std::ostringstream stream;
519         outputResultToStream(&stream, result, controls);
520 
521         checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
522                                     expectedOutputFname, result.spirvWarningsErrors);
523     }
524 
loadFileCompileIoMapAndCheck(const std::string & testDir,const std::string & testName,Source source,Semantics semantics,Target target,const std::string & entryPointName,int baseSamplerBinding,int baseTextureBinding,int baseImageBinding,int baseUboBinding,int baseSsboBinding,bool autoMapBindings,bool flattenUniformArrays)525     void loadFileCompileIoMapAndCheck(const std::string& testDir,
526                                       const std::string& testName,
527                                       Source source,
528                                       Semantics semantics,
529                                       Target target,
530                                       const std::string& entryPointName,
531                                       int baseSamplerBinding,
532                                       int baseTextureBinding,
533                                       int baseImageBinding,
534                                       int baseUboBinding,
535                                       int baseSsboBinding,
536                                       bool autoMapBindings,
537                                       bool flattenUniformArrays)
538     {
539         const std::string inputFname = testDir + "/" + testName;
540         const std::string expectedOutputFname =
541             testDir + "/baseResults/" + testName + ".out";
542         std::string input, expectedOutput;
543 
544         tryLoadFile(inputFname, "input", &input);
545         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
546 
547         const EShMessages controls = DeriveOptions(source, semantics, target);
548         GlslangResult result = compileLinkIoMap(testName, input, entryPointName, controls,
549                                                 baseSamplerBinding, baseTextureBinding, baseImageBinding,
550                                                 baseUboBinding, baseSsboBinding,
551                                                 autoMapBindings,
552                                                 flattenUniformArrays);
553 
554         // Generate the hybrid output in the way of glslangValidator.
555         std::ostringstream stream;
556         outputResultToStream(&stream, result, controls);
557 
558         checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
559                                     expectedOutputFname, result.spirvWarningsErrors);
560     }
561 
562     void loadFileCompileRemapAndCheck(const std::string& testDir,
563                                       const std::string& testName,
564                                       Source source,
565                                       Semantics semantics,
566                                       Target target,
567                                       const std::string& entryPointName="",
568                                       const unsigned int remapOptions = spv::spirvbin_t::NONE)
569     {
570         const std::string inputFname = testDir + "/" + testName;
571         const std::string expectedOutputFname =
572             testDir + "/baseResults/" + testName + ".out";
573         std::string input, expectedOutput;
574 
575         tryLoadFile(inputFname, "input", &input);
576         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
577 
578         const EShMessages controls = DeriveOptions(source, semantics, target);
579         GlslangResult result = compileLinkRemap(testName, input, entryPointName, controls, remapOptions);
580 
581         // Generate the hybrid output in the way of glslangValidator.
582         std::ostringstream stream;
583         outputResultToStream(&stream, result, controls);
584 
585         checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
586                                     expectedOutputFname, result.spirvWarningsErrors);
587     }
588 
589     void loadFileRemapAndCheck(const std::string& testDir,
590                                const std::string& testName,
591                                Source source,
592                                Semantics semantics,
593                                Target target,
594                                const unsigned int remapOptions = spv::spirvbin_t::NONE)
595     {
596         const std::string inputFname = testDir + "/" + testName;
597         const std::string expectedOutputFname =
598             testDir + "/baseResults/" + testName + ".out";
599         std::vector<std::uint32_t> input;
600         std::string expectedOutput;
601 
602         tryLoadSpvFile(inputFname, "input", input);
603         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
604 
605         const EShMessages controls = DeriveOptions(source, semantics, target);
606         GlslangResult result = remap(testName, input, controls, remapOptions);
607 
608         // Generate the hybrid output in the way of glslangValidator.
609         std::ostringstream stream;
610         outputResultToStream(&stream, result, controls);
611 
612         checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
613                                     expectedOutputFname, result.spirvWarningsErrors);
614     }
615 
616     // Preprocesses the given |source| code. On success, returns true, the
617     // preprocessed shader, and warning messages. Otherwise, returns false, an
618     // empty string, and error messages.
preprocess(const std::string & source)619     std::tuple<bool, std::string, std::string> preprocess(
620         const std::string& source)
621     {
622         const char* shaderStrings = source.data();
623         const int shaderLengths = static_cast<int>(source.size());
624 
625         glslang::TShader shader(EShLangVertex);
626         shader.setStringsWithLengths(&shaderStrings, &shaderLengths, 1);
627         std::string ppShader;
628         glslang::TShader::ForbidIncluder includer;
629         const bool success = shader.preprocess(
630             &glslang::DefaultTBuiltInResource, defaultVersion, defaultProfile,
631             forceVersionProfile, isForwardCompatible, (EShMessages)(EShMsgOnlyPreprocessor | EShMsgCascadingErrors),
632             &ppShader, includer);
633 
634         std::string log = shader.getInfoLog();
635         log += shader.getInfoDebugLog();
636         if (success) {
637             return std::make_tuple(true, ppShader, log);
638         } else {
639             return std::make_tuple(false, "", log);
640         }
641     }
642 
loadFilePreprocessAndCheck(const std::string & testDir,const std::string & testName)643     void loadFilePreprocessAndCheck(const std::string& testDir,
644                                     const std::string& testName)
645     {
646         const std::string inputFname = testDir + "/" + testName;
647         const std::string expectedOutputFname =
648             testDir + "/baseResults/" + testName + ".out";
649         const std::string expectedErrorFname =
650             testDir + "/baseResults/" + testName + ".err";
651         std::string input, expectedOutput, expectedError;
652 
653         tryLoadFile(inputFname, "input", &input);
654         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
655         tryLoadFile(expectedErrorFname, "expected error", &expectedError);
656 
657         bool ppOk;
658         std::string output, error;
659         std::tie(ppOk, output, error) = preprocess(input);
660         if (!output.empty()) output += '\n';
661         if (!error.empty()) error += '\n';
662 
663         checkEqAndUpdateIfRequested(expectedOutput, output,
664                                     expectedOutputFname);
665         checkEqAndUpdateIfRequested(expectedError, error,
666                                     expectedErrorFname);
667     }
668 
669     void loadCompileUpgradeTextureToSampledTextureAndDropSamplersAndCheck(const std::string& testDir,
670                                                                           const std::string& testName,
671                                                                           Source source,
672                                                                           Semantics semantics,
673                                                                           Target target,
674                                                                           const std::string& entryPointName = "")
675     {
676         const std::string inputFname = testDir + "/" + testName;
677         const std::string expectedOutputFname = testDir + "/baseResults/" + testName + ".out";
678         std::string input, expectedOutput;
679 
680         tryLoadFile(inputFname, "input", &input);
681         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
682 
683         const EShMessages controls = DeriveOptions(source, semantics, target);
684         GlslangResult result = compileAndLink(testName, input, entryPointName, controls,
685                                               glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, false,
686                                               EShTexSampTransUpgradeTextureRemoveSampler);
687 
688         // Generate the hybrid output in the way of glslangValidator.
689         std::ostringstream stream;
690         outputResultToStream(&stream, result, controls);
691 
692         checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
693                                     expectedOutputFname, result.spirvWarningsErrors);
694     }
695 
options()696     glslang::SpvOptions& options() { return spirvOptions; }
697 
698 private:
699     const int defaultVersion;
700     const EProfile defaultProfile;
701     const bool forceVersionProfile;
702     const bool isForwardCompatible;
703     glslang::SpvOptions spirvOptions;
704 };
705 
706 }  // namespace glslangtest
707 
708 #endif  // GLSLANG_GTESTS_TEST_FIXTURE_H
709