trial.reporters.visualtrial 192/199(96%) line coverage

      
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
200
210
220
230
240
250
260
270
280
290
300
310
320
330
340
350
360
370
380
390
400
410
420
430
440
450
460
470
480
490
500
510
520
530
540
550
560
570
586
590
606
610
620
630
640
651
661
670
680
690
700
710
720
730
740
750
760
770
781
791
801
811
821
831
841
853
861
871
880
890
900
910
920
934
944
954
960
974
983
992
1002
1012
1022
1032
1040
1050
1060
1074
1080
1094
1104
1110
1120
1130
1140
1150
1161
1171
1181
1190
1202
1210
1220
1230
1240
1250
1261
1271
1280
1291
1301
1311
1321
1331
1340
1351
1360
1372
1380
1390
1400
1410
1420
1430
1440
1450
1460
1470
1481
1491
1500
1511
1521
1531
1541
1550
1561
1570
1582
1590
1600
1610
1620
1630
1641
1651
1660
1671
1681
1691
1700
1711
1720
1732
1740
1750
1760
1770
1780
1790
1800
1810
1820
1830
1841
1851
1860
1871
1881
1890
1901
1910
1922
1930
1940
1950
1960
1971
1980
1990
2000
2011
2020
2031
2041
2050
2061
2071
2081
2090
2101
2110
2122
2130
2140
2150
2160
2170
2180
2190
2200
2210
2220
2230
2240
2250
2260
2270
2280
2290
2300
2310
2320
2330
2340
2350
2360
2370
2380
2390
24021
2415
2424
2430
2445
2455
2465
2475
2480
2490
25016
2514
2524
2534
2541
2550
2560
2574
2584
2594
2600
2610
26212
2630
2640
2650
26612
2672
2682
2690
2700
27110
2720
27310
2741
2751
2760
2770
2781
2790
2800
2819
2829
2830
2849
2851
2861
2871
2880
2891
2901
2911
2920
2931
2941
2951
2960
2971
2981
2991
3000
3011
3021
3031
3040
3051
3061
3071
3080
3091
3101
3111
3120
3131
3140
3151
3160
3171
3181
3190
3200
3211
3221
3230
3241
3251
3261
3271
3281
3290
3300
3310
3320
3330
3340
3350
3360
3370
3380
3390
3401
3412
3421
3430
3441
3452
3462
3472
3482
3490
3501
3512
3520
3531
3542
3550
3561
3572
3580
3591
3602
3610
3621
3632
3640
3651
3662
3670
3681
3692
3700
3710
3720
3730
3740
3751
3762
3771
3780
3791
3800
3811
3821
3831
3841
3851
3860
3872
3882
3892
3900
3911
3922
3930
3940
3950
3960
3971
3980
3991
4001
4012
4020
4030
4041
4051
4060
4071
4081
4090
4102
4110
4120
4130
4140
4151
4160
4171
4181
4191
4202
4210
4221
4232
4240
4250
4260
4270
4281
4291
4300
4311
4322
4331
4340
4350
4361
4371
4381
4390
4402
4410
4420
4430
4441
4451
4460
4470
/++ A module containing the Visual Trial reporter used to send data to the Visual Studio Code plugin Copyright: © 2017 Szabo Bogdan License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. Authors: Szabo Bogdan +/ module trial.reporters.visualtrial; import std.conv; import std.string; import std.algorithm; import std.stdio; import std.datetime; import std.exception; version (Have_fluent_asserts) { version = Have_fluent_asserts_core; } version(Have_fluent_asserts_core) { import fluentasserts.core.base; import fluentasserts.core.results; } import trial.interfaces; import trial.reporters.writer; enum Tokens : string { beginTest = "BEGIN TEST;", endTest = "END TEST;", suite = "suite", test = "test", file = "file", line = "line", labels = "labels", status = "status", errorFile = "errorFile", errorLine = "errorLine", message = "message" } /// This reporter will print the results using thr Test anything protocol version 13 class VisualTrialReporter : ILifecycleListener, ITestCaseLifecycleListener { private { ReportWriter writer; } /// this() { writer = defaultWriter; } /// this(ReportWriter writer) { this.writer = writer; } /// void begin(ulong testCount) { writer.writeln("", ReportWriter.Context._default); writer.writeln("", ReportWriter.Context._default); } /// void update() { } /// void end(SuiteResult[]) { } /// void begin(string suite, ref TestResult result) { std.stdio.stdout.flush; std.stdio.stderr.flush; writer.writeln("BEGIN TEST;", ReportWriter.Context._default); writer.writeln("suite:" ~ suite, ReportWriter.Context._default); writer.writeln("test:" ~ result.name, ReportWriter.Context._default); writer.writeln("file:" ~ result.fileName, ReportWriter.Context._default); writer.writeln("line:" ~ result.line.to!string, ReportWriter.Context._default); writer.writeln("labels:[" ~ result.labels.map!(a => a.toString).join(", ") ~ "]", ReportWriter.Context._default); std.stdio.stdout.flush; std.stdio.stderr.flush; } /// void end(string suite, ref TestResult test) { std.stdio.stdout.flush; std.stdio.stderr.flush; writer.writeln("status:" ~ test.status.to!string, ReportWriter.Context._default); if(test.status != TestResult.Status.success) { if(test.throwable !is null) { writer.writeln("errorFile:" ~ test.throwable.file, ReportWriter.Context._default); writer.writeln("errorLine:" ~ test.throwable.line.to!string, ReportWriter.Context._default); writer.writeln("message:" ~ test.throwable.msg.split("\n")[0], ReportWriter.Context._default); writer.write("error:", ReportWriter.Context._default); writer.writeln(test.throwable.toString, ReportWriter.Context._default); } } writer.writeln("END TEST;", ReportWriter.Context._default); std.stdio.stdout.flush; std.stdio.stderr.flush; } } /// it should print "The Plan" at the beginning unittest { auto writer = new BufferedWriter; auto reporter = new VisualTrialReporter(writer); reporter.begin(10); writer.buffer.should.equal("\n\n"); } /// it should print the test location unittest { auto writer = new BufferedWriter; auto reporter = new VisualTrialReporter(writer); auto test = new TestResult("other test"); test.fileName = "someFile.d"; test.line = 100; test.labels = [ Label("name", "value"), Label("name1", "value1") ]; test.status = TestResult.Status.success; reporter.begin("some suite", test); writer.buffer.should.equal("BEGIN TEST;\n" ~ "suite:some suite\n" ~ "test:other test\n" ~ "file:someFile.d\n" ~ "line:100\n" ~ `labels:[{ "name": "name", "value": "value" }, { "name": "name1", "value": "value1" }]` ~ "\n"); } /// it should print a sucess test unittest { auto writer = new BufferedWriter; auto reporter = new VisualTrialReporter(writer); auto test = new TestResult("other test"); test.fileName = "someFile.d"; test.line = 100; test.status = TestResult.Status.success; reporter.end("some suite", test); writer.buffer.should.equal("status:success\nEND TEST;\n"); } /// it should print a failing test with a basic throwable unittest { auto writer = new BufferedWriter; auto reporter = new VisualTrialReporter(writer); auto test = new TestResult("other's test"); test.status = TestResult.Status.failure; test.throwable = new Exception("Test's failure", "file.d", 42); reporter.end("some suite", test); writer.buffer.should.equal("status:failure\n" ~ "errorFile:file.d\n" ~ "errorLine:42\n" ~ "message:Test's failure\n" ~ "error:object.Exception@file.d(42): Test's failure\n" ~ "END TEST;\n"); } /// it should not print the YAML if the throwable is missing unittest { auto writer = new BufferedWriter; auto reporter = new VisualTrialReporter(writer); auto test = new TestResult("other's test"); test.status = TestResult.Status.failure; reporter.end("some suite", test); writer.buffer.should.equal("status:failure\nEND TEST;\n"); } /// it should print the results of a TestException unittest { IResult[] results = [ cast(IResult) new MessageResult("message"), cast(IResult) new ExtraMissingResult("a", "b") ]; auto exception = new TestException(results, "unknown", 0); auto writer = new BufferedWriter; auto reporter = new VisualTrialReporter(writer); auto test = new TestResult("other's test"); test.status = TestResult.Status.failure; test.throwable = exception; reporter.end("some suite", test); writer.buffer.should.equal("status:failure\n" ~ "errorFile:unknown\n" ~ "errorLine:0\n" ~ "message:message\n" ~ "error:fluentasserts.core.base.TestException@unknown(0): message\n\n" ~ " Extra:a\n" ~ " Missing:b\n\n" ~ "END TEST;\n"); } /// Parse the output from the visual trial reporter class VisualTrialReporterParser { TestResult testResult; string suite; bool readingTest; alias ResultEvent = void delegate(TestResult); alias OutputEvent = void delegate(string); ResultEvent onResult; OutputEvent onOutput; private { bool readingErrorMessage; } /// add a line to the parser void add(string line) { if(line == Tokens.beginTest) { if(testResult is null) { testResult = new TestResult("unknown"); } readingTest = true; testResult.begin = Clock.currTime; testResult.end = Clock.currTime; return; } if(line == Tokens.endTest) { enforce(testResult !is null, "The test result was not created!"); readingTest = false; if(onResult !is null) { onResult(testResult); } readingErrorMessage = false; testResult = null; return; } if(!readingTest) { return; } if(readingErrorMessage) { testResult.throwable.msg ~= "\n" ~ line; return; } auto pos = line.indexOf(":"); if(pos == -1) { if(onOutput !is null) { onOutput(line); } return; } string token = line[0 .. pos]; string value = line[pos+1 .. $]; switch(token) { case Tokens.suite: suite = value; break; case Tokens.test: testResult.name = value; break; case Tokens.file: testResult.fileName = value; break; case Tokens.line: testResult.line = value.to!size_t; break; case Tokens.labels: testResult.labels = Label.fromJsonArray(value); break; case Tokens.status: testResult.status = value.to!(TestResult.Status); break; case Tokens.errorFile: if(testResult.throwable is null) { testResult.throwable = new ParsedVisualTrialException(); } testResult.throwable.file = value; break; case Tokens.errorLine: if(testResult.throwable is null) { testResult.throwable = new ParsedVisualTrialException(); } testResult.throwable.line = value.to!size_t; break; case Tokens.message: enforce(testResult.throwable !is null, "The throwable must exist!"); testResult.throwable.msg = value; readingErrorMessage = true; break; default: if(onOutput !is null) { onOutput(line); } } } } /// Parse a successful test unittest { auto parser = new VisualTrialReporterParser(); parser.testResult.should.beNull; auto begin = Clock.currTime; parser.add("BEGIN TEST;"); parser.testResult.should.not.beNull; parser.testResult.begin.should.be.greaterThan(begin); parser.testResult.end.should.be.greaterThan(begin); parser.testResult.status.should.equal(TestResult.Status.created); parser.add("suite:suite name"); parser.suite.should.equal("suite name"); parser.add("test:test name"); parser.testResult.name.should.equal("test name"); parser.add("file:some file.d"); parser.testResult.fileName.should.equal("some file.d"); parser.add("line:22"); parser.testResult.line.should.equal(22); parser.add(`labels:[ { "name": "name1", "value": "label1" }, { "name": "name2", "value": "label2" }]`); parser.testResult.labels.should.equal([Label("name1", "label1"), Label("name2", "label2")]); parser.add("status:success"); parser.testResult.status.should.equal(TestResult.Status.success); parser.add("END TEST;"); parser.testResult.should.beNull; } /// Parse a failing test unittest { auto parser = new VisualTrialReporterParser(); parser.testResult.should.beNull; auto begin = Clock.currTime; parser.add("BEGIN TEST;"); parser.add("errorFile:file.d"); parser.add("errorLine:147"); parser.add("message:line1"); parser.add("line2"); parser.add("line3"); parser.testResult.throwable.should.not.beNull; parser.testResult.throwable.file.should.equal("file.d"); parser.testResult.throwable.line.should.equal(147); parser.add("END TEST;"); parser.testResult.should.beNull; } /// Raise an event when the test is ended unittest { bool called; void checkResult(TestResult result) { called = true; result.should.not.beNull; } auto parser = new VisualTrialReporterParser(); parser.onResult = &checkResult; parser.add("BEGIN TEST;"); parser.add("END TEST;"); called.should.equal(true); } /// It should not replace a test result that was already assigned unittest { auto testResult = new TestResult(""); auto parser = new VisualTrialReporterParser(); parser.testResult = testResult; parser.add("BEGIN TEST;"); parser.testResult.should.equal(testResult); parser.add("END TEST;"); parser.testResult.should.beNull; } /// It should raise an event with unparsed lines unittest { bool raised; auto parser = new VisualTrialReporterParser(); void onOutput(string line) { line.should.equal("some output"); raised = true; } parser.onOutput = &onOutput; parser.add("BEGIN TEST;"); parser.add("some output"); raised.should.equal(true); } class ParsedVisualTrialException : Exception { this() { super(""); } }