1 module unit_threaded.runner; 2 3 import unit_threaded.factory; 4 import unit_threaded.testsuite; 5 import unit_threaded.io; 6 import unit_threaded.options; 7 import unit_threaded.testcase; 8 9 import std.stdio; 10 import std.traits; 11 import std.typetuple; 12 import std.concurrency; 13 import std.conv; 14 import std.algorithm; 15 import core.thread; 16 17 18 /** 19 * Runs all tests in passed-in modules. Modules can be symbols 20 * or strings. Arguments are taken from the command-line. 21 * -s Can be passed to run in single-threaded mode. The rest 22 * of argv is considered to be test names to be run. 23 * Returns: integer suitable for program return code. 24 */ 25 int runTests(MODULES...)(string[] args) { 26 const options = getOptions(args); 27 if(options.list) { 28 writeln("Listing tests:"); 29 foreach(test; getTestNames!MODULES()) { 30 writeln(test); 31 } 32 } 33 34 if(options.exit) return 0; 35 if(options.debugOutput) enableDebugOutput(); 36 if(options.forceEscCodes) forceEscCodes(); 37 38 immutable success = runTests!MODULES(options); 39 return success ? 0 : 1; 40 } 41 42 private auto getTestNames(MOD_SYMBOLS...)() if(!anySatisfy!(isSomeString, typeof(MOD_SYMBOLS))) { 43 return getAllTestCases!MOD_SYMBOLS.map!(a => a.name); 44 } 45 46 private auto getTestNames(MOD_STRINGS...)() if(allSatisfy!(isSomeString, typeof(MOD_STRINGS))) { 47 mixin(getImportTestsCompileString!MOD_STRINGS()); //e.g. import foo, bar, baz; 48 enum mod_symbols = getModulesCompileString!MOD_STRINGS; //e.g. foo, bar, baz 49 mixin("return getAllTestCases!(" ~ mod_symbols ~ ").map!(a => a.name);"); 50 } 51 52 /** 53 * Runs all tests in passed-in modules. Modules are symbols. 54 */ 55 bool runTests(MOD_SYMBOLS...)(in Options options) if(!anySatisfy!(isSomeString, typeof(MOD_SYMBOLS))) { 56 WriterThread.get(); //make sure this is up 57 //sleep to give WriterThread some time to set up. Otherwise, 58 //tests with output could write to stdout in the meanwhile 59 Thread.sleep(5.msecs); 60 61 auto tests = createTests!MOD_SYMBOLS(options.tests); 62 if(!tests) { 63 utWritelnRed("Error! No tests to run for args: "); 64 utWriteln(options.tests); 65 return false; 66 } 67 68 auto suite = TestSuite(tests); 69 immutable elapsed = suite.run(options); 70 71 if(!suite.numTestsRun) { 72 writeln("Did not run any tests!!!"); 73 return false; 74 } 75 76 utWriteln("\nTime taken: ", elapsed); 77 utWrite(suite.numTestsRun, " test(s) run, "); 78 const failuresStr = text(suite.numFailures, " failed"); 79 if(suite.numFailures) { 80 utWriteRed(failuresStr); 81 } else { 82 utWrite(failuresStr); 83 } 84 85 void printAbout(string attr)(in string msg) { 86 const num = getAllTestCases!MOD_SYMBOLS.filter!(a => mixin("a. " ~ attr)).count; 87 if(num) { 88 utWrite(", "); 89 utWriteYellow(num, " " ~ msg); 90 } 91 } 92 93 printAbout!"hidden"("hidden"); 94 printAbout!"shouldFail"("failing as expected"); 95 96 utWriteln(".\n"); 97 98 if(!suite.passed) { 99 utWritelnRed("Unit tests failed!\n"); 100 return false; //oops 101 } 102 103 utWritelnGreen("OK!\n"); 104 105 return true; 106 } 107 108 /** 109 * Runs all tests in passed-in modules. Modules are strings. 110 */ 111 bool runTests(MOD_STRINGS...)(in Options options) if(allSatisfy!(isSomeString, typeof(MOD_STRINGS))) { 112 mixin(getImportTestsCompileString!MOD_STRINGS); //e.g. import foo, bar, baz; 113 enum runStr = getRunTestsCompileString!MOD_STRINGS; 114 mixin(getRunTestsCompileString!MOD_STRINGS); //e.g. runTests!(foo, bar, baz)(); 115 } 116 117 private string getImportTestsCompileString(MOD_STRINGS...)() { 118 return "import " ~ getModulesCompileString!MOD_STRINGS ~ ";"; 119 } 120 121 private string getRunTestsCompileString(MOD_STRINGS...)() { 122 return "return runTests!(" ~ getModulesCompileString!MOD_STRINGS ~ ")(options);"; 123 } 124 125 private string getModulesCompileString(MOD_STRINGS...)() { 126 import std.array; 127 string[] modules; 128 foreach(mod; MOD_STRINGS) modules ~= mod; 129 return modules.join(", "); 130 }