From 2f1ee4525da5bb5e058245c29a47a477dd159e33 Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Tue, 17 May 2016 23:03:28 -0400 Subject: [PATCH] Run link tests in the GTest framework. --- gtests/CMakeLists.txt | 1 + gtests/Link.FromFile.cpp | 107 +++++++++++++++++++++++++++++++++++++++++++ gtests/TestFixture.h | 117 ++++++++++++++++++++++++++++------------------- 3 files changed, 177 insertions(+), 48 deletions(-) create mode 100644 gtests/Link.FromFile.cpp diff --git a/gtests/CMakeLists.txt b/gtests/CMakeLists.txt index d247a91..fe122c5 100644 --- a/gtests/CMakeLists.txt +++ b/gtests/CMakeLists.txt @@ -14,6 +14,7 @@ if (TARGET gmock) ${CMAKE_CURRENT_SOURCE_DIR}/AST.FromFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BuiltInResource.FromFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Hlsl.FromFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Link.FromFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Pp.FromFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Spv.FromFile.cpp ) diff --git a/gtests/Link.FromFile.cpp b/gtests/Link.FromFile.cpp new file mode 100644 index 0000000..6db43d9 --- /dev/null +++ b/gtests/Link.FromFile.cpp @@ -0,0 +1,107 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include + +#include + +#include "TestFixture.h" + +namespace glslangtest { +namespace { + +using LinkTest = GlslangTest< + ::testing::TestWithParam>>; + +TEST_P(LinkTest, FromFile) +{ + const auto& fileNames = GetParam(); + const size_t fileCount = fileNames.size(); + const EShMessages controls = DeriveOptions( + Source::GLSL, Semantics::OpenGL, Target::AST); + GlslangResult result; + + // Compile each input shader file. + std::vector> shaders; + for (size_t i = 0; i < fileCount; ++i) { + std::string contents; + tryLoadFile(GLSLANG_TEST_DIRECTORY "/" + fileNames[i], + "input", &contents); + shaders.emplace_back( + new glslang::TShader(GetShaderStage(GetSuffix(fileNames[i])))); + auto* shader = shaders.back().get(); + compile(shader, contents, "", controls); + result.shaderResults.push_back( + {fileNames[i], shader->getInfoLog(), shader->getInfoDebugLog()}); + } + + // Link all of them. + glslang::TProgram program; + for (const auto& shader : shaders) program.addShader(shader.get()); + program.link(controls); + result.linkingOutput = program.getInfoLog(); + result.linkingError = program.getInfoDebugLog(); + + std::ostringstream stream; + outputResultToStream(&stream, result, controls); + + // Check with expected results. + const std::string expectedOutputFname = + GLSLANG_TEST_DIRECTORY "/baseResults/" + fileNames.front() + ".out"; + std::string expectedOutput; + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + + checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname); +} + +// clang-format off +INSTANTIATE_TEST_CASE_P( + Glsl, LinkTest, + ::testing::ValuesIn(std::vector>({ + {"mains1.frag", "mains2.frag", "noMain1.geom", "noMain2.geom"}, + {"noMain.vert", "mains.frag"}, + {"link1.frag", "link2.frag", "link3.frag"}, + {"recurse1.vert", "recurse1.frag", "recurse2.frag"}, + {"300link.frag"}, + {"300link2.frag"}, + {"300link3.frag"}, + {"empty.frag", "empty2.frag", "empty3.frag"}, + {"150.tesc", "150.tese", "400.tesc", "400.tese", "410.tesc", "420.tesc", "420.tese"}, + {"max_vertices_0.geom"}, + })), +); +// clang-format on + +} // anonymous namespace +} // namespace glslangtest diff --git a/gtests/TestFixture.h b/gtests/TestFixture.h index 0b389ad..1d692ed 100644 --- a/gtests/TestFixture.h +++ b/gtests/TestFixture.h @@ -151,48 +151,61 @@ public: } } + struct ShaderResult { + std::string shaderName; + std::string output; + std::string error; + }; + // A struct for holding all the information returned by glslang compilation // and linking. struct GlslangResult { - const std::string compilationOutput; - const std::string compilationError; - const std::string linkingOutput; - const std::string linkingError; - const std::string spirvWarningsErrors; - const std::string spirv; // Optional SPIR-V disassembly text. + std::vector shaderResults; + std::string linkingOutput; + std::string linkingError; + std::string spirvWarningsErrors; + std::string spirv; // Optional SPIR-V disassembly text. }; - // Compiles and links the given source |code| of the given shader - // |stage| into the given |target| under the given |semantics|. Returns - // a GlslangResult instance containing all the information generated - // during the process. If |target| is Target::Spirv, also disassembles - // the result and returns disassembly text. - GlslangResult compile(const std::string& code, Source source, - const std::string& stage, Semantics semantics, - Target target, const std::string& entryPointName) + // Compiles and the given source |code| of the given shader |stage| into + // the target under the semantics conveyed via |controls|. Returns true + // and modifies |shader| on success. + bool compile(glslang::TShader* shader, const std::string& code, + const std::string& entryPointName, EShMessages controls) { const char* shaderStrings = code.data(); const int shaderLengths = static_cast(code.size()); - const EShLanguage kind = GetShaderStage(stage); - glslang::TShader shader(kind); - shader.setStringsWithLengths(&shaderStrings, &shaderLengths, 1); - if (!entryPointName.empty()) shader.setEntryPoint(entryPointName.c_str()); - const EShMessages messages = DeriveOptions(source, semantics, target); + shader->setStringsWithLengths(&shaderStrings, &shaderLengths, 1); + if (!entryPointName.empty()) shader->setEntryPoint(entryPointName.c_str()); // Reinitialize glslang if the semantics change. GlslangInitializer::InitializationToken token = - GlobalTestSettings.initializer->acquire(messages); - bool success = - shader.parse(&glslang::DefaultTBuiltInResource, defaultVersion, - isForwardCompatible, messages); + GlobalTestSettings.initializer->acquire(controls); + return shader->parse(&glslang::DefaultTBuiltInResource, defaultVersion, + isForwardCompatible, controls); + } + + // Compiles and links the given source |code| of the given shader + // |stage| into the target under the semantics specified via |controls|. + // Returns a GlslangResult instance containing all the information generated + // during the process. If the target includes SPIR-V, also disassembles + // the result and returns disassembly text. + GlslangResult compileAndLink( + const std::string shaderName, const std::string& code, + const std::string& entryPointName, EShMessages controls) + { + const EShLanguage kind = GetShaderStage(GetSuffix(shaderName)); + + glslang::TShader shader(kind); + bool success = compile(&shader, code, entryPointName, controls); glslang::TProgram program; program.addShader(&shader); - success &= program.link(messages); + success &= program.link(controls); spv::SpvBuildLogger logger; - if (success && (target == Target::Spv || target == Target::BothASTAndSpv)) { + if (success && (controls & EShMsgSpvRules)) { std::vector spirv_binary; glslang::GlslangToSpv(*program.getIntermediate(kind), spirv_binary, &logger); @@ -200,13 +213,37 @@ public: std::ostringstream disassembly_stream; spv::Parameterize(); spv::Disassemble(disassembly_stream, spirv_binary); - return {shader.getInfoLog(), shader.getInfoDebugLog(), + return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, program.getInfoLog(), program.getInfoDebugLog(), logger.getAllMessages(), disassembly_stream.str()}; } else { - return {shader.getInfoLog(), shader.getInfoDebugLog(), - program.getInfoLog(), program.getInfoDebugLog(), - "", ""}; + return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, + program.getInfoLog(), program.getInfoDebugLog(), "", ""}; + } + } + + void outputResultToStream(std::ostringstream* stream, + const GlslangResult& result, + EShMessages controls) + { + const auto outputIfNotEmpty = [&stream](const std::string& str) { + if (!str.empty()) *stream << str << "\n"; + }; + + for (const auto& shaderResult : result.shaderResults) { + *stream << shaderResult.shaderName << "\n"; + outputIfNotEmpty(shaderResult.output); + outputIfNotEmpty(shaderResult.error); + } + outputIfNotEmpty(result.linkingOutput); + outputIfNotEmpty(result.linkingError); + *stream << result.spirvWarningsErrors; + + if (controls & EShMsgSpvRules) { + *stream + << (result.spirv.empty() + ? "SPIR-V is not generated for failed compile or link\n" + : result.spirv); } } @@ -225,29 +262,13 @@ public: tryLoadFile(inputFname, "input", &input); tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + const EShMessages controls = DeriveOptions(source, semantics, target); GlslangResult result = - compile(input, source, GetSuffix(testName), - semantics, target, entryPointName); + compileAndLink(testName, input, entryPointName, controls); // Generate the hybrid output in the way of glslangValidator. std::ostringstream stream; - - const auto outputIfNotEmpty = [&stream](const std::string& str) { - if (!str.empty()) stream << str << "\n"; - }; - - stream << testName << "\n"; - outputIfNotEmpty(result.compilationOutput); - outputIfNotEmpty(result.compilationError); - outputIfNotEmpty(result.linkingOutput); - outputIfNotEmpty(result.linkingError); - stream << result.spirvWarningsErrors; - if (target == Target::Spv || target == Target::BothASTAndSpv) { - stream - << (result.spirv.empty() - ? "SPIR-V is not generated for failed compile or link\n" - : result.spirv); - } + outputResultToStream(&stream, result, controls); checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname); -- 2.7.4