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