summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/slang-test/slang-test-main.cpp301
-rw-r--r--tools/slang-test/test-context.h37
2 files changed, 268 insertions, 70 deletions
diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp
index b6c29b423..5e7a3d6f3 100644
--- a/tools/slang-test/slang-test-main.cpp
+++ b/tools/slang-test/slang-test-main.cpp
@@ -3,6 +3,7 @@
#include "../../source/core/slang-io.h"
#include "../../source/core/slang-token-reader.h"
#include "../../source/core/slang-std-writers.h"
+#include "../../source/core/slang-hex-dump-util.h"
#include "../../slang-com-helper.h"
@@ -30,6 +31,8 @@ using namespace Slang;
#include <stdlib.h>
#include <stdarg.h>
+#define SLANG_PRELUDE_NAMESPACE CPPPrelude
+#include "../../tests/cross-compile/slang-cpp-prelude.h"
// Options for a particular test
struct TestOptions
@@ -490,24 +493,41 @@ static bool _hasOption(const List<String>& args, const String& argName)
return args.indexOf(argName) != Index(-1);
}
-static BackendType _toBackendType(const UnownedStringSlice& slice)
+static SlangPassThrough _toPassThroughType(const UnownedStringSlice& slice)
{
if (slice == "dxc")
{
- return BackendType::Dxc;
+ return SLANG_PASS_THROUGH_DXC;
}
else if (slice == "fxc")
{
- return BackendType::Fxc;
+ return SLANG_PASS_THROUGH_FXC;
}
else if (slice == "glslang")
{
- return BackendType::Glslang;
+ return SLANG_PASS_THROUGH_GLSLANG;
}
- return BackendType::Unknown;
+ else if (slice == "c" || slice == "cpp")
+ {
+ return SLANG_PASS_THROUGH_GENERIC_C_CPP;
+ }
+ else if (slice == "clang")
+ {
+ return SLANG_PASS_THROUGH_CLANG;
+ }
+ else if (slice == "gcc")
+ {
+ return SLANG_PASS_THROUGH_GCC;
+ }
+ else if (slice == "vs" || slice == "visualstudio")
+ {
+ return SLANG_PASS_THROUGH_VISUAL_STUDIO;
+ }
+
+ return SLANG_PASS_THROUGH_NONE;
}
-static BackendFlags _getBackendFlagsForTarget(SlangCompileTarget target)
+static PassThroughFlags _getPassThroughFlagsForTarget(SlangCompileTarget target)
{
switch (target)
{
@@ -523,18 +543,25 @@ static BackendFlags _getBackendFlagsForTarget(SlangCompileTarget target)
case SLANG_DXBC:
case SLANG_DXBC_ASM:
{
- return BackendFlag::Fxc;
+ return PassThroughFlag::Fxc;
}
case SLANG_SPIRV:
case SLANG_SPIRV_ASM:
{
- return BackendFlag::Glslang;
+ return PassThroughFlag::Glslang;
}
case SLANG_DXIL:
case SLANG_DXIL_ASM:
{
- return BackendFlag::Dxc;
+ return PassThroughFlag::Dxc;
+ }
+
+ case SLANG_EXECUTABLE:
+ case SLANG_SHARED_LIBRARY:
+ {
+ return PassThroughFlag::Generic_C_CPP;
}
+
default:
{
SLANG_ASSERT(!"Unknown type");
@@ -543,17 +570,6 @@ static BackendFlags _getBackendFlagsForTarget(SlangCompileTarget target)
}
}
-static BackendType _toBackendTypeFromPassThroughType(SlangPassThrough passThru)
-{
- switch (passThru)
- {
- case SLANG_PASS_THROUGH_DXC: return BackendType::Dxc;
- case SLANG_PASS_THROUGH_FXC: return BackendType::Fxc;
- case SLANG_PASS_THROUGH_GLSLANG: return BackendType::Glslang;
- default: return BackendType::Unknown;
- }
-}
-
static SlangCompileTarget _getCompileTarget(const UnownedStringSlice& name)
{
#define CASE(NAME, TARGET) if(name == NAME) return SLANG_##TARGET;
@@ -571,6 +587,9 @@ static SlangCompileTarget _getCompileTarget(const UnownedStringSlice& name)
CASE("dxil-asm", DXIL_ASM)
CASE("c", C_SOURCE)
CASE("cpp", CPP_SOURCE)
+ CASE("exe", EXECUTABLE)
+ CASE("sharedlib", SHARED_LIBRARY)
+ CASE("dll", SHARED_LIBRARY)
#undef CASE
return SLANG_TARGET_UNKNOWN;
@@ -680,11 +699,11 @@ static SlangResult _extractRenderTestRequirements(const CommandLine& cmdLine, Te
if (passThru == SLANG_PASS_THROUGH_NONE)
{
// Work out backends needed based on the target
- ioRequirements->addUsedBackends(_getBackendFlagsForTarget(target));
+ ioRequirements->addUsedBackends(_getPassThroughFlagsForTarget(target));
}
else
{
- ioRequirements->addUsed(_toBackendTypeFromPassThroughType(passThru));
+ ioRequirements->addUsed(passThru);
}
// Add the render api used
@@ -701,7 +720,7 @@ static SlangResult _extractSlangCTestRequirements(const CommandLine& cmdLine, Te
String passThrough;
if (SLANG_SUCCEEDED(_extractArg(cmdLine, "-pass-through", passThrough)))
{
- ioRequirements->addUsed(_toBackendType(passThrough.getUnownedSlice()));
+ ioRequirements->addUsed(_toPassThroughType(passThrough.getUnownedSlice()));
}
}
@@ -711,7 +730,7 @@ static SlangResult _extractSlangCTestRequirements(const CommandLine& cmdLine, Te
if (SLANG_SUCCEEDED(_extractArg(cmdLine, "-target", targetName)))
{
const SlangCompileTarget target = _getCompileTarget(targetName.getUnownedSlice());
- ioRequirements->addUsedBackends(_getBackendFlagsForTarget(target));
+ ioRequirements->addUsedBackends(_getPassThroughFlagsForTarget(target));
}
}
return SLANG_OK;
@@ -921,17 +940,48 @@ TestResult asTestResult(ToolReturnCode code)
} \
}
+static SlangResult _executeBinary(const UnownedStringSlice& hexDump, ExecuteResult& outExeRes)
+{
+ // We need to extract the binary
+ List<uint8_t> data;
+ SLANG_RETURN_ON_FAIL(HexDumpUtil::parseWithMarkers(hexDump, data));
+
+ // Need to write this off to a temporary file
+ String fileName;
+ SLANG_RETURN_ON_FAIL(File::generateTemporary(UnownedStringSlice("slang-test"), fileName));
+
+ fileName.append(ProcessUtil::getExecutableSuffix());
+
+ TemporaryFileSet temporaryFileSet;
+ temporaryFileSet.add(fileName);
+
+ {
+ ComPtr<ISlangWriter> writer;
+ SLANG_RETURN_ON_FAIL(FileWriter::createBinary(fileName.getBuffer(), 0, writer));
+
+ SLANG_RETURN_ON_FAIL(writer->write((const char*)data.getBuffer(), data.getCount()));
+ }
+
+ // Make executable... (for linux/unix like targets)
+ SLANG_RETURN_ON_FAIL(File::makeExecutable(fileName));
+
+ // Execute it
+ CommandLine cmdLine;
+ cmdLine.m_executable = fileName;
+ cmdLine.m_executableType = CommandLine::ExecutableType::Path;
+ return ProcessUtil::execute(cmdLine, outExeRes);
+}
+
+
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;
CommandLine cmdLine;
_initSlangCompiler(context, cmdLine);
- cmdLine.addArg(filePath999);
+ cmdLine.addArg(input.filePath);
for( auto arg : input.testOptions->args )
{
@@ -946,6 +996,28 @@ TestResult runSimpleTest(TestContext* context, TestInput& input)
return TestResult::Pass;
}
+ // See what kind of target it is
+ SlangCompileTarget target = SLANG_TARGET_UNKNOWN;
+ {
+ const auto& args = input.testOptions->args;
+ const Index targetIndex = args.indexOf("-target");
+ if (targetIndex != Index(-1) && targetIndex + 1 < args.getCount())
+ {
+ target = _getCompileTarget(args[targetIndex + 1].getUnownedSlice());
+ }
+ }
+
+ // If it's executable we run it and use it's output
+ if (target == SLANG_EXECUTABLE)
+ {
+ ExecuteResult runExeRes;
+ if (SLANG_FAILED(_executeBinary(exeRes.standardOutput.getUnownedSlice(), runExeRes)))
+ {
+ return TestResult::Fail;
+ }
+ exeRes = runExeRes;
+ }
+
String actualOutput = getOutput(exeRes);
String expectedOutputPath = outputStem + ".expected";
@@ -988,6 +1060,136 @@ TestResult runSimpleTest(TestContext* context, TestInput& input)
return result;
}
+static SlangResult _loadAsSharedLibrary(const UnownedStringSlice& hexDump, TemporaryFileSet& inOutTemporaryFileSet, SharedLibrary::Handle& outSharedLibrary)
+{
+ // We need to extract the binary
+ List<uint8_t> data;
+ SLANG_RETURN_ON_FAIL(HexDumpUtil::parseWithMarkers(hexDump, data));
+
+ // Need to write this off to a temporary file
+ String fileName;
+ SLANG_RETURN_ON_FAIL(File::generateTemporary(UnownedStringSlice("slang-test"), fileName));
+
+ // Need to work out the dll name
+ String sharedLibraryName = SharedLibrary::calcPlatformPath(fileName.getUnownedSlice());
+ inOutTemporaryFileSet.add(sharedLibraryName);
+
+ {
+ ComPtr<ISlangWriter> writer;
+ SLANG_RETURN_ON_FAIL(FileWriter::createBinary(sharedLibraryName.getBuffer(), 0, writer));
+ SLANG_RETURN_ON_FAIL(writer->write((const char*)data.getBuffer(), data.getCount()));
+ }
+
+ // Make executable... (for linux/unix like targets)
+ //SLANG_RETURN_ON_FAIL(File::makeExecutable(fileName));
+
+ return SharedLibrary::loadWithPlatformPath(sharedLibraryName.getBuffer(), outSharedLibrary);
+}
+
+static void _writeBuffer(const CPPPrelude::RWStructuredBuffer<int32_t>& in, StringBuilder& out)
+{
+ for (size_t i = 0; i < in.count; ++i)
+ {
+ if (i > 0)
+ {
+ out << ", ";
+ }
+ out << in[i];
+ }
+ out << "\n";
+}
+
+TestResult runCPUExecuteTest(TestContext* context, TestInput& input)
+{
+ auto outputStem = input.outputStem;
+
+ CommandLine cmdLine;
+ _initSlangCompiler(context, cmdLine);
+
+ cmdLine.addArg(input.filePath);
+
+ for (auto arg : input.testOptions->args)
+ {
+ cmdLine.addArg(arg);
+ }
+
+ ExecuteResult exeRes;
+ TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes));
+
+ if (context->isCollectingRequirements())
+ {
+ return TestResult::Pass;
+ }
+
+ TemporaryFileSet temporaryFileSet;
+ SharedLibrary::Handle sharedLibrary = SharedLibrary::Handle(0);
+ if (SLANG_FAILED(_loadAsSharedLibrary(exeRes.standardOutput.getUnownedSlice(), temporaryFileSet, sharedLibrary)))
+ {
+ return TestResult::Fail;
+ }
+
+ StringBuilder actualOutput;
+
+ // TODO(JS): For moment just assume function name/data/paramters
+ {
+ SharedLibrary::FuncPtr func = SharedLibrary::findFuncByName(sharedLibrary, "computeMain");
+ if (!func)
+ {
+ SharedLibrary::unload(sharedLibrary);
+ return TestResult::Fail;
+ }
+
+ typedef void (*Func)(CPPPrelude::Vector<uint32_t,3> threadID, CPPPrelude::RWStructuredBuffer<int32_t> buffer);
+
+ Func runFunc = Func(func);
+ int32_t data[4] = { 0, 0, 0, 0};
+ CPPPrelude::RWStructuredBuffer<int32_t> buffer{data, 4};
+
+ for (Int i = 0; i < 4; ++i)
+ {
+ CPPPrelude::Vector<uint32_t, 3> threadID{ uint32_t(i), 0, 0};
+ runFunc(threadID, buffer);
+ }
+
+ SharedLibrary::unload(sharedLibrary);
+
+ // Write the data
+ _writeBuffer(buffer, actualOutput);
+ }
+
+ String expectedOutputPath = outputStem + ".expected";
+ String expectedOutput;
+ try
+ {
+ expectedOutput = Slang::File::readAllText(expectedOutputPath);
+ }
+ catch (Slang::IOException)
+ {
+ }
+
+ 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 runSimpleCompareCommandLineTest(TestContext* context, TestInput& input)
{
TestInput workInput(input);
@@ -1158,6 +1360,8 @@ static TestResult runCPPCompilerCompile(TestContext* context, TestInput& input)
CPPCompiler::CompileOptions options;
options.sourceType = (targetExt == "c") ? CPPCompiler::SourceType::C : CPPCompiler::SourceType::CPP;
+ options.includePaths.add("tests/cross-compile");
+
// Create a filename to write this out to
String cppSource = modulePath + "." + targetExt;
Slang::File::writeAllText(cppSource, actualOutput);
@@ -2217,7 +2421,8 @@ static const TestCommandInfo s_testCommandInfos[] =
{ "CROSS_COMPILE", &runCrossCompilerTest},
{ "CPP_COMPILER_EXECUTE", &runCPPCompilerExecute},
{ "CPP_COMPILER_SHARED_LIBRARY", &runCPPCompilerSharedLibrary},
- { "CPP_COMPILER_COMPILE", &runCPPCompilerCompile}
+ { "CPP_COMPILER_COMPILE", &runCPPCompilerCompile},
+ { "CPU_EXECUTE", &runCPUExecuteTest},
};
TestResult runTest(
@@ -2624,44 +2829,42 @@ SlangResult innerMain(int argc, char** argv)
auto unixCatagory = categorySet.add("unix", fullTestCategory);
#endif
+ // An un-categorized test will always belong to the `full` category
+ categorySet.defaultCategory = fullTestCategory;
+
+
TestCategory* fxcCategory = nullptr;
TestCategory* dxcCategory = nullptr;
TestCategory* glslangCategory = nullptr;
- // Might be better if we had an API on slang so we could get what 'pass-through's are available
- // This works whilst these targets imply the pass-through/backends
+ // Work out what backends/pass-thrus are available
{
SlangSession* session = context.getSession();
- if (SLANG_SUCCEEDED(spSessionCheckPassThroughSupport(session, SLANG_PASS_THROUGH_FXC)))
+
+ for (int i = 0; i < SLANG_PASS_THROUGH_COUNT_OF; ++i)
+ {
+ SlangPassThrough passThru = SlangPassThrough(i);
+
+ if (SLANG_SUCCEEDED(spSessionCheckPassThroughSupport(session, passThru)))
+ {
+ context.availableBackendFlags |= PassThroughFlags(1) << int(i);
+ }
+ }
+
+ if (context.availableBackendFlags & PassThroughFlag::Fxc)
{
fxcCategory = categorySet.add("fxc", fullTestCategory);
}
- if (SLANG_SUCCEEDED(spSessionCheckPassThroughSupport(session, SLANG_PASS_THROUGH_GLSLANG)))
+ if (context.availableBackendFlags & PassThroughFlag::Glslang)
{
glslangCategory = categorySet.add("glslang", fullTestCategory);
}
- if (SLANG_SUCCEEDED(spSessionCheckPassThroughSupport(session, SLANG_PASS_THROUGH_DXC)))
+ if (context.availableBackendFlags & PassThroughFlag::Dxc)
{
dxcCategory = categorySet.add("dxc", fullTestCategory);
}
}
- // An un-categorized test will always belong to the `full` category
- categorySet.defaultCategory = fullTestCategory;
-
- // Work out what backends are available
- {
- SlangSession* session = context.getSession();
- const SlangPassThrough passThrus[] = { SLANG_PASS_THROUGH_DXC, SLANG_PASS_THROUGH_FXC, SLANG_PASS_THROUGH_GLSLANG };
- for (auto passThru: passThrus)
- {
- if (SLANG_SUCCEEDED(spSessionCheckPassThroughSupport(session, passThru)))
- {
- context.availableBackendFlags |= BackendFlags(1) << int(_toBackendTypeFromPassThroughType(passThru));
- }
- }
- }
-
// Working out what renderApis is worked on on demand through
// _getAvailableRenderApiFlags()
diff --git a/tools/slang-test/test-context.h b/tools/slang-test/test-context.h
index 895cb9c06..a66047687 100644
--- a/tools/slang-test/test-context.h
+++ b/tools/slang-test/test-context.h
@@ -13,23 +13,18 @@
#include "options.h"
-enum class BackendType
+typedef uint32_t PassThroughFlags;
+struct PassThroughFlag
{
- Unknown = -1,
- Dxc,
- Fxc,
- Glslang,
- CountOf,
-};
-
-typedef uint32_t BackendFlags;
-struct BackendFlag
-{
- enum Enum : uint32_t
+ enum Enum : PassThroughFlags
{
- Dxc = 1 << int(BackendType::Dxc),
- Fxc = 1 << int(BackendType::Fxc),
- Glslang = 1 << int(BackendType::Glslang),
+ Dxc = 1 << int(SLANG_PASS_THROUGH_DXC),
+ Fxc = 1 << int(SLANG_PASS_THROUGH_FXC),
+ Glslang = 1 << int(SLANG_PASS_THROUGH_GLSLANG),
+ VisualStudio = 1 << int(SLANG_PASS_THROUGH_VISUAL_STUDIO),
+ GCC = 1 << int(SLANG_PASS_THROUGH_GCC),
+ Clang = 1 << int(SLANG_PASS_THROUGH_CLANG),
+ Generic_C_CPP = 1 << int(SLANG_PASS_THROUGH_GENERIC_C_CPP),
};
};
@@ -37,11 +32,11 @@ struct BackendFlag
/// back-end availability
struct TestRequirements
{
- TestRequirements& addUsed(BackendType type)
+ TestRequirements& addUsed(SlangPassThrough type)
{
- if (type != BackendType::Unknown)
+ if (type != SLANG_PASS_THROUGH_NONE)
{
- usedBackendFlags |= BackendFlags(1) << int(type);
+ usedBackendFlags |= PassThroughFlags(1) << int(type);
}
return *this;
}
@@ -54,7 +49,7 @@ struct TestRequirements
}
return *this;
}
- TestRequirements& addUsedBackends(BackendFlags flags)
+ TestRequirements& addUsedBackends(PassThroughFlags flags)
{
usedBackendFlags |= flags;
return *this;
@@ -71,7 +66,7 @@ struct TestRequirements
}
Slang::RenderApiType explicitRenderApi = Slang::RenderApiType::Unknown; ///< The render api explicitly specified
- BackendFlags usedBackendFlags = 0; ///< Used backends
+ PassThroughFlags usedBackendFlags = 0; ///< Used backends
Slang::RenderApiFlags usedRenderApiFlags = 0; ///< Used render api flags (some might be implied)
};
@@ -111,7 +106,7 @@ class TestContext
/// If set then tests are not run, but their requirements are set
TestRequirements* testRequirements = nullptr;
- BackendFlags availableBackendFlags = 0;
+ PassThroughFlags availableBackendFlags = 0;
Slang::RenderApiFlags availableRenderApiFlags = 0;
bool isAvailableRenderApiFlagsValid = false;