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 }