예제 #1
0
		public async Task<RunSummary> RunAsync (VsixTestCase testCase, IMessageBus messageBus, ExceptionAggregator aggregator, object[] constructorArguments)
		{
			if (Process == null) {
				if (!Start ()) {
					Stop ();
					if (!Start ()) {
						Stop ();

						messageBus.QueueMessage (new TestFailed (new XunitTest (testCase, testCase.DisplayName), 0,
							string.Format ("Failed to start Visual Studio {0}{1}.", visualStudioVersion, rootSuffix),
							new TimeoutException ()));

						return new RunSummary {
							Failed = 1,
						};
					}
				}
			}

			if (runner == null) {
				var hostUrl = RemotingUtil.GetHostUri (pipeName);
				var clientPipeName = Guid.NewGuid ().ToString ("N");

				clientChannel = RemotingUtil.CreateChannel (Constants.ClientChannelName + clientPipeName, clientPipeName);

				try {
					runner = (IVsRemoteRunner)RemotingServices.Connect (typeof (IVsRemoteRunner), hostUrl);
					// We don't require restart anymore since we write to the registry directly the binding paths,
					// rather than installing a VSIX
					//if (runner.ShouldRestart ()) {
					//    Stop ();
					//    return await RunAsync (testCase, messageBus, aggregator, constructorArguments);
					//}

					if (Debugger.IsAttached) {
						// Add default trace listeners to the remote process.
						foreach (var listener in Trace.Listeners.OfType<TraceListener> ()) {
							runner.AddListener (listener);
						}
					}

				} catch (Exception ex) {
					messageBus.QueueMessage (new TestFailed (new XunitTest (testCase, testCase.DisplayName), 0, ex.Message, ex));
					return new RunSummary {
						Failed = 1
					};
				}
			}

			var xunitTest = new XunitTest (testCase, testCase.DisplayName);

			try {
				var outputHelper = constructorArguments.OfType<TestOutputHelper> ().FirstOrDefault ();
				if (outputHelper != null)
					outputHelper.Initialize (messageBus, xunitTest);

				// Special case for test output, since it's not MBR.
				var args = constructorArguments.Select (arg => {
					var helper = arg as ITestOutputHelper;
					if (helper != null) {
						var remoteHeper = new RemoteTestOutputHelper (helper);
						remoteObjects.Add (remoteHeper);
						return remoteHeper;
					}

					return arg;
				}).ToArray ();

				var remoteBus = new RemoteMessageBus (messageBus);
				remoteObjects.Add (remoteBus);

				var summary = await System.Threading.Tasks.Task.Run (
					() => runner.Run (testCase, remoteBus, args))
					.TimeoutAfter (testCase.TimeoutSeconds * 1000);

				// Dump output only if a debugger is attached, meaning that most likely
				// there is a single test being run/debugged.
				if (Debugger.IsAttached && outputHelper != null && !string.IsNullOrEmpty (outputHelper.Output)) {
					Trace.WriteLine (outputHelper.Output);
					Debugger.Log (0, "", outputHelper.Output);
					Console.WriteLine (outputHelper.Output);
				}

				if (summary.Exception != null)
					aggregator.Add (summary.Exception);

				return summary.ToRunSummary ();
			} catch (Exception ex) {
				aggregator.Add (ex);
				messageBus.QueueMessage (new TestFailed (xunitTest, 0, ex.Message, ex));
				return new RunSummary {
					Failed = 1
				};
			} finally {
				var outputHelper = constructorArguments.OfType<TestOutputHelper> ().FirstOrDefault ();
				if (outputHelper != null)
					outputHelper.Uninitialize ();
			}
		}
예제 #2
0
        private static async Task RunTestClassAsync(IMessageBus messageBus,
                                                    ITestCollection collection,
                                                    Dictionary<Type, object> collectionFixtureMappings,
                                                    IReflectionTypeInfo testClass,
                                                    IEnumerable<XunitTestCase> testCases,
                                                    ITestCaseOrderer orderer,
                                                    RunSummary classSummary,
                                                    ExceptionAggregator aggregator,
                                                    CancellationTokenSource cancellationTokenSource)
        {
            var testClassType = testClass.Type;
            var fixtureMappings = new Dictionary<Type, object>();
            var constructorArguments = new List<object>();

            var ordererAttribute = testClass.GetCustomAttributes(typeof(TestCaseOrdererAttribute)).SingleOrDefault();
            if (ordererAttribute != null)
                orderer = GetTestCaseOrderer(ordererAttribute);

            if (testClassType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollectionFixture<>)))
                aggregator.Add(new TestClassException("A test class may not be decorated with ICollectionFixture<> (decorate the test collection class instead)."));

            foreach (var interfaceType in testClassType.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IClassFixture<>)))
                CreateFixture(interfaceType, aggregator, fixtureMappings);

            if (collection.CollectionDefinition != null)
            {
                var declarationType = ((IReflectionTypeInfo)collection.CollectionDefinition).Type;
                foreach (var interfaceType in declarationType.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IClassFixture<>)))
                    CreateFixture(interfaceType, aggregator, fixtureMappings);
            }

            var isStaticClass = testClassType.IsAbstract && testClassType.IsSealed;
            if (!isStaticClass)
            {
                var ctors = testClassType.GetConstructors();
                if (ctors.Length != 1)
                {
                    aggregator.Add(new TestClassException("A test class may only define a single public constructor."));
                }
                else
                {
                    var ctor = ctors.Single();
                    var unusedArguments = new List<string>();

                    foreach (var paramInfo in ctor.GetParameters())
                    {
                        object fixture;

                        if (fixtureMappings.TryGetValue(paramInfo.ParameterType, out fixture) || collectionFixtureMappings.TryGetValue(paramInfo.ParameterType, out fixture))
                            constructorArguments.Add(fixture);
                        else
                            unusedArguments.Add(String.Format("{0} {1}", paramInfo.ParameterType.Name, paramInfo.Name));
                    }

                    if (unusedArguments.Count > 0)
                        aggregator.Add(new TestClassException("The following constructor arguments did not have matching fixture data: " + String.Join(", ", unusedArguments)));
                }
            }

            var orderedTestCases = orderer.OrderTestCases(testCases);
            var methodGroups = orderedTestCases.GroupBy(tc => tc.Method);

            foreach (var method in methodGroups)
            {
                if (!messageBus.QueueMessage(new TestMethodStarting(collection, testClass.Name, method.Key.Name)))
                    cancellationTokenSource.Cancel();
                else
                    await RunTestMethodAsync(messageBus, constructorArguments.ToArray(), method, classSummary, aggregator, cancellationTokenSource);

                if (!messageBus.QueueMessage(new TestMethodFinished(collection, testClass.Name, method.Key.Name)))
                    cancellationTokenSource.Cancel();

                if (cancellationTokenSource.IsCancellationRequested)
                    break;
            }

            foreach (var fixture in fixtureMappings.Values.OfType<IDisposable>())
            {
                try
                {
                    fixture.Dispose();
                }
                catch (Exception ex)
                {
                    if (!messageBus.QueueMessage(new ErrorMessage(ex.Unwrap())))
                        cancellationTokenSource.Cancel();
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Runs a single test for a given test method.
        /// </summary>
        /// <param name="messageSink">The message sink 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>
        /// <param name="executionTime">The time spent executing the tests.</param>
        protected void RunTestWithArguments(IMessageSink messageSink,
                                            Type classUnderTest,
                                            object[] constructorArguments,
                                            MethodInfo methodUnderTest,
                                            object[] testMethodArguments,
                                            string displayName,
                                            List<BeforeAfterTestAttribute> beforeAfterAttributes,
                                            ExceptionAggregator parentAggregator,
                                            CancellationTokenSource cancellationTokenSource,
                                            ref decimal executionTime)
        {
            var aggregator = new ExceptionAggregator(parentAggregator);

            if (!messageSink.OnMessage(new TestStarting(this, displayName)))
                cancellationTokenSource.Cancel();
            else
            {
                if (!String.IsNullOrEmpty(SkipReason))
                {
                    if (!messageSink.OnMessage(new TestSkipped(this, displayName, SkipReason)))
                        cancellationTokenSource.Cancel();
                }
                else
                {
                    var beforeAttributesRun = new Stack<BeforeAfterTestAttribute>();
                    var stopwatch = Stopwatch.StartNew();

                    if (!aggregator.HasExceptions)
                        aggregator.Run(() =>
                        {
                            object testClass = null;

                            if (!methodUnderTest.IsStatic)
                            {
                                if (!messageSink.OnMessage(new TestClassConstructionStarting(this, displayName)))
                                    cancellationTokenSource.Cancel();

                                try
                                {
                                    if (!cancellationTokenSource.IsCancellationRequested)
                                        testClass = Activator.CreateInstance(classUnderTest, constructorArguments);
                                }
                                finally
                                {
                                    if (!messageSink.OnMessage(new TestClassConstructionFinished(this, displayName)))
                                        cancellationTokenSource.Cancel();
                                }
                            }

                            if (!cancellationTokenSource.IsCancellationRequested)
                            {
                                aggregator.Run(() =>
                                {
                                    foreach (var beforeAfterAttribute in beforeAfterAttributes)
                                    {
                                        var attributeName = beforeAfterAttribute.GetType().Name;
                                        if (!messageSink.OnMessage(new BeforeTestStarting(this, displayName, attributeName)))
                                            cancellationTokenSource.Cancel();
                                        else
                                        {
                                            try
                                            {
                                                beforeAfterAttribute.Before(methodUnderTest);
                                                beforeAttributesRun.Push(beforeAfterAttribute);
                                            }
                                            finally
                                            {
                                                if (!messageSink.OnMessage(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);

                                            aggregator.Run(() =>
                                            {
                                                var result = methodUnderTest.Invoke(testClass, ConvertArguments(testMethodArguments ?? EmptyArray, parameterTypes));
                                                var task = result as Task;
                                                if (task != null)
                                                    task.GetAwaiter().GetResult();
                                                else
                                                {
                                                    var ex = asyncSyncContext.WaitForCompletion();
                                                    if (ex != null)
                                                        aggregator.Add(ex);
                                                }
                                            });
                                        }
                                        finally
                                        {
                                            SetSynchronizationContext(oldSyncContext);
                                        }
                                    }
                                });

                                foreach (var beforeAfterAttribute in beforeAttributesRun)
                                {
                                    var attributeName = beforeAfterAttribute.GetType().Name;
                                    if (!messageSink.OnMessage(new AfterTestStarting(this, displayName, attributeName)))
                                        cancellationTokenSource.Cancel();

                                    aggregator.Run(() => beforeAfterAttribute.After(methodUnderTest));

                                    if (!messageSink.OnMessage(new AfterTestFinished(this, displayName, attributeName)))
                                        cancellationTokenSource.Cancel();
                                }
                            }

                            aggregator.Run(() =>
                            {
                                IDisposable disposable = testClass as IDisposable;
                                if (disposable != null)
                                {
                                    if (!messageSink.OnMessage(new TestClassDisposeStarting(this, displayName)))
                                        cancellationTokenSource.Cancel();

                                    try
                                    {
                                        disposable.Dispose();
                                    }
                                    finally
                                    {
                                        if (!messageSink.OnMessage(new TestClassDisposeFinished(this, displayName)))
                                            cancellationTokenSource.Cancel();
                                    }
                                }
                            });
                        });

                    stopwatch.Stop();

                    if (!cancellationTokenSource.IsCancellationRequested)
                    {
                        executionTime = (decimal)stopwatch.Elapsed.TotalSeconds;

                        var exception = aggregator.ToException();
                        var testResult = exception == null ? (TestResultMessage)new TestPassed(this, displayName, executionTime) : new TestFailed(this, displayName, executionTime, exception);
                        if (!messageSink.OnMessage(testResult))
                            cancellationTokenSource.Cancel();
                    }
                }
            }

            if (!messageSink.OnMessage(new TestFinished(this, displayName, executionTime)))
                cancellationTokenSource.Cancel();
        }
예제 #4
0
        /// <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);
        }
예제 #5
0
        private static void RunTestClass(IMessageBus messageBus,
                                         ITestCollection collection,
                                         Dictionary <Type, object> collectionFixtureMappings,
                                         IReflectionTypeInfo testClass,
                                         IEnumerable <XunitTestCase> testCases,
                                         ITestCaseOrderer orderer,
                                         RunSummary classSummary,
                                         ExceptionAggregator aggregator,
                                         CancellationTokenSource cancellationTokenSource)
        {
            var testClassType        = testClass.Type;
            var fixtureMappings      = new Dictionary <Type, object>();
            var constructorArguments = new List <object>();

            var ordererAttribute = testClass.GetCustomAttributes(typeof(TestCaseOrdererAttribute)).SingleOrDefault();

            if (ordererAttribute != null)
            {
                orderer = GetTestCaseOrderer(ordererAttribute);
            }

            if (testClassType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollectionFixture <>)))
            {
                aggregator.Add(new TestClassException("A test class may not be decorated with ICollectionFixture<> (decorate the test collection class instead)."));
            }

            foreach (var interfaceType in testClassType.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IClassFixture <>)))
            {
                CreateFixture(interfaceType, aggregator, fixtureMappings);
            }

            if (collection.CollectionDefinition != null)
            {
                var declarationType = ((IReflectionTypeInfo)collection.CollectionDefinition).Type;
                foreach (var interfaceType in declarationType.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IClassFixture <>)))
                {
                    CreateFixture(interfaceType, aggregator, fixtureMappings);
                }
            }

            var isStaticClass = testClassType.IsAbstract && testClassType.IsSealed;

            if (!isStaticClass)
            {
                var ctors = testClassType.GetConstructors();
                if (ctors.Length != 1)
                {
                    aggregator.Add(new TestClassException("A test class may only define a single public constructor."));
                }
                else
                {
                    var ctor            = ctors.Single();
                    var unusedArguments = new List <string>();

                    foreach (var paramInfo in ctor.GetParameters())
                    {
                        object fixture;

                        if (fixtureMappings.TryGetValue(paramInfo.ParameterType, out fixture) || collectionFixtureMappings.TryGetValue(paramInfo.ParameterType, out fixture))
                        {
                            constructorArguments.Add(fixture);
                        }
                        else
                        {
                            unusedArguments.Add(String.Format("{0} {1}", paramInfo.ParameterType.Name, paramInfo.Name));
                        }
                    }

                    if (unusedArguments.Count > 0)
                    {
                        aggregator.Add(new TestClassException("The following constructor arguments did not have matching fixture data: " + String.Join(", ", unusedArguments)));
                    }
                }
            }

            var orderedTestCases = orderer.OrderTestCases(testCases);
            var methodGroups     = orderedTestCases.GroupBy(tc => tc.Method);

            foreach (var method in methodGroups)
            {
                if (!messageBus.QueueMessage(new TestMethodStarting(collection, testClass.Name, method.Key.Name)))
                {
                    cancellationTokenSource.Cancel();
                }
                else
                {
                    RunTestMethod(messageBus, constructorArguments.ToArray(), method, classSummary, aggregator, cancellationTokenSource);
                }

                if (!messageBus.QueueMessage(new TestMethodFinished(collection, testClass.Name, method.Key.Name)))
                {
                    cancellationTokenSource.Cancel();
                }

                if (cancellationTokenSource.IsCancellationRequested)
                {
                    break;
                }
            }

            foreach (var fixture in fixtureMappings.Values.OfType <IDisposable>())
            {
                try
                {
                    fixture.Dispose();
                }
                catch (Exception ex)
                {
                    if (!messageBus.QueueMessage(new ErrorMessage(ex.Unwrap())))
                    {
                        cancellationTokenSource.Cancel();
                    }
                }
            }
        }
예제 #6
0
        private static bool RunTestClass(IMessageSink messageSink, IGrouping<ITypeInfo, XunitTestCase> group, RunSummary classSummary)
        {
            bool cancelled = false;
            var aggregator = new ExceptionAggregator();

            Type testClassType = ((IReflectionTypeInfo)group.Key).Type;
            Dictionary<Type, object> fixtureMappings = new Dictionary<Type, object>();
            List<object> constructorArguments = new List<object>();

            // TODO: Read class fixtures from test collection
            foreach (var iface in testClassType.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IClassFixture<>)))
            {
                Type fixtureType = iface.GetGenericArguments().Single();
                object fixture = null;
                aggregator.Run(() => fixture = Activator.CreateInstance(fixtureType));
                fixtureMappings.Add(fixtureType, fixture);
            }

            var ctors = testClassType.GetConstructors();
            if (ctors.Length != 1)
            {
                aggregator.Add(new TestClassException("A test class may only define a single public constructor."));
            }
            else
            {
                var ctor = ctors.Single();
                List<string> unusedArguments = new List<string>();

                foreach (var paramInfo in ctor.GetParameters())
                {
                    object fixture;

                    if (fixtureMappings.TryGetValue(paramInfo.ParameterType, out fixture))
                        constructorArguments.Add(fixture);
                    else
                        unusedArguments.Add(paramInfo.ParameterType.Name + " " + paramInfo.Name);
                }

                if (unusedArguments.Count > 0)
                    aggregator.Add(new TestClassException("The following constructor arguments did not have matching fixture data: " + String.Join(", ", unusedArguments)));
            }

            var methodGroups = group.GroupBy(tc => tc.Method);

            foreach (var method in methodGroups)
            {
                if (!messageSink.OnMessage(new TestMethodStarting { ClassName = group.Key.Name, MethodName = method.Key.Name }))
                    cancelled = true;
                else
                    cancelled = RunTestMethod(messageSink, constructorArguments.ToArray(), method, classSummary, aggregator);

                if (!messageSink.OnMessage(new TestMethodFinished { ClassName = group.Key.Name, MethodName = method.Key.Name }))
                    cancelled = true;

                if (cancelled)
                    break;
            }

            foreach (var fixture in fixtureMappings.Values.OfType<IDisposable>())
            {
                try
                {
                    fixture.Dispose();
                }
                catch (Exception ex)
                {
                    if (!messageSink.OnMessage(new ErrorMessage(ex.Unwrap())))
                        cancelled = true;
                }
            }

            return cancelled;
        }
예제 #7
0
        /// <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;
        }