Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 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 | |
John Kessenich | 66ec80e | 2016-08-05 14:04:23 -0600 | [diff] [blame] | 38 | #include <cstdint> |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 39 | #include <fstream> |
| 40 | #include <sstream> |
| 41 | #include <streambuf> |
| 42 | #include <tuple> |
steve-lunarg | a845641 | 2016-08-17 16:18:06 -0600 | [diff] [blame] | 43 | #include <string> |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 44 | |
| 45 | #include <gtest/gtest.h> |
| 46 | |
| 47 | #include "SPIRV/GlslangToSpv.h" |
| 48 | #include "SPIRV/disassemble.h" |
| 49 | #include "SPIRV/doc.h" |
steve-lunarg | a845641 | 2016-08-17 16:18:06 -0600 | [diff] [blame] | 50 | #include "SPIRV/SPVRemapper.h" |
Lei Zhang | 8a9b1ee | 2016-05-19 13:31:43 -0400 | [diff] [blame] | 51 | #include "StandAlone/ResourceLimits.h" |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 52 | #include "glslang/Public/ShaderLang.h" |
| 53 | |
| 54 | #include "Initializer.h" |
| 55 | #include "Settings.h" |
| 56 | |
| 57 | // We need CMake to provide us the absolute path to the directory containing |
| 58 | // test files, so we are certain to find those files no matter where the test |
| 59 | // harness binary is generated. This provides out-of-source build capability. |
| 60 | #ifndef GLSLANG_TEST_DIRECTORY |
| 61 | #error \ |
| 62 | "GLSLANG_TEST_DIRECTORY needs to be defined for gtest to locate test files." |
| 63 | #endif |
| 64 | |
| 65 | namespace glslangtest { |
| 66 | |
| 67 | // This function is used to provide custom test name suffixes based on the |
| 68 | // shader source file names. Otherwise, the test name suffixes will just be |
| 69 | // numbers, which are not quite obvious. |
Lei Zhang | d6f0ed2 | 2016-05-16 12:50:30 -0400 | [diff] [blame] | 70 | std::string FileNameAsCustomTestSuffix( |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 71 | const ::testing::TestParamInfo<std::string>& info); |
| 72 | |
Lei Zhang | d6f0ed2 | 2016-05-16 12:50:30 -0400 | [diff] [blame] | 73 | enum class Source { |
| 74 | GLSL, |
| 75 | HLSL, |
| 76 | }; |
| 77 | |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 78 | // Enum for shader compilation semantics. |
| 79 | enum class Semantics { |
| 80 | OpenGL, |
| 81 | Vulkan, |
| 82 | }; |
| 83 | |
| 84 | // Enum for compilation target. |
| 85 | enum class Target { |
| 86 | AST, |
Lei Zhang | d6f0ed2 | 2016-05-16 12:50:30 -0400 | [diff] [blame] | 87 | Spv, |
| 88 | BothASTAndSpv, |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 89 | }; |
| 90 | |
Lei Zhang | d6f0ed2 | 2016-05-16 12:50:30 -0400 | [diff] [blame] | 91 | EShLanguage GetShaderStage(const std::string& stage); |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 92 | |
Lei Zhang | d6f0ed2 | 2016-05-16 12:50:30 -0400 | [diff] [blame] | 93 | EShMessages DeriveOptions(Source, Semantics, Target); |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 94 | |
| 95 | // Reads the content of the file at the given |path|. On success, returns true |
| 96 | // and the contents; otherwise, returns false and an empty string. |
| 97 | std::pair<bool, std::string> ReadFile(const std::string& path); |
steve-lunarg | a845641 | 2016-08-17 16:18:06 -0600 | [diff] [blame] | 98 | std::pair<bool, std::vector<std::uint32_t> > ReadSpvBinaryFile(const std::string& path); |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 99 | |
| 100 | // Writes the given |contents| into the file at the given |path|. Returns true |
| 101 | // on successful output. |
| 102 | bool WriteFile(const std::string& path, const std::string& contents); |
| 103 | |
| 104 | // Returns the suffix of the given |name|. |
| 105 | std::string GetSuffix(const std::string& name); |
| 106 | |
| 107 | // Base class for glslang integration tests. It contains many handy utility-like |
| 108 | // methods such as reading shader source files, compiling into AST/SPIR-V, and |
| 109 | // comparing with expected outputs. |
| 110 | // |
| 111 | // To write value-Parameterized tests: |
| 112 | // using ValueParamTest = GlslangTest<::testing::TestWithParam<std::string>>; |
| 113 | // To use as normal fixture: |
| 114 | // using FixtureTest = GlslangTest<::testing::Test>; |
| 115 | template <typename GT> |
| 116 | class GlslangTest : public GT { |
| 117 | public: |
| 118 | GlslangTest() |
| 119 | : defaultVersion(100), |
| 120 | defaultProfile(ENoProfile), |
| 121 | forceVersionProfile(false), |
| 122 | isForwardCompatible(false) {} |
| 123 | |
| 124 | // Tries to load the contents from the file at the given |path|. On success, |
| 125 | // writes the contents into |contents|. On failure, errors out. |
| 126 | void tryLoadFile(const std::string& path, const std::string& tag, |
| 127 | std::string* contents) |
| 128 | { |
| 129 | bool fileReadOk; |
| 130 | std::tie(fileReadOk, *contents) = ReadFile(path); |
| 131 | ASSERT_TRUE(fileReadOk) << "Cannot open " << tag << " file: " << path; |
| 132 | } |
| 133 | |
steve-lunarg | a845641 | 2016-08-17 16:18:06 -0600 | [diff] [blame] | 134 | // Tries to load the contents from the file at the given |path|. On success, |
| 135 | // writes the contents into |contents|. On failure, errors out. |
| 136 | void tryLoadSpvFile(const std::string& path, const std::string& tag, |
| 137 | std::vector<uint32_t>& contents) |
| 138 | { |
| 139 | bool fileReadOk; |
| 140 | std::tie(fileReadOk, contents) = ReadSpvBinaryFile(path); |
| 141 | ASSERT_TRUE(fileReadOk) << "Cannot open " << tag << " file: " << path; |
| 142 | } |
| 143 | |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 144 | // Checks the equality of |expected| and |real|. If they are not equal, |
Lei Zhang | fc697cc | 2016-05-17 16:43:05 -0400 | [diff] [blame] | 145 | // write |real| to the given file named as |fname| if update mode is on. |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 146 | void checkEqAndUpdateIfRequested(const std::string& expected, |
| 147 | const std::string& real, |
| 148 | const std::string& fname) |
| 149 | { |
Lei Zhang | fc697cc | 2016-05-17 16:43:05 -0400 | [diff] [blame] | 150 | // In order to output the message we want under proper circumstances, |
| 151 | // we need the following operator<< stuff. |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 152 | EXPECT_EQ(expected, real) |
| 153 | << (GlobalTestSettings.updateMode |
| 154 | ? ("Mismatch found and update mode turned on - " |
| 155 | "flushing expected result output.") |
| 156 | : ""); |
| 157 | |
| 158 | // Update the expected output file if requested. |
| 159 | // It looks weird to duplicate the comparison between expected_output |
Lei Zhang | fc697cc | 2016-05-17 16:43:05 -0400 | [diff] [blame] | 160 | // and stream.str(). However, if creating a variable for the comparison |
| 161 | // result, we cannot have pretty print of the string diff in the above. |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 162 | if (GlobalTestSettings.updateMode && expected != real) { |
| 163 | EXPECT_TRUE(WriteFile(fname, real)) << "Flushing failed"; |
| 164 | } |
| 165 | } |
| 166 | |
Lei Zhang | 2f1ee45 | 2016-05-17 23:03:28 -0400 | [diff] [blame] | 167 | struct ShaderResult { |
| 168 | std::string shaderName; |
| 169 | std::string output; |
| 170 | std::string error; |
| 171 | }; |
| 172 | |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 173 | // A struct for holding all the information returned by glslang compilation |
| 174 | // and linking. |
| 175 | struct GlslangResult { |
Lei Zhang | 2f1ee45 | 2016-05-17 23:03:28 -0400 | [diff] [blame] | 176 | std::vector<ShaderResult> shaderResults; |
| 177 | std::string linkingOutput; |
| 178 | std::string linkingError; |
| 179 | std::string spirvWarningsErrors; |
| 180 | std::string spirv; // Optional SPIR-V disassembly text. |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 181 | }; |
| 182 | |
Lei Zhang | 2f1ee45 | 2016-05-17 23:03:28 -0400 | [diff] [blame] | 183 | // Compiles and the given source |code| of the given shader |stage| into |
| 184 | // the target under the semantics conveyed via |controls|. Returns true |
| 185 | // and modifies |shader| on success. |
| 186 | bool compile(glslang::TShader* shader, const std::string& code, |
Lei Zhang | 1b14172 | 2016-05-19 13:50:49 -0400 | [diff] [blame] | 187 | const std::string& entryPointName, EShMessages controls, |
| 188 | const TBuiltInResource* resources=nullptr) |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 189 | { |
Lei Zhang | d6f0ed2 | 2016-05-16 12:50:30 -0400 | [diff] [blame] | 190 | const char* shaderStrings = code.data(); |
| 191 | const int shaderLengths = static_cast<int>(code.size()); |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 192 | |
Lei Zhang | 2f1ee45 | 2016-05-17 23:03:28 -0400 | [diff] [blame] | 193 | shader->setStringsWithLengths(&shaderStrings, &shaderLengths, 1); |
| 194 | if (!entryPointName.empty()) shader->setEntryPoint(entryPointName.c_str()); |
Lei Zhang | 1b14172 | 2016-05-19 13:50:49 -0400 | [diff] [blame] | 195 | return shader->parse( |
| 196 | (resources ? resources : &glslang::DefaultTBuiltInResource), |
| 197 | defaultVersion, isForwardCompatible, controls); |
Lei Zhang | 2f1ee45 | 2016-05-17 23:03:28 -0400 | [diff] [blame] | 198 | } |
| 199 | |
| 200 | // Compiles and links the given source |code| of the given shader |
| 201 | // |stage| into the target under the semantics specified via |controls|. |
| 202 | // Returns a GlslangResult instance containing all the information generated |
| 203 | // during the process. If the target includes SPIR-V, also disassembles |
| 204 | // the result and returns disassembly text. |
| 205 | GlslangResult compileAndLink( |
| 206 | const std::string shaderName, const std::string& code, |
| 207 | const std::string& entryPointName, EShMessages controls) |
| 208 | { |
| 209 | const EShLanguage kind = GetShaderStage(GetSuffix(shaderName)); |
| 210 | |
| 211 | glslang::TShader shader(kind); |
| 212 | bool success = compile(&shader, code, entryPointName, controls); |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 213 | |
| 214 | glslang::TProgram program; |
| 215 | program.addShader(&shader); |
Lei Zhang | 2f1ee45 | 2016-05-17 23:03:28 -0400 | [diff] [blame] | 216 | success &= program.link(controls); |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 217 | |
Lei Zhang | 17535f7 | 2016-05-04 15:55:59 -0400 | [diff] [blame] | 218 | spv::SpvBuildLogger logger; |
Lei Zhang | 09caf12 | 2016-05-02 18:11:54 -0400 | [diff] [blame] | 219 | |
Lei Zhang | 2f1ee45 | 2016-05-17 23:03:28 -0400 | [diff] [blame] | 220 | if (success && (controls & EShMsgSpvRules)) { |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 221 | std::vector<uint32_t> spirv_binary; |
Lei Zhang | d6f0ed2 | 2016-05-16 12:50:30 -0400 | [diff] [blame] | 222 | glslang::GlslangToSpv(*program.getIntermediate(kind), |
Lei Zhang | 17535f7 | 2016-05-04 15:55:59 -0400 | [diff] [blame] | 223 | spirv_binary, &logger); |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 224 | |
| 225 | std::ostringstream disassembly_stream; |
| 226 | spv::Parameterize(); |
| 227 | spv::Disassemble(disassembly_stream, spirv_binary); |
Lei Zhang | 2f1ee45 | 2016-05-17 23:03:28 -0400 | [diff] [blame] | 228 | return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 229 | program.getInfoLog(), program.getInfoDebugLog(), |
Lei Zhang | 17535f7 | 2016-05-04 15:55:59 -0400 | [diff] [blame] | 230 | logger.getAllMessages(), disassembly_stream.str()}; |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 231 | } else { |
Lei Zhang | 2f1ee45 | 2016-05-17 23:03:28 -0400 | [diff] [blame] | 232 | return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, |
| 233 | program.getInfoLog(), program.getInfoDebugLog(), "", ""}; |
| 234 | } |
| 235 | } |
| 236 | |
steve-lunarg | a845641 | 2016-08-17 16:18:06 -0600 | [diff] [blame] | 237 | // This is like compileAndLink but with remapping of the SPV binary |
| 238 | // through spirvbin_t::remap(). While technically this could be merged |
| 239 | // with compileAndLink() above (with the remap step optionally being a no-op) |
| 240 | // it is given separately here for ease of future extraction. |
| 241 | GlslangResult compileLinkRemap( |
| 242 | const std::string shaderName, const std::string& code, |
| 243 | const std::string& entryPointName, EShMessages controls, |
| 244 | const unsigned int remapOptions = spv::spirvbin_t::NONE) |
| 245 | { |
| 246 | const EShLanguage kind = GetShaderStage(GetSuffix(shaderName)); |
| 247 | |
| 248 | glslang::TShader shader(kind); |
| 249 | bool success = compile(&shader, code, entryPointName, controls); |
| 250 | |
| 251 | glslang::TProgram program; |
| 252 | program.addShader(&shader); |
| 253 | success &= program.link(controls); |
| 254 | |
| 255 | spv::SpvBuildLogger logger; |
| 256 | |
| 257 | if (success && (controls & EShMsgSpvRules)) { |
| 258 | std::vector<uint32_t> spirv_binary; |
| 259 | glslang::GlslangToSpv(*program.getIntermediate(kind), |
| 260 | spirv_binary, &logger); |
| 261 | |
| 262 | spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, remapOptions); |
| 263 | |
| 264 | std::ostringstream disassembly_stream; |
| 265 | spv::Parameterize(); |
| 266 | spv::Disassemble(disassembly_stream, spirv_binary); |
| 267 | return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, |
| 268 | program.getInfoLog(), program.getInfoDebugLog(), |
| 269 | logger.getAllMessages(), disassembly_stream.str()}; |
| 270 | } else { |
| 271 | return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, |
| 272 | program.getInfoLog(), program.getInfoDebugLog(), "", ""}; |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | // remap the binary in 'code' with the options in remapOptions |
| 277 | GlslangResult remap( |
| 278 | const std::string shaderName, const std::vector<uint32_t>& code, |
| 279 | EShMessages controls, |
| 280 | const unsigned int remapOptions = spv::spirvbin_t::NONE) |
| 281 | { |
| 282 | if ((controls & EShMsgSpvRules)) { |
| 283 | std::vector<uint32_t> spirv_binary(code); // scratch copy |
| 284 | |
| 285 | spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, remapOptions); |
| 286 | |
| 287 | std::ostringstream disassembly_stream; |
| 288 | spv::Parameterize(); |
| 289 | spv::Disassemble(disassembly_stream, spirv_binary); |
| 290 | |
| 291 | return {{{shaderName, "", ""},}, |
| 292 | "", "", |
| 293 | "", disassembly_stream.str()}; |
| 294 | } else { |
| 295 | return {{{shaderName, "", ""},}, "", "", "", ""}; |
| 296 | } |
| 297 | } |
| 298 | |
Lei Zhang | 2f1ee45 | 2016-05-17 23:03:28 -0400 | [diff] [blame] | 299 | void outputResultToStream(std::ostringstream* stream, |
| 300 | const GlslangResult& result, |
| 301 | EShMessages controls) |
| 302 | { |
| 303 | const auto outputIfNotEmpty = [&stream](const std::string& str) { |
| 304 | if (!str.empty()) *stream << str << "\n"; |
| 305 | }; |
| 306 | |
| 307 | for (const auto& shaderResult : result.shaderResults) { |
| 308 | *stream << shaderResult.shaderName << "\n"; |
| 309 | outputIfNotEmpty(shaderResult.output); |
| 310 | outputIfNotEmpty(shaderResult.error); |
| 311 | } |
| 312 | outputIfNotEmpty(result.linkingOutput); |
| 313 | outputIfNotEmpty(result.linkingError); |
| 314 | *stream << result.spirvWarningsErrors; |
| 315 | |
| 316 | if (controls & EShMsgSpvRules) { |
| 317 | *stream |
| 318 | << (result.spirv.empty() |
| 319 | ? "SPIR-V is not generated for failed compile or link\n" |
| 320 | : result.spirv); |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 321 | } |
| 322 | } |
| 323 | |
| 324 | void loadFileCompileAndCheck(const std::string& testDir, |
| 325 | const std::string& testName, |
Lei Zhang | d6f0ed2 | 2016-05-16 12:50:30 -0400 | [diff] [blame] | 326 | Source source, |
| 327 | Semantics semantics, |
| 328 | Target target, |
| 329 | const std::string& entryPointName="") |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 330 | { |
| 331 | const std::string inputFname = testDir + "/" + testName; |
| 332 | const std::string expectedOutputFname = |
| 333 | testDir + "/baseResults/" + testName + ".out"; |
| 334 | std::string input, expectedOutput; |
| 335 | |
| 336 | tryLoadFile(inputFname, "input", &input); |
| 337 | tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); |
| 338 | |
Lei Zhang | 2f1ee45 | 2016-05-17 23:03:28 -0400 | [diff] [blame] | 339 | const EShMessages controls = DeriveOptions(source, semantics, target); |
John Kessenich | a86836e | 2016-07-09 14:50:57 -0600 | [diff] [blame] | 340 | GlslangResult result = compileAndLink(testName, input, entryPointName, controls); |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 341 | |
| 342 | // Generate the hybrid output in the way of glslangValidator. |
| 343 | std::ostringstream stream; |
Lei Zhang | 2f1ee45 | 2016-05-17 23:03:28 -0400 | [diff] [blame] | 344 | outputResultToStream(&stream, result, controls); |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 345 | |
| 346 | checkEqAndUpdateIfRequested(expectedOutput, stream.str(), |
| 347 | expectedOutputFname); |
| 348 | } |
| 349 | |
steve-lunarg | a845641 | 2016-08-17 16:18:06 -0600 | [diff] [blame] | 350 | void loadFileCompileRemapAndCheck(const std::string& testDir, |
| 351 | const std::string& testName, |
| 352 | Source source, |
| 353 | Semantics semantics, |
| 354 | Target target, |
| 355 | const std::string& entryPointName="", |
| 356 | const unsigned int remapOptions = spv::spirvbin_t::NONE) |
| 357 | { |
| 358 | const std::string inputFname = testDir + "/" + testName; |
| 359 | const std::string expectedOutputFname = |
| 360 | testDir + "/baseResults/" + testName + ".out"; |
| 361 | std::string input, expectedOutput; |
| 362 | |
| 363 | tryLoadFile(inputFname, "input", &input); |
| 364 | tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); |
| 365 | |
| 366 | const EShMessages controls = DeriveOptions(source, semantics, target); |
| 367 | GlslangResult result = compileLinkRemap(testName, input, entryPointName, controls, remapOptions); |
| 368 | |
| 369 | // Generate the hybrid output in the way of glslangValidator. |
| 370 | std::ostringstream stream; |
| 371 | outputResultToStream(&stream, result, controls); |
| 372 | |
| 373 | checkEqAndUpdateIfRequested(expectedOutput, stream.str(), |
| 374 | expectedOutputFname); |
| 375 | } |
| 376 | |
| 377 | void loadFileRemapAndCheck(const std::string& testDir, |
| 378 | const std::string& testName, |
| 379 | Source source, |
| 380 | Semantics semantics, |
| 381 | Target target, |
| 382 | const unsigned int remapOptions = spv::spirvbin_t::NONE) |
| 383 | { |
| 384 | const std::string inputFname = testDir + "/" + testName; |
| 385 | const std::string expectedOutputFname = |
| 386 | testDir + "/baseResults/" + testName + ".out"; |
| 387 | std::vector<std::uint32_t> input; |
| 388 | std::string expectedOutput; |
| 389 | |
| 390 | tryLoadSpvFile(inputFname, "input", input); |
| 391 | tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); |
| 392 | |
| 393 | const EShMessages controls = DeriveOptions(source, semantics, target); |
| 394 | GlslangResult result = remap(testName, input, controls, remapOptions); |
| 395 | |
| 396 | // Generate the hybrid output in the way of glslangValidator. |
| 397 | std::ostringstream stream; |
| 398 | outputResultToStream(&stream, result, controls); |
| 399 | |
| 400 | checkEqAndUpdateIfRequested(expectedOutput, stream.str(), |
| 401 | expectedOutputFname); |
| 402 | } |
| 403 | |
Lei Zhang | d6f0ed2 | 2016-05-16 12:50:30 -0400 | [diff] [blame] | 404 | // Preprocesses the given |source| code. On success, returns true, the |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 405 | // preprocessed shader, and warning messages. Otherwise, returns false, an |
| 406 | // empty string, and error messages. |
Lei Zhang | d6f0ed2 | 2016-05-16 12:50:30 -0400 | [diff] [blame] | 407 | std::tuple<bool, std::string, std::string> preprocess( |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 408 | const std::string& source) |
| 409 | { |
| 410 | const char* shaderStrings = source.data(); |
| 411 | const int shaderLengths = static_cast<int>(source.size()); |
| 412 | |
| 413 | glslang::TShader shader(EShLangVertex); |
| 414 | shader.setStringsWithLengths(&shaderStrings, &shaderLengths, 1); |
| 415 | std::string ppShader; |
| 416 | glslang::TShader::ForbidInclude includer; |
| 417 | const bool success = shader.preprocess( |
| 418 | &glslang::DefaultTBuiltInResource, defaultVersion, defaultProfile, |
John Kessenich | a86836e | 2016-07-09 14:50:57 -0600 | [diff] [blame] | 419 | forceVersionProfile, isForwardCompatible, (EShMessages)(EShMsgOnlyPreprocessor | EShMsgCascadingErrors), |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 420 | &ppShader, includer); |
| 421 | |
| 422 | std::string log = shader.getInfoLog(); |
| 423 | log += shader.getInfoDebugLog(); |
| 424 | if (success) { |
| 425 | return std::make_tuple(true, ppShader, log); |
| 426 | } else { |
| 427 | return std::make_tuple(false, "", log); |
| 428 | } |
| 429 | } |
| 430 | |
| 431 | void loadFilePreprocessAndCheck(const std::string& testDir, |
| 432 | const std::string& testName) |
| 433 | { |
| 434 | const std::string inputFname = testDir + "/" + testName; |
| 435 | const std::string expectedOutputFname = |
| 436 | testDir + "/baseResults/" + testName + ".out"; |
| 437 | const std::string expectedErrorFname = |
| 438 | testDir + "/baseResults/" + testName + ".err"; |
| 439 | std::string input, expectedOutput, expectedError; |
| 440 | |
| 441 | tryLoadFile(inputFname, "input", &input); |
| 442 | tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); |
| 443 | tryLoadFile(expectedErrorFname, "expected error", &expectedError); |
| 444 | |
| 445 | bool ppOk; |
| 446 | std::string output, error; |
Lei Zhang | d6f0ed2 | 2016-05-16 12:50:30 -0400 | [diff] [blame] | 447 | std::tie(ppOk, output, error) = preprocess(input); |
Lei Zhang | 414eb60 | 2016-03-04 16:22:34 -0500 | [diff] [blame] | 448 | if (!output.empty()) output += '\n'; |
| 449 | if (!error.empty()) error += '\n'; |
| 450 | |
| 451 | checkEqAndUpdateIfRequested(expectedOutput, output, |
| 452 | expectedOutputFname); |
| 453 | checkEqAndUpdateIfRequested(expectedError, error, |
| 454 | expectedErrorFname); |
| 455 | } |
| 456 | |
| 457 | private: |
| 458 | const int defaultVersion; |
| 459 | const EProfile defaultProfile; |
| 460 | const bool forceVersionProfile; |
| 461 | const bool isForwardCompatible; |
| 462 | }; |
| 463 | |
| 464 | } // namespace glslangtest |
| 465 | |
| 466 | #endif // GLSLANG_GTESTS_TEST_FIXTURE_H |