trial.reporters.xunit 106/107(99%) 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
25236
260
27236
28236
29236
30236
31236
320
33236
340
350
360
370
380
390
400
410
420
430
441
451
460
470
480
491
500
510
520
530
540
550
560
570
581
591
600
610
62129
6342
6442
650
6642
670
680
690
700
710
720
730
740
750
760
770
780
790
8047
81273
820
830
84273
85273
8647
87226
883
892
900
9147
9246
930
940
9547
960
970
980
9947
1000
1010
1020
1030
1040
1050
1060
1070
1080
1090
1100
1110
1121
1131
1141
1150
1161
1171
1181
1191
1200
1211
1220
1231
1240
1251
1260
1272
1280
1290
1300
1310
1320
1330
1340
1350
1361
1371
1381
1390
1401
1411
1421
1431
1440
1451
1460
1471
1480
1491
1500
1512
1520
1530
1540
1550
1560
1570
1580
1590
1600
1611
1621
1631
1640
1651
1661
1671
1681
1690
1701
1710
1721
1730
1741
1750
1762
1770
1780
1790
1800
1810
1820
1830
1840
1850
1860
1871
1881
1891
1900
1911
1921
1931
1941
1950
1961
1970
1981
1990
2001
2010
2022
2030
2040
2050
2060
2070
2080
2090
2100
2110
2121
2131
2141
2151
2160
2171
2180
2192
2200
2210
2220
2230
2240
2250
2260
2270
2280
2290
2300
2310
232228
233228
2340
235228
2362
2371
2380
2391
2400
2411
2420
243226
2441
245225
2461
2470
2480
249228
2500
251228
2520
2530
2540
2550
2560
2570
2581
2590
2601
2611
2621
2631
2640
2651
2660
2672
2680
2690
2700
2710
2720
2731
2741
2751
2761
2771
2781
2790
2801
2812
2820
2830
2840
/++ A module containing the XUnitReporter 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.xunit; import std.stdio; import std.array; import std.conv; import std.datetime; import std.string; import std.algorithm; import std.file; import std.path; import std.uuid; import std.range; import trial.interfaces; import trial.reporters.writer; private string escapeXUnit(string data) { string escapedData = data.dup; escapedData = escapedData.replace(`&`, `&`); escapedData = escapedData.replace(`"`, `"`); escapedData = escapedData.replace(`'`, `'`); escapedData = escapedData.replace(`<`, `<`); escapedData = escapedData.replace(`>`, `>`); return escapedData; } /// The XUnit reporter creates a xml containing the test results class XUnitReporter : ILifecycleListener { private { immutable string destination; } this(string destination) { this.destination = destination; } void begin(ulong testCount) { if(exists(destination)) { std.file.rmdirRecurse(destination); } } void update() {} void end(SuiteResult[] result) { if(!exists(destination)) { destination.mkdirRecurse; } foreach(item; result) { string uuid = randomUUID.toString; string xml = `<?xml version="1.0" encoding="UTF-8"?>` ~ "\n"~ `<testsuites>` ~ "\n" ~ XUnitSuiteXml(item, uuid).toString ~ "\n</testsuites>\n"; std.file.write(buildPath(destination, item.name ~ ".xml"), xml); } } } struct XUnitSuiteXml { /// The suite result SuiteResult result; /// The suite id string uuid; /// Converts the suiteResult to a xml string string toString() { auto epoch = SysTime.fromUnixTime(0); string tests = result.tests.map!(a => XUnitTestXml(a, uuid).toString).array.join("\n"); auto failures = result.tests.filter!(a => a.status == TestResult.Status.failure).count; auto skipped = result.tests.filter!(a => a.status == TestResult.Status.skip).count; auto errors = result.tests.filter!(a => a.status != TestResult.Status.success && a.status != TestResult.Status.skip && a.status != TestResult.Status.failure).count; if(tests != "") { tests = "\n" ~ tests; } auto xml = ` <testsuite name="` ~ result.name ~ `" errors="` ~ errors.to!string ~ `" skipped="` ~ skipped.to!string ~ `" tests="` ~ result.tests.length.to!string ~ `" failures="` ~ failures.to!string ~ `" time="0" timestamp="` ~ result.begin.toISOExtString ~ `">` ~ tests ~ ` </testsuite>`; return xml; } } version(unittest) { version(Have_fluent_asserts) { import fluent.asserts; } } /// XUnitTestXml should transform a suite with a success test unittest { auto epoch = SysTime.fromUnixTime(0); auto result = SuiteResult("Test Suite"); result.begin = Clock.currTime; TestResult test = new TestResult("Test"); test.begin = Clock.currTime; test.end = Clock.currTime; test.status = TestResult.Status.success; result.end = Clock.currTime; result.tests = [ test ]; auto xunit = XUnitSuiteXml(result); xunit.toString.should.equal(` <testsuite name="` ~ result.name.escapeXUnit ~ `" errors="0" skipped="0" tests="1" failures="0" time="0" timestamp="`~result.begin.toISOExtString~`"> <testcase name="Test"> </testcase> </testsuite>`); } /// XUnitTestXml should transform a suite with a failed test unittest { auto epoch = SysTime.fromUnixTime(0); auto result = SuiteResult("Test Suite"); result.begin = Clock.currTime; TestResult test = new TestResult("Test"); test.begin = Clock.currTime; test.end = Clock.currTime; test.status = TestResult.Status.failure; result.end = Clock.currTime; result.tests = [ test ]; auto xunit = XUnitSuiteXml(result); xunit.toString.should.equal(` <testsuite name="` ~ result.name.escapeXUnit ~ `" errors="0" skipped="0" tests="1" failures="1" time="0" timestamp="`~result.begin.toISOExtString~`"> <testcase name="Test"> <failure/> </testcase> </testsuite>`); } /// XUnitTestXml should transform a suite with a skipped test unittest { auto epoch = SysTime.fromUnixTime(0); auto result = SuiteResult("Test Suite"); result.begin = Clock.currTime; TestResult test = new TestResult("Test"); test.begin = Clock.currTime; test.end = Clock.currTime; test.status = TestResult.Status.skip; result.end = Clock.currTime; result.tests = [ test ]; auto xunit = XUnitSuiteXml(result); xunit.toString.should.equal(` <testsuite name="` ~ result.name.escapeXUnit ~ `" errors="0" skipped="1" tests="1" failures="0" time="0" timestamp="`~result.begin.toISOExtString~`"> <testcase name="Test"> <skipped/> </testcase> </testsuite>`); } /// XUnitTestXml should transform a suite with a unknown test unittest { auto epoch = SysTime.fromUnixTime(0); auto result = SuiteResult("Test Suite"); result.begin = Clock.currTime; TestResult test = new TestResult("Test"); test.begin = Clock.currTime; test.end = Clock.currTime; test.status = TestResult.Status.unknown; result.end = Clock.currTime; result.tests = [ test ]; auto xunit = XUnitSuiteXml(result); xunit.toString.should.equal(` <testsuite name="` ~ result.name.escapeXUnit ~ `" errors="1" skipped="0" tests="1" failures="0" time="0" timestamp="`~result.begin.toISOExtString~`"> <testcase name="Test"> <error message="unknown status">unknown</error> </testcase> </testsuite>`); } /// XUnitTestXml should transform an empty suite unittest { auto epoch = SysTime.fromUnixTime(0); auto result = SuiteResult("Test Suite"); result.begin = Clock.currTime; result.end = Clock.currTime; auto xunit = XUnitSuiteXml(result); xunit.toString.should.equal(` <testsuite name="` ~ result.name.escapeXUnit ~ `" errors="0" skipped="0" tests="0" failures="0" time="0" timestamp="` ~ result.begin.toISOExtString ~ `"> </testsuite>`); } struct XUnitTestXml { /// TestResult result; /// string uuid; /// Return the string representation of the test string toString() { auto time = (result.end -result.begin).total!"msecs"; string xml = ` <testcase name="` ~ result.name.escapeXUnit ~ `">` ~ "\n"; if(result.status == TestResult.Status.failure) { if(result.throwable !is null) { auto lines = result.throwable.msg.split("\n") ~ "no message"; xml ~= ` <failure message="` ~ lines[0].escapeXUnit ~ `">` ~ result.throwable.to!string.escapeXUnit ~ `</failure>` ~ "\n"; } else { xml ~= ` <failure/>` ~ "\n"; } } else if(result.status == TestResult.Status.skip) { xml ~= ` <skipped/>` ~ "\n"; } else if(result.status != TestResult.Status.success) { xml ~= ` <error message="unknown status">` ~ result.status.to!string.escapeXUnit ~ `</error>` ~ "\n"; } xml ~= ` </testcase>`; return xml; } } /// XUnitTestXml should transform a success test unittest { auto epoch = SysTime.fromUnixTime(0); TestResult result = new TestResult("Test"); result.begin = Clock.currTime; result.end = Clock.currTime; result.status = TestResult.Status.success; auto allure = XUnitTestXml(result); allure.toString.strip.should.equal(`<testcase name="Test">` ~ "\n </testcase>"); } /// XUnitTestXml should transform a failing test unittest { auto epoch = SysTime.fromUnixTime(0); TestResult result = new TestResult("Test"); result.begin = Clock.currTime; result.end = Clock.currTime; result.status = TestResult.Status.failure; result.throwable = new Exception("message"); auto xunit = XUnitTestXml(result); xunit.toString.strip.should.equal(`<testcase name="Test">` ~ "\n" ~ ` <failure message="message">` ~ result.throwable.to!string ~ `</failure>` ~ "\n" ~ ` </testcase>`); }