summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2021-11-18 15:58:12 -0500
committerGitHub <noreply@github.com>2021-11-18 15:58:12 -0500
commit1d5f815b3964edee8a2d701e1a6cc078c89d677f (patch)
treeaa5b4b1473344e635d7ce1d2159fc57eeb40b841 /tools
parentb482844b689eb109ee1d70c527e098400ac6d409 (diff)
RTTI/JSON (#2021)
* #include an absolute path didn't work - because paths were taken to always be relative. * Use 'Process' to communicate with an command line tool. * Remove slang-win-stream * Tidy up windows ProcessUtil. * First version of BufferedReadStream. * Windows working IPC for steams. * Test proxy count option. * Split Process/ProcessUtil. Process is platform dependant. ProcessUtil are functions that are platform independent. * First implementation of Unix Process interface. * Unix process compiles on cygwin. * Fix typo in unix process. * Separate unix pipe stream error of invalid access, from pipe availability. * Fix in standard line extraction. * Make fd non blocking. * Fix issues with Windows Process streams. * Added UnixPipe. * Some fixes around UnixPipeStream. * Make a unix stream closed explicit. * Hack to debug linux process/stream. * Revert to old linux pipe handling. * Pass executable path for unit tests. Split out CommandLine into own source. * Small improvements in process/command line. * Check process behavior with crash. * Make stderr and stdout unbuffered for crash testing. * Only turn disable buffering in crash test. * Disable crash test on CI. * Fix crash on clang/linux. * Enable crash test. Remove _appendBuffer as can use StreamUtil functionality. * Added inital processing for http headers. * Small improvements to HttpHeader. * First pass HTTPPacketConnection working on windows. * Enable other Process communication tests. * Update comments. * WIP JSON RPC. * Add terminate to Process. Made JSONRPC a Util. * Small tidy up around HTTPPacketConnection. * Improve process termination options. * WIP for test-server. * Add diagnostics error handling to test-server. * Improved JSON support. Parsing/creating JSON-RPC messages. * WIP JSONRPC parsing. * First pass RttiInfo support. * WIP converting between JSON/native types. * Project files. * Split out RttiUtil. Made RttiInfo constuction thread safe. * WIP RTTI<->JSON. * Add diagnostics to JSON<->native conversions. * Make RttiInfo for structs globals. Avoids problem around derived types (like pointers), being able to cause an abort. * Add pointer support to RTTI. Fixed some compilation issues on linux. * Add fixed array support. * Added Rtti unit test. * Add rtti unit test. * Split out quoted/unquoted key handling. Fix bugs in JSON value/container. Added JSON native test. * Make default array allocator use malloc/free. Remove the new[] handler (doesn't work on visuals studio). * Fix for linux warning. * Remove some test code. * Fix issues on x86 win. * Fix warning on aarch64.
Diffstat (limited to 'tools')
-rw-r--r--tools/slang-unit-test/unit-test-json-native.cpp117
-rw-r--r--tools/slang-unit-test/unit-test-rtti.cpp74
-rw-r--r--tools/test-server/test-server-diagnostic-defs.h26
-rw-r--r--tools/test-server/test-server-diagnostics.cpp13
-rw-r--r--tools/test-server/test-server-diagnostics.h21
-rw-r--r--tools/test-server/test-server-main.cpp510
6 files changed, 761 insertions, 0 deletions
diff --git a/tools/slang-unit-test/unit-test-json-native.cpp b/tools/slang-unit-test/unit-test-json-native.cpp
new file mode 100644
index 000000000..1d2085751
--- /dev/null
+++ b/tools/slang-unit-test/unit-test-json-native.cpp
@@ -0,0 +1,117 @@
+// unit-test-json-native.cpp
+
+#include "../../source/core/slang-rtti-info.h"
+
+#include "../../source/compiler-core/slang-json-native.h"
+#include "../../source/compiler-core/slang-json-parser.h"
+
+#include "tools/unit-test/slang-unit-test.h"
+
+using namespace Slang;
+
+namespace { // anonymous
+
+struct SomeStruct
+{
+ typedef SomeStruct ThisType;
+
+ bool operator==(const ThisType& rhs) const
+ {
+ return a == rhs.a && b == rhs.b && s == rhs.s && list == rhs.list;
+ }
+ bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
+
+ int a = 0;
+ float b = 2.0f;
+ String s;
+ List<String> list;
+
+ static const StructRttiInfo g_rttiInfo;
+};
+
+} // anonymous
+
+static const StructRttiInfo _makeSomeStructRtti()
+{
+ SomeStruct obj;
+ StructRttiBuilder builder(&obj, "SomeStruct", nullptr);
+
+ builder.addField("a", &obj.a);
+ builder.addField("b", &obj.b);
+ builder.addField("s", &obj.s);
+ builder.addField("list", &obj.list);
+
+ return builder.make();
+}
+/* static */const StructRttiInfo SomeStruct::g_rttiInfo = _makeSomeStructRtti();
+
+static SlangResult _check()
+{
+ // Convert into a JSON string
+
+ SomeStruct s;
+ s.list.add("Hello!");
+ s.s = "There";
+
+ // Try serializing it out
+
+ SourceManager sourceManager;
+ sourceManager.initialize(nullptr, nullptr);
+
+ DiagnosticSink sink(&sourceManager, &JSONLexer::calcLexemeLocation);
+
+ RefPtr<JSONContainer> container(new JSONContainer(&sourceManager));
+
+ String json;
+ {
+ NativeToJSONConverter converter(container, &sink);
+
+ JSONValue value;
+ SLANG_RETURN_ON_FAIL(converter.convert(GetRttiInfo<SomeStruct>::get(), &s, value));
+
+ // Convert into a string
+ JSONWriter writer(JSONWriter::IndentationStyle::Allman);
+ container->traverseRecursively(value, &writer);
+
+ json = writer.getBuilder();
+ }
+
+ JSONValue readValue;
+ {
+ // Now need to parse as JSON
+ String contents(json);
+ SourceFile* sourceFile = sourceManager.createSourceFileWithString(PathInfo::makeUnknown(), contents);
+ SourceView* sourceView = sourceManager.createSourceView(sourceFile, nullptr, SourceLoc());
+
+ JSONLexer lexer;
+ lexer.init(sourceView, &sink);
+
+ JSONBuilder builder(container);
+
+ JSONParser parser;
+ SLANG_RETURN_ON_FAIL(parser.parse(&lexer, sourceView, &builder, &sink));
+
+ readValue = builder.getRootValue();
+ }
+
+ // Convert back to native
+ {
+ JSONToNativeConverter converter(container, &sink);
+
+ {
+ SomeStruct readS;
+ SLANG_RETURN_ON_FAIL(converter.convert(readValue, GetRttiInfo<SomeStruct>::get(), &readS));
+
+ // Should be equal
+ SLANG_CHECK(readS == s);
+ }
+ }
+
+ return SLANG_OK;
+}
+
+SLANG_UNIT_TEST(JSONNative)
+{
+ SLANG_CHECK(SLANG_SUCCEEDED(_check()));
+
+}
diff --git a/tools/slang-unit-test/unit-test-rtti.cpp b/tools/slang-unit-test/unit-test-rtti.cpp
new file mode 100644
index 000000000..f72cfbde2
--- /dev/null
+++ b/tools/slang-unit-test/unit-test-rtti.cpp
@@ -0,0 +1,74 @@
+// unit-test-rtti.cpp
+
+#include "../../source/core/slang-rtti-info.h"
+
+#include "tools/unit-test/slang-unit-test.h"
+
+using namespace Slang;
+
+namespace { // anonymous
+
+struct SomeStruct
+{
+ int a = 0;
+ float b = 2.0f;
+ String s;
+ List<String> list;
+
+ static const StructRttiInfo g_rttiInfo;
+};
+
+} // anonymous
+
+static const StructRttiInfo _makeSomeStructRtti()
+{
+ SomeStruct obj;
+ StructRttiBuilder builder(&obj, "SomeStruct", nullptr);
+
+ builder.addField("a", &obj.a);
+ builder.addField("b", &obj.b);
+ builder.addField("s", &obj.s);
+ builder.addField("list", &obj.list);
+
+ return builder.make();
+}
+/* static */const StructRttiInfo SomeStruct::g_rttiInfo = _makeSomeStructRtti();
+
+SLANG_UNIT_TEST(Rtti)
+{
+ using namespace Slang;
+
+ const RttiInfo* types[] =
+ {
+ GetRttiInfo<int32_t>::get(),
+ GetRttiInfo<int32_t[10]>::get(),
+ GetRttiInfo<String>::get(),
+ GetRttiInfo<List<String>>::get(),
+ GetRttiInfo<List<List<String>>>::get(),
+ GetRttiInfo<int32_t[2][3]>::get(),
+ GetRttiInfo<SomeStruct>::get(),
+ GetRttiInfo<SomeStruct*>::get(),
+ GetRttiInfo<const float*const>::get(),
+ };
+
+ StringBuilder buf;
+
+ for (auto type : types)
+ {
+ RttiInfo::append(type, buf);
+ buf << "\n";
+ }
+
+ const char expected[] =
+ "int32_t\n"
+ "int32_t[10]\n"
+ "String\n"
+ "List<String>\n"
+ "List<List<String>>\n"
+ "int32_t[2][3]\n"
+ "SomeStruct\n"
+ "SomeStruct*\n"
+ "float*\n";
+
+ SLANG_CHECK(buf == expected)
+}
diff --git a/tools/test-server/test-server-diagnostic-defs.h b/tools/test-server/test-server-diagnostic-defs.h
new file mode 100644
index 000000000..ef0b280e8
--- /dev/null
+++ b/tools/test-server/test-server-diagnostic-defs.h
@@ -0,0 +1,26 @@
+//
+
+// The file is meant to be included multiple times, to produce different
+// pieces of declaration/definition code related to diagnostic messages
+//
+// Each diagnostic is declared here with:
+//
+// DIAGNOSTIC(id, severity, name, messageFormat)
+//
+// Where `id` is the unique diagnostic ID, `severity` is the default
+// severity (from the `Severity` enum), `name` is a name used to refer
+// to this diagnostic from code, and `messageFormat` is the default
+// (non-localized) message for the diagnostic, with placeholders
+// for any arguments.
+
+#ifndef DIAGNOSTIC
+#error Need to #define DIAGNOSTIC(...) before including "test-server-diagnostics-defs.h"
+#define DIAGNOSTIC(id, severity, name, messageFormat) /* */
+#endif
+
+DIAGNOSTIC(100000, Error, unableToLoadSharedLibrary, "Unable to load shared library '$0'")
+DIAGNOSTIC(100001, Error, unableToFindFunctionInSharedLibrary, "Unable to find function '$0' in shared library")
+DIAGNOSTIC(100002, Error, unableToGetUnitTestModule, "Unable to get unit test module")
+DIAGNOSTIC(100003, Error, unableToFindTest, "Unable to find test '$0'")
+
+#undef DIAGNOSTIC
diff --git a/tools/test-server/test-server-diagnostics.cpp b/tools/test-server/test-server-diagnostics.cpp
new file mode 100644
index 000000000..ddefc53d3
--- /dev/null
+++ b/tools/test-server/test-server-diagnostics.cpp
@@ -0,0 +1,13 @@
+#include "test-server-diagnostics.h"
+
+namespace TestServer {
+
+namespace ServerDiagnostics
+{
+using namespace Slang;
+
+#define DIAGNOSTIC(id, severity, name, messageFormat) const DiagnosticInfo name = { id, Severity::severity, #name, messageFormat };
+#include "test-server-diagnostic-defs.h"
+}
+
+} // namespace TestServer
diff --git a/tools/test-server/test-server-diagnostics.h b/tools/test-server/test-server-diagnostics.h
new file mode 100644
index 000000000..be816135a
--- /dev/null
+++ b/tools/test-server/test-server-diagnostics.h
@@ -0,0 +1,21 @@
+#ifndef TEST_SERVER_DIAGNOSTICS_H
+#define TEST_SERVER_DIAGNOSTICS_H
+
+#include "../../source/core/slang-basic.h"
+#include "../../source/core/slang-writer.h"
+
+#include "../../source/compiler-core/slang-source-loc.h"
+#include "../../source/compiler-core/slang-diagnostic-sink.h"
+
+namespace TestServer {
+using namespace Slang;
+
+namespace ServerDiagnostics {
+
+#define DIAGNOSTIC(id, severity, name, messageFormat) extern const DiagnosticInfo name;
+#include "test-server-diagnostic-defs.h"
+
+} // ServerDiagnostics
+} // TestServer
+
+#endif
diff --git a/tools/test-server/test-server-main.cpp b/tools/test-server/test-server-main.cpp
new file mode 100644
index 000000000..7e4180441
--- /dev/null
+++ b/tools/test-server/test-server-main.cpp
@@ -0,0 +1,510 @@
+// test-server.cpp
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../source/core/slang-secure-crt.h"
+
+#include "../../slang-com-helper.h"
+
+#include "../../source/core/slang-string.h"
+#include "../../source/core/slang-io.h"
+#include "../../source/core/slang-writer.h"
+#include "../../source/core/slang-string-util.h"
+#include "../../source/core/slang-process-util.h"
+
+#include "../../source/core/slang-shared-library.h"
+
+#include "../../source/core/slang-test-tool-util.h"
+#include "../../source/core/slang-http.h"
+
+#include "../../source/compiler-core/slang-source-loc.h"
+#include "../../source/compiler-core/slang-diagnostic-sink.h"
+
+#include "../../source/compiler-core/slang-json-parser.h"
+#include "../../source/compiler-core/slang-json-rpc.h"
+#include "../../source/compiler-core/slang-json-value.h"
+
+#include "test-server-diagnostics.h"
+
+#include "tools/unit-test/slang-unit-test.h"
+
+namespace TestServer
+{
+using namespace Slang;
+
+class TestReporter : public ITestReporter
+{
+public:
+ // ITestReporter
+ virtual SLANG_NO_THROW void SLANG_MCALL startTest(const char* testName) SLANG_OVERRIDE { }
+ virtual SLANG_NO_THROW void SLANG_MCALL addResult(TestResult result)SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL addResultWithLocation(TestResult result, const char* testText, const char* file, int line) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL addResultWithLocation(bool testSucceeded, const char* testText, const char* file, int line) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL addExecutionTime(double time) SLANG_OVERRIDE { }
+ virtual SLANG_NO_THROW void SLANG_MCALL message(TestMessageType type, const char* message) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL endTest() SLANG_OVERRIDE { }
+
+ StringBuilder m_buf;
+ Index m_failCount = 0;
+ Index m_testCount = 0;
+};
+
+class TestServer
+{
+public:
+ typedef Slang::TestToolUtil::InnerMainFunc InnerMainFunc;
+
+ SlangResult init(int argc, const char* const* argv);
+
+ /// Can return nullptr if cannot create the session
+ slang::IGlobalSession* getGlobalSession();
+
+ /// Can return nullptr if cannot load the tool
+ ISlangSharedLibrary* loadSharedLibrary(const String& name, DiagnosticSink* sink = nullptr);
+
+ /// Get a unit test module. Returns nullptr if not found.
+ IUnitTestModule* getUnitTestModule(const String& name, DiagnosticSink* sink = nullptr);
+
+ /// Given a tool name return it's function pointer. Or nullptr on failure.
+ InnerMainFunc getToolFunction(const String& name, DiagnosticSink* sink = nullptr);
+
+ /// Execute the server
+ SlangResult execute();
+
+ /// Dtor
+ ~TestServer();
+
+protected:
+ SlangResult _executeSingle();
+ SlangResult _executeUnitTest(JSONContainer* container, const JSONValue& root);
+ SlangResult _executeTool(JSONContainer* container, const JSONValue& root);
+ SlangResult _writeResponse(JSONContainer* containers, const JSONValue& root);
+
+ bool m_quit = false;
+
+ ComPtr<slang::IGlobalSession> m_session;
+
+ Dictionary<String, ComPtr<ISlangSharedLibrary>> m_sharedLibraryMap; ///< Maps tool names to the dll
+ Dictionary<String, IUnitTestModule*> m_unitTestModules;
+
+ String m_exePath; ///< Path to executable
+
+ DiagnosticSink m_diagnosticSink;
+ SourceManager m_sourceManager;
+
+ RefPtr<HTTPPacketConnection> m_connection;
+};
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!! TestServer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+SlangResult TestServer::init(int argc, const char* const* argv)
+{
+ m_exePath = Path::getParentDirectory(argv[0]);
+
+ RefPtr<Stream> stdinStream, stdoutStream;
+
+ Process::getStdStream(Process::StreamType::StdIn, stdinStream);
+ Process::getStdStream(Process::StreamType::StdOut, stdoutStream);
+
+ RefPtr<BufferedReadStream> readStream(new BufferedReadStream(stdinStream));
+
+ m_connection = new HTTPPacketConnection(readStream, stdoutStream);
+
+ m_sourceManager.initialize(nullptr, nullptr);
+ m_diagnosticSink.init(&m_sourceManager, &JSONLexer::calcLexemeLocation);
+
+ return SLANG_OK;
+}
+
+TestServer::~TestServer()
+{
+ for (auto& pair : m_unitTestModules)
+ {
+ pair.Value->destroy();
+ }
+}
+
+slang::IGlobalSession* TestServer::getGlobalSession()
+{
+ if (!m_session)
+ {
+ // Just create the global session in the regular way if there isn't one set
+ if (SLANG_FAILED(slang_createGlobalSession(SLANG_API_VERSION, m_session.writeRef())))
+ {
+ return nullptr;
+ }
+ TestToolUtil::setSessionDefaultPreludeFromExePath(m_exePath.getBuffer(), m_session);
+ }
+
+ return m_session;
+}
+
+ISlangSharedLibrary* TestServer::loadSharedLibrary(const String& name, DiagnosticSink* sink)
+{
+ ComPtr<ISlangSharedLibrary> lib;
+ if (m_sharedLibraryMap.TryGetValue(name, lib))
+ {
+ return lib;
+ }
+
+ auto loader = DefaultSharedLibraryLoader::getSingleton();
+
+ auto toolPath = Path::combine(m_exePath, name);
+
+ ComPtr<ISlangSharedLibrary> sharedLibrary;
+ if (SLANG_FAILED(loader->loadSharedLibrary(toolPath.getBuffer(), sharedLibrary.writeRef())))
+ {
+ if (sink)
+ {
+ sink->diagnose(SourceLoc(), ServerDiagnostics::unableToLoadSharedLibrary, name);
+ }
+
+ return nullptr;
+ }
+
+ m_sharedLibraryMap.Add(name, sharedLibrary);
+ return sharedLibrary;
+}
+
+IUnitTestModule* TestServer::getUnitTestModule(const String& name, DiagnosticSink* sink)
+{
+ auto unitTestModulePtr = m_unitTestModules.TryGetValue(name);
+ if (unitTestModulePtr)
+ {
+ return *unitTestModulePtr;
+ }
+
+ ISlangSharedLibrary* sharedLibrary = loadSharedLibrary(name, sink);
+ if (!sharedLibrary)
+ {
+ return nullptr;
+ }
+
+ UnownedStringSlice funcName = UnownedStringSlice::fromLiteral("slangUnitTestGetModule");
+
+ // get the unit test export name
+ UnitTestGetModuleFunc getModuleFunc = (UnitTestGetModuleFunc)sharedLibrary->findFuncByName(funcName.begin());
+ if (!getModuleFunc)
+ {
+ if (sink)
+ {
+ sink->diagnose(SourceLoc(), ServerDiagnostics::unableToFindFunctionInSharedLibrary, funcName);
+ }
+ return nullptr;
+ }
+
+ IUnitTestModule* testModule = getModuleFunc();
+ if (!testModule)
+ {
+ if (sink)
+ {
+ sink->diagnose(SourceLoc(), ServerDiagnostics::unableToGetUnitTestModule);
+ }
+ return nullptr;
+ }
+
+ m_unitTestModules.Add(name, testModule);
+ return testModule;
+}
+
+TestServer::InnerMainFunc TestServer::getToolFunction(const String& name, DiagnosticSink* sink)
+{
+ StringBuilder sharedLibToolBuilder;
+ sharedLibToolBuilder.append(name);
+ sharedLibToolBuilder.append("-tool");
+
+ ISlangSharedLibrary* sharedLibrary = loadSharedLibrary(sharedLibToolBuilder, sink);
+ if (!sharedLibrary)
+ {
+ return nullptr;
+ }
+
+ UnownedStringSlice funcName = UnownedStringSlice::fromLiteral("innerMain");
+
+ auto func = (InnerMainFunc)sharedLibrary->findFuncByName(funcName.begin());
+ if (!func && sink)
+ {
+ sink->diagnose(SourceLoc(), ServerDiagnostics::unableToFindFunctionInSharedLibrary, funcName);
+ }
+
+ return func;
+}
+
+SlangResult TestServer::_writeResponse(JSONContainer* container, const JSONValue& root)
+{
+ // TODO(JS): We may want a non indented style, to reduce size
+ JSONWriter writer(JSONWriter::IndentationStyle::Allman);
+ container->traverseRecursively(root, &writer);
+ const StringBuilder& builder = writer.getBuilder();
+ return m_connection->write(builder.getBuffer(), builder.getLength());
+}
+
+SlangResult TestServer::_executeSingle()
+{
+ // Block waiting for content (or error/closed)
+ SLANG_RETURN_ON_FAIL(m_connection->waitForResult());
+
+ // If we don't have content, we can quit for now
+ if (!m_connection->hasContent())
+ {
+ return SLANG_OK;
+ }
+
+ auto content = m_connection->getContent();
+
+ UnownedStringSlice slice((const char*)content.begin(), content.getCount());
+
+ // Reset for parse
+ m_sourceManager.reset();
+ m_diagnosticSink.reset();
+
+ JSONContainer container(&m_sourceManager);
+
+ // Parse as RPC JSON
+ JSONValue root;
+
+ {
+ SlangResult res = JSONRPCUtil::parseJSON(slice, &container, &m_diagnosticSink, root);
+ // Consume that content/packet
+ m_connection->consumeContent();
+
+ if (SLANG_FAILED(res))
+ {
+ return _writeResponse(&container, JSONRPCUtil::createErrorResponse(&container, JSONRPCUtil::ErrorCode::InvalidRequest, UnownedStringSlice::fromLiteral("Unable to parse JSON")));
+ }
+ }
+
+ JSONRPCUtil::Call call;
+ {
+ SlangResult res = JSONRPCUtil::parseCall(&container, root, call);
+ if (SLANG_FAILED(res))
+ {
+ return _writeResponse(&container, JSONRPCUtil::createErrorResponse(&container, Index(JSONRPCUtil::ErrorCode::InvalidRequest), UnownedStringSlice::fromLiteral("Cannot parse call")));
+ }
+ }
+
+ const auto& method = call.method;
+
+ // Do different things
+ if (method == "quit")
+ {
+ m_quit = true;
+ return SLANG_OK;
+ }
+ else if (method == "unitTest")
+ {
+ SLANG_RETURN_ON_FAIL(_executeUnitTest(&container, root));
+ return SLANG_OK;
+ }
+ else if (method == "tool")
+ {
+ SLANG_RETURN_ON_FAIL(_executeTool(&container, root));
+ return SLANG_OK;
+ }
+
+ return SLANG_FAIL;
+}
+
+static Index _findTestIndex(IUnitTestModule* testModule, const String& name)
+{
+ const auto testCount = testModule->getTestCount();
+ for (SlangInt i = 0; i < testCount; ++i)
+ {
+ auto testName = testModule->getTestName(i);
+
+ if (name == testName)
+ {
+ return Index(i);
+ }
+ }
+ return -1;
+}
+
+SlangResult TestServer::_executeUnitTest(JSONContainer* container, const JSONValue& root)
+{
+ String moduleName;
+ String testName;
+ Int enabledApis = 0;
+
+ IUnitTestModule* testModule = getUnitTestModule(moduleName, &m_diagnosticSink);
+ if (!testModule)
+ {
+ return SLANG_FAIL;
+ }
+
+ Index testIndex = _findTestIndex(testModule, moduleName);
+ if (testIndex < 0)
+ {
+ m_diagnosticSink.diagnose(SourceLoc(), ServerDiagnostics::unableToFindTest, testName);
+ return SLANG_FAIL;
+ }
+
+ TestReporter testReporter;
+
+ testModule->setTestReporter(&testReporter);
+
+ // Assume we will used the shared session
+ slang::IGlobalSession* session = getGlobalSession();
+ if (!session)
+ {
+ return SLANG_FAIL;
+ }
+
+ UnitTestContext unitTestContext;
+ unitTestContext.slangGlobalSession = session;
+ unitTestContext.workDirectory = "";
+ unitTestContext.enabledApis = RenderApiFlags(enabledApis);
+ unitTestContext.executableDirectory = m_exePath.getBuffer();
+
+ auto testCount = testModule->getTestCount();
+ SLANG_ASSERT(testIndex >= 0 && testIndex < testCount);
+
+ UnitTestFunc testFunc = testModule->getTestFunc(testIndex);
+
+ try
+ {
+ testFunc(&unitTestContext);
+ }
+ catch (...)
+ {
+ testReporter.m_failCount++;
+ }
+
+ if (testReporter.m_failCount > 0)
+ {
+ // Write out to stderr...
+ auto writers = StdWriters::createDefault();
+ writers->getError().put(testReporter.m_buf.getUnownedSlice());
+ return SLANG_FAIL;
+ }
+
+ if (testReporter.m_testCount == 0)
+ {
+ return SLANG_E_NOT_AVAILABLE;
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult TestServer::_executeTool(JSONContainer* container, const JSONValue& root)
+{
+ String toolName;
+
+ auto func = getToolFunction(toolName, &m_diagnosticSink);
+ if (!func)
+ {
+ // Write out to diagnostics
+ return SLANG_FAIL;
+ }
+
+ // Assume we will used the shared session
+ slang::IGlobalSession* session = getGlobalSession();
+ if (!session)
+ {
+ return SLANG_FAIL;
+ }
+
+ // Get the args list
+
+ // Work out the args sent to the shared library
+ List<const char*> args;
+
+
+ RefPtr<StdWriters> stdWriters = StdWriters::createDefault();
+
+ const SlangResult res = func(stdWriters, session, int(args.getCount()), args.begin());
+ if (SLANG_FAILED(res))
+ {
+ return res;
+ }
+ return res;
+}
+
+
+SlangResult TestServer::execute()
+{
+ DiagnosticSink diagnosticSink;
+
+ while (m_connection->isActive() && !m_quit)
+ {
+ SlangResult res = _executeSingle();
+ if (m_quit)
+ {
+ break;
+ }
+
+ if (SLANG_FAILED(res))
+ {
+ // Return a result
+ }
+ }
+
+ return SLANG_OK;
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TestReporter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+void TestReporter::message(TestMessageType type, const char* message)
+{
+ if (type == TestMessageType::RunError ||
+ type == TestMessageType::TestFailure)
+ {
+ m_failCount++;
+ }
+
+ m_buf << message << "\n";
+}
+
+void TestReporter::addResultWithLocation(TestResult result, const char* testText, const char* file, int line)
+{
+ if (result == TestResult::Fail)
+ {
+ addResultWithLocation(false, testText, file, line);
+ }
+ else
+ {
+ m_testCount++;
+ }
+}
+
+void TestReporter::addResultWithLocation(bool testSucceeded, const char* testText, const char* file, int line)
+{
+ m_testCount++;
+
+ if (testSucceeded)
+ {
+ return;
+ }
+
+ m_buf << "[Failed]: " << testText << "\n";
+ m_buf << file << ":" << line << "\n";
+
+ m_failCount++;
+}
+
+void TestReporter::addResult(TestResult result)
+{
+ if (result == TestResult::Fail)
+ {
+ m_failCount++;
+ }
+}
+
+
+SlangResult _execute(int argc, const char* const* argv)
+{
+ TestServer server;
+ SLANG_RETURN_ON_FAIL(server.init(argc, argv));
+ SLANG_RETURN_ON_FAIL(server.execute());
+
+ return SLANG_OK;
+}
+
+} // namespace TestServer
+
+int main(int argc, const char* const* argv)
+{
+ return (int)Slang::TestToolUtil::getReturnCode(TestServer:: _execute(argc, argv));
+}