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 }