1 /// Tool to create report files from collider hibon files 2 module tagion.tools.collider.reporter; 3 4 /** 5 * @brief tool generate d files from bdd md files and vice versa 6 */ 7 import std.algorithm.iteration; 8 import std.algorithm.sorting; 9 import std.array; 10 import std.file; 11 import std.format; 12 import std.getopt; 13 import std.path; 14 import std.process : environment; 15 import std.stdio; 16 import std.string; 17 import std.traits; 18 import tagion.behaviour.BehaviourFeature; 19 import tagion.behaviour.BehaviourResult; 20 import tagion.behaviour.Behaviour; 21 import tagion.hibon.Document : Document; 22 import tagion.hibon.HiBONFile : fread, fwrite; 23 import tagion.hibon.HiBONJSON; 24 import tagion.hibon.HiBONRecord : isRecord; 25 import tagion.tools.Basic : Main; 26 import tagion.tools.revision : revision_text; 27 28 mixin Main!(_main); 29 30 enum OutputFormat { 31 github = "github", 32 markdown = "markdown", 33 } 34 35 int _main(string[] args) { 36 immutable program = args[0]; 37 string output; 38 OutputFormat format_style = OutputFormat.markdown; 39 40 auto main_args = getopt(args, 41 "o|output", "output file", &output, 42 "f|format", format("Format style, one of %s", [EnumMembers!OutputFormat]), &format_style, 43 ); 44 45 if (main_args.helpWanted) { 46 defaultGetoptPrinter([ 47 revision_text, 48 "Documentation: https://tagion.org/", 49 "", 50 "Usage:", 51 format("%s [<option>...]", program), 52 "", 53 "<option>:", 54 ].join("\n"), main_args.options); 55 return 0; 56 } 57 58 string log_dir = "."; 59 if (args.length == 2) { 60 log_dir = args[1]; 61 } 62 if (!output) { 63 output = "/dev/stdout"; 64 } 65 66 if (!log_dir.exists || !log_dir.isDir) { 67 stderr.writeln("Log directory does not exist"); 68 return 1; 69 } 70 71 auto result_files = dirEntries(log_dir, SpanMode.depth).filter!(dirEntry => dirEntry.name.endsWith(".hibon")) 72 .map!(dirEntry => dirEntry.name) 73 .array 74 .sort; 75 76 if (result_files.length == 0) { 77 stderr.writeln("No hibon result files found"); 78 } 79 80 FeatureGroup[] featuregroups; 81 foreach (result; result_files) { 82 featuregroups ~= fread!FeatureGroup(result); 83 } 84 85 auto outstring = appender!string; 86 if (format_style == OutputFormat.github) { 87 const REPOROOT = environment.get("REPOROOT", getcwd()); 88 foreach (fg; featuregroups) { 89 foreach (scenario; fg.scenarios) { 90 void printErrors(Info)(Info info) { 91 if (info.result.isRecord!BehaviourError) { 92 auto bdd_err = BehaviourError(info.result); 93 outstring.put( 94 "::error file=%s,line=%s,title=BDD error::%s\n" 95 .format( 96 bdd_err.file.relativePath(REPOROOT), 97 bdd_err.line, 98 bdd_err.msg, 99 ) 100 ); 101 outstring.put("::group::BDD error\n"); 102 foreach (l; bdd_err.trace) { 103 outstring.put(l ~ '\n'); 104 } 105 outstring.put("::endgroup::\n"); 106 } 107 } 108 109 foreach (info; scenario.given.infos) { 110 printErrors(info); 111 } 112 foreach (info; scenario.when.infos) { 113 printErrors(info); 114 } 115 foreach (info; scenario.then.infos) { 116 printErrors(info); 117 } 118 foreach (info; scenario.but.infos) { 119 printErrors(info); 120 } 121 } 122 } 123 } 124 else { 125 foreach (fg; featuregroups) { 126 outstring.put(fg.toMd); 127 } 128 } 129 130 File(output, "w").write(outstring.data); 131 132 return 0; 133 } 134 135 alias MdString = string; 136 137 /// Gh flavor markdown 138 MdString toMd(FeatureGroup fg) { 139 auto result_md = appender!string; 140 string result_type() { 141 if (fg.hasErrors) { 142 return "❌"; 143 } 144 else if (fg.info.result.isRecord!Result) { 145 return "✔️ "; 146 } 147 else { 148 return "❓"; 149 } 150 } 151 152 uint successful = 0; 153 foreach (scenario; fg.scenarios) { 154 if (scenario.info.result.isRecord!Result) { 155 successful += 1; 156 } 157 } 158 const summary = format("<summary> %s (%s/%s) %s </summary>\n\n", result_type, successful, fg.scenarios.length, fg 159 .info.name); 160 161 // result_md.put(format("%s\n\n", result_type)); 162 163 result_md.put("<details>"); 164 result_md.put(summary); 165 result_md.put("```json\n"); 166 result_md.put(format("%s\n", fg.toPretty)); 167 result_md.put("```\n\n"); 168 result_md.put("</details><br>\n\n"); 169 return result_md[]; 170 }