10203040506070809010011012013014015016017018019020021022023124125026027028029030131032133034135136037038139040041042104304414504604704814905005105205305405505605705805916016106206306416506606706806907007107207307407507607707807908008108208308408538618718818909009109209309409509609709809901000101010201030104010501060107010801090110011101120113011401150116011701180119012001210122012301240125012611271128112901301131113211330134013501360137013801390140014101420143014401450146014701480149015001510152015301540155015601570158015901600161016201630164016501660167016801690170017101720173017401750176017701780179018001810182018301840185018601870188018901900191019201930194019501960197019801990200020102020203020402050206020702080209021002110212021302140215021602170218021902200221022202230224022502260227022802290230023102320233023402350236123702381239024012410242024302441245024602470248124902501251025229942539972540255025612571258025912600261026202630264234926502660267026802690270027102720273027402750276027702780279028002810282028302840285028602870288028902900291029202930294029502960297029802990300030103020303030403050306030703080309031003110312031303140315031603170318031903200321032203230324032503260327032803290330033103320333033403351336133713381339134013411342034303440 module trial.runner; import std.stdio; import std.algorithm; import std.datetime; import std.range; import std.traits; import std.string; import std.conv; import std.path; import std.getopt; import std.file; import std.path; import std.exception; import trial.settings; import trial.executor.single; import trial.executor.parallel; import trial.executor.process; static this() { if(LifeCycleListeners.instance is null) { LifeCycleListeners.instance = new LifeCycleListeners; } } void setupLifecycle(Settings settings) { settings.artifactsLocation = settings.artifactsLocation.asAbsolutePath.array; Attachment.destination = buildPath(settings.artifactsLocation, "attachment"); if(!Attachment.destination.exists) { Attachment.destination.mkdirRecurse; } if(LifeCycleListeners.instance is null) { LifeCycleListeners.instance = new LifeCycleListeners; } settings.reporters.map!(a => a.toLower).each!(a => addReporter(a, settings)); addExecutor(settings.executor, settings); } void addExecutor(string name, Settings settings) { switch(name) { case "default": LifeCycleListeners.instance.add(new DefaultExecutor); break; case "parallel": LifeCycleListeners.instance.add(new ParallelExecutor(settings.maxThreads)); break; case "process": LifeCycleListeners.instance.add(new ProcessExecutor()); break; default: if(name != "") { writeln("There is no `" ~ name ~ "` executor. Using the default."); } LifeCycleListeners.instance.add(new DefaultExecutor); } } void addReporter(string name, Settings settings) { import trial.reporters.spec; import trial.reporters.specprogress; import trial.reporters.specsteps; import trial.reporters.dotmatrix; import trial.reporters.landing; import trial.reporters.progress; import trial.reporters.list; import trial.reporters.html; import trial.reporters.allure; import trial.reporters.stats; import trial.reporters.result; import trial.reporters.xunit; import trial.reporters.tap; import trial.reporters.visualtrial; switch(name) { case "spec": LifeCycleListeners.instance.add(new SpecReporter(settings)); break; case "spec-progress": auto storage = statsFromFile(buildPath(settings.artifactsLocation, "stats.csv")); LifeCycleListeners.instance.add(new SpecProgressReporter(storage)); break; case "spec-steps": LifeCycleListeners.instance.add(new SpecStepsReporter(settings)); break; case "dot-matrix": LifeCycleListeners.instance.add(new DotMatrixReporter(settings.glyphs.dotMatrix)); break; case "landing": LifeCycleListeners.instance.add(new LandingReporter(settings.glyphs.landing)); break; case "list": LifeCycleListeners.instance.add(new ListReporter(settings)); break; case "progress": LifeCycleListeners.instance.add(new ProgressReporter(settings.glyphs.progress)); break; case "html": LifeCycleListeners.instance.add( new HtmlReporter(buildPath(settings.artifactsLocation, "result.html"), settings.warningTestDuration, settings.dangerTestDuration)); break; case "allure": LifeCycleListeners.instance.add(new AllureReporter(buildPath(settings.artifactsLocation, "allure"))); break; case "xunit": LifeCycleListeners.instance.add(new XUnitReporter(buildPath(settings.artifactsLocation, "xunit"))); break; case "result": LifeCycleListeners.instance.add(new ResultReporter(settings.glyphs.result)); break; case "stats": LifeCycleListeners.instance.add(new StatsReporter(buildPath(settings.artifactsLocation, "stats.csv"))); break; case "tap": LifeCycleListeners.instance.add(new TapReporter); break; case "visualtrial": LifeCycleListeners.instance.add(new VisualTrialReporter); break; default: writeln("There is no `" ~ name ~ "` reporter"); } } const(TestCase)[][string] describeTests() { return describeTests(LifeCycleListeners.instance.getTestCases); } const(TestCase)[][string] describeTests(const(TestCase)[] tests) { const(TestCase)[][string] groupedTests; foreach(test; tests) { groupedTests[test.suiteName] ~= test; } return groupedTests; } string toJSONHierarchy(T)(const(T)[][string] items) { struct Node { Node[string] nodes; const(T)[] values; void add(string[] path, const(T)[] values) { if(path.length == 0) { this.values = values; return; } if(path[0] !in nodes) { nodes[path[0]] = Node(); } nodes[path[0]].add(path[1..$], values); } string toString(int spaces = 2) { string prefix = leftJustify("", spaces); string endPrefix = leftJustify("", spaces - 2); string listValues = ""; string objectValues = ""; if(values.length > 0) { listValues = values .map!(a => a.toString) .map!(a => prefix ~ a) .join(",\n"); } if(nodes.keys.length > 0) { objectValues = nodes .byKeyValue .map!(a => `"` ~ a.key ~ `": ` ~ a.value.toString(spaces + 2)) .map!(a => prefix ~ a) .join(",\n"); } if(listValues != "" && objectValues != "") { return "{\n" ~ objectValues ~ ",\n" ~ prefix ~ "\"\": [\n" ~ listValues ~ "\n" ~ prefix ~ "]\n" ~ endPrefix ~ "}"; } if(listValues != "") { return "[\n" ~ listValues ~ "\n" ~ endPrefix ~ "]"; } return "{\n" ~ objectValues ~ "\n" ~ endPrefix ~ "}"; } } Node root; foreach(key; items.keys) { root.add(key.split("."), items[key]); } return root.toString; } auto runTests(const(TestCase)[] tests, string testName = "", string suiteName = "") { setupSegmentationHandler!true(); const(TestCase)[] filteredTests = tests; if(testName != "") { filteredTests = tests.filter!(a => a.name.indexOf(testName) != -1).array; } if(suiteName != "") { filteredTests = filteredTests.filter!(a => a.suiteName.indexOf(suiteName) != -1).array; } LifeCycleListeners.instance.begin(filteredTests.length); SuiteResult[] results = LifeCycleListeners.instance.beginExecution(filteredTests); foreach(test; filteredTests) { results ~= LifeCycleListeners.instance.execute(test); } results ~= LifeCycleListeners.instance.endExecution; LifeCycleListeners.instance.end(results); return results; } bool isSuccess(SuiteResult[] results) { return results.map!(a => a.tests).joiner.map!(a => a.status).all!(a => a == TestResult.Status.success || a == TestResult.Status.pending); } void setupSegmentationHandler(bool testRunner)() { import core.runtime; version(CRuntime_Glibc) import core.sys.linux.execinfo; else version(OSX) import core.sys.darwin.execinfo; else version(FreeBSD) import core.sys.freebsd.execinfo; else version(NetBSD) import core.sys.netbsd.execinfo; else version(Windows) import core.sys.windows.stacktrace; else version(Solaris) import core.sys.solaris.execinfo; static if( __traits( compiles, backtrace ) ) { version(Posix) { import core.sys.posix.signal; static extern (C) void unittestSegvHandler(int signum, siginfo_t* info, void* ptr ) nothrow { import core.stdc.stdio; core.stdc.stdio.printf("\n\n"); static if(testRunner) { if(signum == SIGSEGV) { core.stdc.stdio.printf("Got a Segmentation Fault running "); } if(signum == SIGBUS) { core.stdc.stdio.printf("Got a bus error running "); } if(LifeCycleListeners.instance.runningTest != "") { core.stdc.stdio.printf("%s\n\n", LifeCycleListeners.instance.runningTest.ptr); } else { core.stdc.stdio.printf("some setup step. This is probably a Trial bug. Please create an issue on github.\n\n"); } } else { if(signum == SIGSEGV) { core.stdc.stdio.printf("Got a Segmentation Fault! "); } if(signum == SIGBUS) { core.stdc.stdio.printf("Got a bus error! "); } core.stdc.stdio.printf(" This is probably a Trial bug. Please create an issue on github.\n\n"); } static enum MAXFRAMES = 128; void*[MAXFRAMES] callstack; int numframes; numframes = backtrace( callstack.ptr, MAXFRAMES ); backtrace_symbols_fd( callstack.ptr, numframes, 2); } sigaction_t action = void; (cast(byte*) &action)[0 .. action.sizeof] = 0; sigfillset( &action.sa_mask ); action.sa_flags = SA_SIGINFO | SA_RESETHAND; action.sa_sigaction = &unittestSegvHandler; sigaction( SIGSEGV, &action, null ); sigaction( SIGBUS, &action, null ); } } }
module trial.runner; import std.stdio; import std.algorithm; import std.datetime; import std.range; import std.traits; import std.string; import std.conv; import std.path; import std.getopt; import std.file; import std.path; import std.exception; import trial.settings; import trial.executor.single; import trial.executor.parallel; import trial.executor.process; static this() { if(LifeCycleListeners.instance is null) { LifeCycleListeners.instance = new LifeCycleListeners; } } void setupLifecycle(Settings settings) { settings.artifactsLocation = settings.artifactsLocation.asAbsolutePath.array; Attachment.destination = buildPath(settings.artifactsLocation, "attachment"); if(!Attachment.destination.exists) { Attachment.destination.mkdirRecurse; } if(LifeCycleListeners.instance is null) { LifeCycleListeners.instance = new LifeCycleListeners; } settings.reporters.map!(a => a.toLower).each!(a => addReporter(a, settings)); addExecutor(settings.executor, settings); } void addExecutor(string name, Settings settings) { switch(name) { case "default": LifeCycleListeners.instance.add(new DefaultExecutor); break; case "parallel": LifeCycleListeners.instance.add(new ParallelExecutor(settings.maxThreads)); break; case "process": LifeCycleListeners.instance.add(new ProcessExecutor()); break; default: if(name != "") { writeln("There is no `" ~ name ~ "` executor. Using the default."); } LifeCycleListeners.instance.add(new DefaultExecutor); } } void addReporter(string name, Settings settings) { import trial.reporters.spec; import trial.reporters.specprogress; import trial.reporters.specsteps; import trial.reporters.dotmatrix; import trial.reporters.landing; import trial.reporters.progress; import trial.reporters.list; import trial.reporters.html; import trial.reporters.allure; import trial.reporters.stats; import trial.reporters.result; import trial.reporters.xunit; import trial.reporters.tap; import trial.reporters.visualtrial; switch(name) { case "spec": LifeCycleListeners.instance.add(new SpecReporter(settings)); break; case "spec-progress": auto storage = statsFromFile(buildPath(settings.artifactsLocation, "stats.csv")); LifeCycleListeners.instance.add(new SpecProgressReporter(storage)); break; case "spec-steps": LifeCycleListeners.instance.add(new SpecStepsReporter(settings)); break; case "dot-matrix": LifeCycleListeners.instance.add(new DotMatrixReporter(settings.glyphs.dotMatrix)); break; case "landing": LifeCycleListeners.instance.add(new LandingReporter(settings.glyphs.landing)); break; case "list": LifeCycleListeners.instance.add(new ListReporter(settings)); break; case "progress": LifeCycleListeners.instance.add(new ProgressReporter(settings.glyphs.progress)); break; case "html": LifeCycleListeners.instance.add( new HtmlReporter(buildPath(settings.artifactsLocation, "result.html"), settings.warningTestDuration, settings.dangerTestDuration)); break; case "allure": LifeCycleListeners.instance.add(new AllureReporter(buildPath(settings.artifactsLocation, "allure"))); break; case "xunit": LifeCycleListeners.instance.add(new XUnitReporter(buildPath(settings.artifactsLocation, "xunit"))); break; case "result": LifeCycleListeners.instance.add(new ResultReporter(settings.glyphs.result)); break; case "stats": LifeCycleListeners.instance.add(new StatsReporter(buildPath(settings.artifactsLocation, "stats.csv"))); break; case "tap": LifeCycleListeners.instance.add(new TapReporter); break; case "visualtrial": LifeCycleListeners.instance.add(new VisualTrialReporter); break; default: writeln("There is no `" ~ name ~ "` reporter"); } } const(TestCase)[][string] describeTests() { return describeTests(LifeCycleListeners.instance.getTestCases); } const(TestCase)[][string] describeTests(const(TestCase)[] tests) { const(TestCase)[][string] groupedTests; foreach(test; tests) { groupedTests[test.suiteName] ~= test; } return groupedTests; } string toJSONHierarchy(T)(const(T)[][string] items) { struct Node { Node[string] nodes; const(T)[] values; void add(string[] path, const(T)[] values) { if(path.length == 0) { this.values = values; return; } if(path[0] !in nodes) { nodes[path[0]] = Node(); } nodes[path[0]].add(path[1..$], values); } string toString(int spaces = 2) { string prefix = leftJustify("", spaces); string endPrefix = leftJustify("", spaces - 2); string listValues = ""; string objectValues = ""; if(values.length > 0) { listValues = values .map!(a => a.toString) .map!(a => prefix ~ a) .join(",\n"); } if(nodes.keys.length > 0) { objectValues = nodes .byKeyValue .map!(a => `"` ~ a.key ~ `": ` ~ a.value.toString(spaces + 2)) .map!(a => prefix ~ a) .join(",\n"); } if(listValues != "" && objectValues != "") { return "{\n" ~ objectValues ~ ",\n" ~ prefix ~ "\"\": [\n" ~ listValues ~ "\n" ~ prefix ~ "]\n" ~ endPrefix ~ "}"; } if(listValues != "") { return "[\n" ~ listValues ~ "\n" ~ endPrefix ~ "]"; } return "{\n" ~ objectValues ~ "\n" ~ endPrefix ~ "}"; } } Node root; foreach(key; items.keys) { root.add(key.split("."), items[key]); } return root.toString; } auto runTests(const(TestCase)[] tests, string testName = "", string suiteName = "") { setupSegmentationHandler!true(); const(TestCase)[] filteredTests = tests; if(testName != "") { filteredTests = tests.filter!(a => a.name.indexOf(testName) != -1).array; } if(suiteName != "") { filteredTests = filteredTests.filter!(a => a.suiteName.indexOf(suiteName) != -1).array; } LifeCycleListeners.instance.begin(filteredTests.length); SuiteResult[] results = LifeCycleListeners.instance.beginExecution(filteredTests); foreach(test; filteredTests) { results ~= LifeCycleListeners.instance.execute(test); } results ~= LifeCycleListeners.instance.endExecution; LifeCycleListeners.instance.end(results); return results; } bool isSuccess(SuiteResult[] results) { return results.map!(a => a.tests).joiner.map!(a => a.status).all!(a => a == TestResult.Status.success || a == TestResult.Status.pending); } void setupSegmentationHandler(bool testRunner)() { import core.runtime; version(CRuntime_Glibc) import core.sys.linux.execinfo; else version(OSX) import core.sys.darwin.execinfo; else version(FreeBSD) import core.sys.freebsd.execinfo; else version(NetBSD) import core.sys.netbsd.execinfo; else version(Windows) import core.sys.windows.stacktrace; else version(Solaris) import core.sys.solaris.execinfo; static if( __traits( compiles, backtrace ) ) { version(Posix) { import core.sys.posix.signal; static extern (C) void unittestSegvHandler(int signum, siginfo_t* info, void* ptr ) nothrow { import core.stdc.stdio; core.stdc.stdio.printf("\n\n"); static if(testRunner) { if(signum == SIGSEGV) { core.stdc.stdio.printf("Got a Segmentation Fault running "); } if(signum == SIGBUS) { core.stdc.stdio.printf("Got a bus error running "); } if(LifeCycleListeners.instance.runningTest != "") { core.stdc.stdio.printf("%s\n\n", LifeCycleListeners.instance.runningTest.ptr); } else { core.stdc.stdio.printf("some setup step. This is probably a Trial bug. Please create an issue on github.\n\n"); } } else { if(signum == SIGSEGV) { core.stdc.stdio.printf("Got a Segmentation Fault! "); } if(signum == SIGBUS) { core.stdc.stdio.printf("Got a bus error! "); } core.stdc.stdio.printf(" This is probably a Trial bug. Please create an issue on github.\n\n"); } static enum MAXFRAMES = 128; void*[MAXFRAMES] callstack; int numframes; numframes = backtrace( callstack.ptr, MAXFRAMES ); backtrace_symbols_fd( callstack.ptr, numframes, 2); } sigaction_t action = void; (cast(byte*) &action)[0 .. action.sizeof] = 0; sigfillset( &action.sa_mask ); action.sa_flags = SA_SIGINFO | SA_RESETHAND; action.sa_sigaction = &unittestSegvHandler; sigaction( SIGSEGV, &action, null ); sigaction( SIGBUS, &action, null ); } } }