1 module unit_threaded.testsuite; 2 3 import unit_threaded.testcase; 4 import unit_threaded.io; 5 import unit_threaded.options; 6 import std.datetime; 7 import std.parallelism: taskPool; 8 import std.concurrency; 9 import std.stdio; 10 import std.conv; 11 import std.algorithm; 12 13 14 auto runTest(TestCase test) { 15 return test(); 16 } 17 18 /** 19 * Responsible for running tests 20 */ 21 struct TestSuite { 22 this(TestCase[] tests) { 23 _tests = tests; 24 } 25 26 /** 27 * Runs the tests with the given options. 28 * Returns: how long it took to run. 29 */ 30 Duration run(in Options options) { 31 auto tests = getTests(options); 32 _stopWatch.start(); 33 34 if(options.multiThreaded) { 35 _failures = reduce!((a, b) => a ~ b)(_failures, taskPool.amap!runTest(tests)); 36 } else { 37 foreach(test; tests) _failures ~= test(); 38 } 39 40 handleFailures(); 41 42 _stopWatch.stop(); 43 return cast(Duration)_stopWatch.peek(); 44 } 45 46 @property ulong numTestsRun() const { 47 return _tests.map!(a => a.numTestsRun).reduce!((a, b) => a + b); 48 } 49 50 @property ulong numFailures() const pure nothrow { 51 return _failures.length; 52 } 53 54 @property bool passed() const pure nothrow { 55 return numFailures() == 0; 56 } 57 58 private: 59 60 TestCase[] _tests; 61 string[] _failures; 62 StopWatch _stopWatch; 63 64 auto getTests(in Options options) { 65 auto tests = _tests; 66 if(options.random) { 67 import std.random; 68 auto generator = Random(options.seed); 69 tests.randomShuffle(generator); 70 utWriteln("Running tests in random order. To repeat this run, use --seed ", options.seed); 71 } 72 return tests; 73 } 74 75 void handleFailures() { 76 if(_failures) utWriteln(""); 77 foreach(failure; _failures) { 78 utWrite("Test ", failure, " "); 79 utWriteRed("failed"); 80 utWriteln("."); 81 } 82 if(_failures) writeln(""); 83 } 84 }