1 /** 2 * This module implements functions to run the unittests with 3 * command-line options. 4 */ 5 6 module unit_threaded.runner.runner; 7 8 import unit_threaded.from; 9 10 /** 11 * Runs all tests in passed-in modules. Modules can be symbols or 12 * strings but they can't mix and match - either all symbols or all 13 * strings. It's recommended to use strings since then the modules don't 14 * have to be imported first. 15 * Generates a main function and substitutes the default D 16 * runtime unittest runner. This mixin should be used instead of 17 * $(D runTests) if a shared library is used instead of an executable. 18 */ 19 mixin template runTestsMain(Modules...) if(Modules.length > 0) { 20 int main(string[] args) { 21 import unit_threaded.runner.runner: runTests; 22 return runTests!Modules(args); 23 } 24 } 25 26 /** 27 * Runs all tests in passed-in modules. Modules can be symbols or 28 * strings but they can't mix and match - either all symbols or all 29 * strings. It's recommended to use strings since then the modules don't 30 * have to be imported first. 31 * Arguments are taken from the command-line. 32 * -s Can be passed to run in single-threaded mode. The rest 33 * of argv is considered to be test names to be run. 34 * Params: 35 * args = Arguments passed to main. 36 * Returns: An integer suitable for the program's return code. 37 */ 38 template runTests(Modules...) if(Modules.length > 0) { 39 40 shared static this() { 41 import unit_threaded.runner.runner: replaceModuleUnitTester; 42 replaceModuleUnitTester; 43 } 44 45 int runTests(string[] args) { 46 import unit_threaded.runner.reflection: allTestData; 47 return runTests(args, allTestData!Modules); 48 } 49 } 50 51 52 53 /** 54 * Runs all tests in passed-in testData. Arguments are taken from the 55 * command-line. `-s` Can be passed to run in single-threaded mode. The 56 * rest of argv is considered to be test names to be run. 57 * Params: 58 * args = Arguments passed to main. 59 * testData = Data about the tests to run. 60 * Returns: An integer suitable for the program's return code. 61 */ 62 int runTests(string[] args, in from!"unit_threaded.runner.reflection".TestData[] testData) { 63 import unit_threaded.runner.options: getOptions; 64 return runTests(getOptions(args), testData); 65 } 66 67 int runTests(in from!"unit_threaded.runner.options".Options options, 68 in from!"unit_threaded.runner.reflection".TestData[] testData) 69 { 70 import unit_threaded.runner.testsuite: TestSuite; 71 72 handleCmdLineOptions(options, testData); 73 if (options.exit) 74 return 0; 75 76 auto suite = TestSuite(options, testData); 77 return suite.run ? 0 : 1; 78 } 79 80 81 private void handleCmdLineOptions(in from!"unit_threaded.runner.options".Options options, 82 in from!"unit_threaded.runner.reflection".TestData[] testData) 83 { 84 85 import unit_threaded.runner.io: enableDebugOutput, forceEscCodes; 86 import unit_threaded.runner.testcase: enableStackTrace; 87 import std.algorithm: map; 88 89 if (options.list) { 90 import std.stdio: writeln; 91 92 writeln("Listing tests:"); 93 foreach (test; testData.map!(a => a.name)) { 94 writeln(test); 95 } 96 } 97 98 if (options.debugOutput) 99 enableDebugOutput(); 100 101 if (options.forceEscCodes) 102 forceEscCodes(); 103 104 if (options.stackTraces) 105 enableStackTrace(); 106 } 107 108 109 /** 110 * Replace the D runtime's normal unittest block tester. If this is not done, 111 * the tests will run twice. 112 */ 113 void replaceModuleUnitTester() { 114 import core.runtime: Runtime; 115 Runtime.moduleUnitTester = &moduleUnitTester; 116 } 117 118 119 /** 120 * Replacement for the usual unittest runner. Since unit_threaded 121 * runs the tests itself, the moduleUnitTester doesn't really have to do anything. 122 */ 123 private bool moduleUnitTester() { 124 //this is so unit-threaded's own tests run 125 import std.algorithm: startsWith; 126 foreach(module_; ModuleInfo) { 127 if(module_ && module_.unitTest && 128 module_.name.startsWith("unit_threaded") && // we want to run the "normal" unit tests 129 //!module_.name.startsWith("unit_threaded.property") && // left here for fast iteration when developing 130 !module_.name.startsWith("unit_threaded.ut.modules")) //but not the ones from the test modules 131 { 132 version(testing_unit_threaded) { 133 import std.stdio: writeln; 134 writeln("Running unit-threaded UT for module " ~ module_.name); 135 } 136 module_.unitTest()(); 137 138 } 139 } 140 141 return true; 142 }