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     immutable 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 suite = TestSuite(createTests!MOD_SYMBOLS(options.tests));
62     immutable elapsed = suite.run(options.multiThreaded);
63 
64     if(!suite.numTestsRun) {
65         writeln("Did not run any tests!!!");
66         return false;
67     }
68 
69     utWriteln("\nTime taken: ", elapsed, " seconds");
70     utWriteln(suite.numTestsRun, " test(s) run, ",
71               suite.numFailures, " failed.\n");
72 
73     if(!suite.passed) {
74         utWritelnRed("Unit tests failed!\n");
75         return false; //oops
76     }
77 
78     utWritelnGreen("OK!\n");
79 
80     return true;
81 }
82 
83 /**
84  * Runs all tests in passed-in modules. Modules are strings.
85  */
86 bool runTests(MOD_STRINGS...)(in Options options) if(allSatisfy!(isSomeString, typeof(MOD_STRINGS))) {
87     mixin(getImportTestsCompileString!MOD_STRINGS()); //e.g. import foo, bar, baz;
88     static immutable runStr = getRunTestsCompileString!MOD_STRINGS();
89     mixin(getRunTestsCompileString!MOD_STRINGS()); //e.g. runTests!(foo, bar, baz)();
90 }
91 
92 private string getImportTestsCompileString(MOD_STRINGS...)() {
93     return "import " ~ getModulesCompileString!MOD_STRINGS() ~ ";";
94 }
95 
96 private string getRunTestsCompileString(MOD_STRINGS...)() {
97     return "return runTests!(" ~ getModulesCompileString!MOD_STRINGS() ~ ")(options);";
98 }
99 
100 private string getModulesCompileString(MOD_STRINGS...)() {
101     import std.array;
102     string[] modules;
103     foreach(mod; MOD_STRINGS) modules ~= mod;
104     return join(modules, ", ");
105 }