10203040506070809010011012013014015016017018019997200219972299723997249972599726027997280290300310320330340350360370381391400410420431440450460470480490500510521531540550565315717658176590601766106206306406506606706806907007107207307417675117376077078117379117380176819978208308408517686176870880891769009109209317694095096097098099010001010102010301040105010601070108010901100111997112997113011499711501160117011801190120012101229971230124997125012601270128997129013099713101320133013401350 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; } 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 { SuiteResult result; string uuid; 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; } } struct XUnitTestXml { TestResult result; string uuid; 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; } }
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; } 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 { SuiteResult result; string uuid; 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; } } struct XUnitTestXml { TestResult result; string uuid; 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; } }