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 foreach(test; builtinTests) { //builtInTests defined below 36 if(isWantedTest(test.getPath(), testsToRun)) tests ~= test; 37 } 38 39 return tests; 40 } 41 42 private TestCase createTestCase(TestData data) { 43 auto testCase = data.test is null ? 44 cast(TestCase) Object.factory(data.name): 45 new FunctionTestCase(data); 46 47 if(data.test !is null) { 48 assert(testCase !is null, "Could not create FunctionTestCase object for function " ~ data.name); 49 } 50 51 return testCase; 52 } 53 54 private bool isWantedTest(in TestData data, in string[] testsToRun) { 55 if(!testsToRun.length) return !data.hidden; //"all tests (except hidden)" 56 return isWantedTest(data.name, testsToRun); 57 } 58 59 private bool isWantedTest(in string name, in string[] testsToRun) { 60 if(!testsToRun.length) return true; 61 foreach(testToRun; testsToRun) { 62 if(startsWith(name, testToRun)) return true; //even if hidden 63 } 64 return false; 65 } 66 67 private class FunctionTestCase: TestCase { 68 this(immutable TestData data) pure nothrow { 69 _name = data.name; 70 _func = data.test; 71 } 72 73 override void test() { 74 _func(); 75 } 76 77 override string getPath() const pure nothrow { 78 return _name; 79 } 80 81 private string _name; 82 private TestFunction _func; 83 } 84 85 package auto getTestClassesAndFunctions(MODULES...)() { 86 return getAllTests!(q{getTestClassNames}, MODULES)() ~ 87 getAllTests!(q{getTestFunctions}, MODULES)(); 88 } 89 90 private auto getAllTests(string expr, MODULES...)() pure nothrow { 91 //tests is whatever type expr returns 92 ReturnType!(mixin(expr ~ q{!(MODULES[0])})) tests; 93 foreach(mod; TypeTuple!MODULES) { 94 tests ~= mixin(expr ~ q{!mod()}); 95 } 96 return assumeUnique(tests); 97 } 98 99 100 private TestCase[] builtinTests; //built-in unittest blocks 101 102 private class BuiltinTestCase: FunctionTestCase { 103 this(immutable TestData data) pure nothrow { 104 super(data); 105 } 106 107 override void test() { 108 try { 109 super.test(); 110 } catch(Throwable t) { 111 utFail(t.msg, t.file, t.line); 112 } 113 } 114 } 115 116 private bool moduleUnitTester() { 117 foreach(mod; ModuleInfo) { 118 if(mod && mod.unitTest) { 119 if(startsWith(mod.name, "unit_threaded.")) { 120 mod.unitTest()(); 121 } else { 122 enum hidden = false; 123 builtinTests ~= 124 new BuiltinTestCase(TestData(mod.name ~ ".unittest", hidden, mod.unitTest)); 125 } 126 } 127 } 128 129 return true; 130 } 131 132 133 unittest { 134 assert(isWantedTest(TestData("pass_tests.testEqual"), ["pass_tests"])); 135 assert(isWantedTest(TestData("pass_tests.testEqual"), ["pass_tests."])); 136 assert(isWantedTest(TestData("pass_tests.testEqual"), ["pass_tests.testEqual"])); 137 assert(isWantedTest(TestData("pass_tests.testEqual"), [])); 138 assert(!isWantedTest(TestData("pass_tests.testEqual"), ["pass_tests.foo"])); 139 assert(!isWantedTest("example.tests.pass.normal.unittest", 140 ["example.tests.pass.io.TestFoo"])); 141 assert(isWantedTest("example.tests.pass.normal.unittest", [])); 142 }