102030405060708090100110120130140150160170180190200210220230240250260270280291130113103203303403543613713803904034104204304404504611471148114911500510520530540551156115711580590600610620639640650660670680691070071172073074975976977078079080081082083084085228862268708808909019109209309419519609709809901000101010223010323010401052301062301070108230109011023011123011201132301140115230116011723011801192301200121012201230124012501262301272301280129230130013151132013341134411354113601370138511395114001410142230143230144230145230146014723014801490150015101520153015401550156015701580159016011611162116301640165116601670168016911700171017211731174017521762177017801790180018101820183118411851186018701881189019001910192119301940195119601971198119912000201220222030 /++ A module containing the single threaded runner 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.executor.single; public import trial.interfaces; import trial.runner; import std.datetime; import trial.step; import trial.stackresult; /** The default test executor runs test in sequential order in a single thread */ class DefaultExecutor : ITestExecutor, IStepLifecycleListener, IAttachmentListener { private { SuiteResult suiteResult; TestResult testResult; StepResult currentStep; StepResult[] stepStack; } this() { suiteResult = SuiteResult("unknown"); } /// Called when an attachment is ready void attach(ref const Attachment attachment) { if(currentStep is null) { suiteResult.attachments ~= Attachment(attachment.name, attachment.file, attachment.mime); return; } currentStep.attachments ~= Attachment(attachment.name, attachment.file, attachment.mime); } /// Add the step result and update the other listeners on every step void begin(string suite, string test, ref StepResult step) { currentStep.steps ~= step; stepStack ~= currentStep; currentStep = step; LifeCycleListeners.instance.update(); } /// Update the other listeners on every step void end(string suite, string test, ref StepResult step) { currentStep = stepStack[stepStack.length - 1]; stepStack = stepStack[0 .. $ - 1]; LifeCycleListeners.instance.update(); } /// It does nothing SuiteResult[] beginExecution(ref const(TestCase)[]) { return []; } /// Return the result for the last executed suite SuiteResult[] endExecution() { if (suiteResult.begin == SysTime.fromUnixTime(0)) { return []; } LifeCycleListeners.instance.update(); LifeCycleListeners.instance.end(suiteResult); return [ suiteResult ]; } protected { /// Run a test case void runTest(ref const(TestCase) testCase, TestResult testResult) { try { testCase.func(); testResult.status = TestResult.Status.success; } catch (PendingTestException) { testResult.status = TestResult.Status.pending; } catch (Throwable t) { testResult.status = TestResult.Status.failure; testResult.throwable = t.toTestException; } } /// Convert a test case to a test result void createTestResult(const(TestCase) testCase) { testResult = testCase.toTestResult; testResult.begin = Clock.currTime; testResult.status = TestResult.Status.started; currentStep = testResult; stepStack = []; Step.suite = testCase.suiteName; Step.test = testCase.name; LifeCycleListeners.instance.begin(testCase.suiteName, testResult); runTest(testCase, testResult); testResult.end = Clock.currTime; LifeCycleListeners.instance.end(testCase.suiteName, testResult); } } /// Execute a test case SuiteResult[] execute(ref const(TestCase) testCase) { SuiteResult[] result; LifeCycleListeners.instance.update(); if (suiteResult.name != testCase.suiteName) { if (suiteResult.begin != SysTime.fromUnixTime(0)) { suiteResult.end = Clock.currTime; LifeCycleListeners.instance.end(suiteResult); result = [suiteResult]; } suiteResult = SuiteResult(testCase.suiteName, Clock.currTime, Clock.currTime); LifeCycleListeners.instance.begin(suiteResult); } createTestResult(testCase); suiteResult.tests ~= testResult; currentStep = null; LifeCycleListeners.instance.update(); return result; } } version(unittest) { version(Have_fluent_asserts) { import fluent.asserts; } } /// Executing a test case that throws a PendingTestException should mark the test result /// as pending instead of a failure unittest { auto old = LifeCycleListeners.instance; LifeCycleListeners.instance = new LifeCycleListeners; LifeCycleListeners.instance.add(new DefaultExecutor); scope (exit) { LifeCycleListeners.instance = old; } void test() { throw new PendingTestException(); } auto testCase = const TestCase("Some.Suite", "test name", &test, []); auto result = [testCase].runTests; result.length.should.equal(1); result[0].tests[0].status.should.equal(TestResult.Status.pending); } /// Executing a test case should set the right begin and end times unittest { import core.thread; auto old = LifeCycleListeners.instance; LifeCycleListeners.instance = new LifeCycleListeners; LifeCycleListeners.instance.add(new DefaultExecutor); scope (exit) { LifeCycleListeners.instance = old; } void test() { Thread.sleep(1.msecs); } auto testCase = const TestCase("Some.Suite", "test name", &test, []); auto begin = Clock.currTime; auto result = [ testCase ].runTests; auto testResult = result[0].tests[0]; testResult.begin.should.be.greaterThan(begin); testResult.end.should.be.greaterThan(begin + 1.msecs); }
/++ A module containing the single threaded runner 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.executor.single; public import trial.interfaces; import trial.runner; import std.datetime; import trial.step; import trial.stackresult; /** The default test executor runs test in sequential order in a single thread */ class DefaultExecutor : ITestExecutor, IStepLifecycleListener, IAttachmentListener { private { SuiteResult suiteResult; TestResult testResult; StepResult currentStep; StepResult[] stepStack; } this() { suiteResult = SuiteResult("unknown"); } /// Called when an attachment is ready void attach(ref const Attachment attachment) { if(currentStep is null) { suiteResult.attachments ~= Attachment(attachment.name, attachment.file, attachment.mime); return; } currentStep.attachments ~= Attachment(attachment.name, attachment.file, attachment.mime); } /// Add the step result and update the other listeners on every step void begin(string suite, string test, ref StepResult step) { currentStep.steps ~= step; stepStack ~= currentStep; currentStep = step; LifeCycleListeners.instance.update(); } /// Update the other listeners on every step void end(string suite, string test, ref StepResult step) { currentStep = stepStack[stepStack.length - 1]; stepStack = stepStack[0 .. $ - 1]; LifeCycleListeners.instance.update(); } /// It does nothing SuiteResult[] beginExecution(ref const(TestCase)[]) { return []; } /// Return the result for the last executed suite SuiteResult[] endExecution() { if (suiteResult.begin == SysTime.fromUnixTime(0)) { return []; } LifeCycleListeners.instance.update(); LifeCycleListeners.instance.end(suiteResult); return [ suiteResult ]; } protected { /// Run a test case void runTest(ref const(TestCase) testCase, TestResult testResult) { try { testCase.func(); testResult.status = TestResult.Status.success; } catch (PendingTestException) { testResult.status = TestResult.Status.pending; } catch (Throwable t) { testResult.status = TestResult.Status.failure; testResult.throwable = t.toTestException; } } /// Convert a test case to a test result void createTestResult(const(TestCase) testCase) { testResult = testCase.toTestResult; testResult.begin = Clock.currTime; testResult.status = TestResult.Status.started; currentStep = testResult; stepStack = []; Step.suite = testCase.suiteName; Step.test = testCase.name; LifeCycleListeners.instance.begin(testCase.suiteName, testResult); runTest(testCase, testResult); testResult.end = Clock.currTime; LifeCycleListeners.instance.end(testCase.suiteName, testResult); } } /// Execute a test case SuiteResult[] execute(ref const(TestCase) testCase) { SuiteResult[] result; LifeCycleListeners.instance.update(); if (suiteResult.name != testCase.suiteName) { if (suiteResult.begin != SysTime.fromUnixTime(0)) { suiteResult.end = Clock.currTime; LifeCycleListeners.instance.end(suiteResult); result = [suiteResult]; } suiteResult = SuiteResult(testCase.suiteName, Clock.currTime, Clock.currTime); LifeCycleListeners.instance.begin(suiteResult); } createTestResult(testCase); suiteResult.tests ~= testResult; currentStep = null; LifeCycleListeners.instance.update(); return result; } } version(unittest) { version(Have_fluent_asserts) { import fluent.asserts; } } /// Executing a test case that throws a PendingTestException should mark the test result /// as pending instead of a failure unittest { auto old = LifeCycleListeners.instance; LifeCycleListeners.instance = new LifeCycleListeners; LifeCycleListeners.instance.add(new DefaultExecutor); scope (exit) { LifeCycleListeners.instance = old; } void test() { throw new PendingTestException(); } auto testCase = const TestCase("Some.Suite", "test name", &test, []); auto result = [testCase].runTests; result.length.should.equal(1); result[0].tests[0].status.should.equal(TestResult.Status.pending); } /// Executing a test case should set the right begin and end times unittest { import core.thread; auto old = LifeCycleListeners.instance; LifeCycleListeners.instance = new LifeCycleListeners; LifeCycleListeners.instance.add(new DefaultExecutor); scope (exit) { LifeCycleListeners.instance = old; } void test() { Thread.sleep(1.msecs); } auto testCase = const TestCase("Some.Suite", "test name", &test, []); auto begin = Clock.currTime; auto result = [ testCase ].runTests; auto testResult = result[0].tests[0]; testResult.begin.should.be.greaterThan(begin); testResult.end.should.be.greaterThan(begin + 1.msecs); }