diff --git a/src/test_main.cc b/src/test_main.cc index 7a8b394f2f..30e2645d9b 100644 --- a/src/test_main.cc +++ b/src/test_main.cc @@ -15,6 +15,8 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "src/debug.h" +#include "src/utils/command.h" +#include "src/writer/hlsl/test_helper.h" namespace { @@ -22,12 +24,75 @@ void TintInternalCompilerErrorReporter(const tint::diag::List& diagnostics) { FAIL() << diagnostics.str(); } +struct Flags { + bool validate_hlsl = false; + std::string dxc_path; + + bool parse(int argc, char** argv) { + bool errored = false; + for (int i = 1; i < argc && !errored; i++) { + auto match = [&](std::string name) { return name == argv[i]; }; + + auto parse_value = [&](std::string name, std::string& value) { + if (!match(name)) { + return false; + } + if (i + 1 >= argc) { + std::cout << "Expected value for flag " << name << "" << std::endl; + errored = true; + return false; + } + i++; + value = argv[i]; + return true; + }; + + if (match("--validate-hlsl") || parse_value("--dxc-path", dxc_path)) { + validate_hlsl = true; + } else { + std::cout << "Unknown flag '" << argv[i] << "'" << std::endl; + return false; + } + } + return true; + } +}; + } // namespace // Entry point for tint unit tests int main(int argc, char** argv) { testing::InitGoogleMock(&argc, argv); + Flags flags; + if (!flags.parse(argc, argv)) { + return -1; + } + +#if TINT_BUILD_HLSL_WRITER + // This must be kept alive for the duration of RUN_ALL_TESTS() as the c_str() + // is passed into tint::writer::hlsl::EnableHLSLValidation(), which does not + // make a copy. This is to work around Chromium's strict rules on globals + // having no constructors / destructors. + std::string dxc_path; + if (flags.validate_hlsl) { + auto dxc = flags.dxc_path.empty() ? tint::utils::Command::LookPath("dxc") + : tint::utils::Command(flags.dxc_path); + + if (!dxc.Found()) { + std::cout << "DXC executable not found" << std::endl; + return -1; + } + + std::cout << "HLSL validation with DXC enabled" << std::endl; + + dxc_path = dxc.Path(); + tint::writer::hlsl::EnableHLSLValidation(dxc_path.c_str()); + } else { + std::cout << "HLSL validation with DXC is not enabled" << std::endl; + } +#endif // TINT_BUILD_HLSL_WRITER + tint::SetInternalCompilerErrorReporter(&TintInternalCompilerErrorReporter); auto res = RUN_ALL_TESTS(); diff --git a/src/utils/command.h b/src/utils/command.h index c9784800f9..213c7dc390 100644 --- a/src/utils/command.h +++ b/src/utils/command.h @@ -52,6 +52,9 @@ class Command { /// constructor bool Found() const; + /// @returns the path of the command + const std::string& Path() const { return path_; } + /// Invokes the command with the given argument strings, blocking until the /// process has returned. /// @param args the string arguments to pass to the process diff --git a/src/writer/hlsl/test_helper.cc b/src/writer/hlsl/test_helper.cc index 12f6828664..9a7b453590 100644 --- a/src/writer/hlsl/test_helper.cc +++ b/src/writer/hlsl/test_helper.cc @@ -21,12 +21,28 @@ namespace tint { namespace writer { namespace hlsl { +namespace { + +const char* dxc_path = nullptr; + +} // namespace + +void EnableHLSLValidation(const char* dxc) { + dxc_path = dxc; +} + CompileResult Compile(Program* program, GeneratorImpl* generator) { CompileResult result; - auto dxc = utils::Command::LookPath("dxc"); + if (!dxc_path) { + result.status = CompileResult::Status::kVerificationNotEnabled; + return result; + } + + auto dxc = utils::Command(dxc_path); if (!dxc.Found()) { - result.status = CompileResult::Status::kDXCNotFound; + result.output = "DXC not found at '" + std::string(dxc_path) + "'"; + result.status = CompileResult::Status::kFailed; return result; } diff --git a/src/writer/hlsl/test_helper.h b/src/writer/hlsl/test_helper.h index a92e2c1973..2b169f7ad0 100644 --- a/src/writer/hlsl/test_helper.h +++ b/src/writer/hlsl/test_helper.h @@ -31,10 +31,15 @@ namespace tint { namespace writer { namespace hlsl { +/// EnableHLSLValidation enables verification of HLSL shaders by running DXC and +/// checking no errors are reported. +/// @param dxc_path the path to the DXC executable +void EnableHLSLValidation(const char* dxc_path); + /// The return structure of Compile() struct CompileResult { /// Status is an enumerator of status codes from Compile() - enum class Status { kSuccess, kFailed, kDXCNotFound }; + enum class Status { kSuccess, kFailed, kVerificationNotEnabled }; /// The resulting status of the compile Status status; /// Output of DXC.