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 import unit_threaded.writer_thread;
9 
10 import std.stdio;
11 import std.traits;
12 import std.typetuple;
13 import std.concurrency;
14 import std.conv;
15 import std.algorithm;
16 import core.thread;
17 
18 
19 /**
20  * Runs all tests in passed-in modules. Modules can be symbols
21  * or strings. Arguments are taken from the command-line.
22  * -s Can be passed to run in single-threaded mode. The rest
23  * of argv is considered to be test names to be run.
24  * Returns: integer suitable for program return code.
25  */
26 int runTests(MODULES...)(string[] args) {
27     const options = getOptions(args);
28     if(options.list) {
29         writeln("Listing tests:");
30         foreach(test; getTestNames!MODULES()) {
31             writeln(test);
32         }
33     }
34 
35     if(options.exit) return 0;
36     if(options.debugOutput) enableDebugOutput();
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 map!(a => a.name)(getTestClassesAndFunctions!MOD_SYMBOLS());
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     mixin("return map!(a => a.name)(getTestClassesAndFunctions!(" ~
49           getModulesCompileString!MOD_STRINGS() ~ ")());");
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(dur!"msecs"(5));
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.multiThreaded);
70 
71     if(!suite.numTestsRun) {
72         writeln("Did not run any tests!!!");
73         return false;
74     }
75 
76     utWriteln("\nTime taken: ", elapsed, " seconds");
77     utWriteln(suite.numTestsRun, " test(s) run, ",
78               suite.numFailures, " failed.\n");
79 
80     if(!suite.passed) {
81         utWritelnRed("Unit tests failed!\n");
82         return false; //oops
83     }
84 
85     utWritelnGreen("OK!\n");
86 
87     return true;
88 }
89 
90 /**
91  * Runs all tests in passed-in modules. Modules are strings.
92  */
93 bool runTests(MOD_STRINGS...)(in Options options) if(allSatisfy!(isSomeString, typeof(MOD_STRINGS))) {
94     mixin(getImportTestsCompileString!MOD_STRINGS()); //e.g. import foo, bar, baz;
95     static immutable runStr = getRunTestsCompileString!MOD_STRINGS();
96     mixin(getRunTestsCompileString!MOD_STRINGS()); //e.g. runTests!(foo, bar, baz)();
97 }
98 
99 private string getImportTestsCompileString(MOD_STRINGS...)() {
100     return "import " ~ getModulesCompileString!MOD_STRINGS() ~ ";";
101 }
102 
103 private string getRunTestsCompileString(MOD_STRINGS...)() {
104     return "return runTests!(" ~ getModulesCompileString!MOD_STRINGS() ~ ")(options);";
105 }
106 
107 private string getModulesCompileString(MOD_STRINGS...)() {
108     import std.array;
109     string[] modules;
110     foreach(mod; MOD_STRINGS) modules ~= mod;
111     return join(modules, ", ");
112 }