1 module unit_threaded.factory;
2 
3 import unit_threaded.testcase;
4 import unit_threaded.list;
5 import unit_threaded.asserts;
6 import unit_threaded.check;
7 
8 import std.stdio;
9 import std.traits;
10 import std.typetuple;
11 import std.exception;
12 import std.algorithm;
13 import std.string;
14 import core.runtime;
15 
16 /**
17  * Replace the D runtime's normal unittest block tester with our own
18  */
19 static this() {
20     Runtime.moduleUnitTester = &moduleUnitTester;
21 }
22 
23 /**
24  * Creates tests cases from the given modules.
25  * If testsToRun is empty, it means run all tests.
26  */
27 TestCase[] createTests(MODULES...)(in string[] testsToRun = []) if(MODULES.length > 0) {
28     TestCase[] tests;
29     foreach(data; getTestClassesAndFunctions!MODULES()) {
30         if(!isWantedTest(data, testsToRun)) continue;
31         auto test = createTestCase(data);
32         if(test !is null) tests ~= test; //can be null if abtract base class
33     }
34 
35     return tests ~ builtinTests; //builtInTests defined below
36 }
37 
38 private TestCase createTestCase(TestData data) {
39     auto testCase = data.test is null ?
40         cast(TestCase) Object.factory(data.name):
41         new FunctionTestCase(data);
42 
43     if(data.test !is null) {
44         assert(testCase !is null, "Could not create FunctionTestCase object for function " ~ data.name);
45     }
46 
47     return testCase;
48 }
49 
50 private bool isWantedTest(in TestData data, in string[] testsToRun) {
51     if(!testsToRun.length) return !data.hidden; //"all tests (except hidden)"
52     foreach(testToRun; testsToRun) {
53         if(startsWith(data.name, testToRun)) return true; //even if hidden
54     }
55     return false;
56 }
57 
58 private class FunctionTestCase: TestCase {
59     this(immutable TestData data) pure nothrow {
60         _name = data.name;
61         _func = data.test;
62     }
63 
64     override void test() {
65         _func();
66     }
67 
68     override string getPath() const pure nothrow {
69         return _name;
70     }
71 
72     private string _name;
73     private TestFunction _func;
74 }
75 
76 package auto getTestClassesAndFunctions(MODULES...)() {
77     return getAllTests!(q{getTestClassNames}, MODULES)() ~
78            getAllTests!(q{getTestFunctions}, MODULES)();
79 }
80 
81 private auto getAllTests(string expr, MODULES...)() pure nothrow {
82     //tests is whatever type expr returns
83     ReturnType!(mixin(expr ~ q{!(MODULES[0])})) tests;
84     foreach(mod; TypeTuple!MODULES) {
85         tests ~= mixin(expr ~ q{!mod()});
86     }
87     return assumeUnique(tests);
88 }
89 
90 
91 private TestCase[] builtinTests; //built-in unittest blocks
92 
93 private class BuiltinTestCase: FunctionTestCase {
94     this(immutable TestData data) pure nothrow {
95         super(data);
96     }
97 
98     override void test() {
99         try {
100             super.test();
101         } catch(Throwable t) {
102             utFail(t.msg, t.file, t.line);
103         }
104     }
105 }
106 
107 private bool moduleUnitTester() {
108     foreach(mod; ModuleInfo) {
109         if(mod && mod.unitTest) {
110             if(startsWith(mod.name, "unit_threaded.")) {
111                 mod.unitTest()();
112             } else {
113                 enum hidden = false;
114                 builtinTests ~=
115                     new BuiltinTestCase(TestData(mod.name ~ ".unittest", hidden, mod.unitTest));
116             }
117         }
118     }
119 
120     return true;
121 }
122 
123 
124 unittest {
125     assert(isWantedTest(TestData("pass_tests.testEqual"), ["pass_tests"]));
126     assert(isWantedTest(TestData("pass_tests.testEqual"), ["pass_tests."]));
127     assert(isWantedTest(TestData("pass_tests.testEqual"), ["pass_tests.testEqual"]));
128     assert(isWantedTest(TestData("pass_tests.testEqual"), []));
129     assert(!isWantedTest(TestData("pass_tests.testEqual"), ["pass_tests.foo"]));
130 }