summaryrefslogtreecommitdiffstats
path: root/tools/slang-test/main.cpp
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2019-01-07 09:14:01 -0500
committerGitHub <noreply@github.com>2019-01-07 09:14:01 -0500
commitd155eaa92d56a4ec00109d25c8c70fe12fb96c2e (patch)
tree55355d0aa67f99727f520ee3b210c27a647a12a9 /tools/slang-test/main.cpp
parentefa2c8f41aa5cd2c27990fd9b57ea0eff06976e7 (diff)
Feature/unique tool source names (#766)
* Remove AppContext. Use StdChannels to hold writers, and TestToolUtil to hold test tool specific functionality. * StdChannels -> StdWriters * getStdOut -> getOut, getStdError -> getError * Renamed main.cpp files of tools to try and stop visual studio getting confused between files - such that clicking on an error takes editor to the right location.
Diffstat (limited to 'tools/slang-test/main.cpp')
-rw-r--r--tools/slang-test/main.cpp1905
1 files changed, 0 insertions, 1905 deletions
diff --git a/tools/slang-test/main.cpp b/tools/slang-test/main.cpp
deleted file mode 100644
index 102b0b961..000000000
--- a/tools/slang-test/main.cpp
+++ /dev/null
@@ -1,1905 +0,0 @@
-// main.cpp
-
-#include "../../source/core/slang-io.h"
-#include "../../source/core/token-reader.h"
-#include "../../source/core/slang-std-writers.h"
-
-#include "../../slang-com-helper.h"
-
-#include "../../source/core/slang-string-util.h"
-
-using namespace Slang;
-
-#include "os.h"
-#include "render-api-util.h"
-#include "test-context.h"
-#include "test-reporter.h"
-#include "options.h"
-#include "slangc-tool.h"
-
-#define STB_IMAGE_IMPLEMENTATION
-#include "external/stb/stb_image.h"
-
-#ifdef _WIN32
-#define SLANG_TEST_SUPPORT_HLSL 1
-#include <d3dcompiler.h>
-#endif
-
-#include <assert.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-// Options for a particular test
-struct TestOptions
-{
- String command;
- List<String> args;
-
- // The categories that this test was assigned to
- List<TestCategory*> categories;
-};
-
-// Information on tests to run for a particular file
-struct FileTestList
-{
- List<TestOptions> tests;
-};
-
-struct TestInput
-{
- // Path to the input file for the test
- String filePath;
-
- // Prefix for the path that test output should write to
- // (usually the same as `filePath`, but will differ when
- // we run multiple tests out of the same file)
- String outputStem;
-
- // Arguments for the test (usually to be interpreted
- // as command line args)
- TestOptions const* testOptions;
-
- // The list of tests that will be run on this file
- FileTestList const* testList;
-};
-
-typedef TestResult(*TestCallback)(TestContext* context, TestInput& input);
-
-// Globals
-
-/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! Functions !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
-
-bool match(char const** ioCursor, char const* expected)
-{
- char const* cursor = *ioCursor;
- while(*expected && *cursor == *expected)
- {
- cursor++;
- expected++;
- }
- if(*expected != 0) return false;
-
- *ioCursor = cursor;
- return true;
-}
-
-void skipHorizontalSpace(char const** ioCursor)
-{
- char const* cursor = *ioCursor;
- for( ;;)
- {
- switch( *cursor )
- {
- case ' ':
- case '\t':
- cursor++;
- continue;
-
- default:
- break;
- }
-
- break;
- }
- *ioCursor = cursor;
-}
-
-void skipToEndOfLine(char const** ioCursor)
-{
- char const* cursor = *ioCursor;
- for( ;;)
- {
- int c = *cursor;
- switch( c )
- {
- default:
- cursor++;
- continue;
-
- case '\r': case '\n':
- {
- cursor++;
- int d = *cursor;
- if( (c ^ d) == ('\r' ^ '\n') )
- {
- cursor++;
- }
- }
- ; // fall through to:
- case 0:
- *ioCursor = cursor;
- return;
- }
- }
-}
-
-String getString(char const* textBegin, char const* textEnd)
-{
- StringBuilder sb;
- sb.Append(textBegin, textEnd - textBegin);
- return sb.ProduceString();
-}
-
-String collectRestOfLine(char const** ioCursor)
-{
- char const* cursor = *ioCursor;
-
- char const* textBegin = cursor;
- skipToEndOfLine(&cursor);
- char const* textEnd = cursor;
-
- *ioCursor = cursor;
- return getString(textBegin, textEnd);
-}
-
-
-
-TestResult gatherTestOptions(
- TestCategorySet* categorySet,
- char const** ioCursor,
- FileTestList* testList)
-{
- char const* cursor = *ioCursor;
-
- TestOptions testOptions;
-
- // Right after the `TEST` keyword, the user may specify
- // one or more categories for the test.
- if(*cursor == '(')
- {
- cursor++;
- // optional test category
- skipHorizontalSpace(&cursor);
- char const* categoryStart = cursor;
- for(;;)
- {
- switch( *cursor )
- {
- default:
- cursor++;
- continue;
-
- case ',':
- case ')':
- {
- char const* categoryEnd = cursor;
- cursor++;
-
- auto categoryName = getString(categoryStart, categoryEnd);
- TestCategory* category = categorySet->find(categoryName);
-
- if(!category)
- {
- return TestResult::Fail;
- }
-
-
- testOptions.categories.Add(category);
-
- if( *categoryEnd == ',' )
- {
- skipHorizontalSpace(&cursor);
- categoryStart = cursor;
- continue;
- }
- }
- break;
-
- case 0: case '\r': case '\n':
- return TestResult::Fail;
- }
-
- break;
- }
- }
-
- // If no categories were specified, then add the default category
- if(testOptions.categories.Count() == 0)
- {
- testOptions.categories.Add(categorySet->defaultCategory);
- }
-
- if(*cursor == ':')
- cursor++;
- else
- {
- return TestResult::Fail;
- }
-
- // Next scan for a sub-command name
- char const* commandStart = cursor;
- for(;;)
- {
- switch(*cursor)
- {
- default:
- cursor++;
- continue;
-
- case ':':
- break;
-
- case 0: case '\r': case '\n':
- return TestResult::Fail;
- }
-
- break;
- }
- char const* commandEnd = cursor;
-
- testOptions.command = getString(commandStart, commandEnd);
-
- if(*cursor == ':')
- cursor++;
- else
- {
- return TestResult::Fail;
- }
-
- // Now scan for arguments. For now we just assume that
- // any whitespace separation indicates a new argument
- // (we don't support quoting)
- for(;;)
- {
- skipHorizontalSpace(&cursor);
-
- // End of line? then no more options.
- switch( *cursor )
- {
- case 0: case '\r': case '\n':
- skipToEndOfLine(&cursor);
- testList->tests.Add(testOptions);
- return TestResult::Pass;
-
- default:
- break;
- }
-
- // Let's try to read one option
- char const* argBegin = cursor;
- for(;;)
- {
- switch( *cursor )
- {
- default:
- cursor++;
- continue;
-
- case 0: case '\r': case '\n': case ' ': case '\t':
- break;
- }
-
- break;
- }
- char const* argEnd = cursor;
- assert(argBegin != argEnd);
-
- testOptions.args.Add(getString(argBegin, argEnd));
- }
-}
-
-// Try to read command-line options from the test file itself
-TestResult gatherTestsForFile(
- TestCategorySet* categorySet,
- String filePath,
- FileTestList* testList)
-{
- String fileContents;
- try
- {
- fileContents = Slang::File::ReadAllText(filePath);
- }
- catch (Slang::IOException)
- {
- return TestResult::Fail;
- }
-
-
- // Walk through the lines of the file, looking for test commands
- char const* cursor = fileContents.begin();
-
- while(*cursor)
- {
- // We are at the start of a line of input.
-
- skipHorizontalSpace(&cursor);
-
- // Look for a pattern that matches what we want
- if(match(&cursor, "//TEST_IGNORE_FILE"))
- {
- return TestResult::Ignored;
- }
- else if(match(&cursor, "//TEST"))
- {
- if(gatherTestOptions(categorySet, &cursor, testList) != TestResult::Pass)
- return TestResult::Fail;
- }
- else
- {
- skipToEndOfLine(&cursor);
- }
- }
-
- return TestResult::Pass;
-}
-
-OSError spawnAndWait(TestContext* context, const String& testPath, OSProcessSpawner& spawner)
-{
- const auto& options = context->options;
-
- if (!options.useExes)
- {
- String exeName = Path::GetFileNameWithoutEXT(spawner.executableName_);
-
- if (options.shouldBeVerbose)
- {
- StringBuilder builder;
-
- builder << "slang-test";
-
- if (options.binDir)
- {
- builder << " -bindir " << options.binDir;
- }
-
- builder << " " << exeName;
-
- // TODO(js): Potentially this should handle escaping parameters for the command line if need be
- const auto& argList = spawner.argumentList_;
- for (UInt i = 0; i < argList.Count(); ++i)
- {
- builder << " " << argList[i];
- }
-
- context->reporter->messageFormat(TestMessageType::Info, "%s\n", builder.begin());
- }
-
- auto func = context->getInnerMainFunc(String(context->options.binDir), exeName);
- if (func)
- {
- StringBuilder stdErrorString;
- StringBuilder stdOutString;
-
- // Say static so not released
- StringWriter stdError(&stdErrorString, WriterFlag::IsConsole | WriterFlag::IsStatic);
- StringWriter stdOut(&stdOutString, WriterFlag::IsConsole | WriterFlag::IsStatic);
-
- StdWriters* prevStdWriters = StdWriters::getSingleton();
-
- StdWriters stdWriters;
- stdWriters.setWriter(SLANG_WRITER_CHANNEL_STD_ERROR, &stdError);
- stdWriters.setWriter(SLANG_WRITER_CHANNEL_STD_OUTPUT, &stdOut);
-
- if (exeName == "slangc")
- {
- stdWriters.setWriter(SLANG_WRITER_CHANNEL_DIAGNOSTIC, &stdError);
- }
-
- List<const char*> args;
- args.Add(exeName.Buffer());
- for (int i = 0; i < int(spawner.argumentList_.Count()); ++i)
- {
- args.Add(spawner.argumentList_[i].Buffer());
- }
-
- SlangResult res = func(&stdWriters, context->getSession(), int(args.Count()), args.begin());
-
- StdWriters::setSingleton(prevStdWriters);
-
- spawner.standardError_ = stdErrorString;
- spawner.standardOutput_ = stdOutString;
-
- spawner.resultCode_ = TestToolUtil::getReturnCode(res);
-
- return kOSError_None;
- }
- }
-
- if (options.shouldBeVerbose)
- {
- String commandLine = spawner.getCommandLine();
- context->reporter->messageFormat(TestMessageType::Info, "%s\n", commandLine.begin());
- }
-
-
- OSError err = spawner.spawnAndWaitForCompletion();
- if (err != kOSError_None)
- {
-// fprintf(stderr, "failed to run test '%S'\n", testPath.ToWString());
- context->reporter->messageFormat(TestMessageType::RunError, "failed to run test '%S'", testPath.ToWString().begin());
- }
- return err;
-}
-
-String getOutput(OSProcessSpawner& spawner)
-{
- OSProcessSpawner::ResultCode resultCode = spawner.getResultCode();
-
- String standardOuptut = spawner.getStandardOutput();
- String standardError = spawner.getStandardError();
-
- // We construct a single output string that captures the results
- StringBuilder actualOutputBuilder;
- actualOutputBuilder.Append("result code = ");
- actualOutputBuilder.Append(resultCode);
- actualOutputBuilder.Append("\nstandard error = {\n");
- actualOutputBuilder.Append(standardError);
- actualOutputBuilder.Append("}\nstandard output = {\n");
- actualOutputBuilder.Append(standardOuptut);
- actualOutputBuilder.Append("}\n");
-
- return actualOutputBuilder.ProduceString();
-}
-
-// Finds the specialized or default path for expected data for a test.
-// If neither are found, will return an empty string
-String findExpectedPath(const TestInput& input, const char* postFix)
-{
- StringBuilder specializedBuf;
-
- // Try the specialized name first
- specializedBuf << input.outputStem;
- if (postFix)
- {
- specializedBuf << postFix;
- }
- if (File::Exists(specializedBuf))
- {
- return specializedBuf;
- }
-
-
- // Try the default name
- StringBuilder defaultBuf;
- defaultBuf.Clear();
- defaultBuf << input.filePath;
- if (postFix)
- {
- defaultBuf << postFix;
- }
-
- if (File::Exists(defaultBuf))
- {
- return defaultBuf;
- }
-
- // Couldn't find either
- printf("referenceOutput '%s' or '%s' not found.\n", defaultBuf.Buffer(), specializedBuf.Buffer());
-
- return "";
-}
-
-TestResult runSimpleTest(TestContext* context, TestInput& input)
-{
- // need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect
-
- auto filePath999 = input.filePath;
- auto outputStem = input.outputStem;
-
- OSProcessSpawner spawner;
-
- spawner.pushExecutablePath(String(context->options.binDir) + "slangc" + osGetExecutableSuffix());
- spawner.pushArgument(filePath999);
-
- for( auto arg : input.testOptions->args )
- {
- spawner.pushArgument(arg);
- }
-
- if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
- {
- return TestResult::Fail;
- }
-
- String actualOutput = getOutput(spawner);
-
- String expectedOutputPath = outputStem + ".expected";
- String expectedOutput;
- try
- {
- expectedOutput = Slang::File::ReadAllText(expectedOutputPath);
- }
- catch (Slang::IOException)
- {
- }
-
- // If no expected output file was found, then we
- // expect everything to be empty
- if (expectedOutput.Length() == 0)
- {
- expectedOutput = "result code = 0\nstandard error = {\n}\nstandard output = {\n}\n";
- }
-
- TestResult result = TestResult::Pass;
-
- // Otherwise we compare to the expected output
- if (actualOutput != expectedOutput)
- {
- context->reporter->dumpOutputDifference(expectedOutput, actualOutput);
- result = TestResult::Fail;
- }
-
- // If the test failed, then we write the actual output to a file
- // so that we can easily diff it from the command line and
- // diagnose the problem.
- if (result == TestResult::Fail)
- {
- String actualOutputPath = outputStem + ".actual";
- Slang::File::WriteAllText(actualOutputPath, actualOutput);
-
- context->reporter->dumpOutputDifference(expectedOutput, actualOutput);
- }
-
- return result;
-}
-
-TestResult runReflectionTest(TestContext* context, TestInput& input)
-{
- const auto& options = context->options;
- auto filePath = input.filePath;
- auto outputStem = input.outputStem;
-
- OSProcessSpawner spawner;
-
- spawner.pushExecutablePath(String(options.binDir) + "slang-reflection-test" + osGetExecutableSuffix());
- spawner.pushArgument(filePath);
-
- for( auto arg : input.testOptions->args )
- {
- spawner.pushArgument(arg);
- }
-
- if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
- {
- return TestResult::Fail;
- }
-
- String actualOutput = getOutput(spawner);
-
- String expectedOutputPath = outputStem + ".expected";
- String expectedOutput;
- try
- {
- expectedOutput = Slang::File::ReadAllText(expectedOutputPath);
- }
- catch (Slang::IOException)
- {
- }
-
- // If no expected output file was found, then we
- // expect everything to be empty
- if (expectedOutput.Length() == 0)
- {
- expectedOutput = "result code = 0\nstandard error = {\n}\nstandard output = {\n}\n";
- }
-
- TestResult result = TestResult::Pass;
-
- // Otherwise we compare to the expected output
- if (actualOutput != expectedOutput)
- {
- result = TestResult::Fail;
- }
-
- // If the test failed, then we write the actual output to a file
- // so that we can easily diff it from the command line and
- // diagnose the problem.
- if (result == TestResult::Fail)
- {
- String actualOutputPath = outputStem + ".actual";
- Slang::File::WriteAllText(actualOutputPath, actualOutput);
-
- context->reporter->dumpOutputDifference(expectedOutput, actualOutput);
- }
-
- return result;
-}
-
-String getExpectedOutput(String const& outputStem)
-{
- String expectedOutputPath = outputStem + ".expected";
- String expectedOutput;
- try
- {
- expectedOutput = Slang::File::ReadAllText(expectedOutputPath);
- }
- catch (Slang::IOException)
- {
- }
-
- // If no expected output file was found, then we
- // expect everything to be empty
- if (expectedOutput.Length() == 0)
- {
- expectedOutput = "result code = 0\nstandard error = {\n}\nstandard output = {\n}\n";
- }
-
- return expectedOutput;
-}
-
-static SlangCompileTarget _getCompileTarget(const UnownedStringSlice& name)
-{
-#define CASE(NAME, TARGET) if(name == NAME) return SLANG_##TARGET;
-
- CASE("hlsl", HLSL)
- CASE("glsl", GLSL)
- CASE("dxbc", DXBC)
- CASE("dxbc-assembly", DXBC_ASM)
- CASE("dxbc-asm", DXBC_ASM)
- CASE("spirv", SPIRV)
- CASE("spirv-assembly", SPIRV_ASM)
- CASE("spirv-asm", SPIRV_ASM)
- CASE("dxil", DXIL)
- CASE("dxil-assembly", DXIL_ASM)
- CASE("dxil-asm", DXIL_ASM)
-#undef CASE
-
- return SLANG_TARGET_UNKNOWN;
-}
-
-TestResult runCrossCompilerTest(TestContext* context, TestInput& input)
-{
- // need to execute the stand-alone Slang compiler on the file
- // then on the same file + `.glsl` and compare output
-
- auto filePath = input.filePath;
- auto outputStem = input.outputStem;
-
- OSProcessSpawner actualSpawner;
- OSProcessSpawner expectedSpawner;
-
- actualSpawner.pushExecutablePath(String(context->options.binDir) + "slangc" + osGetExecutableSuffix());
- expectedSpawner.pushExecutablePath(String(context->options.binDir) + "slangc" + osGetExecutableSuffix());
-
- actualSpawner.pushArgument(filePath);
-
- const auto& args = input.testOptions->args;
-
- const UInt targetIndex = args.IndexOf("-target");
- if (targetIndex != UInt(-1) && targetIndex + 1 < args.Count())
- {
- SlangCompileTarget target = _getCompileTarget(args[targetIndex + 1].getUnownedSlice());
-
- // Check the session supports it. If not we ignore it
- if (SLANG_FAILED(spSessionCheckCompileTargetSupport(context->getSession(), target)))
- {
- return TestResult::Ignored;
- }
-
- switch (target)
- {
- case SLANG_DXIL_ASM:
- {
- expectedSpawner.pushArgument(filePath + ".hlsl");
- expectedSpawner.pushArgument("-pass-through");
- expectedSpawner.pushArgument("dxc");
- break;
- }
- default:
- {
- expectedSpawner.pushArgument(filePath + ".glsl");
- expectedSpawner.pushArgument("-pass-through");
- expectedSpawner.pushArgument("glslang");
- break;
- }
- }
- }
-
- for( auto arg : input.testOptions->args )
- {
- actualSpawner.pushArgument(arg);
- expectedSpawner.pushArgument(arg);
- }
-
- if (spawnAndWait(context, outputStem, expectedSpawner) != kOSError_None)
- {
- return TestResult::Fail;
- }
-
- String expectedOutput = getOutput(expectedSpawner);
- String expectedOutputPath = outputStem + ".expected";
- try
- {
- Slang::File::WriteAllText(expectedOutputPath, expectedOutput);
- }
- catch (Slang::IOException)
- {
- return TestResult::Fail;
- }
-
- if (spawnAndWait(context, outputStem, actualSpawner) != kOSError_None)
- {
- return TestResult::Fail;
- }
- String actualOutput = getOutput(actualSpawner);
-
- TestResult result = TestResult::Pass;
-
- // Otherwise we compare to the expected output
- if (actualOutput != expectedOutput)
- {
- result = TestResult::Fail;
- }
-
- // Always fail if the compilation produced a failure, just
- // to catch situations where, e.g., command-line options parsing
- // caused the same error in both the Slang and glslang cases.
- //
- if( actualSpawner.getResultCode() != 0 )
- {
- result = TestResult::Fail;
- }
-
- // If the test failed, then we write the actual output to a file
- // so that we can easily diff it from the command line and
- // diagnose the problem.
- if (result == TestResult::Fail)
- {
- String actualOutputPath = outputStem + ".actual";
- Slang::File::WriteAllText(actualOutputPath, actualOutput);
-
- context->reporter->dumpOutputDifference(expectedOutput, actualOutput);
- }
-
- return result;
-}
-
-
-#ifdef SLANG_TEST_SUPPORT_HLSL
-TestResult generateHLSLBaseline(TestContext* context, TestInput& input)
-{
- auto filePath999 = input.filePath;
- auto outputStem = input.outputStem;
-
- OSProcessSpawner spawner;
- spawner.pushExecutablePath(String(context->options.binDir) + "slangc" + osGetExecutableSuffix());
- spawner.pushArgument(filePath999);
-
- for( auto arg : input.testOptions->args )
- {
- spawner.pushArgument(arg);
- }
-
- spawner.pushArgument("-target");
- spawner.pushArgument("dxbc-assembly");
- spawner.pushArgument("-pass-through");
- spawner.pushArgument("fxc");
-
- if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
- {
- return TestResult::Fail;
- }
-
- String expectedOutput = getOutput(spawner);
- String expectedOutputPath = outputStem + ".expected";
- try
- {
- Slang::File::WriteAllText(expectedOutputPath, expectedOutput);
- }
- catch (Slang::IOException)
- {
- return TestResult::Fail;
- }
- return TestResult::Pass;
-}
-
-TestResult runHLSLComparisonTest(TestContext* context, TestInput& input)
-{
- auto filePath999 = input.filePath;
- auto outputStem = input.outputStem;
-
- // We will use the Microsoft compiler to generate out expected output here
- String expectedOutputPath = outputStem + ".expected";
-
- // Generate the expected output using standard HLSL compiler
- generateHLSLBaseline(context, input);
-
- // need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect
-
- OSProcessSpawner spawner;
-
- spawner.pushExecutablePath(String(context->options.binDir) + "slangc" + osGetExecutableSuffix());
- spawner.pushArgument(filePath999);
-
- for( auto arg : input.testOptions->args )
- {
- spawner.pushArgument(arg);
- }
-
- // TODO: The compiler should probably define this automatically...
- spawner.pushArgument("-D");
- spawner.pushArgument("__SLANG__");
-
- spawner.pushArgument("-target");
- spawner.pushArgument("dxbc-assembly");
-
- if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
- {
- return TestResult::Fail;
- }
-
- // We ignore output to stdout, and only worry about what the compiler
- // wrote to stderr.
-
- OSProcessSpawner::ResultCode resultCode = spawner.getResultCode();
-
- String standardOutput = spawner.getStandardOutput();
- String standardError = spawner.getStandardError();
-
- // We construct a single output string that captures the results
- StringBuilder actualOutputBuilder;
- actualOutputBuilder.Append("result code = ");
- actualOutputBuilder.Append(resultCode);
- actualOutputBuilder.Append("\nstandard error = {\n");
- actualOutputBuilder.Append(standardError);
- actualOutputBuilder.Append("}\nstandard output = {\n");
- actualOutputBuilder.Append(standardOutput);
- actualOutputBuilder.Append("}\n");
-
- String actualOutput = actualOutputBuilder.ProduceString();
-
- String expectedOutput;
- try
- {
- expectedOutput = Slang::File::ReadAllText(expectedOutputPath);
- }
- catch (Slang::IOException)
- {
- }
-
- TestResult result = TestResult::Pass;
-
- // If no expected output file was found, then we
- // expect everything to be empty
- if (expectedOutput.Length() == 0)
- {
- if (resultCode != 0) result = TestResult::Fail;
- if (standardError.Length() != 0) result = TestResult::Fail;
- if (standardOutput.Length() != 0) result = TestResult::Fail;
- }
- // Otherwise we compare to the expected output
- else if (actualOutput != expectedOutput)
- {
- result = TestResult::Fail;
- }
-
- // Always fail if the compilation produced a failure, just
- // to catch situations where, e.g., command-line options parsing
- // caused the same error in both the Slang and fxc cases.
- //
- if( resultCode != 0 )
- {
- result = TestResult::Fail;
- }
-
- // If the test failed, then we write the actual output to a file
- // so that we can easily diff it from the command line and
- // diagnose the problem.
- if (result == TestResult::Fail)
- {
- String actualOutputPath = outputStem + ".actual";
- Slang::File::WriteAllText(actualOutputPath, actualOutput);
-
- context->reporter->dumpOutputDifference(expectedOutput, actualOutput);
- }
-
- return result;
-}
-#endif
-
-TestResult doGLSLComparisonTestRun(TestContext* context,
- TestInput& input,
- char const* langDefine,
- char const* passThrough,
- char const* outputKind,
- String* outOutput)
-{
- auto filePath999 = input.filePath;
- auto outputStem = input.outputStem;
-
- OSProcessSpawner spawner;
-
- spawner.pushExecutablePath(String(context->options.binDir) + "slangc" + osGetExecutableSuffix());
- spawner.pushArgument(filePath999);
-
- if( langDefine )
- {
- spawner.pushArgument("-D");
- spawner.pushArgument(langDefine);
- }
-
- if( passThrough )
- {
- spawner.pushArgument("-pass-through");
- spawner.pushArgument(passThrough);
- }
-
- spawner.pushArgument("-target");
- spawner.pushArgument("spirv-assembly");
-
- for( auto arg : input.testOptions->args )
- {
- spawner.pushArgument(arg);
- }
-
- if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
- {
- return TestResult::Fail;
- }
-
- OSProcessSpawner::ResultCode resultCode = spawner.getResultCode();
-
- String standardOuptut = spawner.getStandardOutput();
- String standardError = spawner.getStandardError();
-
- // We construct a single output string that captures the results
- StringBuilder outputBuilder;
- outputBuilder.Append("result code = ");
- outputBuilder.Append(resultCode);
- outputBuilder.Append("\nstandard error = {\n");
- outputBuilder.Append(standardError);
- outputBuilder.Append("}\nstandard output = {\n");
- outputBuilder.Append(standardOuptut);
- outputBuilder.Append("}\n");
-
- String outputPath = outputStem + outputKind;
- String output = outputBuilder.ProduceString();
-
- *outOutput = output;
-
- return TestResult::Pass;
-}
-
-TestResult runGLSLComparisonTest(TestContext* context, TestInput& input)
-{
- auto filePath999 = input.filePath;
- auto outputStem = input.outputStem;
-
- String expectedOutput;
- String actualOutput;
-
- TestResult hlslResult = doGLSLComparisonTestRun(context, input, "__GLSL__", "glslang", ".expected", &expectedOutput);
- TestResult slangResult = doGLSLComparisonTestRun(context, input, "__SLANG__", nullptr, ".actual", &actualOutput);
-
- Slang::File::WriteAllText(outputStem + ".expected", expectedOutput);
- Slang::File::WriteAllText(outputStem + ".actual", actualOutput);
-
- if( hlslResult == TestResult::Fail ) return TestResult::Fail;
- if( slangResult == TestResult::Fail ) return TestResult::Fail;
-
- if (actualOutput != expectedOutput)
- {
- context->reporter->dumpOutputDifference(expectedOutput, actualOutput);
-
- return TestResult::Fail;
- }
-
- return TestResult::Pass;
-}
-
-
-TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, const char *const* langOpts, size_t numLangOpts)
-{
- // TODO: delete any existing files at the output path(s) to avoid stale outputs leading to a false pass
- auto filePath999 = input.filePath;
- auto outputStem = input.outputStem;
-
- const String referenceOutput = findExpectedPath(input, ".expected.txt");
- if (referenceOutput.Length() <= 0)
- {
- return TestResult::Fail;
- }
-
- OSProcessSpawner spawner;
-
- spawner.pushExecutablePath(String(context->options.binDir) + "render-test" + osGetExecutableSuffix());
- spawner.pushArgument(filePath999);
-
- for (auto arg : input.testOptions->args)
- {
- spawner.pushArgument(arg);
- }
-
- for (int i = 0; i < int(numLangOpts); ++i)
- {
- spawner.pushArgument(langOpts[i]);
- }
- spawner.pushArgument("-o");
- auto actualOutputFile = outputStem + ".actual.txt";
- spawner.pushArgument(actualOutputFile);
-
- // clear the stale actual output file first. This will allow us to detect error if render-test fails and outputs nothing.
- File::WriteAllText(actualOutputFile, "");
-
- if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
- {
- printf("error spawning render-test\n");
- return TestResult::Fail;
- }
-
- auto actualOutput = getOutput(spawner);
- auto expectedOutput = getExpectedOutput(outputStem);
- if (actualOutput != expectedOutput)
- {
- context->reporter->dumpOutputDifference(expectedOutput, actualOutput);
-
- String actualOutputPath = outputStem + ".actual";
- Slang::File::WriteAllText(actualOutputPath, actualOutput);
-
- return TestResult::Fail;
- }
-
- // check against reference output
- if (!File::Exists(actualOutputFile))
- {
- printf("render-test not producing expected outputs.\n");
- printf("render-test output:\n%s\n", actualOutput.Buffer());
- return TestResult::Fail;
- }
- if (!File::Exists(referenceOutput))
- {
- printf("referenceOutput %s not found.\n", referenceOutput.Buffer());
- return TestResult::Fail;
- }
- auto actualOutputContent = File::ReadAllText(actualOutputFile);
- auto actualProgramOutput = Split(actualOutputContent, '\n');
- auto referenceProgramOutput = Split(File::ReadAllText(referenceOutput), '\n');
- auto printOutput = [&]()
- {
- context->reporter->messageFormat(TestMessageType::TestFailure, "output mismatch! actual output: {\n%s\n}, \n%s\n", actualOutputContent.Buffer(), actualOutput.Buffer());
- };
- if (actualProgramOutput.Count() < referenceProgramOutput.Count())
- {
- printOutput();
- return TestResult::Fail;
- }
- for (int i = 0; i < (int)referenceProgramOutput.Count(); i++)
- {
- auto reference = String(referenceProgramOutput[i].Trim());
- auto actual = String(actualProgramOutput[i].Trim());
- if (actual != reference)
- {
- printOutput();
- return TestResult::Fail;
- }
- }
- return TestResult::Pass;
-}
-
-TestResult runSlangComputeComparisonTest(TestContext* context, TestInput& input)
-{
- const char* langOpts[] = { "-slang", "-compute" };
- return runComputeComparisonImpl(context, input, langOpts, SLANG_COUNT_OF(langOpts));
-}
-
-TestResult runSlangComputeComparisonTestEx(TestContext* context, TestInput& input)
-{
- return runComputeComparisonImpl(context, input, nullptr, 0);
-}
-
-TestResult runHLSLComputeTest(TestContext* context, TestInput& input)
-{
- const char* langOpts[] = { "--hlsl-rewrite", "-compute" };
- return runComputeComparisonImpl(context, input, langOpts, SLANG_COUNT_OF(langOpts));
-}
-
-TestResult runSlangRenderComputeComparisonTest(TestContext* context, TestInput& input)
-{
- const char* langOpts[] = { "-slang", "-gcompute" };
- return runComputeComparisonImpl(context, input, langOpts, SLANG_COUNT_OF(langOpts));
-}
-
-TestResult doRenderComparisonTestRun(TestContext* context, TestInput& input, char const* langOption, char const* outputKind, String* outOutput)
-{
- // TODO: delete any existing files at the output path(s) to avoid stale outputs leading to a false pass
-
- auto filePath = input.filePath;
- auto outputStem = input.outputStem;
-
- OSProcessSpawner spawner;
-
- spawner.pushExecutablePath(String(context->options.binDir) + "render-test" + osGetExecutableSuffix());
- spawner.pushArgument(filePath);
-
- for( auto arg : input.testOptions->args )
- {
- spawner.pushArgument(arg);
- }
-
- spawner.pushArgument(langOption);
- spawner.pushArgument("-o");
- spawner.pushArgument(outputStem + outputKind + ".png");
-
- if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
- {
- return TestResult::Fail;
- }
-
- OSProcessSpawner::ResultCode resultCode = spawner.getResultCode();
-
- String standardOutput = spawner.getStandardOutput();
- String standardError = spawner.getStandardError();
-
- // We construct a single output string that captures the results
- StringBuilder outputBuilder;
- outputBuilder.Append("result code = ");
- outputBuilder.Append(resultCode);
- outputBuilder.Append("\nstandard error = {\n");
- outputBuilder.Append(standardError);
- outputBuilder.Append("}\nstandard output = {\n");
- outputBuilder.Append(standardOutput);
- outputBuilder.Append("}\n");
-
- String outputPath = outputStem + outputKind;
- String output = outputBuilder.ProduceString();
-
- *outOutput = output;
-
- return TestResult::Pass;
-}
-
-class STBImage
-{
-public:
- typedef STBImage ThisType;
-
- /// Reset back to default initialized state (frees any image set)
- void reset();
- /// True if rhs has same size and amount of channels
- bool isComparable(const ThisType& rhs) const;
-
- /// The width in pixels
- int getWidth() const { return m_width; }
- /// The height in pixels
- int getHeight() const { return m_height; }
- /// The number of channels (typically held as bytes in order)
- int getNumChannels() const { return m_numChannels; }
-
- /// Get the contained pixels, nullptr if nothing loaded
- const unsigned char* getPixels() const { return m_pixels; }
- unsigned char* getPixels() { return m_pixels; }
-
- /// Read an image with filename. SLANG_OK on success
- SlangResult read(const char* filename);
-
- ~STBImage() { reset(); }
-
- int m_width = 0;
- int m_height = 0;
- int m_numChannels = 0;
- unsigned char* m_pixels = nullptr;
-};
-
-void STBImage::reset()
-{
- if (m_pixels)
- {
- stbi_image_free(m_pixels);
- m_pixels = nullptr;
- }
- m_width = 0;
- m_height = 0;
- m_numChannels = 0;
-}
-
-SlangResult STBImage::read(const char* filename)
-{
- reset();
-
- m_pixels = stbi_load(filename, &m_width, &m_height, &m_numChannels, 0);
- if (!m_pixels)
- {
- return SLANG_FAIL;
- }
- return SLANG_OK;
-}
-
-bool STBImage::isComparable(const ThisType& rhs) const
-{
- return (this == &rhs) ||
- (m_width == rhs.m_width && m_height == rhs.m_height && m_numChannels == rhs.m_numChannels);
-}
-
-
-TestResult doImageComparison(TestContext* context, String const& filePath)
-{
- auto reporter = context->reporter;
-
- // Allow a difference in the low bits of the 8-bit result, just to play it safe
- static const int kAbsoluteDiffCutoff = 2;
-
- // Allow a relative 1% difference
- static const float kRelativeDiffCutoff = 0.01f;
-
- String expectedPath = filePath + ".expected.png";
- String actualPath = filePath + ".actual.png";
-
- STBImage expectedImage;
- if (SLANG_FAILED(expectedImage.read(expectedPath.Buffer())))
- {
- reporter->messageFormat(TestMessageType::RunError, "Unable to load image ;%s'", expectedPath.Buffer());
- return TestResult::Fail;
- }
-
- STBImage actualImage;
- if (SLANG_FAILED(actualImage.read(actualPath.Buffer())))
- {
- reporter->messageFormat(TestMessageType::RunError, "Unable to load image ;%s'", actualPath.Buffer());
- return TestResult::Fail;
- }
-
- if (!expectedImage.isComparable(actualImage))
- {
- reporter->messageFormat(TestMessageType::TestFailure, "Images are different sizes '%s' '%s'", actualPath.Buffer(), expectedPath.Buffer());
- return TestResult::Fail;
- }
-
- {
- const unsigned char* expectedPixels = expectedImage.getPixels();
- const unsigned char* actualPixels = actualImage.getPixels();
-
- const int height = actualImage.getHeight();
- const int width = actualImage.getWidth();
- const int numChannels = actualImage.getNumChannels();
- const int rowSize = width * numChannels;
-
- for (int y = 0; y < height; ++y)
- {
- for (int i = 0; i < rowSize; ++i)
- {
- int expectedVal = expectedPixels[i];
- int actualVal = actualPixels[i];
-
- int absoluteDiff = actualVal - expectedVal;
- if (absoluteDiff < 0) absoluteDiff = -absoluteDiff;
-
- if (absoluteDiff < kAbsoluteDiffCutoff)
- {
- // There might be a difference, but we'll consider it to be inside tolerance
- continue;
- }
-
- float relativeDiff = 0.0f;
- if (expectedVal != 0)
- {
- relativeDiff = fabsf(float(actualVal) - float(expectedVal)) / float(expectedVal);
-
- if (relativeDiff < kRelativeDiffCutoff)
- {
- // relative difference was small enough
- continue;
- }
- }
-
- // TODO: may need to do some local search sorts of things, to deal with
- // cases where vertex shader results lead to rendering that is off
- // by one pixel...
-
- const int x = i / numChannels;
- const int channelIndex = i % numChannels;
-
- reporter->messageFormat(TestMessageType::TestFailure, "image compare failure at (%d,%d) channel %d. expected %d got %d (absolute error: %d, relative error: %f)\n",
- x, y, channelIndex,
- expectedVal,
- actualVal,
- absoluteDiff,
- relativeDiff);
-
- // There was a difference we couldn't excuse!
- return TestResult::Fail;
- }
-
- expectedPixels += rowSize;
- actualPixels += rowSize;
- }
- }
-
- return TestResult::Pass;
-}
-
-TestResult runHLSLRenderComparisonTestImpl(
- TestContext* context,
- TestInput& input,
- char const* expectedArg,
- char const* actualArg)
-{
- auto filePath = input.filePath;
- auto outputStem = input.outputStem;
-
- String expectedOutput;
- String actualOutput;
-
- TestResult hlslResult = doRenderComparisonTestRun(context, input, expectedArg, ".expected", &expectedOutput);
- TestResult slangResult = doRenderComparisonTestRun(context, input, actualArg, ".actual", &actualOutput);
-
- Slang::File::WriteAllText(outputStem + ".expected", expectedOutput);
- Slang::File::WriteAllText(outputStem + ".actual", actualOutput);
-
- if( hlslResult == TestResult::Fail ) return TestResult::Fail;
- if( slangResult == TestResult::Fail ) return TestResult::Fail;
-
- if (actualOutput != expectedOutput)
- {
- context->reporter->dumpOutputDifference(expectedOutput, actualOutput);
-
- return TestResult::Fail;
- }
-
- // Next do an image comparison on the expected output images!
-
- TestResult imageCompareResult = doImageComparison(context, outputStem);
- if(imageCompareResult != TestResult::Pass)
- return imageCompareResult;
-
- return TestResult::Pass;
-}
-
-TestResult runHLSLRenderComparisonTest(TestContext* context, TestInput& input)
-{
- return runHLSLRenderComparisonTestImpl(context, input, "-hlsl", "-slang");
-}
-
-TestResult runHLSLCrossCompileRenderComparisonTest(TestContext* context, TestInput& input)
-{
- return runHLSLRenderComparisonTestImpl(context, input, "-slang", "-glsl-cross");
-}
-
-TestResult runHLSLAndGLSLRenderComparisonTest(TestContext* context, TestInput& input)
-{
- return runHLSLRenderComparisonTestImpl(context, input, "-hlsl-rewrite", "-glsl-rewrite");
-}
-
-TestResult skipTest(TestContext* /* context */, TestInput& /*input*/)
-{
- return TestResult::Ignored;
-}
-
-static bool hasRenderOption(RenderApiType apiType, const List<String>& options)
-{
- const RenderApiUtil::Info& info = RenderApiUtil::getInfo(apiType);
-
- List<UnownedStringSlice> namesList;
-
- for (UInt i = 0; i < options.Count(); ++i)
- {
- const String& option = options[i];
-
- if (option.StartsWith("-"))
- {
- const UnownedStringSlice parameter(option.Buffer() + 1, option.Buffer() + option.Length());
- // See if we have a match
- for (int j = 0; j < SLANG_COUNT_OF(RenderApiUtil::s_infos); j++)
- {
- const auto& apiInfo = RenderApiUtil::s_infos[j];
- const UnownedStringSlice names(info.names);
-
- if (names.indexOf(',') >= 0)
- {
- StringUtil::split(names, ',', namesList);
-
- if (namesList.IndexOf(parameter) != UInt(-1))
- {
- return true;
- }
- }
- else if (names == parameter)
- {
- return true;
- }
- }
- }
- }
-
- return false;
-}
-
-bool hasRenderOption(RenderApiType apiType, const TestOptions& options)
-{
- return hasRenderOption(apiType, options.args);
-}
-
-bool hasRenderOption(RenderApiType apiType, const FileTestList& testList)
-{
- const int numTests = int(testList.tests.Count());
- for (int i = 0; i < numTests; i++)
- {
- if (hasRenderOption(apiType, testList.tests[i].args))
- {
- return true;
- }
- }
- return false;
-}
-
-bool isHLSLTest(const String& command)
-{
- return command == "COMPARE_HLSL" ||
- command == "COMPARE_HLSL_RENDER" ||
- command == "COMPARE_HLSL_CROSS_COMPILE_RENDER" ||
- command == "COMPARE_HLSL_GLSL_RENDER";
-}
-
-bool isRenderTest(const String& command)
-{
- return command == "COMPARE_COMPUTE" ||
- command == "COMPARE_COMPUTE_EX" ||
- command == "HLSL_COMPUTE" ||
- command == "COMPARE_RENDER_COMPUTE" ||
- command == "COMPARE_HLSL_RENDER" ||
- command == "COMPARE_HLSL_CROSS_COMPILE_RENDER" ||
- command == "COMPARE_HLSL_GLSL_RENDER";
-}
-
-static bool canIgnoreTestWithDisabledRenderer(const TestOptions& testOptions, const Options& appOptions)
-{
- for (int i = 0; i < int(RenderApiType::CountOf); ++i)
- {
- RenderApiType apiType = RenderApiType(i);
- RenderApiFlag::Enum apiFlag = RenderApiFlag::Enum(1 << i);
-
- if (hasRenderOption(apiType, testOptions) && (appOptions.enabledApis & apiFlag) == 0)
- {
- return true;
- }
- }
-
- return false;
-}
-
-TestResult runTest(
- TestContext* context,
- String const& filePath,
- String const& outputStem,
- TestOptions const& testOptions,
- FileTestList const& testList)
-{
- // If this test can be ignored
- if (canIgnoreTestWithDisabledRenderer(testOptions, context->options))
- {
- return TestResult::Ignored;
- }
-
- // based on command name, dispatch to an appropriate callback
- struct TestCommands
- {
- char const* name;
- TestCallback callback;
- };
-
- static const TestCommands kTestCommands[] =
- {
- { "SIMPLE", &runSimpleTest},
- { "REFLECTION", &runReflectionTest},
-#if SLANG_TEST_SUPPORT_HLSL
- { "COMPARE_HLSL", &runHLSLComparisonTest},
- { "COMPARE_HLSL_RENDER", &runHLSLRenderComparisonTest},
- { "COMPARE_HLSL_CROSS_COMPILE_RENDER", &runHLSLCrossCompileRenderComparisonTest},
- { "COMPARE_HLSL_GLSL_RENDER", &runHLSLAndGLSLRenderComparisonTest },
- { "COMPARE_COMPUTE", runSlangComputeComparisonTest},
- { "COMPARE_COMPUTE_EX", runSlangComputeComparisonTestEx},
- { "HLSL_COMPUTE", runHLSLComputeTest},
- { "COMPARE_RENDER_COMPUTE", &runSlangRenderComputeComparisonTest },
-
-#else
- { "COMPARE_HLSL", &skipTest },
- { "COMPARE_HLSL_RENDER", &skipTest },
- { "COMPARE_HLSL_CROSS_COMPILE_RENDER", &skipTest},
- { "COMPARE_HLSL_GLSL_RENDER", &skipTest },
- { "COMPARE_COMPUTE", &skipTest},
- { "COMPARE_COMPUTE_EX", &skipTest},
- { "HLSL_COMPUTE", &skipTest},
- { "COMPARE_RENDER_COMPUTE", &skipTest },
-#endif
- { "COMPARE_GLSL", &runGLSLComparisonTest },
- { "CROSS_COMPILE", &runCrossCompilerTest },
- { nullptr, nullptr },
- };
-
- for( auto ii = kTestCommands; ii->name; ++ii )
- {
- if(testOptions.command != ii->name)
- continue;
-
- TestInput testInput;
- testInput.filePath = filePath;
- testInput.outputStem = outputStem;
- testInput.testOptions = &testOptions;
- testInput.testList = &testList;
-
- {
- TestReporter* reporter = context->reporter;
- TestReporter::TestScope scope(reporter, outputStem);
-
- TestResult testResult = ii->callback(context, testInput);
- reporter->addResult(testResult);
-
- return testResult;
- }
- }
-
- // No actual test runner found!
-
- return TestResult::Fail;
-}
-
-bool testCategoryMatches(
- TestCategory* sub,
- TestCategory* sup)
-{
- auto ss = sub;
- while(ss)
- {
- if(ss == sup)
- return true;
-
- ss = ss->parent;
- }
- return false;
-}
-
-bool testCategoryMatches(
- TestCategory* categoryToMatch,
- Dictionary<TestCategory*, TestCategory*> categorySet)
-{
- for( auto item : categorySet )
- {
- if(testCategoryMatches(categoryToMatch, item.Value))
- return true;
- }
- return false;
-}
-
-bool testPassesCategoryMask(
- TestContext* context,
- TestOptions const& test)
-{
- // Don't include a test we should filter out
- for( auto testCategory : test.categories )
- {
- if(testCategoryMatches(testCategory, context->options.excludeCategories))
- return false;
- }
-
- // Otherwise inclue any test the user asked for
- for( auto testCategory : test.categories )
- {
- if(testCategoryMatches(testCategory, context->options.includeCategories))
- return true;
- }
-
- // skip by default
- return false;
-}
-
-void runTestsOnFile(
- TestContext* context,
- String filePath)
-{
- // Gather a list of tests to run
- FileTestList testList;
-
- if( gatherTestsForFile(&context->categorySet, filePath, &testList) == TestResult::Ignored )
- {
- // Test was explicitly ignored
- return;
- }
-
- // Note cases where a test file exists, but we found nothing to run
- if( testList.tests.Count() == 0 )
- {
- context->reporter->addTest(filePath, TestResult::Ignored);
- return;
- }
-
- List<TestOptions> synthesizedTests;
-
- // If dx12 is available synthesize Dx12 test
- if ((context->options.synthesizedTestApis & RenderApiFlag::D3D12) != 0)
- {
- // If doesn't have option generate dx12 options from dx11
- if (!hasRenderOption(RenderApiType::D3D12, testList))
- {
- const int numTests = int(testList.tests.Count());
- for (int i = 0; i < numTests; i++)
- {
- const TestOptions& testOptions = testList.tests[i];
- // If it's a render test, and there is on d3d option, add one
- if (isRenderTest(testOptions.command) && !hasRenderOption(RenderApiType::D3D12, testOptions))
- {
- // Add with -dx12 option
- TestOptions testOptionsCopy(testOptions);
- testOptionsCopy.args.Add("-dx12");
-
- synthesizedTests.Add(testOptionsCopy);
- }
- }
- }
- }
-
- // If Vulkan is available synthesize Vulkan test
- if ((context->options.synthesizedTestApis & RenderApiFlag::Vulkan) != 0)
- {
- // If doesn't have option generate dx12 options from dx11
- if (!hasRenderOption(RenderApiType::Vulkan, testList))
- {
- const int numTests = int(testList.tests.Count());
- for (int i = 0; i < numTests; i++)
- {
- const TestOptions& testOptions = testList.tests[i];
- // If it's a render test, and there is on d3d option, add one
- if (isRenderTest(testOptions.command) && !isHLSLTest(testOptions.command) && !hasRenderOption(RenderApiType::Vulkan, testOptions))
- {
- // Add with -vk option
- TestOptions testOptionsCopy(testOptions);
- testOptionsCopy.args.Add("-vk");
-
- UInt index = testOptionsCopy.args.IndexOf("-hlsl");
- if (index != UInt(-1))
- {
- testOptionsCopy.args.RemoveAt(index);
- }
-
- synthesizedTests.Add(testOptionsCopy);
- }
- }
- }
- }
-
- // Add any tests that were synthesized
- for (UInt i = 0; i < synthesizedTests.Count(); ++i)
- {
- testList.tests.Add(synthesizedTests[i]);
- }
-
- // We have found a test to run!
- int subTestCount = 0;
- for( auto& tt : testList.tests )
- {
- int subTestIndex = subTestCount++;
-
- // Check that the test passes our current category mask
- if(!testPassesCategoryMask(context, tt))
- {
- continue;
- }
-
- String outputStem = filePath;
- if(subTestIndex != 0)
- {
- outputStem = outputStem + "." + String(subTestIndex);
- }
-
- /* TestResult result = */ runTest(context, filePath, outputStem, tt, testList);
-
- // Could determine if to continue or not here... based on result
- }
-}
-
-
-static bool endsWithAllowedExtension(
- TestContext* /*context*/,
- String filePath)
-{
- char const* allowedExtensions[] = {
- ".slang",
- ".hlsl",
- ".fx",
- ".glsl",
- ".vert",
- ".frag",
- ".geom",
- ".tesc",
- ".tese",
- ".comp",
- ".internal",
- ".ahit",
- ".chit",
- ".miss",
- ".rgen",
- nullptr };
-
- for( auto ii = allowedExtensions; *ii; ++ii )
- {
- if(filePath.EndsWith(*ii))
- return true;
- }
-
- return false;
-}
-
-static bool shouldRunTest(
- TestContext* context,
- String filePath)
-{
- if(!endsWithAllowedExtension(context, filePath))
- return false;
-
- if( context->options.testPrefix )
- {
- if( strncmp(context->options.testPrefix, filePath.begin(), strlen(context->options.testPrefix)) != 0 )
- {
- return false;
- }
- }
-
- return true;
-}
-
-void runTestsInDirectory(
- TestContext* context,
- String directoryPath)
-{
- for (auto file : osFindFilesInDirectory(directoryPath))
- {
- if( shouldRunTest(context, file) )
- {
-// fprintf(stderr, "slang-test: found '%s'\n", file.Buffer());
- runTestsOnFile(context, file);
- }
- }
- for (auto subdir : osFindChildDirectories(directoryPath))
- {
- runTestsInDirectory(context, subdir);
- }
-}
-
-SlangResult innerMain(int argc, char** argv)
-{
- StdWriters::initDefault();
-
- // The context holds useful things used during testing
- TestContext context;
- SLANG_RETURN_ON_FAIL(SLANG_FAILED(context.init()))
-
- auto& categorySet = context.categorySet;
-
- // Set up our test categories here
- auto fullTestCategory = categorySet.add("full", nullptr);
- auto quickTestCategory = categorySet.add("quick", fullTestCategory);
- /*auto smokeTestCategory = */categorySet.add("smoke", quickTestCategory);
- auto renderTestCategory = categorySet.add("render", fullTestCategory);
- /*auto computeTestCategory = */categorySet.add("compute", fullTestCategory);
- auto vulkanTestCategory = categorySet.add("vulkan", fullTestCategory);
- auto unitTestCatagory = categorySet.add("unit-test", fullTestCategory);
- auto compatibilityIssueCatagory = categorySet.add("compatibility-issue", fullTestCategory);
-
- // An un-categorized test will always belong to the `full` category
- categorySet.defaultCategory = fullTestCategory;
-
- {
- // We can set the slangc command line tool, to just use the function defined here
- context.setInnerMainFunc("slangc", &SlangCTool::innerMain);
- }
-
- SLANG_RETURN_ON_FAIL(Options::parse(argc, argv, &categorySet, StdWriters::getError(), &context.options));
-
- Options& options = context.options;
-
- if (options.subCommand.Length())
- {
- // Get the function from the tool
- auto func = context.getInnerMainFunc(options.binDir, options.subCommand);
- if (!func)
- {
- StdWriters::getError().print("error: Unable to launch tool '%s'\n", options.subCommand.Buffer());
- return SLANG_FAIL;
- }
-
- // Copy args to a char* list
- const auto& srcArgs = options.subCommandArgs;
- List<const char*> args;
- args.SetSize(srcArgs.Count());
- for (UInt i = 0; i < srcArgs.Count(); ++i)
- {
- args[i] = srcArgs[i].Buffer();
- }
-
- return func(StdWriters::getSingleton(), context.getSession(), int(args.Count()), args.Buffer());
- }
-
- if( options.includeCategories.Count() == 0 )
- {
- options.includeCategories.Add(fullTestCategory, fullTestCategory);
- }
-
- // Exclude rendering tests when building under AppVeyor.
- //
- // TODO: this is very ad hoc, and we should do something cleaner.
- if( options.outputMode == TestOutputMode::AppVeyor )
- {
- options.excludeCategories.Add(renderTestCategory, renderTestCategory);
- options.excludeCategories.Add(vulkanTestCategory, vulkanTestCategory);
- }
-
- {
- // Setup the reporter
- TestReporter reporter;
- SLANG_RETURN_ON_FAIL(reporter.init(options.outputMode));
-
- context.reporter = &reporter;
-
- reporter.m_dumpOutputOnFailure = options.dumpOutputOnFailure;
- reporter.m_isVerbose = options.shouldBeVerbose;
-
- {
- TestReporter::SuiteScope suiteScope(&reporter, "tests");
- // Enumerate test files according to policy
- // TODO: add more directories to this list
- // TODO: allow for a command-line argument to select a particular directory
- runTestsInDirectory(&context, "tests/");
- }
-
- // Run the unit tests (these are internal C++ tests - not specified via files in a directory)
- // They are registered with SLANG_UNIT_TEST macro
- {
- TestReporter::SuiteScope suiteScope(&reporter, "unit tests");
- TestReporter::set(&reporter);
-
- // Run the unit tests
- TestRegister* cur = TestRegister::s_first;
- while (cur)
- {
- StringBuilder filePath;
- filePath << "unit-tests/" << cur->m_name << ".internal";
-
- TestOptions testOptions;
- testOptions.categories.Add(unitTestCatagory);
- testOptions.command = filePath;
-
- if (shouldRunTest(&context, testOptions.command))
- {
- if (testPassesCategoryMask(&context, testOptions))
- {
- reporter.startTest(testOptions.command);
- // Run the test function
- cur->m_func();
- reporter.endTest();
- }
- else
- {
- reporter.addTest(testOptions.command, TestResult::Ignored);
- }
- }
-
- // Next
- cur = cur->m_next;
- }
-
- TestReporter::set(nullptr);
- }
-
- reporter.outputSummary();
- return reporter.didAllSucceed() ? SLANG_OK : SLANG_FAIL;
- }
-}
-
-int main(int argc, char** argv)
-{
- const SlangResult res = innerMain(argc, argv);
-#ifdef _MSC_VER
- _CrtDumpMemoryLeaks();
-#endif
- return SLANG_SUCCEEDED(res) ? 0 : 1;
-}