diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/slang-test/main.cpp | 73 | ||||
| -rw-r--r-- | tools/slang-test/os.cpp | 262 | ||||
| -rw-r--r-- | tools/slang-test/os.h | 24 |
3 files changed, 339 insertions, 20 deletions
diff --git a/tools/slang-test/main.cpp b/tools/slang-test/main.cpp index 563fc5dab..50716d2c4 100644 --- a/tools/slang-test/main.cpp +++ b/tools/slang-test/main.cpp @@ -179,7 +179,7 @@ void parseOptions(int* argc, char** argv) } // any arguments left over were positional arguments - argCount = (int)(writeCursor - argv); + argCount = (int)((char**)writeCursor - argv); argCursor = argv; argEnd = argCursor + argCount; @@ -548,14 +548,15 @@ OSError spawnAndWait(String testPath, OSProcessSpawner& spawner) { if( options.shouldBeVerbose ) { - fprintf(stderr, "%s\n", spawner.commandLine_.ToString().begin()); + String commandLine = spawner.getCommandLine(); + fprintf(stderr, "%s\n", commandLine.begin()); } OSError err = spawner.spawnAndWaitForCompletion(); if (err != kOSError_None) { // fprintf(stderr, "failed to run test '%S'\n", testPath.ToWString()); - error("failed to run test '%S'", testPath.ToWString()); + error("failed to run test '%S'", testPath.ToWString().begin()); } return err; } @@ -580,6 +581,8 @@ String getOutput(OSProcessSpawner& spawner) return actualOutputBuilder.ProduceString(); } +List<String> gFailedTests; + struct TestInput { // Path to the input file for the test @@ -609,7 +612,7 @@ TestResult runSimpleTest(TestInput& input) OSProcessSpawner spawner; - spawner.pushExecutablePath(String(options.binDir) + "slangc.exe"); + spawner.pushExecutablePath(String(options.binDir) + "slangc" + osGetExecutableSuffix()); spawner.pushArgument(filePath999); for( auto arg : input.testOptions->args ) @@ -680,7 +683,7 @@ TestResult runEvalTest(TestInput& input) OSProcessSpawner spawner; - spawner.pushExecutablePath(String(options.binDir) + "slang-eval-test.exe"); + spawner.pushExecutablePath(String(options.binDir) + "slang-eval-test" + osGetExecutableSuffix()); spawner.pushArgument(filePath); for( auto arg : input.testOptions->args ) @@ -754,8 +757,8 @@ TestResult runCrossCompilerTest(TestInput& input) OSProcessSpawner actualSpawner; OSProcessSpawner expectedSpawner; - actualSpawner.pushExecutablePath(String(options.binDir) + "slangc.exe"); - expectedSpawner.pushExecutablePath(String(options.binDir) + "slangc.exe"); + actualSpawner.pushExecutablePath(String(options.binDir) + "slangc" + osGetExecutableSuffix()); + expectedSpawner.pushExecutablePath(String(options.binDir) + "slangc" + osGetExecutableSuffix()); actualSpawner.pushArgument(filePath); expectedSpawner.pushArgument(filePath + ".glsl"); @@ -827,7 +830,7 @@ TestResult generateHLSLBaseline(TestInput& input) auto outputStem = input.outputStem; OSProcessSpawner spawner; - spawner.pushExecutablePath(String(options.binDir) + "slangc.exe"); + spawner.pushExecutablePath(String(options.binDir) + "slangc" + osGetExecutableSuffix()); spawner.pushArgument(filePath999); for( auto arg : input.testOptions->args ) @@ -873,7 +876,7 @@ TestResult runHLSLComparisonTest(TestInput& input) OSProcessSpawner spawner; - spawner.pushExecutablePath(String(options.binDir) + "slangc.exe"); + spawner.pushExecutablePath(String(options.binDir) + "slangc" + osGetExecutableSuffix()); spawner.pushArgument(filePath999); for( auto arg : input.testOptions->args ) @@ -973,7 +976,7 @@ TestResult doGLSLComparisonTestRun( OSProcessSpawner spawner; - spawner.pushExecutablePath(String(options.binDir) + "slangc.exe"); + spawner.pushExecutablePath(String(options.binDir) + "slangc" + osGetExecutableSuffix()); spawner.pushArgument(filePath999); if( langDefine ) @@ -1062,7 +1065,7 @@ TestResult doRenderComparisonTestRun(TestInput& input, char const* langOption, c OSProcessSpawner spawner; - spawner.pushExecutablePath(String(options.binDir) + "render-test.exe"); + spawner.pushExecutablePath(String(options.binDir) + "render-test" + osGetExecutableSuffix()); spawner.pushArgument(filePath999); for( auto arg : input.testOptions->args ) @@ -1217,6 +1220,11 @@ TestResult runHLSLAndGLSLComparisonTest(TestInput& input) return runHLSLRenderComparisonTestImpl(input, "-hlsl-rewrite", "-glsl-rewrite"); } +TestResult skipTest(TestInput& input) +{ + return kTestResult_Ignored; +} + TestResult runTest( String const& filePath, String const& outputStem, @@ -1230,10 +1238,17 @@ TestResult runTest( TestCallback callback; } kTestCommands[] = { { "SIMPLE", &runSimpleTest }, +#if SLANG_TEST_SUPPORT_HLSL { "COMPARE_HLSL", &runHLSLComparisonTest }, { "COMPARE_HLSL_RENDER", &runHLSLRenderComparisonTest }, { "COMPARE_HLSL_CROSS_COMPILE_RENDER", &runHLSLCrossCompileRenderComparisonTest}, { "COMPARE_HLSL_GLSL_RENDER", &runHLSLAndGLSLComparisonTest }, +#else + { "COMPARE_HLSL", &skipTest }, + { "COMPARE_HLSL_RENDER", &skipTest }, + { "COMPARE_HLSL_CROSS_COMPILE_RENDER", &skipTest}, + { "COMPARE_HLSL_GLSL_RENDER", &skipTest }, +#endif { "COMPARE_GLSL", &runGLSLComparisonTest }, { "CROSS_COMPILE", &runCrossCompilerTest }, { "EVAL", &runEvalTest }, @@ -1265,6 +1280,7 @@ struct TestContext int totalTestCount; int passedTestCount; int failedTestCount; + int ignoredTestCount; }; // deal with the fallout of a test having completed, whether @@ -1278,6 +1294,7 @@ void handleTestResult( { case kTestResult_Fail: context->failedTestCount++; + gFailedTests.Add(testName); break; case kTestResult_Pass: @@ -1285,8 +1302,7 @@ void handleTestResult( break; case kTestResult_Ignored: - // Note that we don't currently add ignored tests into - // the totals, which is kind of inaccurate. + context->ignoredTestCount++; break; default: @@ -1344,7 +1360,7 @@ void handleTestResult( if( err != kOSError_None ) { - error("failed to add appveyor test results for '%S'\n", testName.ToWString()); + error("failed to add appveyor test results for '%S'\n", testName.ToWString().begin()); #if 0 fprintf(stderr, "[%d] TEST RESULT: %s {%d} {%s} {%s}\n", err, spawner.commandLine_.Buffer(), @@ -1512,6 +1528,7 @@ void runTestsInDirectory( { if( shouldRunTest(context, file) ) { +// fprintf(stderr, "slang-test: found '%s'\n", file.Buffer()); runTestsOnFile(context, file); } } @@ -1571,6 +1588,30 @@ int main( 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; + + auto passCount = context.passedTestCount; + auto rawTotal = context.totalTestCount; + auto ignoredCount = context.ignoredTestCount; + + auto runTotal = rawTotal - ignoredCount; + + printf("\n===\n%d%% of tests passed (%d/%d)", (passCount*100) / runTotal, passCount, runTotal); + if(ignoredCount) + { + printf(", %d tests ingored", ignoredCount); + } + printf("\n===\n\n"); + + if(context.failedTestCount) + { + printf("failing tests:\n"); + printf("---\n"); + for(auto name : gFailedTests) + { + printf("%s\n", name.Buffer()); + } + printf("---\n"); + } + + return passCount == runTotal ? 0 : 1; } diff --git a/tools/slang-test/os.cpp b/tools/slang-test/os.cpp index 40fd8afff..18f81318d 100644 --- a/tools/slang-test/os.cpp +++ b/tools/slang-test/os.cpp @@ -224,6 +224,11 @@ void OSProcessSpawner::pushArgument( commandLine_.Append(argument); } +Slang::String OSProcessSpawner::getCommandLine() +{ + return commandLine_; +} + OSError OSProcessSpawner::spawnAndWaitForCompletion() { SECURITY_ATTRIBUTES securityAttributes; @@ -372,8 +377,263 @@ OSError OSProcessSpawner::spawnAndWaitForCompletion() return kOSError_None; } +char const* osGetExecutableSuffix() +{ + return ".exe"; +} + #else -// TODO(tfoley): write a default POSIX implementation +static bool advance(OSFindFilesResult& result) +{ + result.entry_ = readdir(result.directory_); + return result.entry_ != NULL; +} + +static bool checkValidResult(OSFindFilesResult& result) +{ +// fprintf(stderr, "checkValidResullt(%s)\n", result.entry_->d_name); + + if (strcmp(result.entry_->d_name, ".") == 0) + return false; + + if (strcmp(result.entry_->d_name, "..") == 0) + return false; + + String path = result.directoryPath_ + + String(result.entry_->d_name); + +// fprintf(stderr, "stat(%s)\n", path.Buffer()); + struct stat fileInfo; + if(stat(path.Buffer(), &fileInfo) != 0) + return false; + + if(S_ISDIR(fileInfo.st_mode)) + path = path + "/"; + + + result.filePath_ = path; + return true; +} + +static bool adjustToValidResult(OSFindFilesResult& result) +{ + for (;;) + { + if(checkValidResult(result)) + return true; + + if (!advance(result)) + return false; + } +} + + +bool OSFindFilesResult::findNextFile() +{ +// fprintf(stderr, "OSFindFilesResult::findNextFile()\n"); + if (!advance(*this)) return false; + return adjustToValidResult(*this); +} + +OSFindFilesResult osFindFilesInDirectory( + Slang::String directoryPath) +{ + OSFindFilesResult result; + +// fprintf(stderr, "osFindFilesInDirectory(%s)\n", directoryPath.Buffer()); + + result.directory_ = opendir(directoryPath.Buffer()); + if(!result.directory_) + { + result.entry_ = NULL; + return result; + } + + result.directoryPath_ = directoryPath; + result.findNextFile(); + return result; +} + +OSFindFilesResult osFindChildDirectories( + Slang::String directoryPath) +{ + OSFindFilesResult result; + + result.directory_ = opendir(directoryPath.Buffer()); + if(!result.directory_) + { + result.entry_ = NULL; + return result; + } + + // TODO: Set attributes to ignore everything but directories + + result.directoryPath_ = directoryPath; + result.findNextFile(); + return result; +} + +// OSProcessSpawner + +void OSProcessSpawner::pushExecutableName( + Slang::String executableName) +{ + executableName_ = executableName; + pushArgument(executableName); + isExecutablePath_ = false; +} + +void OSProcessSpawner::pushExecutablePath( + Slang::String executablePath) +{ + executableName_ = executablePath; + pushArgument(executablePath); + isExecutablePath_ = true; +} + +void OSProcessSpawner::pushArgument( + Slang::String argument) +{ + arguments_.Add(argument); +} + +Slang::String OSProcessSpawner::getCommandLine() +{ + Slang::UInt argCount = arguments_.Count(); + + Slang::StringBuilder sb; + for(Slang::UInt ii = 0; ii < argCount; ++ii) + { + if(ii != 0) sb << " "; + sb << arguments_[ii]; + + } + return sb.ProduceString(); +} + +OSError OSProcessSpawner::spawnAndWaitForCompletion() +{ + List<char const*> argPtrs; + for(auto arg : arguments_) + { + argPtrs.Add(arg.Buffer()); + } + argPtrs.Add(NULL); + + int stdoutPipe[2]; + int stderrPipe[2]; + + if(pipe(stdoutPipe) == -1) + return kOSError_OperationFailed; + + if(pipe(stderrPipe) == -1) + return kOSError_OperationFailed; + + pid_t childProcessID = fork(); + if(childProcessID == 0) + { + // We are the child process. + + dup2(stdoutPipe[1], STDOUT_FILENO); + dup2(stderrPipe[1], STDERR_FILENO); + + close(stdoutPipe[0]); + close(stdoutPipe[1]); + + close(stderrPipe[0]); + close(stderrPipe[1]); + + execvp( + argPtrs[0], + (char* const*) &argPtrs[0]); + + // If we get here, then `exec` failed + fprintf(stderr, "error: `exec` failed\n"); + exit(1); + } + else + { + // We are the parent process + + close(stdoutPipe[1]); + close(stderrPipe[1]); + + int stdoutFD = stdoutPipe[0]; + int stderrFD = stderrPipe[0]; + + int maxFD = stdoutFD > stderrFD ? stdoutFD : stderrFD; + + fd_set readSet; + int result; + + int remainingCount = 2; + while(remainingCount) + { + FD_ZERO(&readSet); + FD_SET(stdoutFD, &readSet); + FD_SET(stderrFD, &readSet); + + result = select(maxFD + 1, &readSet, NULL, NULL, NULL); + + if(result == -1 || errno == EINTR) + continue; + + enum { kBufferSize = 1024 }; + char buffer[kBufferSize]; + + if(FD_ISSET(stdoutFD, &readSet)) + { + auto count = read(stdoutFD, buffer, kBufferSize); + if(count == 0) + remainingCount--; + + standardOutput_.append( + buffer, buffer + count); + } + + if(FD_ISSET(stderrFD, &readSet)) + { + auto count = read(stderrFD, buffer, kBufferSize); + if(count == 0) + remainingCount--; + + standardError_.append( + buffer, buffer + count); + } + } + + int childStatus = 0; + for(;;) + { + pid_t terminatedProcessID = wait(&childStatus); + if(terminatedProcessID == childProcessID) + { + if(WIFEXITED(childStatus)) + { + resultCode_ = (int)(int8_t)WEXITSTATUS(childStatus); + + } + else + { + resultCode_ = 1; + } + + close(stdoutPipe[0]); + close(stderrPipe[0]); + + return kOSError_None; + } + } + + } + + return kOSError_OperationFailed; +} + +char const* osGetExecutableSuffix() +{ + return ""; +} #endif diff --git a/tools/slang-test/os.h b/tools/slang-test/os.h index c6eb8f410..cc1fa4065 100644 --- a/tools/slang-test/os.h +++ b/tools/slang-test/os.h @@ -18,6 +18,14 @@ #undef NOMINMAX #else + +#include <dirent.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + #endif // A simple set of error codes for possible runtime failures @@ -41,6 +49,8 @@ struct OSFindFilesResult DWORD disallowedMask_; OSError error_; #else + DIR* directory_; + dirent* entry_; #endif bool findNextFile(); @@ -68,6 +78,7 @@ struct OSFindFilesResult #ifdef WIN32 Iterator result = { findHandle_ ? this : NULL }; #else + Iterator result = { entry_ ? this : NULL }; #endif return result; } @@ -138,6 +149,10 @@ struct OSProcessSpawner void pushArgument( Slang::String argument); + // Get a printable version of the command line + // that will be run (can be used for debugging) + Slang::String getCommandLine(); + // 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, @@ -157,12 +172,15 @@ struct OSProcessSpawner Slang::String standardOutput_; Slang::String standardError_; ResultCode resultCode_; -#ifdef WIN32 Slang::String executableName_; +#ifdef WIN32 Slang::StringBuilder commandLine_; +#else + Slang::List<Slang::String> arguments_; +#endif // Is the executable specified by path, rather than just by name? bool isExecutablePath_; -#else -#endif }; + +char const* osGetExecutableSuffix(); |
