1 module zug.tap.consumer; 2 3 import zug.tap; 4 5 string[] read_dir(string source_dir, bool verbose = false, bool do_debug = false) { 6 import std.stdio : writeln; 7 import std.file : DirEntry, dirEntries, SpanMode; 8 9 import std.array : array; 10 import std.path : baseName; 11 import std.string : indexOf; 12 import std.regex : match; 13 import std.algorithm: sort; 14 15 string[] files; 16 17 auto entries = dirEntries(source_dir, "*", SpanMode.shallow); 18 foreach (DirEntry entry; entries) { 19 if (entry.isDir) { 20 if (entry.name.baseName.indexOf('.') == 0) { 21 if (do_debug) { 22 writeln("HIDDEN DIR found ", entry.name); 23 } 24 continue; 25 } 26 if (do_debug) { 27 writeln("DIR found ", entry.name); 28 } 29 files ~= read_dir(entry.name); 30 } else { 31 if (!entry.name.match(r"\.d$")) { 32 if (do_debug) { 33 writeln("NOT A .D FILE", entry.name); 34 } 35 continue; 36 } 37 38 files ~= entry.name; 39 } 40 } 41 42 return files.sort.array; 43 } 44 45 struct TestResults { 46 int passed = 0; // passed tests 47 int failed = 0; // failed tests 48 int planned = 0; // planned tests; 49 bool done_testing = false; // was there a plan printed and were all the planned tests run 50 } 51 52 TestResults run_test(string test, bool verbose = false, bool do_debug = false) { 53 import std.stdio : writeln, write; 54 import std.process : pipeProcess, Redirect, wait; 55 import std.regex : ctRegex, match; 56 import std.uni : toLower; 57 import std.conv: to; 58 59 TestResults raw_test_data; 60 write(test, " running now ... "); 61 if (verbose) { writeln(""); } 62 auto processPipe = pipeProcess(["/usr/bin/dub", "--single", test], 63 Redirect.stdout | Redirect.stderr); 64 wait(processPipe.pid); 65 66 if (do_debug) { writeln("ran ", test, " looking at output"); } 67 68 auto plan = ctRegex!(`^\s*(\d+)\.\.(\d+)\s*`, "i"); 69 auto ok = ctRegex!(`^\s*ok\s(\d+)\s+(.*)`); 70 auto not_ok = ctRegex!(`^\s*not ok\s(\d+)\s+(.*)`); 71 auto diagnostic = ctRegex!(`^\s*#diagnostic:`); 72 auto note = ctRegex!(`^\s*#note:`); 73 auto comment = ctRegex!(`^\s*#(.*)`); 74 75 int tests_ran = 0; 76 foreach (line; processPipe.stdout.byLine) { 77 if (auto matched = line.match(plan)) { 78 int planned = matched.front[2].to!int; 79 if (verbose) { writeln("1..", planned ); } 80 raw_test_data.planned = planned; 81 } else if (auto matched = line.match(ok)) { 82 tests_ran++; 83 if (verbose) { writeln("ok ", tests_ran, " ",matched.front[2]); } 84 raw_test_data.passed++; 85 } else if (auto matched = line.match(not_ok)) { 86 tests_ran++; 87 if (verbose) { writeln("not ok ", tests_ran, " ", matched.front[2]); } 88 raw_test_data.failed++; 89 } 90 // TODO later 91 // } else if (auto matched = line.match(diagnostic)) { 92 // debug writeln("+++++++ ", matched); 93 // debug writeln("DIAGNOSTIC: ", line); 94 // } else if (auto matched = line.match(note)) { 95 // debug writeln("+++++++ ", matched); 96 // debug writeln("NOTE: ", line); 97 // } else if (auto matched = line.match(comment)) { 98 // debug writeln("+++++++ ", matched); 99 // debug writeln("COMMENT: ", line); 100 // } else { 101 // debug writeln("DON'T KNOW: ", line); 102 // } 103 } 104 105 106 if (raw_test_data.failed + raw_test_data.passed == raw_test_data.planned) { 107 raw_test_data.done_testing = true; 108 } 109 if (!verbose) { 110 if (raw_test_data.failed > 0) { 111 writeln( 112 "Failed ", raw_test_data.failed, "/", 113 raw_test_data.planned > 0 ? raw_test_data.planned : raw_test_data.passed + raw_test_data.failed 114 ); 115 } else { 116 writeln("ok"); 117 } 118 } 119 120 if (do_debug) { writeln(test, raw_test_data); } 121 return raw_test_data; 122 }