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
25220
260
27220
28220
29220
30220
31220
320
33220
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
62123
6340
6440
650
6640
670
680
690
700
710
720
730
740
750
760
770
780
790
8045
81255
820
830
84255
85255
8645
87210
883
892
900
9145
9244
930
940
9545
960
970
980
9945
1000
1010
1020
1030
1040
1050
1060
1070
1080
1090
1101
1111
1121
1130
1141
1151
1161
1171
1180
1191
1200
1211
1220
1231
1240
1252
1260
1270
1280
1290
1300
1310
1320
1330
1341
1351
1361
1370
1381
1391
1401
1411
1420
1431
1440
1451
1460
1471
1480
1492
1500
1510
1520
1530
1540
1550
1560
1570
1580
1591
1601
1611
1620
1631
1641
1651
1661
1670
1681
1690
1701
1710
1721
1730
1742
1750
1760
1770
1780
1790
1800
1810
1820
1830
1840
1851
1861
1871
1880
1891
1901
1911
1921
1930
1941
1950
1961
1970
1981
1990
2002
2010
2020
2030
2040
2050
2060
2070
2080
2090
2101
2111
2121
2131
2140
2151
2160
2172
2180
2190
2200
2210
2220
2230
2240
2250
2260
2270
2280
2290
230212
231212
2320
233212
2342
2351
2360
2371
2380
2391
2400
241210
2421
243209
2441
2450
2460
247212
2480
249212
2500
2510
2520
2530
2540
2550
2561
2570
2581
2591
2601
2611
2620
2631
2640
2652
2660
2670
2680
2690
2700
2711
2721
2731
2741
2751
2761
2770
2781
2792
2800
2810
2820
/++ 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) { 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>`); }