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 }