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.reflection: allTestCaseData;
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     return runTests(args, allTestCaseData!MODULES);
28 }
29 
30 int runTests(string[] args, in TestData[] testData) {
31     const options = getOptions(args);
32 
33     if(options.list) {
34         writeln("Listing tests:");
35         foreach(test; testData.map!(a => a.name)) {
36             writeln(test);
37         }
38     }
39 
40     if(options.exit) return 0;
41     if(options.debugOutput) enableDebugOutput();
42     if(options.forceEscCodes) forceEscCodes();
43 
44     immutable success = runTests(options, testData);
45     return success ? 0 : 1;
46 }
47 
48 
49 bool runTests(in Options options, in TestData[] testData) {
50     WriterThread.get(); //make sure this is up
51     scope(exit) WriterThread.get().join();
52 
53     //sleep to give WriterThread some time to set up. Otherwise,
54     //tests with output could write to stdout in the meanwhile
55     Thread.sleep(5.msecs);
56 
57     auto testCases = createTestCases(testData, options.testsToRun);
58     if(!testCases) {
59         utWritelnRed("Error! No tests to run for args: ");
60         utWriteln(options.testsToRun);
61         return false;
62     }
63 
64     auto suite = TestSuite(testCases);
65     immutable elapsed = suite.run(options);
66 
67     if(!suite.numTestsRun) {
68         writeln("Did not run any tests!!!");
69         return false;
70     }
71 
72     utWriteln("\nTime taken: ", elapsed);
73     utWrite(suite.numTestsRun, " test(s) run, ");
74     const failuresStr = text(suite.numFailures, " failed");
75     if(suite.numFailures) {
76         utWriteRed(failuresStr);
77     } else {
78         utWrite(failuresStr);
79     }
80 
81     void printAbout(string attr)(in string msg) {
82         const num = testData.filter!(a => mixin("a. " ~ attr)).count;
83         if(num) {
84             utWrite(", ");
85             utWriteYellow(num, " " ~ msg);
86         }
87     }
88 
89     printAbout!"hidden"("hidden");
90     printAbout!"shouldFail"("failing as expected");
91 
92     utWriteln(".\n");
93 
94     if(!suite.passed) {
95         utWritelnRed("Unit tests failed!\n");
96         return false; //oops
97     }
98 
99     utWritelnGreen("OK!\n");
100 
101     return true;
102 }