10203040506070809010011012013014015016017018019020021022023024025026027028029030031032033034035036037038039040041042043044045046047048049050051052053054055056057058059060061062063064065066067068069070071072073074075076077078079080081082083084085086087088089090091092093094095096097098099010001010102010301040105010601070108010901100111011201130114011501160117011801190120012101220123012401250126012701280129013001310132013301340135013601370138013901400141014201430144014501460147014801490150015101520153015401550156015701580159016001610162016301640165016601670168016901700171017201730174017501760 module trial.reporters.stats; import std.algorithm; import std.string; import std.conv; import std.exception; import std.array; import std.datetime; import std.stdio; import std.file; import std.path; import trial.runner; import trial.interfaces; struct Stat { string name; SysTime begin; SysTime end; TestResult.Status status = TestResult.Status.unknown; SourceLocation location; } class StatStorage { Stat[] values; } Stat find(StatStorage storage, const(string) name) { auto res = storage.values.filter!(a => a.name == name); if (res.empty) { return Stat("", SysTime.min, SysTime.min); } return res.front; } class StatsReporter : ILifecycleListener, ITestCaseLifecycleListener, ISuiteLifecycleListener, IStepLifecycleListener { private { immutable string destination; StatStorage storage; string[][string] path; } this(StatStorage storage, string destination) { this.storage = storage; this.destination = destination; } this(string destination) { this(new StatStorage, destination); } private { auto lastItem(string key) { enforce(path[key].length > 0, "There is no defined path"); return path[key][path.length - 1]; } } void update() { } void begin(ref SuiteResult suite) { } void end(ref SuiteResult suite) { storage.values ~= Stat(suite.name, suite.begin, Clock.currTime); } void begin(string suite, ref TestResult test) { } void end(string suite, ref TestResult test) { storage.values ~= Stat(suite ~ "." ~ test.name, test.begin, Clock.currTime, test.status, SourceLocation(test.fileName, test.line)); } void begin(string suite, string test, ref StepResult step) { string key = suite ~ "." ~ test; path[key] ~= step.name; } void end(string suite, string test, ref StepResult step) { string key = suite ~ "." ~ test; enforce(lastItem(key) == step.name, "Invalid step name"); storage.values ~= Stat(key ~ "." ~ path[key].join('.'), step.begin, Clock.currTime); path[key] = path[key][0 .. $ - 1]; } void begin(ulong) { } void end(SuiteResult[]) { auto parent = buildPath(pathSplitter(destination).array[0..$-1]); if(parent != "" && !parent.exists) { mkdirRecurse(parent); } std.file.write(destination, storage.toCsv); auto attachment = const Attachment("stats", destination, "text/csv"); if(LifeCycleListeners.instance !is null) { LifeCycleListeners.instance.attach(attachment); } } } string toCsv(const(StatStorage) storage) { return storage.values.map!(a => [a.name, a.begin.toISOExtString, a.end.toISOExtString, a.status.to!string, a.location.fileName, a.location.line.to!string]).map!(a => a.join(',')).join('\n'); } StatStorage toStatStorage(const(string) data) { auto stat = new StatStorage; stat.values = data .split('\n') .map!(a => a.split(',')) .filter!(a => a.length == 6) .map!(a => Stat(a[0], SysTime.fromISOExtString(a[1]), SysTime.fromISOExtString(a[2]), a[3].to!(TestResult.Status), SourceLocation(a[4], a[5].to!size_t))) .array; return stat; } StatStorage statsFromFile(string fileName) { if (!fileName.exists) { return new StatStorage(); } return fileName.readText.toStatStorage; }
module trial.reporters.stats; import std.algorithm; import std.string; import std.conv; import std.exception; import std.array; import std.datetime; import std.stdio; import std.file; import std.path; import trial.runner; import trial.interfaces; struct Stat { string name; SysTime begin; SysTime end; TestResult.Status status = TestResult.Status.unknown; SourceLocation location; } class StatStorage { Stat[] values; } Stat find(StatStorage storage, const(string) name) { auto res = storage.values.filter!(a => a.name == name); if (res.empty) { return Stat("", SysTime.min, SysTime.min); } return res.front; } class StatsReporter : ILifecycleListener, ITestCaseLifecycleListener, ISuiteLifecycleListener, IStepLifecycleListener { private { immutable string destination; StatStorage storage; string[][string] path; } this(StatStorage storage, string destination) { this.storage = storage; this.destination = destination; } this(string destination) { this(new StatStorage, destination); } private { auto lastItem(string key) { enforce(path[key].length > 0, "There is no defined path"); return path[key][path.length - 1]; } } void update() { } void begin(ref SuiteResult suite) { } void end(ref SuiteResult suite) { storage.values ~= Stat(suite.name, suite.begin, Clock.currTime); } void begin(string suite, ref TestResult test) { } void end(string suite, ref TestResult test) { storage.values ~= Stat(suite ~ "." ~ test.name, test.begin, Clock.currTime, test.status, SourceLocation(test.fileName, test.line)); } void begin(string suite, string test, ref StepResult step) { string key = suite ~ "." ~ test; path[key] ~= step.name; } void end(string suite, string test, ref StepResult step) { string key = suite ~ "." ~ test; enforce(lastItem(key) == step.name, "Invalid step name"); storage.values ~= Stat(key ~ "." ~ path[key].join('.'), step.begin, Clock.currTime); path[key] = path[key][0 .. $ - 1]; } void begin(ulong) { } void end(SuiteResult[]) { auto parent = buildPath(pathSplitter(destination).array[0..$-1]); if(parent != "" && !parent.exists) { mkdirRecurse(parent); } std.file.write(destination, storage.toCsv); auto attachment = const Attachment("stats", destination, "text/csv"); if(LifeCycleListeners.instance !is null) { LifeCycleListeners.instance.attach(attachment); } } } string toCsv(const(StatStorage) storage) { return storage.values.map!(a => [a.name, a.begin.toISOExtString, a.end.toISOExtString, a.status.to!string, a.location.fileName, a.location.line.to!string]).map!(a => a.join(',')).join('\n'); } StatStorage toStatStorage(const(string) data) { auto stat = new StatStorage; stat.values = data .split('\n') .map!(a => a.split(',')) .filter!(a => a.length == 6) .map!(a => Stat(a[0], SysTime.fromISOExtString(a[1]), SysTime.fromISOExtString(a[2]), a[3].to!(TestResult.Status), SourceLocation(a[4], a[5].to!size_t))) .array; return stat; } StatStorage statsFromFile(string fileName) { if (!fileName.exists) { return new StatStorage(); } return fileName.readText.toStatStorage; }