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