Merge pull request #2826 from amdrexu/bugfix
[platform/upstream/glslang.git] / gtests / TestFixture.h
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:
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.
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.
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 #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
257         if (success)
258             program.mapIO();
259 #endif
260
261         if (success && (controls & EShMsgSpvRules)) {
262             spv::SpvBuildLogger logger;
263             std::vector<uint32_t> spirv_binary;
264             options().disableOptimizer = !enableOptimizer;
265             options().generateDebugInfo = enableDebug;
266             glslang::GlslangToSpv(*program.getIntermediate(stage),
267                                   spirv_binary, &logger, &options());
268
269             std::ostringstream disassembly_stream;
270             spv::Parameterize();
271             spv::Disassemble(disassembly_stream, spirv_binary);
272             bool validation_result = !options().validate || logger.getAllMessages().empty();
273             return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
274                     program.getInfoLog(), program.getInfoDebugLog(),
275                     validation_result, logger.getAllMessages(), disassembly_stream.str()};
276         } else {
277             return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
278                     program.getInfoLog(), program.getInfoDebugLog(), true, "", ""};
279         }
280     }
281
282     // Compiles and links the given source |code| of the given shader
283     // |stage| into the target under the semantics specified via |controls|.
284     // Returns a GlslangResult instance containing all the information generated
285     // during the process. If the target includes SPIR-V, also disassembles
286     // the result and returns disassembly text.
287     GlslangResult compileLinkIoMap(
288             const std::string shaderName, const std::string& code,
289             const std::string& entryPointName, EShMessages controls,
290             int baseSamplerBinding,
291             int baseTextureBinding,
292             int baseImageBinding,
293             int baseUboBinding,
294             int baseSsboBinding,
295             bool autoMapBindings,
296             bool flattenUniformArrays)
297     {
298         const EShLanguage stage = GetShaderStage(GetSuffix(shaderName));
299
300         glslang::TShader shader(stage);
301         shader.setShiftSamplerBinding(baseSamplerBinding);
302         shader.setShiftTextureBinding(baseTextureBinding);
303         shader.setShiftImageBinding(baseImageBinding);
304         shader.setShiftUboBinding(baseUboBinding);
305         shader.setShiftSsboBinding(baseSsboBinding);
306         shader.setAutoMapBindings(autoMapBindings);
307         shader.setAutoMapLocations(true);
308 #ifdef ENABLE_HLSL
309         shader.setFlattenUniformArrays(flattenUniformArrays);
310 #endif
311
312         bool success = compile(&shader, code, entryPointName, controls);
313
314         glslang::TProgram program;
315         program.addShader(&shader);
316         
317         success &= program.link(controls);
318 #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
319         if (success)
320             program.mapIO();
321 #endif
322
323         spv::SpvBuildLogger logger;
324
325         if (success && (controls & EShMsgSpvRules)) {
326             std::vector<uint32_t> spirv_binary;
327             glslang::GlslangToSpv(*program.getIntermediate(stage),
328                                   spirv_binary, &logger, &options());
329
330             std::ostringstream disassembly_stream;
331             spv::Parameterize();
332             spv::Disassemble(disassembly_stream, spirv_binary);
333             bool validation_result = !options().validate || logger.getAllMessages().empty();
334             return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
335                     program.getInfoLog(), program.getInfoDebugLog(),
336                     validation_result, logger.getAllMessages(), disassembly_stream.str()};
337         } else {
338             return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
339                     program.getInfoLog(), program.getInfoDebugLog(), true, "", ""};
340         }
341     }
342
343     // This is like compileAndLink but with remapping of the SPV binary
344     // through spirvbin_t::remap().  While technically this could be merged
345     // with compileAndLink() above (with the remap step optionally being a no-op)
346     // it is given separately here for ease of future extraction.
347     GlslangResult compileLinkRemap(
348             const std::string shaderName, const std::string& code,
349             const std::string& entryPointName, EShMessages controls,
350             const unsigned int remapOptions = spv::spirvbin_t::NONE)
351     {
352         const EShLanguage stage = GetShaderStage(GetSuffix(shaderName));
353
354         glslang::TShader shader(stage);
355         shader.setAutoMapBindings(true);
356         shader.setAutoMapLocations(true);
357
358         bool success = compile(&shader, code, entryPointName, controls);
359
360         glslang::TProgram program;
361         program.addShader(&shader);
362         success &= program.link(controls);
363 #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
364         if (success)
365             program.mapIO();
366 #endif
367
368         if (success && (controls & EShMsgSpvRules)) {
369         spv::SpvBuildLogger logger;
370             std::vector<uint32_t> spirv_binary;
371             glslang::GlslangToSpv(*program.getIntermediate(stage),
372                                   spirv_binary, &logger, &options());
373
374             spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, remapOptions);
375
376             std::ostringstream disassembly_stream;
377             spv::Parameterize();
378             spv::Disassemble(disassembly_stream, spirv_binary);
379             bool validation_result = !options().validate || logger.getAllMessages().empty();
380             return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
381                     program.getInfoLog(), program.getInfoDebugLog(),
382                     validation_result, logger.getAllMessages(), disassembly_stream.str()};
383         } else {
384             return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
385                     program.getInfoLog(), program.getInfoDebugLog(), true, "", ""};
386         }
387     }
388
389     // remap the binary in 'code' with the options in remapOptions
390     GlslangResult remap(
391             const std::string shaderName, const std::vector<uint32_t>& code,
392             EShMessages controls,
393             const unsigned int remapOptions = spv::spirvbin_t::NONE)
394     {
395         if ((controls & EShMsgSpvRules)) {
396             std::vector<uint32_t> spirv_binary(code); // scratch copy
397
398             spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, remapOptions);
399             
400             std::ostringstream disassembly_stream;
401             spv::Parameterize();
402             spv::Disassemble(disassembly_stream, spirv_binary);
403
404             return {{{shaderName, "", ""},},
405                     "", "",
406                     true, "", disassembly_stream.str()};
407         } else {
408             return {{{shaderName, "", ""},}, "", "", true, "", ""};
409         }
410     }
411
412     void outputResultToStream(std::ostringstream* stream,
413                               const GlslangResult& result,
414                               EShMessages controls)
415     {
416         const auto outputIfNotEmpty = [&stream](const std::string& str) {
417             if (!str.empty()) *stream << str << "\n";
418         };
419
420         for (const auto& shaderResult : result.shaderResults) {
421             *stream << shaderResult.shaderName << "\n";
422             outputIfNotEmpty(shaderResult.output);
423             outputIfNotEmpty(shaderResult.error);
424         }
425         outputIfNotEmpty(result.linkingOutput);
426         outputIfNotEmpty(result.linkingError);
427         if (!result.validationResult) {
428           *stream << "Validation failed\n";
429         }
430
431         if (controls & EShMsgSpvRules) {
432             *stream
433                 << (result.spirv.empty()
434                         ? "SPIR-V is not generated for failed compile or link\n"
435                         : result.spirv);
436         }
437     }
438
439     void loadFileCompileAndCheck(const std::string& testDir,
440                                  const std::string& testName,
441                                  Source source,
442                                  Semantics semantics,
443                                  glslang::EShTargetClientVersion clientTargetVersion,
444                                  glslang::EShTargetLanguageVersion targetLanguageVersion,
445                                  Target target,
446                                  bool automap = true,
447                                  const std::string& entryPointName="",
448                                  const std::string& baseDir="/baseResults/",
449                                  const bool enableOptimizer = false,
450                                  const bool enableDebug = false)
451     {
452         const std::string inputFname = testDir + "/" + testName;
453         const std::string expectedOutputFname =
454             testDir + baseDir + testName + ".out";
455         std::string input, expectedOutput;
456
457         tryLoadFile(inputFname, "input", &input);
458         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
459
460         EShMessages controls = DeriveOptions(source, semantics, target);
461         if (enableOptimizer)
462             controls = static_cast<EShMessages>(controls & ~EShMsgHlslLegalization);
463         if (enableDebug)
464             controls = static_cast<EShMessages>(controls | EShMsgDebugInfo);
465         GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion,
466             targetLanguageVersion, false, EShTexSampTransKeep, enableOptimizer, enableDebug, automap);
467
468         // Generate the hybrid output in the way of glslangValidator.
469         std::ostringstream stream;
470         outputResultToStream(&stream, result, controls);
471
472         checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
473                                     expectedOutputFname, result.spirvWarningsErrors);
474     }
475
476     void loadFileCompileAndCheckWithOptions(const std::string &testDir,
477                                             const std::string &testName,
478                                             Source source,
479                                             Semantics semantics,
480                                             glslang::EShTargetClientVersion clientTargetVersion,
481                                             glslang::EShTargetLanguageVersion targetLanguageVersion,
482                                             Target target, bool automap = true, const std::string &entryPointName = "",
483                                             const std::string &baseDir = "/baseResults/",
484                                             const EShMessages additionalOptions = EShMessages::EShMsgDefault)
485     {
486         const std::string inputFname = testDir + "/" + testName;
487         const std::string expectedOutputFname = testDir + baseDir + testName + ".out";
488         std::string input, expectedOutput;
489
490         tryLoadFile(inputFname, "input", &input);
491         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
492
493         EShMessages controls = DeriveOptions(source, semantics, target);
494         controls = static_cast<EShMessages>(controls | additionalOptions);
495         GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion,
496             targetLanguageVersion, false, EShTexSampTransKeep, false, automap);
497
498         // Generate the hybrid output in the way of glslangValidator.
499         std::ostringstream stream;
500         outputResultToStream(&stream, result, controls);
501
502         checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname);
503     }
504
505     void loadFileCompileFlattenUniformsAndCheck(const std::string& testDir,
506                                                 const std::string& testName,
507                                                 Source source,
508                                                 Semantics semantics,
509                                                 Target target,
510                                                 const std::string& entryPointName="")
511     {
512         const std::string inputFname = testDir + "/" + testName;
513         const std::string expectedOutputFname =
514             testDir + "/baseResults/" + testName + ".out";
515         std::string input, expectedOutput;
516
517         tryLoadFile(inputFname, "input", &input);
518         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
519
520         const EShMessages controls = DeriveOptions(source, semantics, target);
521         GlslangResult result = compileAndLink(testName, input, entryPointName, controls,
522                                               glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, true);
523
524         // Generate the hybrid output in the way of glslangValidator.
525         std::ostringstream stream;
526         outputResultToStream(&stream, result, controls);
527
528         checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
529                                     expectedOutputFname, result.spirvWarningsErrors);
530     }
531
532     void loadFileCompileIoMapAndCheck(const std::string& testDir,
533                                       const std::string& testName,
534                                       Source source,
535                                       Semantics semantics,
536                                       Target target,
537                                       const std::string& entryPointName,
538                                       int baseSamplerBinding,
539                                       int baseTextureBinding,
540                                       int baseImageBinding,
541                                       int baseUboBinding,
542                                       int baseSsboBinding,
543                                       bool autoMapBindings,
544                                       bool flattenUniformArrays)
545     {
546         const std::string inputFname = testDir + "/" + testName;
547         const std::string expectedOutputFname =
548             testDir + "/baseResults/" + testName + ".out";
549         std::string input, expectedOutput;
550
551         tryLoadFile(inputFname, "input", &input);
552         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
553
554         const EShMessages controls = DeriveOptions(source, semantics, target);
555         GlslangResult result = compileLinkIoMap(testName, input, entryPointName, controls,
556                                                 baseSamplerBinding, baseTextureBinding, baseImageBinding,
557                                                 baseUboBinding, baseSsboBinding,
558                                                 autoMapBindings,
559                                                 flattenUniformArrays);
560
561         // Generate the hybrid output in the way of glslangValidator.
562         std::ostringstream stream;
563         outputResultToStream(&stream, result, controls);
564
565         checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
566                                     expectedOutputFname, result.spirvWarningsErrors);
567     }
568
569     void loadFileCompileRemapAndCheck(const std::string& testDir,
570                                       const std::string& testName,
571                                       Source source,
572                                       Semantics semantics,
573                                       Target target,
574                                       const std::string& entryPointName="",
575                                       const unsigned int remapOptions = spv::spirvbin_t::NONE)
576     {
577         const std::string inputFname = testDir + "/" + testName;
578         const std::string expectedOutputFname =
579             testDir + "/baseResults/" + testName + ".out";
580         std::string input, expectedOutput;
581
582         tryLoadFile(inputFname, "input", &input);
583         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
584
585         const EShMessages controls = DeriveOptions(source, semantics, target);
586         GlslangResult result = compileLinkRemap(testName, input, entryPointName, controls, remapOptions);
587
588         // Generate the hybrid output in the way of glslangValidator.
589         std::ostringstream stream;
590         outputResultToStream(&stream, result, controls);
591
592         checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
593                                     expectedOutputFname, result.spirvWarningsErrors);
594     }
595
596     void loadFileRemapAndCheck(const std::string& testDir,
597                                const std::string& testName,
598                                Source source,
599                                Semantics semantics,
600                                Target target,
601                                const unsigned int remapOptions = spv::spirvbin_t::NONE)
602     {
603         const std::string inputFname = testDir + "/" + testName;
604         const std::string expectedOutputFname =
605             testDir + "/baseResults/" + testName + ".out";
606         std::vector<std::uint32_t> input;
607         std::string expectedOutput;
608
609         tryLoadSpvFile(inputFname, "input", input);
610         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
611
612         const EShMessages controls = DeriveOptions(source, semantics, target);
613         GlslangResult result = remap(testName, input, controls, remapOptions);
614
615         // Generate the hybrid output in the way of glslangValidator.
616         std::ostringstream stream;
617         outputResultToStream(&stream, result, controls);
618
619         checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
620                                     expectedOutputFname, result.spirvWarningsErrors);
621     }
622
623     // Preprocesses the given |source| code. On success, returns true, the
624     // preprocessed shader, and warning messages. Otherwise, returns false, an
625     // empty string, and error messages.
626     std::tuple<bool, std::string, std::string> preprocess(
627         const std::string& source)
628     {
629         const char* shaderStrings = source.data();
630         const int shaderLengths = static_cast<int>(source.size());
631
632         glslang::TShader shader(EShLangVertex);
633         shader.setStringsWithLengths(&shaderStrings, &shaderLengths, 1);
634         std::string ppShader;
635         glslang::TShader::ForbidIncluder includer;
636         const bool success = shader.preprocess(
637             &glslang::DefaultTBuiltInResource, defaultVersion, defaultProfile,
638             forceVersionProfile, isForwardCompatible, (EShMessages)(EShMsgOnlyPreprocessor | EShMsgCascadingErrors),
639             &ppShader, includer);
640
641         std::string log = shader.getInfoLog();
642         log += shader.getInfoDebugLog();
643         if (success) {
644             return std::make_tuple(true, ppShader, log);
645         } else {
646             return std::make_tuple(false, "", log);
647         }
648     }
649
650     void loadFilePreprocessAndCheck(const std::string& testDir,
651                                     const std::string& testName)
652     {
653         const std::string inputFname = testDir + "/" + testName;
654         const std::string expectedOutputFname =
655             testDir + "/baseResults/" + testName + ".out";
656         const std::string expectedErrorFname =
657             testDir + "/baseResults/" + testName + ".err";
658         std::string input, expectedOutput, expectedError;
659
660         tryLoadFile(inputFname, "input", &input);
661         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
662         tryLoadFile(expectedErrorFname, "expected error", &expectedError);
663
664         bool ppOk;
665         std::string output, error;
666         std::tie(ppOk, output, error) = preprocess(input);
667         if (!output.empty()) output += '\n';
668         if (!error.empty()) error += '\n';
669
670         checkEqAndUpdateIfRequested(expectedOutput, output,
671                                     expectedOutputFname);
672         checkEqAndUpdateIfRequested(expectedError, error,
673                                     expectedErrorFname);
674     }
675
676     void loadCompileUpgradeTextureToSampledTextureAndDropSamplersAndCheck(const std::string& testDir,
677                                                                           const std::string& testName,
678                                                                           Source source,
679                                                                           Semantics semantics,
680                                                                           Target target,
681                                                                           const std::string& entryPointName = "")
682     {
683         const std::string inputFname = testDir + "/" + testName;
684         const std::string expectedOutputFname = testDir + "/baseResults/" + testName + ".out";
685         std::string input, expectedOutput;
686
687         tryLoadFile(inputFname, "input", &input);
688         tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
689
690         const EShMessages controls = DeriveOptions(source, semantics, target);
691         GlslangResult result = compileAndLink(testName, input, entryPointName, controls,
692                                               glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, false,
693                                               EShTexSampTransUpgradeTextureRemoveSampler);
694
695         // Generate the hybrid output in the way of glslangValidator.
696         std::ostringstream stream;
697         outputResultToStream(&stream, result, controls);
698
699         checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
700                                     expectedOutputFname, result.spirvWarningsErrors);
701     }
702
703     glslang::SpvOptions& options() { return spirvOptions; }
704
705 private:
706     const int defaultVersion;
707     const EProfile defaultProfile;
708     const bool forceVersionProfile;
709     const bool isForwardCompatible;
710     glslang::SpvOptions spirvOptions;
711 };
712
713 }  // namespace glslangtest
714
715 #endif  // GLSLANG_GTESTS_TEST_FIXTURE_H