diff options
| author | Tim Foley <tfoley@nvidia.com> | 2017-06-09 11:34:21 -0700 |
|---|---|---|
| committer | Tim Foley <tfoley@nvidia.com> | 2017-06-09 13:44:59 -0700 |
| commit | fcf83dbf9effab3bd98bad2b83b2468b7eb05cfd (patch) | |
| tree | 41047c94883b86ec085a81597391ce3ef557cd43 /tools/slang-test | |
| parent | 52e8d4b9a27ab0060f874c3a63ab531847be35c0 (diff) | |
Initial import of code.
Diffstat (limited to 'tools/slang-test')
| -rw-r--r-- | tools/slang-test/main.cpp | 1030 | ||||
| -rw-r--r-- | tools/slang-test/os.cpp | 370 | ||||
| -rw-r--r-- | tools/slang-test/os.h | 160 | ||||
| -rw-r--r-- | tools/slang-test/slang-test.vcxproj | 277 | ||||
| -rw-r--r-- | tools/slang-test/slang-test.vcxproj.filters | 30 |
5 files changed, 1867 insertions, 0 deletions
diff --git a/tools/slang-test/main.cpp b/tools/slang-test/main.cpp new file mode 100644 index 000000000..d8a04a050 --- /dev/null +++ b/tools/slang-test/main.cpp @@ -0,0 +1,1030 @@ +// main.cpp + +#include "../../source/core/slang-io.h" + +using namespace CoreLib::Basic; +using namespace CoreLib::IO; + +#include "os.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> + +struct Options +{ + char const* appName = "slang-test"; + + // Directory to use when looking for binaries to run + char const* binDir = ""; + + // only run test cases with names that have this prefix + char const* testPrefix = nullptr; + + // generate extra output (notably: command lines we run) + bool shouldBeVerbose = false; + + // force generation of baselines for HLSL tests + bool generateHLSLBaselines = false; +}; +Options options; + +void parseOptions(int* argc, char** argv) +{ + int argCount = *argc; + char const* const* argCursor = argv; + char const* const* argEnd = argCursor + argCount; + + char const** writeCursor = (char const**) argv; + + // first argument is the application name + if( argCursor != argEnd ) + { + options.appName = *argCursor++; + } + + // now iterate over arguments to collect options + while(argCursor != argEnd) + { + char const* arg = *argCursor++; + if( arg[0] != '-' ) + { + *writeCursor++ = arg; + continue; + } + + if( strcmp(arg, "--") == 0 ) + { + while(argCursor != argEnd) + { + char const* arg = *argCursor++; + *writeCursor++ = arg; + } + break; + } + + if( strcmp(arg, "--bindir") == 0 ) + { + if( argCursor == argEnd ) + { + fprintf(stderr, "error: expected operand for '%s'\n", arg); + exit(1); + } + options.binDir = *argCursor++; + } + else if( strcmp(arg, "-v") == 0 ) + { + options.shouldBeVerbose = true; + } + else if( strcmp(arg, "-generate-hlsl-baselines") == 0 ) + { + options.generateHLSLBaselines = true; + } + else if( strcmp(arg, "-release") == 0 ) + { + // Assumed to be handle by .bat file that called us + } + else if( strcmp(arg, "-debug") == 0 ) + { + // Assumed to be handle by .bat file that called us + } + else + { + fprintf(stderr, "unknown option '%s'\n", arg); + exit(1); + } + } + + // any arguments left over were positional arguments + argCount = (int)(writeCursor - argv); + argCursor = argv; + argEnd = argCursor + argCount; + + // first positional argument is a "filter" to apply + if( argCursor != argEnd ) + { + options.testPrefix = *argCursor++; + } + + // any remaining arguments represent an error + if(argCursor != argEnd) + { + fprintf(stderr, "unexpected arguments\n"); + exit(1); + } + + *argc = 0; +} + +// Called for an error in the test-runner (not for an error involving +// a test itself). +void error(char const* message, ...) +{ + fprintf(stderr, "error: "); + + va_list args; + va_start(args, message); + vfprintf(stderr, message, args); + va_end(args); + + fprintf(stderr, "\n"); +} + +enum TestResult +{ + kTestResult_Fail, + kTestResult_Pass, + kTestResult_Ignored, +}; + +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); +} + +// Optiosn for a particular test +struct TestOptions +{ + String command; + List<String> args; +}; + +// Information on tests to run for a particular file +struct FileTestList +{ + List<TestOptions> tests; +}; + +TestResult gatherTestOptions( + char const** ioCursor, + FileTestList* testList) +{ + char const* cursor = *ioCursor; + + // Start by scanning for the sub-command name: + char const* commandStart = cursor; + for(;;) + { + switch(*cursor) + { + default: + cursor++; + continue; + + case ':': + break; + + case 0: case '\r': case '\n': + return kTestResult_Fail; + } + + break; + } + char const* commandEnd = cursor; + if(*cursor == ':') + cursor++; + + TestOptions testOptions; + testOptions.command = getString(commandStart, commandEnd); + + // 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 kTestResult_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( + String filePath, + FileTestList* testList) +{ + String fileContents; + try + { + fileContents = CoreLib::IO::File::ReadAllText(filePath); + } + catch (CoreLib::IO::IOException) + { + return kTestResult_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:")) + { + if(gatherTestOptions(&cursor, testList) != kTestResult_Pass) + return kTestResult_Fail; + } + else if(match(&cursor, "//TEST_IGNORE_FILE")) + { + return kTestResult_Ignored; + } + else + { + skipToEndOfLine(&cursor); + } + } + + return kTestResult_Pass; +} + +OSError spawnAndWait(String testPath, OSProcessSpawner& spawner) +{ + if( options.shouldBeVerbose ) + { + fprintf(stderr, "%s\n", spawner.commandLine_.Buffer()); + } + + OSError err = spawner.spawnAndWaitForCompletion(); + if (err != kOSError_None) + { + error("failed to run test '%S'", testPath.ToWString()); + } + 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(); +} + +struct TestInput +{ + String filePath; + TestOptions const* testOptions; + FileTestList const* testList; +}; + +typedef TestResult (*TestCallback)(TestInput& input); + +TestResult runSimpleTest(TestInput& input) +{ + // need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect + + auto filePath = input.filePath; + + OSProcessSpawner spawner; + + spawner.pushExecutableName(String(options.binDir) + "slangc.exe"); + spawner.pushArgument(filePath); + + for( auto arg : input.testOptions->args ) + { + spawner.pushArgument(arg); + } + + if (spawnAndWait(filePath, spawner) != kOSError_None) + { + return kTestResult_Fail; + } + + String actualOutput = getOutput(spawner); + + String expectedOutputPath = filePath + ".expected"; + String expectedOutput; + try + { + expectedOutput = CoreLib::IO::File::ReadAllText(expectedOutputPath); + } + catch (CoreLib::IO::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 = kTestResult_Pass; + + // Otherwise we compare to the expected output + if (actualOutput != expectedOutput) + { + result = kTestResult_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 == kTestResult_Fail) + { + String actualOutputPath = filePath + ".actual"; + CoreLib::IO::File::WriteAllText(actualOutputPath, actualOutput); + } + + return result; +} + +#ifdef SLANG_TEST_SUPPORT_HLSL +TestResult generateHLSLBaseline(TestInput& input) +{ + auto filePath = input.filePath; + + OSProcessSpawner spawner; + spawner.pushExecutableName(String(options.binDir) + "slangc.exe"); + spawner.pushArgument(filePath); + + 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(filePath, spawner) != kOSError_None) + { + return kTestResult_Fail; + } + + String expectedOutput = getOutput(spawner); + String expectedOutputPath = filePath + ".expected"; + try + { + CoreLib::IO::File::WriteAllText(expectedOutputPath, expectedOutput); + } + catch (CoreLib::IO::IOException) + { + return kTestResult_Fail; + } + return kTestResult_Pass; +} + +TestResult runHLSLComparisonTest(TestInput& input) +{ + auto filePath = input.filePath; + + // We will use the Microsoft compiler to generate out expected output here + String expectedOutputPath = filePath + ".expected"; + + // Generate the expected output using standard HLSL compiler + generateHLSLBaseline(input); + + // need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect + + OSProcessSpawner spawner; + + spawner.pushExecutableName(String(options.binDir) + "slangc.exe"); + spawner.pushArgument(filePath); + + 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(filePath, spawner) != kOSError_None) + { + return kTestResult_Fail; + } + + // We ignore output to stdout, and only worry about what the compiler + // wrote to stderr. + + 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"); + + String actualOutput = actualOutputBuilder.ProduceString(); + + String expectedOutput; + try + { + expectedOutput = CoreLib::IO::File::ReadAllText(expectedOutputPath); + } + catch (CoreLib::IO::IOException) + { + } + + TestResult result = kTestResult_Pass; + + // If no expected output file was found, then we + // expect everything to be empty + if (expectedOutput.Length() == 0) + { + if (resultCode != 0) result = kTestResult_Fail; + if (standardError.Length() != 0) result = kTestResult_Fail; + if (standardOuptut.Length() != 0) result = kTestResult_Fail; + } + // Otherwise we compare to the expected output + else if (actualOutput != expectedOutput) + { + result = kTestResult_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 == kTestResult_Fail) + { + String actualOutputPath = filePath + ".actual"; + CoreLib::IO::File::WriteAllText(actualOutputPath, actualOutput); + } + + return result; +} +#endif + +TestResult doGLSLComparisonTestRun( + TestInput& input, + char const* langDefine, + char const* passThrough, + char const* outputKind, + String* outOutput) +{ + auto filePath = input.filePath; + + OSProcessSpawner spawner; + + spawner.pushExecutableName(String(options.binDir) + "slangc.exe"); + spawner.pushArgument(filePath); + + if( langDefine ) + { + spawner.pushArgument("-D"); + spawner.pushArgument(langDefine); + } + + if( passThrough ) + { + spawner.pushArgument("-pass-through"); + spawner.pushArgument(passThrough); + } + + spawner.pushArgument("-no-checking"); + + spawner.pushArgument("-target"); + spawner.pushArgument("spirv-assembly"); + + for( auto arg : input.testOptions->args ) + { + spawner.pushArgument(arg); + } + + if (spawnAndWait(filePath, spawner) != kOSError_None) + { + return kTestResult_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 = filePath + outputKind; + String output = outputBuilder.ProduceString(); + + *outOutput = output; + + return kTestResult_Pass; +} + +TestResult runGLSLComparisonTest(TestInput& input) +{ + auto filePath = input.filePath; + + String expectedOutput; + String actualOutput; + + TestResult hlslResult = doGLSLComparisonTestRun(input, "__GLSL__", "glslang", ".expected", &expectedOutput); + TestResult slangResult = doGLSLComparisonTestRun(input, "__SLANG__", nullptr, ".actual", &actualOutput); + + CoreLib::IO::File::WriteAllText(filePath + ".expected", expectedOutput); + CoreLib::IO::File::WriteAllText(filePath + ".actual", actualOutput); + + if( hlslResult == kTestResult_Fail ) return kTestResult_Fail; + if( slangResult == kTestResult_Fail ) return kTestResult_Fail; + + if (actualOutput != expectedOutput) + { + return kTestResult_Fail; + } + + return kTestResult_Pass; +} + + + +TestResult doRenderComparisonTestRun(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; + + OSProcessSpawner spawner; + + spawner.pushExecutableName(String(options.binDir) + "render-test.exe"); + spawner.pushArgument(filePath); + + for( auto arg : input.testOptions->args ) + { + spawner.pushArgument(arg); + } + + spawner.pushArgument(langOption); + spawner.pushArgument("-o"); + spawner.pushArgument(filePath + outputKind + ".png"); + + if (spawnAndWait(filePath, spawner) != kOSError_None) + { + return kTestResult_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 = filePath + outputKind; + String output = outputBuilder.ProduceString(); + + *outOutput = output; + + return kTestResult_Pass; +} + +TestResult doImageComparison(String const& filePath) +{ + // Allow a difference in the low bits of the 8-bit result, just to play it safe + static const int kAbsoluteDiffCutoff = 2; + + // Allow a relatie 1% difference + static const float kRelativeDiffCutoff = 0.01f; + + String expectedPath = filePath + ".expected.png"; + String actualPath = filePath + ".actual.png"; + + int expectedX, expectedY, expectedN; + int actualX, actualY, actualN; + + + unsigned char* expectedData = stbi_load(expectedPath.begin(), &expectedX, &expectedY, &expectedN, 0); + unsigned char* actualData = stbi_load(actualPath.begin(), &actualX, &actualY, &actualN, 0); + + if(!expectedData) return kTestResult_Fail; + if(!actualData) return kTestResult_Fail; + + if(expectedX != actualX) return kTestResult_Fail; + if(expectedY != actualY) return kTestResult_Fail; + if(expectedN != actualN) return kTestResult_Fail; + + unsigned char* expectedCursor = expectedData; + unsigned char* actualCursor = actualData; + + for( int y = 0; y < actualY; ++y ) + for( int x = 0; x < actualX; ++x ) + for( int n = 0; n < actualN; ++n ) + { + int expectedVal = *expectedCursor++; + int actualVal = *actualCursor++; + + 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; + } + + if( expectedVal != 0 ) + { + float 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... + + // There was a difference we couldn't excuse! + return kTestResult_Fail; + } + + return kTestResult_Pass; +} + +TestResult runHLSLRenderComparisonTestImpl( + TestInput& input, + char const* expectedArg, + char const* actualArg) +{ + auto filePath = input.filePath; + + String expectedOutput; + String actualOutput; + + TestResult hlslResult = doRenderComparisonTestRun(input, expectedArg, ".expected", &expectedOutput); + TestResult slangResult = doRenderComparisonTestRun(input, actualArg, ".actual", &actualOutput); + + CoreLib::IO::File::WriteAllText(filePath + ".expected", expectedOutput); + CoreLib::IO::File::WriteAllText(filePath + ".actual", actualOutput); + + if( hlslResult == kTestResult_Fail ) return kTestResult_Fail; + if( slangResult == kTestResult_Fail ) return kTestResult_Fail; + + if (actualOutput != expectedOutput) + { + return kTestResult_Fail; + } + + // Next do an image comparison on the expected output images! + + TestResult imageCompareResult = doImageComparison(filePath); + if(imageCompareResult != kTestResult_Pass) + return imageCompareResult; + + return kTestResult_Pass; +} + +TestResult runHLSLRenderComparisonTest(TestInput& input) +{ + return runHLSLRenderComparisonTestImpl(input, "-hlsl", "-slang"); +} + +TestResult runHLSLCrossCompileRenderComparisonTest(TestInput& input) +{ + return runHLSLRenderComparisonTestImpl(input, "-slang", "-glsl-cross"); +} + +TestResult runTest( + String const& filePath, + TestOptions const& testOptions, + FileTestList const& testList) +{ + // based on command name, dispatch to an appropriate callback + static const struct TestCommands + { + char const* name; + TestCallback callback; + } kTestCommands[] = { + { "SIMPLE", &runSimpleTest }, + { "COMPARE_HLSL", &runHLSLComparisonTest }, + { "COMPARE_HLSL_RENDER", &runHLSLRenderComparisonTest }, + { "COMPARE_HLSL_CROSS_COMPILE_RENDER", &runHLSLCrossCompileRenderComparisonTest}, + { "COMPARE_GLSL", &runGLSLComparisonTest }, + { nullptr, nullptr }, + }; + + for( auto ii = kTestCommands; ii->name; ++ii ) + { + if(testOptions.command != ii->name) + continue; + + TestInput testInput; + testInput.filePath = filePath; + testInput.testOptions = &testOptions; + testInput.testList = &testList; + + return ii->callback(testInput); + } + + // No actual test runner found! + + return kTestResult_Fail; +} + + +struct TestContext +{ + int totalTestCount; + int passedTestCount; + int failedTestCount; +}; + +void runTestsOnFile( + TestContext* context, + String filePath) +{ + // Gather a list of tests to run + FileTestList testList; + + if( gatherTestsForFile(filePath, &testList) == kTestResult_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->totalTestCount++; + context->failedTestCount++; + + printf("FAILED test: '%S' (no test commands found)\n", filePath.ToWString()); + return; + } + + // We have found a test to run! + int subTestCount = 0; + for( auto& tt : testList.tests ) + { + context->totalTestCount++; + + int subTestIndex = subTestCount++; + + TestResult result = runTest(filePath, tt, testList); + if(result == kTestResult_Ignored) + return; + + if (result == kTestResult_Pass) + { + printf("passed"); + context->passedTestCount++; + } + else + { + printf("FAILED"); + context->failedTestCount++; + } + + printf(" test: '%S'", filePath.ToWString()); + if( subTestIndex ) + { + printf(" subtest:%d", subTestIndex); + } + printf("\n"); + } +} + + +static bool endsWithAllowedExtension( + TestContext* context, + String filePath) +{ + char const* allowedExtensions[] = { + ".slang", + ".hlsl", + ".fx", + ".glsl", + ".vert", + ".frag", + ".geom", + ".tesc", + ".tese", + ".comp", + 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( options.testPrefix ) + { + if( strncmp(options.testPrefix, filePath.begin(), strlen(options.testPrefix)) != 0 ) + { + return false; + } + } + + return true; +} + +void runTestsInDirectory( + TestContext* context, + String directoryPath) +{ + for (auto file : osFindFilesInDirectory(directoryPath)) + { + if( shouldRunTest(context, file) ) + { + runTestsOnFile(context, file); + } + } + for (auto subdir : osFindChildDirectories(directoryPath)) + { + runTestsInDirectory(context, subdir); + } +} + +// + +int main( + int argc, + char** argv) +{ + parseOptions(&argc, argv); + + TestContext context = { 0 }; + + // 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/"); + + if (!context.totalTestCount) + { + printf("no tests run\n"); + return 0; + } + + printf("\n===\n%d%% of tests passed (%d/%d)\n===\n\n", (context.passedTestCount*100) / context.totalTestCount, context.passedTestCount, context.totalTestCount); + return context.passedTestCount == context.totalTestCount ? 0 : 1; +} diff --git a/tools/slang-test/os.cpp b/tools/slang-test/os.cpp new file mode 100644 index 000000000..8f5172ae6 --- /dev/null +++ b/tools/slang-test/os.cpp @@ -0,0 +1,370 @@ +// os.cpp +#include "os.h" + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +using namespace CoreLib::Basic; + +// Platform-specific code follows + +#ifdef _WIN32 + +#include <Windows.h> + +static bool advance(OSFindFilesResult& result) +{ + return FindNextFileW(result.findHandle_, &result.fileData_) != 0; +} + +static bool adjustToValidResult(OSFindFilesResult& result) +{ + for (;;) + { + if ((result.fileData_.dwFileAttributes & result.requiredMask_) != result.requiredMask_) + goto skip; + + if ((result.fileData_.dwFileAttributes & result.disallowedMask_) != 0) + goto skip; + + if (wcscmp(result.fileData_.cFileName, L".") == 0) + goto skip; + + if (wcscmp(result.fileData_.cFileName, L"..") == 0) + goto skip; + + result.filePath_ = result.directoryPath_ + String::FromWString(result.fileData_.cFileName); + if (result.fileData_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + result.filePath_ = result.filePath_ + "/"; + + return true; + + skip: + if (!advance(result)) + return false; + } +} + + +bool OSFindFilesResult::findNextFile() +{ + if (!advance(*this)) return false; + return adjustToValidResult(*this); +} + +OSFindFilesResult osFindFilesInDirectoryMatchingPattern( + CoreLib::Basic::String directoryPath, + CoreLib::Basic::String pattern) +{ + // TODO: add separator to end of directory path if needed + + String searchPath = directoryPath + pattern; + + OSFindFilesResult result; + HANDLE findHandle = FindFirstFileW( + searchPath.ToWString(), + &result.fileData_); + + result.directoryPath_ = directoryPath; + result.findHandle_ = findHandle; + result.requiredMask_ = 0; + result.disallowedMask_ = FILE_ATTRIBUTE_DIRECTORY; + + if (findHandle == INVALID_HANDLE_VALUE) + { + result.findHandle_ = NULL; + result.error_ = kOSError_FileNotFound; + return result; + } + + result.error_ = kOSError_None; + if (!adjustToValidResult(result)) + { + result.findHandle_ = NULL; + } + return result; +} + +OSFindFilesResult osFindFilesInDirectory( + CoreLib::Basic::String directoryPath) +{ + return osFindFilesInDirectoryMatchingPattern(directoryPath, "*"); +} + +OSFindFilesResult osFindChildDirectories( + CoreLib::Basic::String directoryPath) +{ + // TODO: add separator to end of directory path if needed + + String searchPath = directoryPath + "*"; + + OSFindFilesResult result; + HANDLE findHandle = FindFirstFileW( + searchPath.ToWString(), + &result.fileData_); + + result.directoryPath_ = directoryPath; + result.findHandle_ = findHandle; + result.requiredMask_ = FILE_ATTRIBUTE_DIRECTORY; + result.disallowedMask_ = 0; + + if (findHandle == INVALID_HANDLE_VALUE) + { + result.findHandle_ = NULL; + result.error_ = kOSError_FileNotFound; + return result; + } + + result.error_ = kOSError_None; + if (!adjustToValidResult(result)) + { + result.findHandle_ = NULL; + } + return result; +} + +// OSProcessSpawner + +struct OSProcessSpawner_ReaderThreadInfo +{ + HANDLE file; + String output; +}; + +static DWORD WINAPI osReaderThreadProc(LPVOID threadParam) +{ + OSProcessSpawner_ReaderThreadInfo* info = (OSProcessSpawner_ReaderThreadInfo*)threadParam; + HANDLE file = info->file; + + static const int kChunkSize = 1024; + char buffer[kChunkSize]; + + StringBuilder outputBuilder; + + // We need to re-write the output to deal with line + // endings, so we check for paired '\r' and '\n' + // characters, which may span chunks. + int prevChar = -1; + + for (;;) + { + DWORD bytesRead = 0; + BOOL readResult = ReadFile(file, buffer, kChunkSize, &bytesRead, nullptr); + + if (!readResult || GetLastError() == ERROR_BROKEN_PIPE) + { + break; + } + + // walk the buffer and rewrite to eliminate '\r' '\n' pairs + char* readCursor = buffer; + char const* end = buffer + bytesRead; + char* writeCursor = buffer; + + while (readCursor != end) + { + int p = prevChar; + int c = *readCursor++; + prevChar = c; + switch (c) + { + case '\r': case '\n': + // swallow input if '\r' and '\n' appear in sequence + if ((p ^ c) == ('\r' ^ '\n')) + { + // but don't swallow the next byte + prevChar = -1; + continue; + } + // always replace '\r' with '\n' + c = '\n'; + break; + + default: + break; + } + + *writeCursor++ = c; + } + bytesRead = (DWORD)(writeCursor - buffer); + + // Note: Current Slang CoreLib gives no way to know + // the length of the buffer, so we ultimately have + // to just assume null termination... + outputBuilder.Append(buffer, bytesRead); + } + + info->output = outputBuilder.ProduceString(); + + return 0; +} + +void OSProcessSpawner::pushExecutableName( + CoreLib::Basic::String executableName) +{ + executableName_ = executableName; + commandLine_.Append(executableName); +} + +void OSProcessSpawner::pushArgument( + CoreLib::Basic::String argument) +{ + // TODO(tfoley): handle cases where arguments need some escaping + commandLine_.Append(" "); + commandLine_.Append(argument); +} + +OSError OSProcessSpawner::spawnAndWaitForCompletion() +{ + SECURITY_ATTRIBUTES securityAttributes; + securityAttributes.nLength = sizeof(securityAttributes); + securityAttributes.lpSecurityDescriptor = nullptr; + securityAttributes.bInheritHandle = true; + + // create stdout pipe for child process + HANDLE childStdOutReadTmp = nullptr; + HANDLE childStdOutWrite = nullptr; + if (!CreatePipe(&childStdOutReadTmp, &childStdOutWrite, &securityAttributes, 0)) + { + return kOSError_OperationFailed; + } + + // create stderr pipe for child process + HANDLE childStdErrReadTmp = nullptr; + HANDLE childStdErrWrite = nullptr; + if (!CreatePipe(&childStdErrReadTmp, &childStdErrWrite, &securityAttributes, 0)) + { + return kOSError_OperationFailed; + } + + // create stdin pipe for child process + HANDLE childStdInRead = nullptr; + HANDLE childStdInWriteTmp = nullptr; + if (!CreatePipe(&childStdInRead, &childStdInWriteTmp, &securityAttributes, 0)) + { + return kOSError_OperationFailed; + } + + HANDLE currentProcess = GetCurrentProcess(); + + // create a non-inheritable duplicate of the stdout reader + HANDLE childStdOutRead = nullptr; + if (!DuplicateHandle( + currentProcess, childStdOutReadTmp, + currentProcess, &childStdOutRead, + 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + return kOSError_OperationFailed; + } + if (!CloseHandle(childStdOutReadTmp)) + { + return kOSError_OperationFailed; + } + + // create a non-inheritable duplicate of the stderr reader + HANDLE childStdErrRead = nullptr; + if (!DuplicateHandle( + currentProcess, childStdErrReadTmp, + currentProcess, &childStdErrRead, + 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + return kOSError_OperationFailed; + } + if (!CloseHandle(childStdErrReadTmp)) + { + return kOSError_OperationFailed; + } + + // create a non-inheritable duplicate of the stdin writer + HANDLE childStdInWrite = nullptr; + if (!DuplicateHandle( + currentProcess, childStdInWriteTmp, + currentProcess, &childStdInWrite, + 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + return kOSError_OperationFailed; + } + if (!CloseHandle(childStdInWriteTmp)) + { + return kOSError_OperationFailed; + } + + // Now we can actually get around to starting a process + PROCESS_INFORMATION processInfo; + ZeroMemory(&processInfo, sizeof(processInfo)); + + // TODO: switch to proper wide-character versions of these... + STARTUPINFOW startupInfo; + ZeroMemory(&startupInfo, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + startupInfo.hStdError = childStdErrWrite; + startupInfo.hStdOutput = childStdOutWrite; + startupInfo.hStdInput = childStdInRead; + startupInfo.dwFlags = STARTF_USESTDHANDLES; + + // `CreateProcess` requires write access to this, for some reason... + BOOL success = CreateProcessW( + executableName_.ToWString(), + (LPWSTR)commandLine_.ToString().ToWString(), + nullptr, + nullptr, + true, + CREATE_NO_WINDOW, + nullptr, // TODO: allow specifying environment variables? + nullptr, + &startupInfo, + &processInfo); + if (!success) + { + return kOSError_OperationFailed; + } + + // close handles we are now done with + CloseHandle(processInfo.hThread); + CloseHandle(childStdOutWrite); + CloseHandle(childStdErrWrite); + CloseHandle(childStdInRead); + + // Create a thread to read from the child's stdout. + OSProcessSpawner_ReaderThreadInfo stdOutThreadInfo; + stdOutThreadInfo.file = childStdOutRead; + HANDLE stdOutThread = CreateThread(nullptr, 0, &osReaderThreadProc, (LPVOID)&stdOutThreadInfo, 0, nullptr); + + // Create a thread to read from the child's stderr. + OSProcessSpawner_ReaderThreadInfo stdErrThreadInfo; + stdErrThreadInfo.file = childStdErrRead; + HANDLE stdErrThread = CreateThread(nullptr, 0, &osReaderThreadProc, (LPVOID)&stdErrThreadInfo, 0, nullptr); + + // wait for the process to exit + // TODO: set a timeout as a safety measure... + WaitForSingleObject(processInfo.hProcess, INFINITE); + + // get exit code for process + DWORD childExitCode = 0; + if (!GetExitCodeProcess(processInfo.hProcess, &childExitCode)) + { + return kOSError_OperationFailed; + } + + // wait for the reader threads + WaitForSingleObject(stdOutThread, INFINITE); + WaitForSingleObject(stdErrThread, INFINITE); + + CloseHandle(processInfo.hProcess); + CloseHandle(childStdOutRead); + CloseHandle(childStdErrRead); + CloseHandle(childStdInWrite); + + standardOutput_ = stdOutThreadInfo.output; + standardError_ = stdErrThreadInfo.output; + resultCode_ = childExitCode; + + return kOSError_None; +} + +#else + +// TODO(tfoley): write a default POSIX implementation + +#endif diff --git a/tools/slang-test/os.h b/tools/slang-test/os.h new file mode 100644 index 000000000..2996001e7 --- /dev/null +++ b/tools/slang-test/os.h @@ -0,0 +1,160 @@ +// os.h + +#include "../../source/core/slang-io.h" + +// This file encapsulates the platform-specific operations needed by the test +// runner that are not already provided by the core Slang libs + +#ifdef _WIN32 + +// Include Windows header in a way that minimized namespace pollution. +// TODO: We could try to avoid including this at all, but it would +// mean trying to hide certain struct layouts, which would add +// more dynamic allocation. +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#else +#endif + +// A simple set of error codes for possible runtime failures +enum OSError +{ + kOSError_None = 0, + kOSError_InvalidArgument, + kOSError_OperationFailed, + kOSError_FileNotFound, +}; + +// A helper type used during enumeration of files in a directory. +struct OSFindFilesResult +{ + CoreLib::Basic::String directoryPath_; + CoreLib::Basic::String filePath_; +#ifdef WIN32 + HANDLE findHandle_; + WIN32_FIND_DATAW fileData_; + DWORD requiredMask_; + DWORD disallowedMask_; + OSError error_; +#else +#endif + + bool findNextFile(); + + struct Iterator + { + OSFindFilesResult* context_; + + bool operator!=(Iterator other) const { return context_ != other.context_; } + void operator++() + { + if (!context_->findNextFile()) + { + context_ = NULL; + } + } + CoreLib::Basic::String const& operator*() const + { + return context_->filePath_; + } + }; + + Iterator begin() + { +#ifdef WIN32 + Iterator result = { findHandle_ ? this : NULL }; +#else +#endif + return result; + } + + Iterator end() + { + Iterator result = { NULL }; + return result; + } +}; + +// Enumerate subdirectories in the given `directoryPath` and return a logical +// collection of the results that can be iterated with a range-based +// `for` loop: +// +// for( auto subdir : osFindChildDirectories(dir)) +// { ... } +// +// Each element in the range is a `CoreLib::Basic::String` representing the +// path to a subdirecotry of the directory. +OSFindFilesResult osFindChildDirectories( + CoreLib::Basic::String directoryPath); + +// Enumerate files in the given `directoryPath` that match the provided +// `pattern` as a simplified regex for files to return (e.g., "*.txt") +// and return a logical collection of the results +// that can be iterated with a range-based `for` loop: +// +// for( auto file : osFindFilesInDirectoryMatchingPattern(dir, "*.txt")) +// { ... } +// +// Each element in the range is a `CoreLib::Basic::String` representing the +// path to a file in the directory. +OSFindFilesResult osFindFilesInDirectoryMatchingPattern( + CoreLib::Basic::String directoryPath, + CoreLib::Basic::String pattern); + +// Enumerate files in the given `directoryPath` and return a logical +// collection of the results that can be iterated with a range-based +// `for` loop: +// +// for( auto file : osFindFilesInDirectory(dir)) +// { ... } +// +// Each element in the range is a `CoreLib::Basic::String` representing the +// path to a file in the directory. +OSFindFilesResult osFindFilesInDirectory( + CoreLib::Basic::String directoryPath); + + +// An `OSProcessSpawner` can be used to launch a process, and handles +// putting together the arguments in the form required by the target +// platform, as well as capturing any output from the process (both +// standard output and standard error) as strings. +struct OSProcessSpawner +{ + // Set the executable name for the process to be spawned. + // Note: this call must be made before any arguments are pushed. + void pushExecutableName( + CoreLib::Basic::String executableName); + + // Append an argument for the process to be spawned. + void pushArgument( + CoreLib::Basic::String argument); + + // Attempt to spawn the process, and wait for it to complete. + // Returns an error if the attempt to spawn and/or wait fails, + // but returns `kOSError_None` if the process is run to completion, + // whether or not the process returns "successfully" (with a zero + // result code); + OSError spawnAndWaitForCompletion(); + + // If the process is successfully spawned and completes, then + // the user can query the result code that the process produce + // on exit, along with the output it wrote to stdout and stderr. + typedef int ResultCode; + ResultCode getResultCode() { return resultCode_; } + CoreLib::Basic::String const& getStandardOutput() { return standardOutput_; } + CoreLib::Basic::String const& getStandardError() { return standardError_; } + + // "private" data follows + CoreLib::Basic::String standardOutput_; + CoreLib::Basic::String standardError_; + ResultCode resultCode_; +#ifdef WIN32 + CoreLib::Basic::String executableName_; + CoreLib::Basic::StringBuilder commandLine_; +#else +#endif +}; diff --git a/tools/slang-test/slang-test.vcxproj b/tools/slang-test/slang-test.vcxproj new file mode 100644 index 000000000..b8d23d569 --- /dev/null +++ b/tools/slang-test/slang-test.vcxproj @@ -0,0 +1,277 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug_VS2013|Win32"> + <Configuration>Debug_VS2013</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug_VS2013|x64"> + <Configuration>Debug_VS2013</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release_VS2013|Win32"> + <Configuration>Release_VS2013</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release_VS2013|x64"> + <Configuration>Release_VS2013</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{0C768A18-1D25-4000-9F37-DA5FE99E3B64}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>SpireTestTool</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v120</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|Win32'"> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|x64'"> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|Win32'"> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|x64'"> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|Win32'"> + <LinkIncremental>false</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|x64'"> + <LinkIncremental>false</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|Win32'"> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|x64'"> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|x64'"> + <ClCompile> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <Optimization>Disabled</Optimization> + </ClCompile> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + <ClCompile Include="os.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="os.h" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\source\core\core.vcxproj"> + <Project>{f9be7957-8399-899e-0c49-e714fddd4b65}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/tools/slang-test/slang-test.vcxproj.filters b/tools/slang-test/slang-test.vcxproj.filters new file mode 100644 index 000000000..3549e6046 --- /dev/null +++ b/tools/slang-test/slang-test.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="os.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="os.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project>
\ No newline at end of file |
