diff --git a/src/platform/test/cinema-main.c b/src/platform/test/cinema-main.c index 1bc317755..228cde08d 100644 --- a/src/platform/test/cinema-main.c +++ b/src/platform/test/cinema-main.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -32,11 +33,13 @@ #include #define MAX_TEST 200 +#define MAX_JOBS 128 static const struct option longOpts[] = { { "base", required_argument, 0, 'b' }, { "diffs", no_argument, 0, 'd' }, { "help", no_argument, 0, 'h' }, + { "jobs", required_argument, 0, 'j' }, { "dry-run", no_argument, 0, 'n' }, { "outdir", required_argument, 0, 'o' }, { "quiet", no_argument, 0, 'q' }, @@ -46,7 +49,7 @@ static const struct option longOpts[] = { { 0, 0, 0, 0 } }; -static const char shortOpts[] = "b:dhno:qrv"; +static const char shortOpts[] = "b:dhj:no:qrv"; enum CInemaStatus { CI_PASS, @@ -91,8 +94,17 @@ static bool diffs = false; static bool rebaseline = false; static int verbosity = 0; +static struct Table configTree; +static Mutex configMutex; + +static int jobs = 1; +static size_t jobIndex = 0; +static Mutex jobMutex; +static Thread jobThreads[MAX_JOBS]; +static int jobStatus; + bool CInemaTestInit(struct CInemaTest*, const char* directory, const char* filename); -void CInemaTestRun(struct CInemaTest*, struct Table* configTree); +void CInemaTestRun(struct CInemaTest*); bool CInemaConfigGetUInt(struct Table* configTree, const char* testName, const char* key, unsigned* value); void CInemaConfigLoad(struct Table* configTree, const char* testName, struct mCore* core); @@ -142,6 +154,9 @@ static bool parseCInemaArgs(int argc, char* const* argv) { case 'h': showUsage = true; break; + case 'j': + jobs = atoi(optarg); + break; case 'n': dryRun = true; break; @@ -552,9 +567,11 @@ static void _writeBaseline(struct VDir* dir, const struct CInemaImage* image, si } } -void CInemaTestRun(struct CInemaTest* test, struct Table* configTree) { +void CInemaTestRun(struct CInemaTest* test) { unsigned ignore = 0; - CInemaConfigGetUInt(configTree, test->name, "ignore", &ignore); + MutexLock(&configMutex); + CInemaConfigGetUInt(&configTree, test->name, "ignore", &ignore); + MutexUnlock(&configMutex); if (ignore) { test->status = CI_SKIP; return; @@ -603,11 +620,13 @@ void CInemaTestRun(struct CInemaTest* test, struct Table* configTree) { unsigned fail = 0; unsigned video = 0; - CInemaConfigGetUInt(configTree, test->name, "frames", &limit); - CInemaConfigGetUInt(configTree, test->name, "skip", &skip); - CInemaConfigGetUInt(configTree, test->name, "fail", &fail); - CInemaConfigGetUInt(configTree, test->name, "video", &video); - CInemaConfigLoad(configTree, test->name, core); + MutexLock(&configMutex); + CInemaConfigGetUInt(&configTree, test->name, "frames", &limit); + CInemaConfigGetUInt(&configTree, test->name, "skip", &skip); + CInemaConfigGetUInt(&configTree, test->name, "fail", &fail); + CInemaConfigGetUInt(&configTree, test->name, "video", &video); + CInemaConfigLoad(&configTree, test->name, core); + MutexUnlock(&configMutex); struct VFile* save = VFileMemChunk(NULL, 0); core->loadROM(core, rom); @@ -825,6 +844,69 @@ void CInemaTestRun(struct CInemaTest* test, struct Table* configTree) { dir->close(dir); } +static bool CInemaTask(struct CInemaTestList* tests, size_t i) { + bool success = true; + struct CInemaTest* test = CInemaTestListGetPointer(tests, i); + if (dryRun) { + CIlog(-1, "%s\n", test->name); + } else { + CIlog(1, "%s: ", test->name); + fflush(stdout); + CInemaTestRun(test); + switch (test->status) { + case CI_PASS: + CIlog(1, "pass\n"); + break; + case CI_FAIL: + success = false; + CIlog(1, "fail\n"); + break; + case CI_XPASS: + CIlog(1, "xpass\n"); + break; + case CI_XFAIL: + CIlog(1, "xfail\n"); + break; + case CI_SKIP: + CIlog(1, "skip\n"); + break; + case CI_ERROR: + success = false; + CIlog(1, "error\n"); + break; + } + if (test->failedFrames) { + CIlog(2, "\tfailed frames: %u/%u (%1.3g%%)\n", test->failedFrames, test->totalFrames, test->failedFrames / (test->totalFrames * 0.01)); + CIlog(2, "\tfailed pixels: %" PRIu64 "/%" PRIu64 " (%1.3g%%)\n", test->failedPixels, test->totalPixels, test->failedPixels / (test->totalPixels * 0.01)); + CIlog(2, "\tdistance: %" PRIu64 "/%" PRIu64 " (%1.3g%%)\n", test->totalDistance, test->totalPixels * 765, test->totalDistance / (test->totalPixels * 7.65)); + } + } + return success; +} + +static THREAD_ENTRY CInemaJob(void* context) { + struct CInemaTestList* tests = context; + bool success = true; + while (true) { + size_t i; + MutexLock(&jobMutex); + i = jobIndex; + ++jobIndex; + MutexUnlock(&jobMutex); + if (i >= CInemaTestListSize(tests)) { + break; + } + if (!CInemaTask(tests, i)) { + success = false; + } + } + MutexLock(&jobMutex); + if (!success) { + jobStatus = 1; + } + MutexUnlock(&jobMutex); +} + void _log(struct mLogger* log, int category, enum mLogLevel level, const char* format, va_list args) { UNUSED(log); if (verbosity < 0) { @@ -918,48 +1000,31 @@ int main(int argc, char** argv) { reduceTestList(&tests); } - struct Table configTree; HashTableInit(&configTree, 0, free); + MutexInit(&configMutex); - size_t i; - for (i = 0; i < CInemaTestListSize(&tests); ++i) { - struct CInemaTest* test = CInemaTestListGetPointer(&tests, i); - if (dryRun) { - CIlog(-1, "%s\n", test->name); - } else { - CIlog(1, "%s: ", test->name); - fflush(stdout); - CInemaTestRun(test, &configTree); - switch (test->status) { - case CI_PASS: - CIlog(1, "pass\n"); - break; - case CI_FAIL: + if (jobs == 1) { + size_t i; + for (i = 0; i < CInemaTestListSize(&tests); ++i) { + bool success = CInemaTask(&tests, i); + if (!success) { status = 1; - CIlog(1, "fail\n"); - break; - case CI_XPASS: - CIlog(1, "xpass\n"); - break; - case CI_XFAIL: - CIlog(1, "xfail\n"); - break; - case CI_SKIP: - CIlog(1, "skip\n"); - break; - case CI_ERROR: - status = 1; - CIlog(1, "error\n"); - break; - } - if (test->failedFrames) { - CIlog(2, "\tfailed frames: %u/%u (%1.3g%%)\n", test->failedFrames, test->totalFrames, test->failedFrames / (test->totalFrames * 0.01)); - CIlog(2, "\tfailed pixels: %" PRIu64 "/%" PRIu64 " (%1.3g%%)\n", test->failedPixels, test->totalPixels, test->failedPixels / (test->totalPixels * 0.01)); - CIlog(2, "\tdistance: %" PRIu64 "/%" PRIu64 " (%1.3g%%)\n", test->totalDistance, test->totalPixels * 765, test->totalDistance / (test->totalPixels * 7.65)); } } + } else { + MutexInit(&jobMutex); + int i; + for (i = 0; i < jobs; ++i) { + ThreadCreate(&jobThreads[i], CInemaJob, &tests); + } + for (i = 0; i < jobs; ++i) { + ThreadJoin(&jobThreads[i]); + } + MutexDeinit(&jobMutex); + status = jobStatus; } + MutexDeinit(&configMutex); HashTableEnumerate(&configTree, _unloadConfigTree, NULL); HashTableDeinit(&configTree); CInemaTestListDeinit(&tests);