1 /++ 2 A module containing the TAP13 reporter https://testanything.org/ 3 4 This is an example of how this reporter looks 5 <script type="text/javascript" src="https://asciinema.org/a/135734.js" id="asciicast-135734" async></script> 6 7 Copyright: © 2017 Szabo Bogdan 8 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 9 Authors: Szabo Bogdan 10 +/ 11 module trial.reporters.tap; 12 13 import std.conv; 14 import std..string; 15 import std.algorithm; 16 17 version(Have_fluent_asserts) { 18 import fluentasserts.core.base; 19 import fluentasserts.core.results; 20 } 21 22 import trial.interfaces; 23 import trial.reporters.writer; 24 25 /// This reporter will print the results using thr Test anything protocol version 13 26 class TapReporter : ILifecycleListener, ITestCaseLifecycleListener 27 { 28 private { 29 ReportWriter writer; 30 } 31 32 /// 33 this() 34 { 35 writer = defaultWriter; 36 } 37 38 /// 39 this(ReportWriter writer) 40 { 41 this.writer = writer; 42 } 43 44 /// 45 void begin(ulong testCount) { 46 writer.writeln("TAP version 13", ReportWriter.Context._default); 47 writer.writeln("1.." ~ testCount.to!string, ReportWriter.Context._default); 48 } 49 50 /// 51 void update() { } 52 53 /// 54 void end(SuiteResult[]) { } 55 56 /// 57 void begin(string, ref TestResult) 58 { 59 } 60 61 /// 62 void end(string suite, ref TestResult test) 63 { 64 if(test.status == TestResult.Status.success) { 65 writer.writeln("ok - " ~ suite ~ "." ~ test.name, ReportWriter.Context._default); 66 } else { 67 writer.writeln("not ok - " ~ suite ~ "." ~ test.name, ReportWriter.Context._default); 68 69 version(Have_fluent_asserts) { 70 if(test.throwable !is null) { 71 if(cast(TestException) test.throwable !is null) { 72 printTestException(test); 73 } else { 74 printThrowable(test); 75 } 76 77 writer.writeln(""); 78 } 79 } else { 80 printThrowable(test); 81 } 82 } 83 } 84 85 version(Have_fluent_asserts) { 86 private void printTestException(ref TestResult test) { 87 auto diagnostic = test.throwable.msg.split("\n").map!(a => "# " ~ a).join("\n"); 88 89 auto msg = test.throwable.msg.split("\n")[0]; 90 91 writer.writeln(diagnostic, ReportWriter.Context._default); 92 writer.writeln(" ---", ReportWriter.Context._default); 93 writer.writeln(" message: '" ~ msg ~ "'", ReportWriter.Context._default); 94 writer.writeln(" severity: " ~ test.status.to!string, ReportWriter.Context._default); 95 writer.writeln(" location:", ReportWriter.Context._default); 96 writer.writeln(" fileName: '" ~ test.throwable.file.replace("'", "\'") ~ "'", ReportWriter.Context._default); 97 writer.writeln(" line: " ~ test.throwable.line.to!string, ReportWriter.Context._default); 98 } 99 } 100 101 private void printThrowable(ref TestResult test) { 102 writer.writeln(" ---", ReportWriter.Context._default); 103 writer.writeln(" message: '" ~ test.throwable.msg ~ "'", ReportWriter.Context._default); 104 writer.writeln(" severity: " ~ test.status.to!string, ReportWriter.Context._default); 105 writer.writeln(" location:", ReportWriter.Context._default); 106 writer.writeln(" fileName: '" ~ test.throwable.file.replace("'", "\'") ~ "'", ReportWriter.Context._default); 107 writer.writeln(" line: " ~ test.throwable.line.to!string, ReportWriter.Context._default); 108 } 109 } 110 111 version(unittest) { 112 version(Have_fluent_asserts) { 113 import fluent.asserts; 114 } 115 } 116 117 /// it should print "The Plan" at the beginning 118 unittest { 119 auto writer = new BufferedWriter; 120 auto reporter = new TapReporter(writer); 121 reporter.begin(10); 122 123 writer.buffer.should.equal("TAP version 13\n1..10\n"); 124 } 125 126 /// it should print a sucess test 127 unittest 128 { 129 auto writer = new BufferedWriter; 130 auto reporter = new TapReporter(writer); 131 132 auto test = new TestResult("other test"); 133 test.status = TestResult.Status.success; 134 135 reporter.end("some suite", test); 136 137 writer.buffer.should.equal("ok - some suite.other test\n"); 138 } 139 140 /// it should print a failing test with a basic throwable 141 unittest 142 { 143 auto writer = new BufferedWriter; 144 auto reporter = new TapReporter(writer); 145 146 auto test = new TestResult("other's test"); 147 test.status = TestResult.Status.failure; 148 test.throwable = new Exception("Test's failure", "file.d", 42); 149 150 reporter.end("some suite", test); 151 152 writer.buffer.should.equal("not ok - some suite.other's test\n" ~ 153 " ---\n" ~ 154 " message: 'Test\'s failure'\n" ~ 155 " severity: failure\n" ~ 156 " location:\n" ~ 157 " fileName: 'file.d'\n" ~ 158 " line: 42\n\n"); 159 } 160 161 /// it should not print the YAML if the throwable is missing 162 unittest 163 { 164 auto writer = new BufferedWriter; 165 auto reporter = new TapReporter(writer); 166 167 auto test = new TestResult("other's test"); 168 test.status = TestResult.Status.failure; 169 170 reporter.end("some suite", test); 171 172 writer.buffer.should.equal("not ok - some suite.other's test\n"); 173 } 174 175 /// it should print the results of a TestException 176 unittest { 177 IResult[] results = [ 178 cast(IResult) new MessageResult("message"), 179 cast(IResult) new ExtraMissingResult("a", "b") ]; 180 181 auto exception = new TestException(results, "unknown", 0); 182 183 auto writer = new BufferedWriter; 184 auto reporter = new TapReporter(writer); 185 186 auto test = new TestResult("other's test"); 187 test.status = TestResult.Status.failure; 188 test.throwable = exception; 189 190 reporter.end("some suite", test); 191 192 writer.buffer.should.equal("not ok - some suite.other's test\n" ~ 193 "# message\n" ~ 194 "# \n" ~ 195 "# Extra:a\n" ~ 196 "# Missing:b\n" ~ 197 "# \n" ~ 198 " ---\n" ~ 199 " message: 'message'\n" ~ 200 " severity: failure\n" ~ 201 " location:\n" ~ 202 " fileName: 'unknown'\n" ~ 203 " line: 0\n\n"); 204 }