Beispiel #1
0
    /// <summary>
    /// Creates the test class (if necessary), and invokes the test method.
    /// </summary>
    /// <param name="ctxt">The invoker context</param>
    /// <returns>Returns the time (in seconds) spent creating the test class, running
    /// the test, and disposing of the test class.</returns>
    protected async ValueTask <decimal> RunAsync(TContext ctxt)
    {
        await ctxt.InitializeAsync();

        try
        {
            return(await ctxt.Aggregator.RunAsync(async() =>
            {
                if (ctxt.CancellationTokenSource.IsCancellationRequested)
                {
                    return 0m;
                }

                SetTestContext(ctxt, TestEngineStatus.Initializing);

                object?testClassInstance = null;
                var elapsedTime = ExecutionTimer.Measure(() => { testClassInstance = CreateTestClass(ctxt); });

                var asyncDisposable = testClassInstance as IAsyncDisposable;
                var disposable = testClassInstance as IDisposable;

                var testAssemblyUniqueID = ctxt.Test.TestCase.TestCollection.TestAssembly.UniqueID;
                var testCollectionUniqueID = ctxt.Test.TestCase.TestCollection.UniqueID;
                var testClassUniqueID = ctxt.Test.TestCase.TestClass?.UniqueID;
                var testMethodUniqueID = ctxt.Test.TestCase.TestMethod?.UniqueID;
                var testCaseUniqueID = ctxt.Test.TestCase.UniqueID;
                var testUniqueID = ctxt.Test.UniqueID;

                try
                {
                    if (testClassInstance is IAsyncLifetime asyncLifetime)
                    {
                        elapsedTime += await ExecutionTimer.MeasureAsync(asyncLifetime.InitializeAsync);
                    }

                    try
                    {
                        if (!ctxt.CancellationTokenSource.IsCancellationRequested)
                        {
                            elapsedTime += await ExecutionTimer.MeasureAsync(() => BeforeTestMethodInvokedAsync(ctxt));

                            SetTestContext(ctxt, TestEngineStatus.Running);

                            if (!ctxt.CancellationTokenSource.IsCancellationRequested && !ctxt.Aggregator.HasExceptions)
                            {
                                await InvokeTestMethodAsync(ctxt, testClassInstance);
                            }

                            SetTestContext(ctxt, TestEngineStatus.CleaningUp, TestState.FromException((decimal)elapsedTime.TotalSeconds, ctxt.Aggregator.ToException()));

                            elapsedTime += await ExecutionTimer.MeasureAsync(() => AfterTestMethodInvokedAsync(ctxt));
                        }
                    }
                    finally
                    {
                        if (asyncDisposable != null || disposable != null)
                        {
                            var testClassDisposeStarting = new _TestClassDisposeStarting
                            {
                                AssemblyUniqueID = testAssemblyUniqueID,
                                TestCaseUniqueID = testCaseUniqueID,
                                TestClassUniqueID = testClassUniqueID,
                                TestCollectionUniqueID = testCollectionUniqueID,
                                TestMethodUniqueID = testMethodUniqueID,
                                TestUniqueID = testUniqueID
                            };

                            if (!ctxt.MessageBus.QueueMessage(testClassDisposeStarting))
                            {
                                ctxt.CancellationTokenSource.Cancel();
                            }
                        }

                        if (asyncDisposable != null)
                        {
                            elapsedTime += await ExecutionTimer.MeasureAsync(() => ctxt.Aggregator.RunAsync(asyncDisposable.DisposeAsync));
                        }
                    }
                }
                finally
                {
                    if (disposable != null)
                    {
                        elapsedTime += ExecutionTimer.Measure(() => ctxt.Aggregator.Run(disposable.Dispose));
                    }

                    if (asyncDisposable != null || disposable != null)
                    {
                        var testClassDisposeFinished = new _TestClassDisposeFinished
                        {
                            AssemblyUniqueID = testAssemblyUniqueID,
                            TestCaseUniqueID = testCaseUniqueID,
                            TestClassUniqueID = testClassUniqueID,
                            TestCollectionUniqueID = testCollectionUniqueID,
                            TestMethodUniqueID = testMethodUniqueID,
                            TestUniqueID = testUniqueID
                        };

                        if (!ctxt.MessageBus.QueueMessage(testClassDisposeFinished))
                        {
                            ctxt.CancellationTokenSource.Cancel();
                        }
                    }
                }

                return (decimal)elapsedTime.TotalSeconds;
            }, 0m));
        }
        finally
        {
            await ctxt.DisposeAsync();
        }
    }
Beispiel #2
0
    /// <summary>
    /// Invokes the test method on the given test class instance. This method sets up support for "async void"
    /// test methods, ensures that the test method has the correct number of arguments, then calls <see cref="CallTestMethod"/>
    /// to do the actual method invocation. It ensure that any async test method is fully completed before returning, and
    /// returns the measured clock time that the invocation took. This method should NEVER throw; any exceptions should be
    /// placed into the aggregator in <paramref name="ctxt"/>.
    /// </summary>
    /// <param name="ctxt">The context that describes the current test</param>
    /// <param name="testClassInstance">The test class instance</param>
    /// <returns>Returns the amount of time the test took to run, in seconds</returns>
    protected virtual async ValueTask <decimal> InvokeTestMethodAsync(
        TContext ctxt,
        object?testClassInstance)
    {
        var oldSyncContext   = default(SynchronizationContext);
        var asyncSyncContext = default(AsyncTestSyncContext);

        try
        {
            if (AsyncUtility.IsAsyncVoid(ctxt.TestMethod))
            {
                oldSyncContext   = SynchronizationContext.Current;
                asyncSyncContext = new AsyncTestSyncContext(oldSyncContext);
                SetSynchronizationContext(asyncSyncContext);
            }

            var elapsed = await ExecutionTimer.MeasureAsync(
                () => ctxt.Aggregator.RunAsync(
                    async() =>
            {
                var parameterCount = ctxt.TestMethod.GetParameters().Length;
                var valueCount     = ctxt.TestMethodArguments == null ? 0 : ctxt.TestMethodArguments.Length;
                if (parameterCount != valueCount)
                {
                    ctxt.Aggregator.Add(
                        new InvalidOperationException(
                            $"The test method expected {parameterCount} parameter value{(parameterCount == 1 ? "" : "s")}, but {valueCount} parameter value{(valueCount == 1 ? "" : "s")} {(valueCount == 1 ? "was" : "were")} provided."
                            )
                        );
                }
                else
                {
                    var result    = CallTestMethod(ctxt, testClassInstance);
                    var valueTask = AsyncUtility.TryConvertToValueTask(result);
                    if (valueTask.HasValue)
                    {
                        await valueTask.Value;
                    }
                    else if (asyncSyncContext != null)
                    {
                        var ex = await asyncSyncContext.WaitForCompletionAsync();
                        if (ex != null)
                        {
                            ctxt.Aggregator.Add(ex);
                        }
                    }
                }
            }
                    )
                );

            return((decimal)elapsed.TotalSeconds);
        }
        finally
        {
            if (asyncSyncContext != null)
            {
                SetSynchronizationContext(oldSyncContext);
            }
        }
    }