/// <summary> /// Runs a single test for a given test method. /// </summary> /// <param name="messageBus">The message bus to send results to.</param> /// <param name="classUnderTest">The class under test.</param> /// <param name="constructorArguments">The arguments to pass to the constructor.</param> /// <param name="methodUnderTest">The method under test.</param> /// <param name="testMethodArguments">The arguments to pass to the test method.</param> /// <param name="displayName">The display name for the test.</param> /// <param name="beforeAfterAttributes">The <see cref="BeforeAfterTestAttribute"/> instances attached to the test.</param> /// <param name="parentAggregator">The parent aggregator that contains the exceptions up to this point.</param> /// <param name="cancellationTokenSource">The cancellation token source that indicates whether cancellation has been requested.</param> protected async Task <decimal> RunTestWithArgumentsAsync(IMessageBus messageBus, Type classUnderTest, object[] constructorArguments, MethodInfo methodUnderTest, object[] testMethodArguments, string displayName, List <BeforeAfterTestAttribute> beforeAfterAttributes, ExceptionAggregator parentAggregator, CancellationTokenSource cancellationTokenSource) { var executionTimeInSeconds = 0.0m; var aggregator = new ExceptionAggregator(parentAggregator); var output = String.Empty; // TODO: Add output facilities for v2 if (!messageBus.QueueMessage(new TestStarting(this, displayName))) { cancellationTokenSource.Cancel(); } else { if (!String.IsNullOrEmpty(SkipReason)) { if (!messageBus.QueueMessage(new TestSkipped(this, displayName, SkipReason))) { cancellationTokenSource.Cancel(); } } else { var beforeAttributesRun = new Stack <BeforeAfterTestAttribute>(); var executionTime = new ExecutionTime(); if (!aggregator.HasExceptions) { await aggregator.RunAsync(async() => { object testClass = null; if (!methodUnderTest.IsStatic) { if (!messageBus.QueueMessage(new TestClassConstructionStarting(this, displayName))) { cancellationTokenSource.Cancel(); } try { if (!cancellationTokenSource.IsCancellationRequested) { executionTime.Aggregate(() => testClass = Activator.CreateInstance(classUnderTest, constructorArguments)); } } finally { if (!messageBus.QueueMessage(new TestClassConstructionFinished(this, displayName))) { cancellationTokenSource.Cancel(); } } } if (!cancellationTokenSource.IsCancellationRequested) { await aggregator.RunAsync(async() => { foreach (var beforeAfterAttribute in beforeAfterAttributes) { var attributeName = beforeAfterAttribute.GetType().Name; if (!messageBus.QueueMessage(new BeforeTestStarting(this, displayName, attributeName))) { cancellationTokenSource.Cancel(); } else { try { executionTime.Aggregate(() => beforeAfterAttribute.Before(methodUnderTest)); beforeAttributesRun.Push(beforeAfterAttribute); } finally { if (!messageBus.QueueMessage(new BeforeTestFinished(this, displayName, attributeName))) { cancellationTokenSource.Cancel(); } } } if (cancellationTokenSource.IsCancellationRequested) { return; } } if (!cancellationTokenSource.IsCancellationRequested) { var parameterTypes = methodUnderTest.GetParameters().Select(p => p.ParameterType).ToArray(); var oldSyncContext = SynchronizationContext.Current; try { var asyncSyncContext = new AsyncTestSyncContext(); SetSynchronizationContext(asyncSyncContext); await aggregator.RunAsync(async() => { await executionTime.AggregateAsync(async() => { var result = methodUnderTest.Invoke(testClass, Reflector.ConvertArguments(testMethodArguments, parameterTypes)); var task = result as Task; if (task != null) { await task; } else { var ex = await asyncSyncContext.WaitForCompletionAsync(); if (ex != null) { aggregator.Add(ex); } } }); }); } finally { SetSynchronizationContext(oldSyncContext); } } }); foreach (var beforeAfterAttribute in beforeAttributesRun) { var attributeName = beforeAfterAttribute.GetType().Name; if (!messageBus.QueueMessage(new AfterTestStarting(this, displayName, attributeName))) { cancellationTokenSource.Cancel(); } aggregator.Run(() => executionTime.Aggregate(() => beforeAfterAttribute.After(methodUnderTest))); if (!messageBus.QueueMessage(new AfterTestFinished(this, displayName, attributeName))) { cancellationTokenSource.Cancel(); } } } aggregator.Run(() => { var disposable = testClass as IDisposable; if (disposable != null) { if (!messageBus.QueueMessage(new TestClassDisposeStarting(this, displayName))) { cancellationTokenSource.Cancel(); } try { executionTime.Aggregate(disposable.Dispose); } finally { if (!messageBus.QueueMessage(new TestClassDisposeFinished(this, displayName))) { cancellationTokenSource.Cancel(); } } } }); }); } if (!cancellationTokenSource.IsCancellationRequested) { executionTimeInSeconds = (decimal)executionTime.Total.TotalSeconds; var exception = aggregator.ToException(); var testResult = exception == null ? (TestResultMessage) new TestPassed(this, displayName, executionTimeInSeconds, output) : new TestFailed(this, displayName, executionTimeInSeconds, output, exception); if (!messageBus.QueueMessage(testResult)) { cancellationTokenSource.Cancel(); } } } } if (!messageBus.QueueMessage(new TestFinished(this, displayName, executionTimeInSeconds, output))) { cancellationTokenSource.Cancel(); } return(executionTimeInSeconds); }
/// <summary> /// Runs a single test for a given test method. /// </summary> /// <param name="messageBus">The message bus to send results to.</param> /// <param name="classUnderTest">The class under test.</param> /// <param name="constructorArguments">The arguments to pass to the constructor.</param> /// <param name="methodUnderTest">The method under test.</param> /// <param name="testMethodArguments">The arguments to pass to the test method.</param> /// <param name="displayName">The display name for the test.</param> /// <param name="beforeAfterAttributes">The <see cref="BeforeAfterTestAttribute"/> instances attached to the test.</param> /// <param name="parentAggregator">The parent aggregator that contains the exceptions up to this point.</param> /// <param name="cancellationTokenSource">The cancellation token source that indicates whether cancellation has been requested.</param> protected async Task<decimal> RunTestWithArgumentsAsync(IMessageBus messageBus, Type classUnderTest, object[] constructorArguments, MethodInfo methodUnderTest, object[] testMethodArguments, string displayName, List<BeforeAfterTestAttribute> beforeAfterAttributes, ExceptionAggregator parentAggregator, CancellationTokenSource cancellationTokenSource) { var executionTimeInSeconds = 0.0m; var aggregator = new ExceptionAggregator(parentAggregator); var output = String.Empty; // TODO: Add output facilities for v2 if (!messageBus.QueueMessage(new TestStarting(this, displayName))) cancellationTokenSource.Cancel(); else { if (!String.IsNullOrEmpty(SkipReason)) { if (!messageBus.QueueMessage(new TestSkipped(this, displayName, SkipReason))) cancellationTokenSource.Cancel(); } else { var beforeAttributesRun = new Stack<BeforeAfterTestAttribute>(); var executionTime = new ExecutionTime(); if (!aggregator.HasExceptions) await aggregator.RunAsync(async () => { object testClass = null; if (!methodUnderTest.IsStatic) { if (!messageBus.QueueMessage(new TestClassConstructionStarting(this, displayName))) cancellationTokenSource.Cancel(); try { if (!cancellationTokenSource.IsCancellationRequested) executionTime.Aggregate(() => testClass = Activator.CreateInstance(classUnderTest, constructorArguments)); } finally { if (!messageBus.QueueMessage(new TestClassConstructionFinished(this, displayName))) cancellationTokenSource.Cancel(); } } if (!cancellationTokenSource.IsCancellationRequested) { await aggregator.RunAsync(async () => { foreach (var beforeAfterAttribute in beforeAfterAttributes) { var attributeName = beforeAfterAttribute.GetType().Name; if (!messageBus.QueueMessage(new BeforeTestStarting(this, displayName, attributeName))) cancellationTokenSource.Cancel(); else { try { executionTime.Aggregate(() => beforeAfterAttribute.Before(methodUnderTest)); beforeAttributesRun.Push(beforeAfterAttribute); } finally { if (!messageBus.QueueMessage(new BeforeTestFinished(this, displayName, attributeName))) cancellationTokenSource.Cancel(); } } if (cancellationTokenSource.IsCancellationRequested) return; } if (!cancellationTokenSource.IsCancellationRequested) { var parameterTypes = methodUnderTest.GetParameters().Select(p => p.ParameterType).ToArray(); var oldSyncContext = SynchronizationContext.Current; try { var asyncSyncContext = new AsyncTestSyncContext(); SetSynchronizationContext(asyncSyncContext); await aggregator.RunAsync(async () => { await executionTime.AggregateAsync(async () => { var result = methodUnderTest.Invoke(testClass, Reflector.ConvertArguments(testMethodArguments, parameterTypes)); var task = result as Task; if (task != null) await task; else { var ex = await asyncSyncContext.WaitForCompletionAsync(); if (ex != null) aggregator.Add(ex); } }); }); } finally { SetSynchronizationContext(oldSyncContext); } } }); foreach (var beforeAfterAttribute in beforeAttributesRun) { var attributeName = beforeAfterAttribute.GetType().Name; if (!messageBus.QueueMessage(new AfterTestStarting(this, displayName, attributeName))) cancellationTokenSource.Cancel(); aggregator.Run(() => executionTime.Aggregate(() => beforeAfterAttribute.After(methodUnderTest))); if (!messageBus.QueueMessage(new AfterTestFinished(this, displayName, attributeName))) cancellationTokenSource.Cancel(); } } aggregator.Run(() => { var disposable = testClass as IDisposable; if (disposable != null) { if (!messageBus.QueueMessage(new TestClassDisposeStarting(this, displayName))) cancellationTokenSource.Cancel(); try { executionTime.Aggregate(disposable.Dispose); } finally { if (!messageBus.QueueMessage(new TestClassDisposeFinished(this, displayName))) cancellationTokenSource.Cancel(); } } }); }); if (!cancellationTokenSource.IsCancellationRequested) { executionTimeInSeconds = (decimal)executionTime.Total.TotalSeconds; var exception = aggregator.ToException(); var testResult = exception == null ? (TestResultMessage)new TestPassed(this, displayName, executionTimeInSeconds, output) : new TestFailed(this, displayName, executionTimeInSeconds, output, exception); if (!messageBus.QueueMessage(testResult)) cancellationTokenSource.Cancel(); } } } if (!messageBus.QueueMessage(new TestFinished(this, displayName, executionTimeInSeconds, output))) cancellationTokenSource.Cancel(); return executionTimeInSeconds; }