/// <summary> /// Creates a new thread task but does not start it. /// </summary> /// <remarks> /// <para> /// There is no need to call <see cref="WatchTask" /> on the returned task. /// </para> /// </remarks> /// <param name="name">The name of the task, or null to create a new name based /// on the method associated with the action.</param> /// <param name="action">The action to perform.</param> /// <returns>The new thread task.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="action"/> is null.</exception> public static ThreadTask CreateThreadTask(string name, Action action) { if (action == null) throw new ArgumentNullException("action"); var task = new TestEnvironmentAwareThreadTask(GetTaskName(name, action), action, null); WatchTask(task); return task; }
public void Run() { var test = (PatternTest)testCommand.Test; TestContextCookie? parentContextCookie = null; try { if (parentContext != null) parentContextCookie = parentContext.Enter(); // The first time we call Run, we check whether the ApartmentState of the // Thread is correct. If it is not, then we start a new thread and reenter // with a flag set to skip initial processing. if (!reentered) { if (executor.progressMonitor.IsCanceled) { result = new TestResult(TestOutcome.Canceled); return; } if (!testCommand.AreDependenciesSatisfied()) { ITestContext context = testCommand.StartPrimaryChildStep(parentTestStep); context.LogWriter.Warnings.WriteLine("Skipped due to an unsatisfied test dependency."); result = context.FinishStep(TestOutcome.Skipped, null); return; } executor.progressMonitor.SetStatus(test.Name); if (test.ApartmentState != ApartmentState.Unknown && Thread.CurrentThread.GetApartmentState() != test.ApartmentState) { reentered = true; ThreadTask task = new TestEnvironmentAwareThreadTask("Test Runner " + test.ApartmentState, (Action) Run, executor.environmentManager); task.ApartmentState = test.ApartmentState; task.Run(null); if (!task.Result.HasValue) { throw new ModelException( String.Format("Failed to perform action in thread with overridden apartment state {0}.", test.ApartmentState), task.Result.Exception); } return; } } // Actually run the test. // Yes, this is a monstrously long method due to the inlining optimzation to minimize stack depth. using (Sandbox sandbox = parentSandbox.CreateChild()) { using (new ProcessIsolation()) { using (sandbox.StartTimer(test.TimeoutFunc())) { TestOutcome outcome; PatternTestActions testActions = test.TestActions; if (testActionsDecorator != null) outcome = testActionsDecorator(sandbox, ref testActions); else outcome = TestOutcome.Passed; if (outcome.Status == TestStatus.Passed) { PatternTestStep primaryTestStep = new PatternTestStep(test, parentTestStep); PatternTestState testState = new PatternTestState(primaryTestStep, testActions, executor.converter, executor.formatter, testCommand.IsExplicit); bool invisibleTest = true; outcome = outcome.CombineWith(sandbox.Run(TestLog.Writer, new BeforeTestAction(testState).Run, "Before Test")); if (outcome.Status == TestStatus.Passed) { bool reusePrimaryTestStep = !testState.BindingContext.HasBindings; if (!reusePrimaryTestStep) primaryTestStep.IsTestCase = false; invisibleTest = false; TestContext primaryContext = TestContext.PrepareContext( testCommand.StartStep(primaryTestStep), sandbox); testState.SetInContext(primaryContext); using (primaryContext.Enter()) { primaryContext.LifecyclePhase = LifecyclePhases.Initialize; outcome = outcome.CombineWith(primaryContext.Sandbox.Run(TestLog.Writer, new InitializeTestAction(testState).Run, "Initialize")); } if (outcome.Status == TestStatus.Passed) { var actions = new List<RunTestDataItemAction>(); try { foreach (IDataItem bindingItem in testState.BindingContext.GetItems(!executor.options.SkipDynamicTests)) actions.Add(new RunTestDataItemAction(executor, testCommand, testState, primaryContext, reusePrimaryTestStep, bindingItem)); if (actions.Count == 0) { TestLog.Warnings.WriteLine("Test skipped because it is parameterized but no data was provided."); outcome = TestOutcome.Skipped; } else { if (actions.Count == 1 || ! test.IsParallelizable) { foreach (var action in actions) action.Run(); } else { executor.scheduler.Run(GenericCollectionUtils.ConvertAllToArray<RunTestDataItemAction, Action>( actions, action => action.Run)); } TestOutcome combinedOutcome = TestOutcome.Passed; foreach (var action in actions) combinedOutcome = combinedOutcome.CombineWith(action.Outcome); outcome = outcome.CombineWith(reusePrimaryTestStep ? combinedOutcome : combinedOutcome.Generalize()); } } catch (TestException ex) { if (ex.Outcome.Status == TestStatus.Failed) { TestLog.Failures.WriteException(ex, String.Format("An exception occurred while getting data items for test '{0}'.", testState.Test.FullName)); } else { TestLog.Warnings.WriteException(ex); } outcome = ex.Outcome; } catch (Exception ex) { TestLog.Failures.WriteException(ex, String.Format("An exception occurred while getting data items for test '{0}'.", testState.Test.FullName)); outcome = TestOutcome.Error; } } primaryContext.SetInterimOutcome(outcome); using (primaryContext.Enter()) { primaryContext.LifecyclePhase = LifecyclePhases.Dispose; outcome = outcome.CombineWith(primaryContext.Sandbox.Run(TestLog.Writer, new DisposeTestAction(testState).Run, "Dispose")); } result = primaryContext.FinishStep(outcome); } outcome = outcome.CombineWith(sandbox.Run(TestLog.Writer, new AfterTestAction(testState).Run, "After Test")); if (invisibleTest) result = PublishOutcomeFromInvisibleTest(testCommand, primaryTestStep, outcome); } } } } } catch (Exception ex) { result = ReportTestError(testCommand, parentTestStep, ex, String.Format("An exception occurred while preparing to run test '{0}'.", test.FullName)); } finally { if (parentContextCookie.HasValue) parentContextCookie.Value.ExitContext(); executor.progressMonitor.SetStatus(""); executor.progressMonitor.Worked(1); } }
/// <inheritdoc /> protected override void DecorateTest(IPatternScope scope, ICodeElementInfo codeElement) { scope.TestBuilder.TestInstanceActions.RunTestInstanceBodyChain.Around((state, inner) => { TaskContainer container = new TaskContainer(); try { TestOutcome[] threadOutcomes = new TestOutcome[numThreads]; TestContext context = TestContext.CurrentContext; for (int i = 0; i < numThreads; i++) { int index = i; string name = String.Format("Threaded Repetition #{0}", index + 1); var task = new TestEnvironmentAwareThreadTask(name, delegate { TestContext threadContext = TestStep.RunStep(name, delegate { TestOutcome innerOutcome = inner(state); if (innerOutcome.Status != TestStatus.Passed) throw new SilentTestException(innerOutcome); }, null, false, codeElement); threadOutcomes[index] = threadContext.Outcome; }, null); task.Terminated += delegate { if (! task.Result.HasValue) { threadOutcomes[index] = TestOutcome.Error; context.LogWriter.Default.WriteException(task.Result.Exception, String.Format("An exception occurred while starting Threaded Repetition #{0}.", index)); } }; container.Watch(task); task.Start(); } container.JoinAll(null); TestOutcome outcome = TestOutcome.Passed; int passedCount = 0; foreach (TestOutcome threadOutcome in threadOutcomes) { outcome = outcome.CombineWith(threadOutcome); if (threadOutcome.Status == TestStatus.Passed) passedCount += 1; } context.LogWriter.Default.WriteLine(String.Format("{0} of {1} threaded repetitions passed.", passedCount, numThreads)); return outcome; } finally { container.AbortAll(); container.JoinAll(null); } }); }