/// <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(); } }