From e98da917de25bb2b17942c687338905b82e02245 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sun, 23 Feb 2025 16:54:20 +0000 Subject: [PATCH 01/17] Fix AsyncLocal flowing between BeforeTestSession > Test --- .../Framework/TUnitServiceProvider.cs | 17 +++--- TUnit.Engine/Framework/TUnitTestFramework.cs | 42 ++++++++++++- TUnit.Engine/Services/HooksCollector.cs | 47 +++++++++------ TUnit.Engine/Services/TUnitTestDiscoverer.cs | 59 ++----------------- TUnit.Engine/Services/TestsFinder.cs | 4 +- TUnit.TestProject/Bugs/1914/Tests.cs | 54 +++++++++++++++++ 6 files changed, 138 insertions(+), 85 deletions(-) create mode 100644 TUnit.TestProject/Bugs/1914/Tests.cs diff --git a/TUnit.Engine/Framework/TUnitServiceProvider.cs b/TUnit.Engine/Framework/TUnitServiceProvider.cs index 6b5b8ec5e2..8cb33beaed 100644 --- a/TUnit.Engine/Framework/TUnitServiceProvider.cs +++ b/TUnit.Engine/Framework/TUnitServiceProvider.cs @@ -25,6 +25,7 @@ internal class TUnitServiceProvider : IServiceProvider, IAsyncDisposable public TUnitFrameworkLogger Logger { get; } public TUnitMessageBus TUnitMessageBus { get; } + public HooksCollector HooksCollector { get; set; } public TUnitInitializer Initializer { get; } public StandardOutConsoleInterceptor StandardOutConsoleInterceptor { get; } public StandardErrorConsoleInterceptor StandardErrorConsoleInterceptor { get; } @@ -70,7 +71,7 @@ public TUnitServiceProvider(IExtension extension, var instanceTracker = Register(new InstanceTracker()); - var hooksCollector = Register(new HooksCollector(context.Request.Session.SessionUid.Value)); + HooksCollector = Register(new HooksCollector(context.Request.Session.SessionUid.Value)); var dependencyCollector = new DependencyCollector(); @@ -80,17 +81,17 @@ public TUnitServiceProvider(IExtension extension, TestGrouper = Register(new TestGrouper()); - AssemblyHookOrchestrator = Register(new AssemblyHookOrchestrator(instanceTracker, hooksCollector)); + AssemblyHookOrchestrator = Register(new AssemblyHookOrchestrator(instanceTracker, HooksCollector)); - TestDiscoveryHookOrchestrator = Register(new TestDiscoveryHookOrchestrator(hooksCollector, stringFilter)); - TestSessionHookOrchestrator = Register(new TestSessionHookOrchestrator(hooksCollector, AssemblyHookOrchestrator, stringFilter)); + TestDiscoveryHookOrchestrator = Register(new TestDiscoveryHookOrchestrator(HooksCollector, stringFilter)); + TestSessionHookOrchestrator = Register(new TestSessionHookOrchestrator(HooksCollector, AssemblyHookOrchestrator, stringFilter)); - var classHookOrchestrator = Register(new ClassHookOrchestrator(instanceTracker, hooksCollector)); + var classHookOrchestrator = Register(new ClassHookOrchestrator(instanceTracker, HooksCollector)); - var testHookOrchestrator = Register(new TestHookOrchestrator(hooksCollector)); + var testHookOrchestrator = Register(new TestHookOrchestrator(HooksCollector)); var testRegistrar = Register(new TestRegistrar(instanceTracker, AssemblyHookOrchestrator, classHookOrchestrator)); - TestDiscoverer = Register(new TUnitTestDiscoverer(hooksCollector, testsConstructor, testFilterService, TestGrouper, testRegistrar, TestDiscoveryHookOrchestrator, TUnitMessageBus, Logger, extension)); + TestDiscoverer = Register(new TUnitTestDiscoverer(testsConstructor, testFilterService, TestGrouper, testRegistrar, TUnitMessageBus, Logger, extension)); TestFinder = Register(new TestsFinder(TestDiscoverer)); Register(TestFinder); @@ -109,7 +110,7 @@ public TUnitServiceProvider(IExtension extension, OnEndExecutor = Register(new OnEndExecutor(CommandLineOptions, Logger)); } - + public Disposer Disposer { get; } public async ValueTask DisposeAsync() diff --git a/TUnit.Engine/Framework/TUnitTestFramework.cs b/TUnit.Engine/Framework/TUnitTestFramework.cs index a396542372..bb79117f85 100644 --- a/TUnit.Engine/Framework/TUnitTestFramework.cs +++ b/TUnit.Engine/Framework/TUnitTestFramework.cs @@ -81,8 +81,48 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) { serviceProvider.EngineCancellationToken.Initialise(context.CancellationToken); + var beforeDiscoveryHooks = serviceProvider.TestDiscoveryHookOrchestrator.CollectBeforeHooks(); + var beforeContext = serviceProvider.TestDiscoveryHookOrchestrator.GetBeforeContext(); + + foreach (var beforeDiscoveryHook in beforeDiscoveryHooks) + { + if (beforeDiscoveryHook.IsSynchronous) + { + await logger.LogDebugAsync("Executing synchronous [Before(TestDiscovery)] hook"); + + beforeDiscoveryHook.Execute(beforeContext, CancellationToken.None); + } + else + { + await logger.LogDebugAsync("Executing asynchronous [Before(TestDiscovery)] hook"); + + await beforeDiscoveryHook.ExecuteAsync(beforeContext, CancellationToken.None); + } + } + + var allDiscoveredTests = serviceProvider.TestDiscoverer.GetTests(serviceProvider.EngineCancellationToken.Token); + + var afterDiscoveryHooks = serviceProvider.TestDiscoveryHookOrchestrator.CollectAfterHooks(); + var afterContext = serviceProvider.TestDiscoveryHookOrchestrator.GetAfterContext(allDiscoveredTests); + + foreach (var afterDiscoveryHook in afterDiscoveryHooks) + { + if (afterDiscoveryHook.IsSynchronous) + { + await logger.LogDebugAsync("Executing asynchronous [After(TestDiscovery)] hook"); + + afterDiscoveryHook.Execute(afterContext, CancellationToken.None); + } + else + { + await logger.LogDebugAsync("Executing asynchronous [After(TestDiscovery)] hook"); + + await afterDiscoveryHook.ExecuteAsync(afterContext, CancellationToken.None); + } + } + var filteredTests = await serviceProvider.TestDiscoverer - .FilterTests(context, stringFilter, serviceProvider.EngineCancellationToken.Token); + .FilterTests(context, serviceProvider.EngineCancellationToken.Token); switch (context.Request) { diff --git a/TUnit.Engine/Services/HooksCollector.cs b/TUnit.Engine/Services/HooksCollector.cs index 04f99eb2b2..0c8f8eeee7 100644 --- a/TUnit.Engine/Services/HooksCollector.cs +++ b/TUnit.Engine/Services/HooksCollector.cs @@ -5,7 +5,7 @@ namespace TUnit.Engine.Services; -internal class HooksCollector(string sessionId) +internal class HooksCollector { internal readonly List> BeforeTestDiscoveryHooks = []; internal readonly List> BeforeTestSessionHooks = []; @@ -26,45 +26,54 @@ internal class HooksCollector(string sessionId) internal readonly List> AfterEveryAssemblyHooks = []; internal readonly List> AfterEveryClassHooks = []; internal readonly List> AfterEveryTestHooks = []; + private readonly string _sessionId; - public void CollectDiscoveryHooks() + public HooksCollector(string sessionId) + { + _sessionId = sessionId; + + CollectDiscoveryHooks(); + CollectHooks(); + } + + private void CollectDiscoveryHooks() { foreach (var hookSource in Sources.TestDiscoveryHookSources) { - foreach (var beforeHook in hookSource.CollectBeforeTestDiscoveryHooks(sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeTestDiscoveryHooks(_sessionId)) { BeforeTestDiscoveryHooks.Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterTestDiscoveryHooks(sessionId)) + foreach (var afterHook in hookSource.CollectAfterTestDiscoveryHooks(_sessionId)) { AfterTestDiscoveryHooks.Add(afterHook); } } } - public void CollectHooks() + private void CollectHooks() { foreach (var hookSource in Sources.TestHookSources) { - foreach (var beforeHook in hookSource.CollectBeforeTestHooks(sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeTestHooks(_sessionId)) { var beforeList = BeforeTestHooks.GetOrAdd(beforeHook.ClassType, _ => []); beforeList.Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterTestHooks(sessionId)) + foreach (var afterHook in hookSource.CollectAfterTestHooks(_sessionId)) { var afterList = AfterTestHooks.GetOrAdd(afterHook.ClassType, _ => []); afterList.Add(afterHook); } - foreach (var beforeHook in hookSource.CollectBeforeEveryTestHooks(sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeEveryTestHooks(_sessionId)) { BeforeEveryTestHooks.Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterEveryTestHooks(sessionId)) + foreach (var afterHook in hookSource.CollectAfterEveryTestHooks(_sessionId)) { AfterEveryTestHooks.Add(afterHook); } @@ -72,24 +81,24 @@ public void CollectHooks() foreach (var hookSource in Sources.ClassHookSources) { - foreach (var beforeHook in hookSource.CollectBeforeClassHooks(sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeClassHooks(_sessionId)) { var beforeList = BeforeClassHooks.GetOrAdd(beforeHook.ClassType, _ => []); beforeList.Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterClassHooks(sessionId)) + foreach (var afterHook in hookSource.CollectAfterClassHooks(_sessionId)) { var afterList = AfterClassHooks.GetOrAdd(afterHook.ClassType, _ => []); afterList.Add(afterHook); } - foreach (var beforeHook in hookSource.CollectBeforeEveryClassHooks(sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeEveryClassHooks(_sessionId)) { BeforeEveryClassHooks.Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterEveryClassHooks(sessionId)) + foreach (var afterHook in hookSource.CollectAfterEveryClassHooks(_sessionId)) { AfterEveryClassHooks.Add(afterHook); } @@ -97,22 +106,22 @@ public void CollectHooks() foreach (var hookSource in Sources.AssemblyHookSources) { - foreach (var beforeHook in hookSource.CollectBeforeAssemblyHooks(sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeAssemblyHooks(_sessionId)) { BeforeAssemblyHooks.GetOrAdd(beforeHook.Assembly, _ => []).Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterAssemblyHooks(sessionId)) + foreach (var afterHook in hookSource.CollectAfterAssemblyHooks(_sessionId)) { AfterAssemblyHooks.GetOrAdd(afterHook.Assembly, _ => []).Add(afterHook); } - foreach (var beforeHook in hookSource.CollectBeforeEveryAssemblyHooks(sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeEveryAssemblyHooks(_sessionId)) { BeforeEveryAssemblyHooks.Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterEveryAssemblyHooks(sessionId)) + foreach (var afterHook in hookSource.CollectAfterEveryAssemblyHooks(_sessionId)) { AfterEveryAssemblyHooks.Add(afterHook); } @@ -120,12 +129,12 @@ public void CollectHooks() foreach (var hookSource in Sources.TestSessionHookSources) { - foreach (var beforeHook in hookSource.CollectBeforeTestSessionHooks(sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeTestSessionHooks(_sessionId)) { BeforeTestSessionHooks.Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterTestSessionHooks(sessionId)) + foreach (var afterHook in hookSource.CollectAfterTestSessionHooks(_sessionId)) { AfterTestSessionHooks.Add(afterHook); } diff --git a/TUnit.Engine/Services/TUnitTestDiscoverer.cs b/TUnit.Engine/Services/TUnitTestDiscoverer.cs index c51a9aa353..1b57693db5 100644 --- a/TUnit.Engine/Services/TUnitTestDiscoverer.cs +++ b/TUnit.Engine/Services/TUnitTestDiscoverer.cs @@ -4,35 +4,32 @@ using Microsoft.Testing.Platform.Requests; using TUnit.Core; using TUnit.Core.Logging; -using TUnit.Engine.Hooks; using TUnit.Engine.Logging; using TUnit.Engine.Models; namespace TUnit.Engine.Services; internal class TUnitTestDiscoverer( - HooksCollector hooksCollector, TestsConstructor testsConstructor, TestFilterService testFilterService, TestGrouper testGrouper, TestRegistrar testRegistrar, - TestDiscoveryHookOrchestrator testDiscoveryHookOrchestrator, ITUnitMessageBus tUnitMessageBus, TUnitFrameworkLogger logger, IExtension extension) : IDataProducer { private IReadOnlyCollection? _cachedTests; - public IReadOnlyCollection GetCachedTests() + public IReadOnlyCollection GetTests(CancellationToken cancellationToken = default) { - return _cachedTests!; + return _cachedTests ??= testsConstructor.GetTests(cancellationToken); } - public async Task FilterTests(ExecuteRequestContext context, string? stringTestFilter, CancellationToken cancellationToken) + public async Task FilterTests(ExecuteRequestContext context, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - var allDiscoveredTests = _cachedTests ??= await DiscoverTests(cancellationToken); + var allDiscoveredTests = _cachedTests ??= GetTests(cancellationToken); var executionRequest = context.Request as TestExecutionRequest; @@ -60,54 +57,6 @@ await testRegistrar.RegisterInstance(discoveredTest: test, } } - private async Task> DiscoverTests(CancellationToken cancellationToken) - { - hooksCollector.CollectDiscoveryHooks(); - - var beforeDiscoveryHooks = testDiscoveryHookOrchestrator.CollectBeforeHooks(); - var beforeContext = testDiscoveryHookOrchestrator.GetBeforeContext(); - - foreach (var beforeDiscoveryHook in beforeDiscoveryHooks) - { - if (beforeDiscoveryHook.IsSynchronous) - { - await logger.LogDebugAsync("Executing synchronous [Before(TestDiscovery)] hook"); - - beforeDiscoveryHook.Execute(beforeContext, CancellationToken.None); - } - else - { - await logger.LogDebugAsync("Executing asynchronous [Before(TestDiscovery)] hook"); - - await beforeDiscoveryHook.ExecuteAsync(beforeContext, CancellationToken.None); - } - } - - var allDiscoveredTests = testsConstructor.GetTests(cancellationToken); - - var afterDiscoveryHooks = testDiscoveryHookOrchestrator.CollectAfterHooks(); - var afterContext = testDiscoveryHookOrchestrator.GetAfterContext(allDiscoveredTests); - - foreach (var afterDiscoveryHook in afterDiscoveryHooks) - { - if (afterDiscoveryHook.IsSynchronous) - { - await logger.LogDebugAsync("Executing asynchronous [After(TestDiscovery)] hook"); - - afterDiscoveryHook.Execute(afterContext, CancellationToken.None); - } - else - { - await logger.LogDebugAsync("Executing asynchronous [After(TestDiscovery)] hook"); - - await afterDiscoveryHook.ExecuteAsync(afterContext, CancellationToken.None); - } - } - hooksCollector.CollectHooks(); - - return allDiscoveredTests; - } - public Task IsEnabledAsync() { return extension.IsEnabledAsync(); diff --git a/TUnit.Engine/Services/TestsFinder.cs b/TUnit.Engine/Services/TestsFinder.cs index 054bdcfd9f..7b2e40f727 100644 --- a/TUnit.Engine/Services/TestsFinder.cs +++ b/TUnit.Engine/Services/TestsFinder.cs @@ -7,14 +7,14 @@ internal class TestsFinder(TUnitTestDiscoverer testDiscoverer) : ITestFinder { public IEnumerable GetTests(Type classType) { - return testDiscoverer.GetCachedTests() + return testDiscoverer.GetTests() .Where(x => x.TestDetails.TestClass.Type == classType) .Select(x => x.TestContext); } public TestContext[] GetTestsByNameAndParameters(string testName, IEnumerable methodParameterTypes, Type classType, IEnumerable classParameterTypes, IEnumerable classArguments) { - var testsWithoutMethodParameterTypesMatching = testDiscoverer.GetCachedTests().Where(x => + var testsWithoutMethodParameterTypesMatching = testDiscoverer.GetTests().Where(x => x.TestContext.TestDetails.TestName == testName && x.TestContext.TestDetails.TestClass.Type == classType && x.TestContext.TestDetails.TestClassParameterTypes.SequenceEqual(classParameterTypes) && diff --git a/TUnit.TestProject/Bugs/1914/Tests.cs b/TUnit.TestProject/Bugs/1914/Tests.cs new file mode 100644 index 0000000000..6d09a81908 --- /dev/null +++ b/TUnit.TestProject/Bugs/1914/Tests.cs @@ -0,0 +1,54 @@ +using TUnit.Assertions; +using TUnit.Assertions.Extensions; + +namespace TUnit.TestProject.Bugs._1914; + +public class Tests +{ + private static readonly AsyncLocal _0BeforeTestDiscoveryLocal = new(); + private static readonly AsyncLocal _1BeforeTestSessionLocal = new(); + private static readonly AsyncLocal _2BeforeAssemblyLocal = new(); + private static readonly AsyncLocal _3BeforeClassLocal = new(); + private static readonly AsyncLocal _4BeforeTestLocal = new(); + + [BeforeEvery(TestDiscovery)] + public static void BeforeTestDiscovery(BeforeTestDiscoveryContext context) + => _0BeforeTestDiscoveryLocal.Value = "BeforeTestDiscovery"; + + [BeforeEvery(TestSession)] + public static void BeforeTestSession(TestSessionContext context) + => _1BeforeTestSessionLocal.Value = "BeforeTestSession"; + + [BeforeEvery(Assembly)] + public static void BeforeAssembly(AssemblyHookContext context) + => _2BeforeAssemblyLocal.Value = "BeforeAssembly"; + + [BeforeEvery(Class)] + public static void BeforeClass(ClassHookContext context) + => _3BeforeClassLocal.Value = "BeforeClass"; + + [BeforeEvery(Test)] + public static void BeforeTest(TestContext context) + => _4BeforeTestLocal.Value = "BeforeTest"; + + + [Test] + [Arguments(1)] + [Arguments(2)] + [Arguments(3)] + [Arguments(4)] + [Arguments(5)] + [Arguments(6)] + [Arguments(7)] + [Arguments(8)] + public async Task TestAsyncLocal(int i) + { + using var _ = Assert.Multiple(); + + await Assert.That(_0BeforeTestDiscoveryLocal.Value).IsEqualTo("BeforeTestDiscovery"); + await Assert.That(_1BeforeTestSessionLocal.Value).IsEqualTo("BeforeTestSession"); + // await Assert.That(_2BeforeAssemblyLocal.Value).IsEqualTo("BeforeAssembly"); + // await Assert.That(_3BeforeClassLocal.Value).IsEqualTo("BeforeClass"); + await Assert.That(_4BeforeTestLocal.Value).IsEqualTo("BeforeTest"); + } +} \ No newline at end of file From c7d2d07821c05114127e2ac208566eadacfd3d26 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sun, 23 Feb 2025 19:25:04 +0000 Subject: [PATCH 02/17] WIP --- .../Framework/TUnitServiceProvider.cs | 2 +- TUnit.Engine/Services/SingleTestExecutor.cs | 99 +----------- TUnit.Engine/Services/TestsExecutor.cs | 153 +++++++++++++++++- TUnit.Engine/TestRegistrar.cs | 2 +- TUnit.TestProject/Bugs/1914/Tests.cs | 2 +- 5 files changed, 154 insertions(+), 104 deletions(-) diff --git a/TUnit.Engine/Framework/TUnitServiceProvider.cs b/TUnit.Engine/Framework/TUnitServiceProvider.cs index 8cb33beaed..c39223d48c 100644 --- a/TUnit.Engine/Framework/TUnitServiceProvider.cs +++ b/TUnit.Engine/Framework/TUnitServiceProvider.cs @@ -106,7 +106,7 @@ public TUnitServiceProvider(IExtension extension, var singleTestExecutor = Register(new SingleTestExecutor(extension, instanceTracker, testInvoker, parallelLimitProvider, AssemblyHookOrchestrator, classHookOrchestrator, TUnitMessageBus, Logger, EngineCancellationToken, testRegistrar)); - TestsExecutor = Register(new TestsExecutor(singleTestExecutor, Logger, CommandLineOptions, EngineCancellationToken)); + TestsExecutor = Register(new TestsExecutor(singleTestExecutor, Logger, CommandLineOptions, EngineCancellationToken, AssemblyHookOrchestrator, classHookOrchestrator)); OnEndExecutor = Register(new OnEndExecutor(CommandLineOptions, Logger)); } diff --git a/TUnit.Engine/Services/SingleTestExecutor.cs b/TUnit.Engine/Services/SingleTestExecutor.cs index c23777a1f0..5c01f2bd15 100644 --- a/TUnit.Engine/Services/SingleTestExecutor.cs +++ b/TUnit.Engine/Services/SingleTestExecutor.cs @@ -41,7 +41,8 @@ public Task ExecuteTestAsync(DiscoveredTest test, ITestExecutionFilter? filter, } } - private async Task ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter? filter, ExecuteRequestContext context, bool isStartedAsDependencyForAnotherTest) + private async Task ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionFilter? filter, + ExecuteRequestContext context, bool isStartedAsDependencyForAnotherTest) { var semaphore = WaitForParallelLimiter(test, isStartedAsDependencyForAnotherTest); @@ -91,96 +92,6 @@ private async Task ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionF throw new SkipTestException("The test session has been cancelled..."); } - // Ideally all these 'Set up' hooks would be refactored into inner/classes and/or methods, - // But users may want to set AsyncLocal values, and so the method must be a parent/ancestor of the method that starts the test! - // So actually refactoring these into other methods would mean they wouldn't be a parent/ancestor and would break async local! - var assemblyHooksTaskCompletionSource = assemblyHookOrchestrator.PreviouslyRunBeforeHooks.GetOrAdd(testContext.TestDetails.TestClass.Type.Assembly, - _ => new TaskCompletionSource(), out var assemblyHooksTaskPreviouslyExisted); - - if (assemblyHooksTaskPreviouslyExisted) - { - await assemblyHooksTaskCompletionSource.Task; - } - else - { - try - { - var beforeAssemblyHooks = - assemblyHookOrchestrator.CollectBeforeHooks(test.TestContext.TestDetails.TestClass.Type.Assembly); - var assemblyHookContext = - assemblyHookOrchestrator.GetContext(test.TestContext.TestDetails.TestClass.Type.Assembly); - - AssemblyHookContext.Current = assemblyHookContext; - - foreach (var beforeHook in beforeAssemblyHooks) - { - if (beforeHook.IsSynchronous) - { - await logger.LogDebugAsync("Executing synchronous [Before(Assembly)] hook"); - - beforeHook.Execute(assemblyHookContext, CancellationToken.None); - } - else - { - await logger.LogDebugAsync("Executing asynchronous [Before(Assembly)] hook"); - - await beforeHook.ExecuteAsync(assemblyHookContext, CancellationToken.None); - } - } - - AssemblyHookContext.Current = null; - assemblyHooksTaskCompletionSource.SetResult(false); - } - catch (Exception e) - { - assemblyHooksTaskCompletionSource.SetException(e); - throw; - } - } - - var classHooksTaskCompletionSource = classHookOrchestrator.PreviouslyRunBeforeHooks.GetOrAdd(testContext.TestDetails.TestClass.Type, - _ => new TaskCompletionSource(), out var classHooksTaskPreviouslyExisted); - - if (classHooksTaskPreviouslyExisted) - { - await classHooksTaskCompletionSource.Task; - } - else - { - try - { - var beforeClassHooks = - classHookOrchestrator.CollectBeforeHooks(test.TestContext.TestDetails.TestClass.Type); - var classHookContext = classHookOrchestrator.GetContext(test.TestContext.TestDetails.TestClass.Type); - - ClassHookContext.Current = classHookContext; - - foreach (var beforeHook in beforeClassHooks) - { - if (beforeHook.IsSynchronous) - { - await logger.LogDebugAsync("Executing synchronous [Before(Class)] hook"); - - beforeHook.Execute(classHookContext, CancellationToken.None); - } - else - { - await logger.LogDebugAsync("Executing asynchronous [Before(Class)] hook"); - - await beforeHook.ExecuteAsync(classHookContext, CancellationToken.None); - } - } - - ClassHookContext.Current = null; - classHooksTaskCompletionSource.SetResult(false); - } - catch (Exception e) - { - classHooksTaskCompletionSource.SetException(e); - throw; - } - } - TestContext.Current = testContext; foreach (var testStartEventsObject in testContext.GetTestStartEventObjects()) @@ -231,18 +142,18 @@ private async Task ExecuteTestInternalAsync(DiscoveredTest test, ITestExecutionF } } - private Task RegisterIfNotAlready(TestContext testContext) + private ValueTask RegisterIfNotAlready(TestContext testContext) { lock (Lock) { - // Could not be registered if it's triggered from a [DependsOn] + // Could not be registered if wasn't in the original filter and it's triggered from a [DependsOn] if (!testContext.IsRegistered) { return testRegistrar.RegisterInstance(testContext.InternalDiscoveredTest, _ => default); } - return Task.CompletedTask; + return default; } } diff --git a/TUnit.Engine/Services/TestsExecutor.cs b/TUnit.Engine/Services/TestsExecutor.cs index 6c367a0593..8751dae4ae 100644 --- a/TUnit.Engine/Services/TestsExecutor.cs +++ b/TUnit.Engine/Services/TestsExecutor.cs @@ -1,11 +1,14 @@ using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using EnumerableAsyncProcessor.Extensions; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions.TestFramework; using Microsoft.Testing.Platform.Requests; using Polyfills; using TUnit.Core; +using TUnit.Core.Logging; using TUnit.Engine.CommandLineProviders; +using TUnit.Engine.Hooks; using TUnit.Engine.Logging; using TUnit.Engine.Models; @@ -18,6 +21,9 @@ internal class TestsExecutor private readonly ICommandLineOptions _commandLineOptions; private readonly EngineCancellationToken _engineCancellationToken; + private readonly AssemblyHookOrchestrator _assemblyHookOrchestrator; + private readonly ClassHookOrchestrator _classHookOrchestrator; + private readonly Counter _executionCounter = new(); private readonly TaskCompletionSource _onFinished = new(); @@ -26,12 +32,14 @@ internal class TestsExecutor public TestsExecutor(SingleTestExecutor singleTestExecutor, TUnitFrameworkLogger logger, ICommandLineOptions commandLineOptions, - EngineCancellationToken engineCancellationToken) + EngineCancellationToken engineCancellationToken, AssemblyHookOrchestrator assemblyHookOrchestrator, ClassHookOrchestrator classHookOrchestrator) { _singleTestExecutor = singleTestExecutor; _logger = logger; _commandLineOptions = commandLineOptions; _engineCancellationToken = engineCancellationToken; + _assemblyHookOrchestrator = assemblyHookOrchestrator; + _classHookOrchestrator = classHookOrchestrator; _maximumParallelTests = GetParallelTestsLimit(); @@ -96,11 +104,18 @@ private async Task ProcessParallelGroups(ConcurrentDictionary tests) { + using var executionContext = ExecutionContext.Capture(); + await Task.Run(async delegate { - while (tests.TryDequeue(out var testInformation, out _)) + while (tests.TryDequeue(out var test, out _)) { - await ProcessTest(testInformation, filter, context, context.CancellationToken); + if (test.TestContext.SkipReason != null) + { + await OnBeforeStart(test.TestContext, executionContext!); + } + + await ProcessTest(test, filter, context, context.CancellationToken, executionContext!); } }); } @@ -111,29 +126,53 @@ private async Task ProcessParallelTests(IEnumerable queue, ITest await ProcessCollection(queue, filter, context); } + [SuppressMessage("ReSharper", "AccessToDisposedClosure")] private async Task ProcessCollection(IEnumerable queue, ITestExecutionFilter? filter, ExecuteRequestContext context) { #if NET + using var executionContext = ExecutionContext.Capture(); + await Parallel.ForEachAsync(queue, new ParallelOptions { MaxDegreeOfParallelism = _maximumParallelTests, CancellationToken = context.CancellationToken - }, (test, token) => ProcessTest(test, filter, context, token)); + }, async (test, token) => + { + if (test.TestContext.SkipReason != null) + { + await OnBeforeStart(test.TestContext, executionContext!); + } + + await ProcessTest(test, filter, context, token, executionContext!); + }); #else + using var executionContext = ExecutionContext.Capture(); + await queue - .ForEachAsync(test => ProcessTest(test, filter, context, context.CancellationToken)) + .ForEachAsync(async test => + { + if (test.TestContext.SkipReason != null) + { + await OnBeforeStart(test.TestContext, executionContext!); + } + + await ProcessTest(test, filter, context, context.CancellationToken, executionContext!); + }) .ProcessInParallel(_maximumParallelTests); #endif } #if NET private async ValueTask ProcessTest(DiscoveredTest test, - ITestExecutionFilter? filter, ExecuteRequestContext context, CancellationToken cancellationToken) + ITestExecutionFilter? filter, ExecuteRequestContext context, CancellationToken cancellationToken, + ExecutionContext executionContext) { try { + ExecutionContext.Restore(executionContext); + await Task.Run(() => _singleTestExecutor.ExecuteTestAsync(test, filter, context, false), cancellationToken); } catch @@ -146,7 +185,8 @@ private async ValueTask ProcessTest(DiscoveredTest test, } #else private async Task ProcessTest(DiscoveredTest test, - ITestExecutionFilter? filter, ExecuteRequestContext context, CancellationToken cancellationToken) + ITestExecutionFilter? filter, ExecuteRequestContext context, CancellationToken cancellationToken, + ExecutionContext executionContext) { try { @@ -162,6 +202,105 @@ private async Task ProcessTest(DiscoveredTest test, } #endif + private async Task OnBeforeStart(TestContext testContext, ExecutionContext executionContext) + { +#if NET + ExecutionContext.Restore(executionContext); + #endif + + // Ideally all these 'Set up' hooks would be refactored into inner/classes and/or methods, + // But users may want to set AsyncLocal values, and so the method must be a parent/ancestor of the method that starts the test! + // So actually refactoring these into other methods would mean they wouldn't be a parent/ancestor and would break async local! + var assemblyHooksTaskCompletionSource = _assemblyHookOrchestrator.PreviouslyRunBeforeHooks.GetOrAdd( + testContext.TestDetails.TestClass.Type.Assembly, _ => new TaskCompletionSource(), + out var assemblyHooksTaskPreviouslyExisted); + + if (assemblyHooksTaskPreviouslyExisted) + { + await assemblyHooksTaskCompletionSource.Task; + } + else + { + try + { + var beforeAssemblyHooks = + _assemblyHookOrchestrator.CollectBeforeHooks(testContext.TestDetails.TestClass.Type.Assembly); + var assemblyHookContext = + _assemblyHookOrchestrator.GetContext(testContext.TestDetails.TestClass.Type.Assembly); + + AssemblyHookContext.Current = assemblyHookContext; + + foreach (var beforeHook in beforeAssemblyHooks) + { + if (beforeHook.IsSynchronous) + { + await _logger.LogDebugAsync("Executing synchronous [Before(Assembly)] hook"); + + beforeHook.Execute(assemblyHookContext, CancellationToken.None); + } + else + { + await _logger.LogDebugAsync("Executing asynchronous [Before(Assembly)] hook"); + + await beforeHook.ExecuteAsync(assemblyHookContext, CancellationToken.None); + } + } + + AssemblyHookContext.Current = null; + assemblyHooksTaskCompletionSource.SetResult(false); + } + catch (Exception e) + { + assemblyHooksTaskCompletionSource.SetException(e); + throw; + } + } + + var classHooksTaskCompletionSource = _classHookOrchestrator.PreviouslyRunBeforeHooks.GetOrAdd( + testContext.TestDetails.TestClass.Type, _ => new TaskCompletionSource(), + out var classHooksTaskPreviouslyExisted); + + if (classHooksTaskPreviouslyExisted) + { + await classHooksTaskCompletionSource.Task; + } + else + { + try + { + var beforeClassHooks = + _classHookOrchestrator.CollectBeforeHooks(testContext.TestDetails.TestClass.Type); + var classHookContext = _classHookOrchestrator.GetContext(testContext.TestDetails.TestClass.Type); + + ClassHookContext.Current = classHookContext; + + foreach (var beforeHook in beforeClassHooks) + { + if (beforeHook.IsSynchronous) + { + await _logger.LogDebugAsync("Executing synchronous [Before(Class)] hook"); + + beforeHook.Execute(classHookContext, CancellationToken.None); + } + else + { + await _logger.LogDebugAsync("Executing asynchronous [Before(Class)] hook"); + + await beforeHook.ExecuteAsync(classHookContext, CancellationToken.None); + } + } + + ClassHookContext.Current = null; + classHooksTaskCompletionSource.SetResult(false); + } + catch (Exception e) + { + classHooksTaskCompletionSource.SetException(e); + throw; + } + } + } + private int GetParallelTestsLimit() { if (_commandLineOptions.TryGetOptionArgumentList(MaximumParallelTestsCommandProvider.MaximumParallelTests, diff --git a/TUnit.Engine/TestRegistrar.cs b/TUnit.Engine/TestRegistrar.cs index 94f0a44fc4..7c808406b0 100644 --- a/TUnit.Engine/TestRegistrar.cs +++ b/TUnit.Engine/TestRegistrar.cs @@ -7,7 +7,7 @@ namespace TUnit.Engine; internal class TestRegistrar(InstanceTracker instanceTracker, AssemblyHookOrchestrator assemblyHookOrchestrator, ClassHookOrchestrator classHookOrchestrator) { - internal async Task RegisterInstance(DiscoveredTest discoveredTest, Func onFailureToInitialize) + internal async ValueTask RegisterInstance(DiscoveredTest discoveredTest, Func onFailureToInitialize) { try { diff --git a/TUnit.TestProject/Bugs/1914/Tests.cs b/TUnit.TestProject/Bugs/1914/Tests.cs index 6d09a81908..e795371be0 100644 --- a/TUnit.TestProject/Bugs/1914/Tests.cs +++ b/TUnit.TestProject/Bugs/1914/Tests.cs @@ -47,7 +47,7 @@ public async Task TestAsyncLocal(int i) await Assert.That(_0BeforeTestDiscoveryLocal.Value).IsEqualTo("BeforeTestDiscovery"); await Assert.That(_1BeforeTestSessionLocal.Value).IsEqualTo("BeforeTestSession"); - // await Assert.That(_2BeforeAssemblyLocal.Value).IsEqualTo("BeforeAssembly"); + await Assert.That(_2BeforeAssemblyLocal.Value).IsEqualTo("BeforeAssembly"); // await Assert.That(_3BeforeClassLocal.Value).IsEqualTo("BeforeClass"); await Assert.That(_4BeforeTestLocal.Value).IsEqualTo("BeforeTest"); } From 5ae60008f0d6cdaf832d51ad40497b2b303cd032 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 26 Feb 2025 20:03:17 +0000 Subject: [PATCH 03/17] WIP --- TUnit.Core/Context.cs | 6 +++- TUnit.Engine/Services/TestsExecutor.cs | 49 +++++++++++--------------- TUnit.TestProject/Bugs/1914/Tests.cs | 9 +++-- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/TUnit.Core/Context.cs b/TUnit.Core/Context.cs index 16ae7734d0..bb068acb68 100644 --- a/TUnit.Core/Context.cs +++ b/TUnit.Core/Context.cs @@ -1,4 +1,5 @@ -using System.Diagnostics.CodeAnalysis; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Text; using TUnit.Core.Interfaces; using TUnit.Core.Logging; @@ -29,6 +30,9 @@ internal Context() { } + [EditorBrowsable(EditorBrowsableState.Never)] + public ExecutionContext? ExecutionContext { get; set; } + public string GetStandardOutput() { return _outputStringBuilder?.ToString().Trim() ?? string.Empty; diff --git a/TUnit.Engine/Services/TestsExecutor.cs b/TUnit.Engine/Services/TestsExecutor.cs index 8751dae4ae..6721cb2086 100644 --- a/TUnit.Engine/Services/TestsExecutor.cs +++ b/TUnit.Engine/Services/TestsExecutor.cs @@ -6,6 +6,7 @@ using Microsoft.Testing.Platform.Requests; using Polyfills; using TUnit.Core; +using TUnit.Core.Helpers; using TUnit.Core.Logging; using TUnit.Engine.CommandLineProviders; using TUnit.Engine.Hooks; @@ -104,18 +105,16 @@ private async Task ProcessParallelGroups(ConcurrentDictionary tests) { - using var executionContext = ExecutionContext.Capture(); - await Task.Run(async delegate { while (tests.TryDequeue(out var test, out _)) { - if (test.TestContext.SkipReason != null) + if (test.TestContext.SkipReason == null) { - await OnBeforeStart(test.TestContext, executionContext!); + await OnBeforeStart(test.TestContext); } - await ProcessTest(test, filter, context, context.CancellationToken, executionContext!); + await ProcessTest(test, filter, context, context.CancellationToken); } }); } @@ -132,33 +131,35 @@ private async Task ProcessCollection(IEnumerable queue, ExecuteRequestContext context) { #if NET - using var executionContext = ExecutionContext.Capture(); - await Parallel.ForEachAsync(queue, new ParallelOptions { MaxDegreeOfParallelism = _maximumParallelTests, CancellationToken = context.CancellationToken }, async (test, token) => { - if (test.TestContext.SkipReason != null) + if (test.TestContext.SkipReason == null) + { + await OnBeforeStart(test.TestContext); + } + + var assemblyHookContext = _assemblyHookOrchestrator.GetContext(test.TestDetails.TestClass.Type.Assembly); + if (assemblyHookContext.ExecutionContext != null) { - await OnBeforeStart(test.TestContext, executionContext!); + ExecutionContext.Restore(assemblyHookContext.ExecutionContext); } - await ProcessTest(test, filter, context, token, executionContext!); + await ProcessTest(test, filter, context, token); }); #else - using var executionContext = ExecutionContext.Capture(); - await queue .ForEachAsync(async test => { if (test.TestContext.SkipReason != null) { - await OnBeforeStart(test.TestContext, executionContext!); + await OnBeforeStart(test.TestContext!); } - await ProcessTest(test, filter, context, context.CancellationToken, executionContext!); + await ProcessTest(test, filter, context, context.CancellationToken); }) .ProcessInParallel(_maximumParallelTests); #endif @@ -166,13 +167,10 @@ await queue #if NET private async ValueTask ProcessTest(DiscoveredTest test, - ITestExecutionFilter? filter, ExecuteRequestContext context, CancellationToken cancellationToken, - ExecutionContext executionContext) + ITestExecutionFilter? filter, ExecuteRequestContext context, CancellationToken cancellationToken) { try { - ExecutionContext.Restore(executionContext); - await Task.Run(() => _singleTestExecutor.ExecuteTestAsync(test, filter, context, false), cancellationToken); } catch @@ -185,8 +183,7 @@ private async ValueTask ProcessTest(DiscoveredTest test, } #else private async Task ProcessTest(DiscoveredTest test, - ITestExecutionFilter? filter, ExecuteRequestContext context, CancellationToken cancellationToken, - ExecutionContext executionContext) + ITestExecutionFilter? filter, ExecuteRequestContext context, CancellationToken cancellationToken) { try { @@ -202,12 +199,8 @@ private async Task ProcessTest(DiscoveredTest test, } #endif - private async Task OnBeforeStart(TestContext testContext, ExecutionContext executionContext) + private async Task OnBeforeStart(TestContext testContext) { -#if NET - ExecutionContext.Restore(executionContext); - #endif - // Ideally all these 'Set up' hooks would be refactored into inner/classes and/or methods, // But users may want to set AsyncLocal values, and so the method must be a parent/ancestor of the method that starts the test! // So actually refactoring these into other methods would mean they wouldn't be a parent/ancestor and would break async local! @@ -223,10 +216,8 @@ private async Task OnBeforeStart(TestContext testContext, ExecutionContext execu { try { - var beforeAssemblyHooks = - _assemblyHookOrchestrator.CollectBeforeHooks(testContext.TestDetails.TestClass.Type.Assembly); - var assemblyHookContext = - _assemblyHookOrchestrator.GetContext(testContext.TestDetails.TestClass.Type.Assembly); + var beforeAssemblyHooks = _assemblyHookOrchestrator.CollectBeforeHooks(testContext.TestDetails.TestClass.Type.Assembly); + var assemblyHookContext =_assemblyHookOrchestrator.GetContext(testContext.TestDetails.TestClass.Type.Assembly); AssemblyHookContext.Current = assemblyHookContext; diff --git a/TUnit.TestProject/Bugs/1914/Tests.cs b/TUnit.TestProject/Bugs/1914/Tests.cs index e795371be0..80441817d1 100644 --- a/TUnit.TestProject/Bugs/1914/Tests.cs +++ b/TUnit.TestProject/Bugs/1914/Tests.cs @@ -20,8 +20,13 @@ public static void BeforeTestSession(TestSessionContext context) => _1BeforeTestSessionLocal.Value = "BeforeTestSession"; [BeforeEvery(Assembly)] - public static void BeforeAssembly(AssemblyHookContext context) - => _2BeforeAssemblyLocal.Value = "BeforeAssembly"; + public static async Task BeforeAssembly(AssemblyHookContext context) + { + await Task.CompletedTask; + _2BeforeAssemblyLocal.Value = "BeforeAssembly"; + context.ExecutionContext = ExecutionContext.Capture(); + Console.WriteLine(""); + } [BeforeEvery(Class)] public static void BeforeClass(ClassHookContext context) From a8519a811fb8d6fa0694e451cd231f69bdccff40 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 26 Feb 2025 21:16:14 +0000 Subject: [PATCH 04/17] FlowAsyncLocalValues --- TUnit.Core/Context.cs | 25 ++- .../Extensions/TestContextExtensions.cs | 19 +- TUnit.Engine/Services/TestsExecutor.cs | 164 ++++++++++-------- TUnit.TestProject/Bugs/1914/Tests.cs | 30 ++-- 4 files changed, 146 insertions(+), 92 deletions(-) diff --git a/TUnit.Core/Context.cs b/TUnit.Core/Context.cs index bb068acb68..010c96522b 100644 --- a/TUnit.Core/Context.cs +++ b/TUnit.Core/Context.cs @@ -6,7 +6,7 @@ namespace TUnit.Core; -public abstract class Context : IContext +public abstract class Context : IContext, IDisposable { public static Context Current => TestContext.Current as Context @@ -29,9 +29,18 @@ TestContext.Current as Context internal Context() { } - - [EditorBrowsable(EditorBrowsableState.Never)] - public ExecutionContext? ExecutionContext { get; set; } + + internal List ExecutionContexts { get; } = []; + + public void FlowAsyncLocalValues() + { + var executionContext = ExecutionContext.Capture(); + + if (executionContext != null) + { + ExecutionContexts.Add(executionContext); + } + } public string GetStandardOutput() { @@ -47,4 +56,12 @@ public TUnitLogger GetDefaultLogger() { return new DefaultLogger(); } + + public void Dispose() + { + foreach (var executionContext in ExecutionContexts) + { + executionContext.Dispose(); + } + } } \ No newline at end of file diff --git a/TUnit.Core/Extensions/TestContextExtensions.cs b/TUnit.Core/Extensions/TestContextExtensions.cs index c457d37f54..6683c154e8 100644 --- a/TUnit.Core/Extensions/TestContextExtensions.cs +++ b/TUnit.Core/Extensions/TestContextExtensions.cs @@ -80,9 +80,22 @@ internal static IEnumerable GetTestRetryEventObjects(th internal static IEnumerable GetTestEndEventObjects(this TestContext context) => GetPossibleEventObjects(context).OfType(); - internal static IEnumerable GetOnDisposeObjects(this TestContext context) => - Enumerable.Reverse(GetEventObjects(context)).Where(x => x is IDisposable or IAsyncDisposable).OfType(); - + internal static IEnumerable GetOnDisposeObjects(this TestContext context) + { + IEnumerable disposableObjects = + [ + ..context.TestDetails.Attributes, + context.InternalDiscoveredTest.ClassConstructor, + context.TestDetails.ClassInstance, + context.Events, + context + ]; + + return disposableObjects + .Where(x => x is IDisposable or IAsyncDisposable) + .OfType(); + } + internal static IEnumerable GetTestSkippedEventObjects(this TestContext context) => GetPossibleEventObjects(context).OfType(); diff --git a/TUnit.Engine/Services/TestsExecutor.cs b/TUnit.Engine/Services/TestsExecutor.cs index 6721cb2086..66fee7dc36 100644 --- a/TUnit.Engine/Services/TestsExecutor.cs +++ b/TUnit.Engine/Services/TestsExecutor.cs @@ -111,7 +111,9 @@ await Task.Run(async delegate { if (test.TestContext.SkipReason == null) { - await OnBeforeStart(test.TestContext); + RestoreContexts(await ExecuteBeforeAssemblyHooks(test.TestContext)); + + RestoreContexts(await ExecuteBeforeClassHooks(test.TestContext)); } await ProcessTest(test, filter, context, context.CancellationToken); @@ -139,13 +141,9 @@ private async Task ProcessCollection(IEnumerable queue, { if (test.TestContext.SkipReason == null) { - await OnBeforeStart(test.TestContext); - } + RestoreContexts(await ExecuteBeforeAssemblyHooks(test.TestContext)); - var assemblyHookContext = _assemblyHookOrchestrator.GetContext(test.TestDetails.TestClass.Type.Assembly); - if (assemblyHookContext.ExecutionContext != null) - { - ExecutionContext.Restore(assemblyHookContext.ExecutionContext); + RestoreContexts(await ExecuteBeforeClassHooks(test.TestContext)); } await ProcessTest(test, filter, context, token); @@ -156,7 +154,9 @@ await queue { if (test.TestContext.SkipReason != null) { - await OnBeforeStart(test.TestContext!); + RestoreContexts(await ExecuteBeforeAssemblyHooks(test.TestContext)); + + RestoreContexts(await ExecuteBeforeClassHooks(test.TestContext)); } await ProcessTest(test, filter, context, context.CancellationToken); @@ -165,6 +165,16 @@ await queue #endif } + private void RestoreContexts(List executionContexts) + { +#if NET + foreach (var executionContext in executionContexts) + { + ExecutionContext.Restore(executionContext); + } +#endif + } + #if NET private async ValueTask ProcessTest(DiscoveredTest test, ITestExecutionFilter? filter, ExecuteRequestContext context, CancellationToken cancellationToken) @@ -199,97 +209,101 @@ private async Task ProcessTest(DiscoveredTest test, } #endif - private async Task OnBeforeStart(TestContext testContext) + private async Task> ExecuteBeforeClassHooks(TestContext testContext) { - // Ideally all these 'Set up' hooks would be refactored into inner/classes and/or methods, - // But users may want to set AsyncLocal values, and so the method must be a parent/ancestor of the method that starts the test! - // So actually refactoring these into other methods would mean they wouldn't be a parent/ancestor and would break async local! - var assemblyHooksTaskCompletionSource = _assemblyHookOrchestrator.PreviouslyRunBeforeHooks.GetOrAdd( - testContext.TestDetails.TestClass.Type.Assembly, _ => new TaskCompletionSource(), - out var assemblyHooksTaskPreviouslyExisted); + var classHookContext = _classHookOrchestrator.GetContext(testContext.TestDetails.TestClass.Type); - if (assemblyHooksTaskPreviouslyExisted) + var classHooksTaskCompletionSource = _classHookOrchestrator.PreviouslyRunBeforeHooks.GetOrAdd( + testContext.TestDetails.TestClass.Type, _ => new TaskCompletionSource(), + out var classHooksTaskPreviouslyExisted); + + if (classHooksTaskPreviouslyExisted) { - await assemblyHooksTaskCompletionSource.Task; + await classHooksTaskCompletionSource.Task; + return classHookContext.ExecutionContexts; } - else + + try { - try - { - var beforeAssemblyHooks = _assemblyHookOrchestrator.CollectBeforeHooks(testContext.TestDetails.TestClass.Type.Assembly); - var assemblyHookContext =_assemblyHookOrchestrator.GetContext(testContext.TestDetails.TestClass.Type.Assembly); + var beforeClassHooks = + _classHookOrchestrator.CollectBeforeHooks(testContext.TestDetails.TestClass.Type); - AssemblyHookContext.Current = assemblyHookContext; + ClassHookContext.Current = classHookContext; - foreach (var beforeHook in beforeAssemblyHooks) + foreach (var beforeHook in beforeClassHooks) + { + if (beforeHook.IsSynchronous) { - if (beforeHook.IsSynchronous) - { - await _logger.LogDebugAsync("Executing synchronous [Before(Assembly)] hook"); - - beforeHook.Execute(assemblyHookContext, CancellationToken.None); - } - else - { - await _logger.LogDebugAsync("Executing asynchronous [Before(Assembly)] hook"); - - await beforeHook.ExecuteAsync(assemblyHookContext, CancellationToken.None); - } + await _logger.LogDebugAsync("Executing synchronous [Before(Class)] hook"); + + beforeHook.Execute(classHookContext, CancellationToken.None); } + else + { + await _logger.LogDebugAsync("Executing asynchronous [Before(Class)] hook"); - AssemblyHookContext.Current = null; - assemblyHooksTaskCompletionSource.SetResult(false); - } - catch (Exception e) - { - assemblyHooksTaskCompletionSource.SetException(e); - throw; + await beforeHook.ExecuteAsync(classHookContext, CancellationToken.None); + } } + + ClassHookContext.Current = null; + classHooksTaskCompletionSource.SetResult(false); + } + catch (Exception e) + { + classHooksTaskCompletionSource.SetException(e); + throw; } - var classHooksTaskCompletionSource = _classHookOrchestrator.PreviouslyRunBeforeHooks.GetOrAdd( - testContext.TestDetails.TestClass.Type, _ => new TaskCompletionSource(), - out var classHooksTaskPreviouslyExisted); + return classHookContext.ExecutionContexts; + } - if (classHooksTaskPreviouslyExisted) + private async Task> ExecuteBeforeAssemblyHooks(TestContext testContext) + { + var assemblyHookContext =_assemblyHookOrchestrator.GetContext(testContext.TestDetails.TestClass.Type.Assembly); + + var assemblyHooksTaskCompletionSource = _assemblyHookOrchestrator.PreviouslyRunBeforeHooks.GetOrAdd( + testContext.TestDetails.TestClass.Type.Assembly, _ => new TaskCompletionSource(), + out var assemblyHooksTaskPreviouslyExisted); + + if (assemblyHooksTaskPreviouslyExisted) { - await classHooksTaskCompletionSource.Task; + await assemblyHooksTaskCompletionSource.Task; + return assemblyHookContext.ExecutionContexts; } - else + + try { - try - { - var beforeClassHooks = - _classHookOrchestrator.CollectBeforeHooks(testContext.TestDetails.TestClass.Type); - var classHookContext = _classHookOrchestrator.GetContext(testContext.TestDetails.TestClass.Type); + var beforeAssemblyHooks = _assemblyHookOrchestrator.CollectBeforeHooks(testContext.TestDetails.TestClass.Type.Assembly); - ClassHookContext.Current = classHookContext; + AssemblyHookContext.Current = assemblyHookContext; - foreach (var beforeHook in beforeClassHooks) + foreach (var beforeHook in beforeAssemblyHooks) + { + if (beforeHook.IsSynchronous) { - if (beforeHook.IsSynchronous) - { - await _logger.LogDebugAsync("Executing synchronous [Before(Class)] hook"); - - beforeHook.Execute(classHookContext, CancellationToken.None); - } - else - { - await _logger.LogDebugAsync("Executing asynchronous [Before(Class)] hook"); - - await beforeHook.ExecuteAsync(classHookContext, CancellationToken.None); - } + await _logger.LogDebugAsync("Executing synchronous [Before(Assembly)] hook"); + + beforeHook.Execute(assemblyHookContext, CancellationToken.None); } + else + { + await _logger.LogDebugAsync("Executing asynchronous [Before(Assembly)] hook"); - ClassHookContext.Current = null; - classHooksTaskCompletionSource.SetResult(false); - } - catch (Exception e) - { - classHooksTaskCompletionSource.SetException(e); - throw; + await beforeHook.ExecuteAsync(assemblyHookContext, CancellationToken.None); + } } + + AssemblyHookContext.Current = null; + assemblyHooksTaskCompletionSource.SetResult(false); } + catch (Exception e) + { + assemblyHooksTaskCompletionSource.SetException(e); + throw; + } + + return assemblyHookContext.ExecutionContexts; } private int GetParallelTestsLimit() diff --git a/TUnit.TestProject/Bugs/1914/Tests.cs b/TUnit.TestProject/Bugs/1914/Tests.cs index 80441817d1..33607c1f49 100644 --- a/TUnit.TestProject/Bugs/1914/Tests.cs +++ b/TUnit.TestProject/Bugs/1914/Tests.cs @@ -13,28 +13,38 @@ public class Tests [BeforeEvery(TestDiscovery)] public static void BeforeTestDiscovery(BeforeTestDiscoveryContext context) - => _0BeforeTestDiscoveryLocal.Value = "BeforeTestDiscovery"; - + { + _0BeforeTestDiscoveryLocal.Value = "BeforeTestDiscovery"; + context.FlowAsyncLocalValues(); + } + [BeforeEvery(TestSession)] public static void BeforeTestSession(TestSessionContext context) - => _1BeforeTestSessionLocal.Value = "BeforeTestSession"; + { + _1BeforeTestSessionLocal.Value = "BeforeTestSession"; + context.FlowAsyncLocalValues(); + } [BeforeEvery(Assembly)] - public static async Task BeforeAssembly(AssemblyHookContext context) + public static void BeforeAssembly(AssemblyHookContext context) { - await Task.CompletedTask; _2BeforeAssemblyLocal.Value = "BeforeAssembly"; - context.ExecutionContext = ExecutionContext.Capture(); - Console.WriteLine(""); + context.FlowAsyncLocalValues(); } [BeforeEvery(Class)] public static void BeforeClass(ClassHookContext context) - => _3BeforeClassLocal.Value = "BeforeClass"; + { + _3BeforeClassLocal.Value = "BeforeClass"; + context.FlowAsyncLocalValues(); + } [BeforeEvery(Test)] public static void BeforeTest(TestContext context) - => _4BeforeTestLocal.Value = "BeforeTest"; + { + _4BeforeTestLocal.Value = "BeforeTest"; + context.FlowAsyncLocalValues(); + } [Test] @@ -53,7 +63,7 @@ public async Task TestAsyncLocal(int i) await Assert.That(_0BeforeTestDiscoveryLocal.Value).IsEqualTo("BeforeTestDiscovery"); await Assert.That(_1BeforeTestSessionLocal.Value).IsEqualTo("BeforeTestSession"); await Assert.That(_2BeforeAssemblyLocal.Value).IsEqualTo("BeforeAssembly"); - // await Assert.That(_3BeforeClassLocal.Value).IsEqualTo("BeforeClass"); + await Assert.That(_3BeforeClassLocal.Value).IsEqualTo("BeforeClass"); await Assert.That(_4BeforeTestLocal.Value).IsEqualTo("BeforeTest"); } } \ No newline at end of file From 0f23d7ec8711104d0b9b096f779b7346bc6dd112 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 26 Feb 2025 21:41:57 +0000 Subject: [PATCH 05/17] Rework analyzer --- .../BeforeHookAsyncLocalAnalyzerTests.cs | 57 +++------------ .../BeforeHookAsyncLocalAnalyzer.cs | 73 +++++++++---------- TUnit.Analyzers/Resources.Designer.cs | 6 +- TUnit.Analyzers/Resources.resx | 6 +- TUnit.Analyzers/Rules.cs | 2 +- 5 files changed, 49 insertions(+), 95 deletions(-) diff --git a/TUnit.Analyzers.Tests/BeforeHookAsyncLocalAnalyzerTests.cs b/TUnit.Analyzers.Tests/BeforeHookAsyncLocalAnalyzerTests.cs index 3c91916b41..8ad21b2f8a 100644 --- a/TUnit.Analyzers.Tests/BeforeHookAsyncLocalAnalyzerTests.cs +++ b/TUnit.Analyzers.Tests/BeforeHookAsyncLocalAnalyzerTests.cs @@ -20,49 +20,21 @@ public class MyClass { private static readonly AsyncLocal _asyncLocal = new(); - [Before(Test)] - public void {|#0:MyTest|}() + {|#0:[Before(Class)] + public void MyTest() { _asyncLocal.Value = 1; - } - } - """ - ); - } - - [Test] - public async Task Async_Raises_Error_Setting_AsyncLocal() - { - await Verifier - .VerifyAnalyzerAsync( - """ - using System.Threading; - using System.Threading.Tasks; - using TUnit.Core; - using static TUnit.Core.HookType; - - public class MyClass - { - private static readonly AsyncLocal _asyncLocal = new(); - - {|#1:[Before(Test)] - public async Task MyTest() - { - {|#0:_asyncLocal.Value = 1|}; - await Task.Yield(); }|} } """, - Verifier - .Diagnostic(Rules.AsyncLocalVoidMethod) + .Diagnostic(Rules.AsyncLocalCallFlowValues) .WithLocation(0) - .WithLocation(1) ); } [Test] - public async Task Async_Raises_Error_Setting_AsyncLocal_Nested_Method() + public async Task FlowAsyncLocalValues_No_Error() { await Verifier .VerifyAnalyzerAsync( @@ -76,24 +48,13 @@ public class MyClass { private static readonly AsyncLocal _asyncLocal = new(); - {|#1:[Before(Test)] - public async Task MyTest() + {|#0:[Before(Class)] + public void MyTest(ClassHookContext context) { - SetAsyncLocal(); - await Task.Yield(); + _asyncLocal.Value = 1; + context.FlowAsyncLocalValues(); }|} - - private void SetAsyncLocal() - { - {|#0:_asyncLocal.Value = 1|}; - } } - """, - - Verifier - .Diagnostic(Rules.AsyncLocalVoidMethod) - .WithLocation(0) - .WithLocation(1) - ); + """); } } \ No newline at end of file diff --git a/TUnit.Analyzers/BeforeHookAsyncLocalAnalyzer.cs b/TUnit.Analyzers/BeforeHookAsyncLocalAnalyzer.cs index 31b70efc3f..553704a370 100644 --- a/TUnit.Analyzers/BeforeHookAsyncLocalAnalyzer.cs +++ b/TUnit.Analyzers/BeforeHookAsyncLocalAnalyzer.cs @@ -11,7 +11,7 @@ namespace TUnit.Analyzers; public class BeforeHookAsyncLocalAnalyzer : ConcurrentDiagnosticAnalyzer { public override ImmutableArray SupportedDiagnostics { get; } = - ImmutableArray.Create(Rules.AsyncLocalVoidMethod); + ImmutableArray.Create(Rules.AsyncLocalCallFlowValues); protected override void InitializeInternal(AnalysisContext context) { @@ -39,16 +39,31 @@ private void AnalyzeOperation(OperationAnalysisContext context) return; } - var parent = assignmentOperation.Parent; - while (parent != null) + var methodBodyOperation = GetParentMethod(assignmentOperation.Parent); + + if (methodBodyOperation is null) + { + return; + } + + CheckMethod(context, methodBodyOperation); + } + + private IMethodBodyOperation? GetParentMethod(IOperation? assignmentOperationParent) + { + var parent = assignmentOperationParent; + + while (parent is not null) { if (parent is IMethodBodyOperation methodBodyOperation) { - CheckMethod(context, methodBodyOperation); + return methodBodyOperation; } parent = parent.Parent; } + + return null; } private void CheckMethod(OperationAnalysisContext context, IMethodBodyOperation methodBodyOperation) @@ -59,19 +74,20 @@ private void CheckMethod(OperationAnalysisContext context, IMethodBodyOperation return; } - if (methodSymbol.IsHookMethod(context.Compilation, out _, out _, out var type) - && type is HookType.Before - && !methodSymbol.ReturnsVoid) + if (!methodSymbol.IsHookMethod(context.Compilation, out _, out _, out var type) + || type is not HookType.Before) { - context.ReportDiagnostic(Diagnostic.Create(Rules.AsyncLocalVoidMethod, - context.Operation.Syntax.GetLocation(), - [methodBodyOperation.Syntax.GetLocation()])); return; } - var invocations = methodBodyOperation.SemanticModel - .SyntaxTree - .GetRoot() + var syntax = methodSymbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax(); + + if (syntax is null) + { + return; + } + + var invocations = syntax .DescendantNodes() .OfType(); @@ -84,36 +100,13 @@ private void CheckMethod(OperationAnalysisContext context, IMethodBodyOperation continue; } - if (!SymbolEqualityComparer.Default.Equals(invocationOperation.TargetMethod, methodSymbol)) + if (invocationOperation.TargetMethod.Name == "FlowAsyncLocalValues") { - continue; + return; } - - var parentMethodBody = GetParentMethodBody(invocationOperation); - - if (parentMethodBody == null) - { - continue; - } - - CheckMethod(context, parentMethodBody); - } - } - - private IMethodBodyOperation? GetParentMethodBody(IInvocationOperation invocationOperation) - { - var parent = invocationOperation.Parent; - - while (parent != null) - { - if (parent is IMethodBodyOperation methodBodyOperation) - { - return methodBodyOperation; - } - - parent = parent.Parent; } - return null; + context.ReportDiagnostic(Diagnostic.Create(Rules.AsyncLocalCallFlowValues, + methodBodyOperation.Syntax.GetLocation())); } } \ No newline at end of file diff --git a/TUnit.Analyzers/Resources.Designer.cs b/TUnit.Analyzers/Resources.Designer.cs index de9a78effd..3c2fc747f4 100644 --- a/TUnit.Analyzers/Resources.Designer.cs +++ b/TUnit.Analyzers/Resources.Designer.cs @@ -1302,7 +1302,7 @@ internal static string TUnit0046Title { } /// - /// Looks up a localized string similar to Before hooks setting AsyncLocal values should be non-async void returning methods.. + /// Looks up a localized string similar to For AsyncLocal values set in before hooks, you must call `context.FlowAsyncLocalValues` to access them within tests.. /// internal static string TUnit0047Description { get { @@ -1311,7 +1311,7 @@ internal static string TUnit0047Description { } /// - /// Looks up a localized string similar to Before hooks setting AsyncLocal values should be non-async void returning methods.. + /// Looks up a localized string similar to For AsyncLocal values set in before hooks, you must call `context.FlowAsyncLocalValues` to access them within tests.. /// internal static string TUnit0047MessageFormat { get { @@ -1320,7 +1320,7 @@ internal static string TUnit0047MessageFormat { } /// - /// Looks up a localized string similar to Before hooks setting AsyncLocal values should be non-async void returning methods. + /// Looks up a localized string similar to Call `context.FlowAsyncLocalValues`. /// internal static string TUnit0047Title { get { diff --git a/TUnit.Analyzers/Resources.resx b/TUnit.Analyzers/Resources.resx index 87b419cf55..c3f3ebb771 100644 --- a/TUnit.Analyzers/Resources.resx +++ b/TUnit.Analyzers/Resources.resx @@ -433,13 +433,13 @@ Return a `Func<T>` rather than a `<T>` - Before hooks setting AsyncLocal values should be non-async void returning methods. + For AsyncLocal values set in before hooks, you must call `context.FlowAsyncLocalValues` to access them within tests. - Before hooks setting AsyncLocal values should be non-async void returning methods. + For AsyncLocal values set in before hooks, you must call `context.FlowAsyncLocalValues` to access them within tests. - Before hooks setting AsyncLocal values should be non-async void returning methods + Call `context.FlowAsyncLocalValues` Test methods must not be static. diff --git a/TUnit.Analyzers/Rules.cs b/TUnit.Analyzers/Rules.cs index f4057a8b03..a86cdcf730 100644 --- a/TUnit.Analyzers/Rules.cs +++ b/TUnit.Analyzers/Rules.cs @@ -108,7 +108,7 @@ public static class Rules public static readonly DiagnosticDescriptor ReturnFunc = CreateDescriptor("TUnit0046", UsageCategory, DiagnosticSeverity.Warning); - public static readonly DiagnosticDescriptor AsyncLocalVoidMethod = + public static readonly DiagnosticDescriptor AsyncLocalCallFlowValues = CreateDescriptor("TUnit0047", UsageCategory, DiagnosticSeverity.Warning); public static DiagnosticDescriptor InstanceTestMethod = From cfe3e83bb5c9884058d7e693bf4b2862710befac Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 26 Feb 2025 21:45:07 +0000 Subject: [PATCH 06/17] More assertions to ensure multiple hooks all propagate their async local values --- TUnit.TestProject/Bugs/1914/Tests.cs | 50 ++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/TUnit.TestProject/Bugs/1914/Tests.cs b/TUnit.TestProject/Bugs/1914/Tests.cs index 33607c1f49..26b0cb662c 100644 --- a/TUnit.TestProject/Bugs/1914/Tests.cs +++ b/TUnit.TestProject/Bugs/1914/Tests.cs @@ -6,10 +6,19 @@ namespace TUnit.TestProject.Bugs._1914; public class Tests { private static readonly AsyncLocal _0BeforeTestDiscoveryLocal = new(); + private static readonly AsyncLocal _0BeforeTestDiscoveryLocal2 = new(); + private static readonly AsyncLocal _1BeforeTestSessionLocal = new(); + private static readonly AsyncLocal _1BeforeTestSessionLocal2 = new(); + private static readonly AsyncLocal _2BeforeAssemblyLocal = new(); + private static readonly AsyncLocal _2BeforeAssemblyLocal2 = new(); + private static readonly AsyncLocal _3BeforeClassLocal = new(); + private static readonly AsyncLocal _3BeforeClassLocal2 = new(); + private static readonly AsyncLocal _4BeforeTestLocal = new(); + private static readonly AsyncLocal _4BeforeTestLocal2 = new(); [BeforeEvery(TestDiscovery)] public static void BeforeTestDiscovery(BeforeTestDiscoveryContext context) @@ -17,6 +26,13 @@ public static void BeforeTestDiscovery(BeforeTestDiscoveryContext context) _0BeforeTestDiscoveryLocal.Value = "BeforeTestDiscovery"; context.FlowAsyncLocalValues(); } + + [BeforeEvery(TestDiscovery)] + public static void BeforeTestDiscovery2(BeforeTestDiscoveryContext context) + { + _0BeforeTestDiscoveryLocal2.Value = "BeforeTestDiscovery2"; + context.FlowAsyncLocalValues(); + } [BeforeEvery(TestSession)] public static void BeforeTestSession(TestSessionContext context) @@ -24,6 +40,13 @@ public static void BeforeTestSession(TestSessionContext context) _1BeforeTestSessionLocal.Value = "BeforeTestSession"; context.FlowAsyncLocalValues(); } + + [BeforeEvery(TestSession)] + public static void BeforeTestSession2(TestSessionContext context) + { + _1BeforeTestSessionLocal2.Value = "BeforeTestSession2"; + context.FlowAsyncLocalValues(); + } [BeforeEvery(Assembly)] public static void BeforeAssembly(AssemblyHookContext context) @@ -31,6 +54,13 @@ public static void BeforeAssembly(AssemblyHookContext context) _2BeforeAssemblyLocal.Value = "BeforeAssembly"; context.FlowAsyncLocalValues(); } + + [BeforeEvery(Assembly)] + public static void BeforeAssembly2(AssemblyHookContext context) + { + _2BeforeAssemblyLocal2.Value = "BeforeAssembly2"; + context.FlowAsyncLocalValues(); + } [BeforeEvery(Class)] public static void BeforeClass(ClassHookContext context) @@ -38,6 +68,13 @@ public static void BeforeClass(ClassHookContext context) _3BeforeClassLocal.Value = "BeforeClass"; context.FlowAsyncLocalValues(); } + + [BeforeEvery(Class)] + public static void BeforeClass2(ClassHookContext context) + { + _3BeforeClassLocal2.Value = "BeforeClass2"; + context.FlowAsyncLocalValues(); + } [BeforeEvery(Test)] public static void BeforeTest(TestContext context) @@ -45,6 +82,13 @@ public static void BeforeTest(TestContext context) _4BeforeTestLocal.Value = "BeforeTest"; context.FlowAsyncLocalValues(); } + + [BeforeEvery(Test)] + public static void BeforeTest2(TestContext context) + { + _4BeforeTestLocal2.Value = "BeforeTest2"; + context.FlowAsyncLocalValues(); + } [Test] @@ -65,5 +109,11 @@ public async Task TestAsyncLocal(int i) await Assert.That(_2BeforeAssemblyLocal.Value).IsEqualTo("BeforeAssembly"); await Assert.That(_3BeforeClassLocal.Value).IsEqualTo("BeforeClass"); await Assert.That(_4BeforeTestLocal.Value).IsEqualTo("BeforeTest"); + + await Assert.That(_0BeforeTestDiscoveryLocal2.Value).IsEqualTo("BeforeTestDiscovery2"); + await Assert.That(_1BeforeTestSessionLocal2.Value).IsEqualTo("BeforeTestSession2"); + await Assert.That(_2BeforeAssemblyLocal2.Value).IsEqualTo("BeforeAssembly2"); + await Assert.That(_3BeforeClassLocal2.Value).IsEqualTo("BeforeClass2"); + await Assert.That(_4BeforeTestLocal2.Value).IsEqualTo("BeforeTest2"); } } \ No newline at end of file From 3dea5b66b08610c49f289125e68f39376a722281 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 26 Feb 2025 22:02:09 +0000 Subject: [PATCH 07/17] Refactor WIP --- .../Writers/Hooks/AssemblyHooksWriter.cs | 11 +- .../Writers/Hooks/ClassHooksWriter.cs | 11 +- .../Writers/Hooks/GlobalTestHooksWriter.cs | 13 +- .../Writers/Hooks/TestHooksWriter.cs | 18 +-- TUnit.Core/Hooks/AfterAssemblyHookMethod.cs | 2 +- TUnit.Core/Hooks/AfterClassHookMethod.cs | 2 +- .../Hooks/AfterTestDiscoveryHookMethod.cs | 2 +- TUnit.Core/Hooks/AfterTestHookMethod.cs | 2 +- .../Hooks/AfterTestSessionHookMethod.cs | 2 +- TUnit.Core/Hooks/BeforeAssemblyHookMethod.cs | 2 +- TUnit.Core/Hooks/BeforeClassHookMethod.cs | 2 +- .../Hooks/BeforeTestDiscoveryHookMethod.cs | 2 +- TUnit.Core/Hooks/BeforeTestHookMethod.cs | 2 +- .../Hooks/BeforeTestSessionHookMethod.cs | 2 +- TUnit.Core/Hooks/IExecutableHook.cs | 6 +- TUnit.Core/Hooks/InstanceHookMethod.cs | 20 +-- TUnit.Core/Hooks/StaticHookMethod.cs | 5 +- .../Framework/TUnitServiceProvider.cs | 4 +- TUnit.Engine/Framework/TUnitTestFramework.cs | 52 ++----- .../Hooks/AssemblyHookOrchestrator.cs | 43 +++++- TUnit.Engine/Hooks/ClassHookOrchestrator.cs | 45 +++++- TUnit.Engine/Services/SingleTestExecutor.cs | 26 +--- TUnit.Engine/Services/TestInvoker.cs | 30 +--- TUnit.Engine/Services/TestsExecutor.cs | 112 +-------------- TUnit.TestProject/Bugs/1914/AsyncHookTests.cs | 129 ++++++++++++++++++ .../Bugs/1914/{Tests.cs => SyncHookTests.cs} | 2 +- docs/docs/tutorial-extras/setup.md | 16 +++ 27 files changed, 279 insertions(+), 284 deletions(-) create mode 100644 TUnit.TestProject/Bugs/1914/AsyncHookTests.cs rename TUnit.TestProject/Bugs/1914/{Tests.cs => SyncHookTests.cs} (99%) diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/AssemblyHooksWriter.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/AssemblyHooksWriter.cs index 35f534b6d4..7d3406e760 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/AssemblyHooksWriter.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/AssemblyHooksWriter.cs @@ -27,15 +27,8 @@ public static void Execute(SourceCodeWriter sourceBuilder, HooksDataModel? model sourceBuilder.WriteTabs(); sourceBuilder.Write("MethodInfo = "); SourceInformationWriter.GenerateMethodInformation(sourceBuilder, model.Context, model.ClassType, model.Method, null, ','); - - if (model.IsVoid) - { - sourceBuilder.WriteLine($"Body = (context, cancellationToken) => {model.FullyQualifiedTypeName}.{model.MethodName}({GetArgs(model)}),"); - } - else - { - sourceBuilder.WriteLine($"AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => {model.FullyQualifiedTypeName}.{model.MethodName}({GetArgs(model)})),"); - } + + sourceBuilder.WriteLine($"Body = (context, cancellationToken) => AsyncConvert.Convert(() => {model.FullyQualifiedTypeName}.{model.MethodName}({GetArgs(model)})),"); sourceBuilder.WriteLine($"HookExecutor = {HookExecutorHelper.GetHookExecutor(model.HookExecutor)},"); sourceBuilder.WriteLine($"Order = {model.Order},"); diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/ClassHooksWriter.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/ClassHooksWriter.cs index 84fb9fe47f..5eb741dcaf 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/ClassHooksWriter.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/ClassHooksWriter.cs @@ -22,15 +22,8 @@ public static void Execute(SourceCodeWriter sourceBuilder, HooksDataModel model) sourceBuilder.WriteTabs(); sourceBuilder.Write("MethodInfo = "); SourceInformationWriter.GenerateMethodInformation(sourceBuilder, model.Context, model.ClassType, model.Method, null, ','); - - if (model.IsVoid) - { - sourceBuilder.WriteLine($"Body = (context, cancellationToken) => {model.FullyQualifiedTypeName}.{model.MethodName}({GetArgs(model)}),"); - } - else - { - sourceBuilder.WriteLine($"AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => {model.FullyQualifiedTypeName}.{model.MethodName}({GetArgs(model)})),"); - } + + sourceBuilder.WriteLine($"Body = (context, cancellationToken) => AsyncConvert.Convert(() => {model.FullyQualifiedTypeName}.{model.MethodName}({GetArgs(model)})),"); sourceBuilder.WriteLine($"HookExecutor = {HookExecutorHelper.GetHookExecutor(model.HookExecutor)},"); sourceBuilder.WriteLine($"Order = {model.Order},"); diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/GlobalTestHooksWriter.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/GlobalTestHooksWriter.cs index 452e72789d..0369073fbd 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/GlobalTestHooksWriter.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/GlobalTestHooksWriter.cs @@ -15,17 +15,8 @@ public static void Execute(SourceCodeWriter sourceBuilder, HooksDataModel model) sourceBuilder.WriteTabs(); sourceBuilder.Write("MethodInfo = "); SourceInformationWriter.GenerateMethodInformation(sourceBuilder, model.Context, model.ClassType, model.Method, null, ','); - - if (model.IsVoid) - { - sourceBuilder.WriteLine( - $"Body = (context, cancellationToken) => {model.FullyQualifiedTypeName}.{model.MethodName}({GetArgs(model, model.HookLocationType)}),"); - } - else - { - sourceBuilder.WriteLine( - $"AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => {model.FullyQualifiedTypeName}.{model.MethodName}({GetArgs(model, model.HookLocationType)})),"); - } + + sourceBuilder.WriteLine($"Body = (context, cancellationToken) => AsyncConvert.Convert(() => {model.FullyQualifiedTypeName}.{model.MethodName}({GetArgs(model, model.HookLocationType)})),"); sourceBuilder.WriteLine($"HookExecutor = {HookExecutorHelper.GetHookExecutor(model.HookExecutor)},"); sourceBuilder.WriteLine($"Order = {model.Order},"); diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs index b593adff45..04d7b44894 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs @@ -25,14 +25,7 @@ public static void Execute(SourceCodeWriter sourceBuilder, HooksDataModel model) sourceBuilder.Write("MethodInfo = "); SourceInformationWriter.GenerateMethodInformation(sourceBuilder, model.Context, model.ClassType, model.Method, null, ','); - if (model.IsVoid) - { - sourceBuilder.WriteLine($"Body = (context, cancellationToken) => {model.FullyQualifiedTypeName}.{model.MethodName}({GetArgs(model)}),"); - } - else - { - sourceBuilder.WriteLine($"AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => {model.FullyQualifiedTypeName}.{model.MethodName}({GetArgs(model)})),"); - } + sourceBuilder.WriteLine($"Body = (context, cancellationToken) => AsyncConvert.Convert(() => {model.FullyQualifiedTypeName}.{model.MethodName}({GetArgs(model)})),"); sourceBuilder.WriteLine($"HookExecutor = {HookExecutorHelper.GetHookExecutor(model.HookExecutor)},"); sourceBuilder.WriteLine($"Order = {model.Order},"); @@ -77,14 +70,7 @@ public static void Execute(SourceCodeWriter sourceBuilder, HooksDataModel model) } else { - if (model.ClassType.IsGenericDefinition()) - { - sourceBuilder.WriteLine($"AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => classInstance.GetType().GetMethod(\"{model.MethodName}\", [{string.Join(", ", model.ParameterTypes.Select(x => $"typeof({x})"))}]).Invoke(classInstance, {GetArgsOrEmptyArray(model)})),"); - } - else - { - sourceBuilder.WriteLine($"AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => (({model.FullyQualifiedTypeName})classInstance).{model.MethodName}({GetArgs(model)})),"); - } + sourceBuilder.WriteLine($"Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => (({model.FullyQualifiedTypeName})classInstance).{model.MethodName}({GetArgs(model)})),"); } sourceBuilder.WriteLine($"HookExecutor = {HookExecutorHelper.GetHookExecutor(model.HookExecutor)},"); diff --git a/TUnit.Core/Hooks/AfterAssemblyHookMethod.cs b/TUnit.Core/Hooks/AfterAssemblyHookMethod.cs index 957ecf2889..c05e505cad 100644 --- a/TUnit.Core/Hooks/AfterAssemblyHookMethod.cs +++ b/TUnit.Core/Hooks/AfterAssemblyHookMethod.cs @@ -18,7 +18,7 @@ public override bool Execute(AssemblyHookContext context, CancellationToken canc public override Task ExecuteAsync(AssemblyHookContext context, CancellationToken cancellationToken) { return HookExecutor.ExecuteAsynchronousAfterAssemblyHook(MethodInfo, context, - () => AsyncBody!.Invoke(context, cancellationToken) + () => Body!.Invoke(context, cancellationToken) ); } } \ No newline at end of file diff --git a/TUnit.Core/Hooks/AfterClassHookMethod.cs b/TUnit.Core/Hooks/AfterClassHookMethod.cs index 1290a59c29..15f44ebecc 100644 --- a/TUnit.Core/Hooks/AfterClassHookMethod.cs +++ b/TUnit.Core/Hooks/AfterClassHookMethod.cs @@ -18,7 +18,7 @@ public override bool Execute(ClassHookContext context, CancellationToken cancell public override Task ExecuteAsync(ClassHookContext context, CancellationToken cancellationToken) { return HookExecutor.ExecuteAsynchronousAfterClassHook(MethodInfo, context, - () => AsyncBody!.Invoke(context, cancellationToken) + () => Body!.Invoke(context, cancellationToken) ); } } \ No newline at end of file diff --git a/TUnit.Core/Hooks/AfterTestDiscoveryHookMethod.cs b/TUnit.Core/Hooks/AfterTestDiscoveryHookMethod.cs index be9d5564b2..e022d3ef47 100644 --- a/TUnit.Core/Hooks/AfterTestDiscoveryHookMethod.cs +++ b/TUnit.Core/Hooks/AfterTestDiscoveryHookMethod.cs @@ -18,7 +18,7 @@ public override bool Execute(TestDiscoveryContext context, CancellationToken can public override Task ExecuteAsync(TestDiscoveryContext context, CancellationToken cancellationToken) { return HookExecutor.ExecuteAsynchronousAfterTestDiscoveryHook(MethodInfo, context, - () => AsyncBody!.Invoke(context, cancellationToken) + () => Body!.Invoke(context, cancellationToken) ); } } \ No newline at end of file diff --git a/TUnit.Core/Hooks/AfterTestHookMethod.cs b/TUnit.Core/Hooks/AfterTestHookMethod.cs index b73d8102b9..080e6a35c6 100644 --- a/TUnit.Core/Hooks/AfterTestHookMethod.cs +++ b/TUnit.Core/Hooks/AfterTestHookMethod.cs @@ -18,7 +18,7 @@ public override bool Execute(TestContext context, CancellationToken cancellation public override Task ExecuteAsync(TestContext context, CancellationToken cancellationToken) { return HookExecutor.ExecuteAsynchronousAfterTestHook(MethodInfo, context, - () => AsyncBody!.Invoke(context, cancellationToken) + () => Body!.Invoke(context, cancellationToken) ); } } \ No newline at end of file diff --git a/TUnit.Core/Hooks/AfterTestSessionHookMethod.cs b/TUnit.Core/Hooks/AfterTestSessionHookMethod.cs index a6c9d0b2a6..258656d83f 100644 --- a/TUnit.Core/Hooks/AfterTestSessionHookMethod.cs +++ b/TUnit.Core/Hooks/AfterTestSessionHookMethod.cs @@ -18,7 +18,7 @@ public override bool Execute(TestSessionContext context, CancellationToken cance public override Task ExecuteAsync(TestSessionContext context, CancellationToken cancellationToken) { return HookExecutor.ExecuteAsynchronousAfterTestSessionHook(MethodInfo, context, - () => AsyncBody!.Invoke(context, cancellationToken) + () => Body!.Invoke(context, cancellationToken) ); } } \ No newline at end of file diff --git a/TUnit.Core/Hooks/BeforeAssemblyHookMethod.cs b/TUnit.Core/Hooks/BeforeAssemblyHookMethod.cs index a85c95b4cf..24d6f5ef72 100644 --- a/TUnit.Core/Hooks/BeforeAssemblyHookMethod.cs +++ b/TUnit.Core/Hooks/BeforeAssemblyHookMethod.cs @@ -18,7 +18,7 @@ public override bool Execute(AssemblyHookContext context, CancellationToken canc public override Task ExecuteAsync(AssemblyHookContext context, CancellationToken cancellationToken) { return HookExecutor.ExecuteAsynchronousBeforeAssemblyHook(MethodInfo, context, - () => AsyncBody!.Invoke(context, cancellationToken) + () => Body!.Invoke(context, cancellationToken) ); } } \ No newline at end of file diff --git a/TUnit.Core/Hooks/BeforeClassHookMethod.cs b/TUnit.Core/Hooks/BeforeClassHookMethod.cs index 4180b92ce4..6f1a952985 100644 --- a/TUnit.Core/Hooks/BeforeClassHookMethod.cs +++ b/TUnit.Core/Hooks/BeforeClassHookMethod.cs @@ -18,7 +18,7 @@ public override bool Execute(ClassHookContext context, CancellationToken cancell public override Task ExecuteAsync(ClassHookContext context, CancellationToken cancellationToken) { return HookExecutor.ExecuteAsynchronousBeforeClassHook(MethodInfo, context, - () => AsyncBody!.Invoke(context, cancellationToken) + () => Body!.Invoke(context, cancellationToken) ); } } \ No newline at end of file diff --git a/TUnit.Core/Hooks/BeforeTestDiscoveryHookMethod.cs b/TUnit.Core/Hooks/BeforeTestDiscoveryHookMethod.cs index e2e3b7196d..aa4ed1c694 100644 --- a/TUnit.Core/Hooks/BeforeTestDiscoveryHookMethod.cs +++ b/TUnit.Core/Hooks/BeforeTestDiscoveryHookMethod.cs @@ -18,7 +18,7 @@ public override bool Execute(BeforeTestDiscoveryContext context, CancellationTok public override Task ExecuteAsync(BeforeTestDiscoveryContext context, CancellationToken cancellationToken) { return HookExecutor.ExecuteAsynchronousBeforeTestDiscoveryHook(MethodInfo, context, - () => AsyncBody!.Invoke(context, cancellationToken) + () => Body!.Invoke(context, cancellationToken) ); } } \ No newline at end of file diff --git a/TUnit.Core/Hooks/BeforeTestHookMethod.cs b/TUnit.Core/Hooks/BeforeTestHookMethod.cs index 343540415b..d18c776653 100644 --- a/TUnit.Core/Hooks/BeforeTestHookMethod.cs +++ b/TUnit.Core/Hooks/BeforeTestHookMethod.cs @@ -18,7 +18,7 @@ public override bool Execute(TestContext context, CancellationToken cancellation public override Task ExecuteAsync(TestContext context, CancellationToken cancellationToken) { return HookExecutor.ExecuteAsynchronousBeforeTestHook(MethodInfo, context, - () => AsyncBody!.Invoke(context, cancellationToken) + () => Body!.Invoke(context, cancellationToken) ); } } \ No newline at end of file diff --git a/TUnit.Core/Hooks/BeforeTestSessionHookMethod.cs b/TUnit.Core/Hooks/BeforeTestSessionHookMethod.cs index 853c4f96a0..cd3db893a5 100644 --- a/TUnit.Core/Hooks/BeforeTestSessionHookMethod.cs +++ b/TUnit.Core/Hooks/BeforeTestSessionHookMethod.cs @@ -18,7 +18,7 @@ public override bool Execute(TestSessionContext context, CancellationToken cance public override Task ExecuteAsync(TestSessionContext context, CancellationToken cancellationToken) { return HookExecutor.ExecuteAsynchronousBeforeTestSessionHook(MethodInfo, context, - () => AsyncBody!.Invoke(context, cancellationToken) + () => Body!.Invoke(context, cancellationToken) ); } } \ No newline at end of file diff --git a/TUnit.Core/Hooks/IExecutableHook.cs b/TUnit.Core/Hooks/IExecutableHook.cs index ebc0e38751..c82783a203 100644 --- a/TUnit.Core/Hooks/IExecutableHook.cs +++ b/TUnit.Core/Hooks/IExecutableHook.cs @@ -1,13 +1,9 @@ -using System.Reflection; - -namespace TUnit.Core.Hooks; +namespace TUnit.Core.Hooks; public interface IExecutableHook { string Name { get; } SourceGeneratedMethodInformation MethodInfo { get; } int Order { get; } - bool Execute(T context, CancellationToken cancellationToken); Task ExecuteAsync(T context, CancellationToken cancellationToken); - bool IsSynchronous { get; } } \ No newline at end of file diff --git a/TUnit.Core/Hooks/InstanceHookMethod.cs b/TUnit.Core/Hooks/InstanceHookMethod.cs index fa6a7a8feb..dbf0eafe54 100644 --- a/TUnit.Core/Hooks/InstanceHookMethod.cs +++ b/TUnit.Core/Hooks/InstanceHookMethod.cs @@ -33,28 +33,12 @@ public record InstanceHookMethod : IExecutableHook public required int Order { get; init; } - public Func? AsyncBody { get; init; } - public Action? Body { get; init; } - - public bool IsSynchronous => Body != null; - - public bool Execute(TestContext context, CancellationToken cancellationToken) - { - if (Body != null) - { - HookExecutor.ExecuteSynchronousBeforeTestHook(MethodInfo, context, - () => Body.Invoke(context.TestDetails.ClassInstance, context, cancellationToken) - ); - return true; - } - - return false; - } + public Func? Body { get; init; } public Task ExecuteAsync(TestContext context, CancellationToken cancellationToken) { return HookExecutor.ExecuteAsynchronousBeforeTestHook(MethodInfo, context, - () => AsyncBody!.Invoke(context.TestDetails.ClassInstance, context, cancellationToken) + () => Body!.Invoke(context.TestDetails.ClassInstance, context, cancellationToken) ); } } \ No newline at end of file diff --git a/TUnit.Core/Hooks/StaticHookMethod.cs b/TUnit.Core/Hooks/StaticHookMethod.cs index 63fa7a1d86..4f570db77c 100644 --- a/TUnit.Core/Hooks/StaticHookMethod.cs +++ b/TUnit.Core/Hooks/StaticHookMethod.cs @@ -9,12 +9,9 @@ namespace TUnit.Core.Hooks; #endif public abstract record StaticHookMethod : StaticHookMethod, IExecutableHook { - public Func? AsyncBody { get; init; } - public Action? Body { get; init; } - + public Func? Body { get; init; } public abstract bool Execute(T context, CancellationToken cancellationToken); public abstract Task ExecuteAsync(T context, CancellationToken cancellationToken); - public bool IsSynchronous => Body != null; } #if !DEBUG diff --git a/TUnit.Engine/Framework/TUnitServiceProvider.cs b/TUnit.Engine/Framework/TUnitServiceProvider.cs index 244cc8702b..59e3a80a92 100644 --- a/TUnit.Engine/Framework/TUnitServiceProvider.cs +++ b/TUnit.Engine/Framework/TUnitServiceProvider.cs @@ -81,12 +81,12 @@ public TUnitServiceProvider(IExtension extension, TestGrouper = Register(new TestGrouper()); - AssemblyHookOrchestrator = Register(new AssemblyHookOrchestrator(instanceTracker, HooksCollector)); + AssemblyHookOrchestrator = Register(new AssemblyHookOrchestrator(instanceTracker, HooksCollector, Logger)); TestDiscoveryHookOrchestrator = Register(new TestDiscoveryHookOrchestrator(HooksCollector, stringFilter)); TestSessionHookOrchestrator = Register(new TestSessionHookOrchestrator(HooksCollector, AssemblyHookOrchestrator, stringFilter)); - var classHookOrchestrator = Register(new ClassHookOrchestrator(instanceTracker, HooksCollector)); + var classHookOrchestrator = Register(new ClassHookOrchestrator(instanceTracker, HooksCollector, Logger)); var testHookOrchestrator = Register(new TestHookOrchestrator(HooksCollector)); diff --git a/TUnit.Engine/Framework/TUnitTestFramework.cs b/TUnit.Engine/Framework/TUnitTestFramework.cs index bb79117f85..1d505fef61 100644 --- a/TUnit.Engine/Framework/TUnitTestFramework.cs +++ b/TUnit.Engine/Framework/TUnitTestFramework.cs @@ -86,18 +86,9 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) foreach (var beforeDiscoveryHook in beforeDiscoveryHooks) { - if (beforeDiscoveryHook.IsSynchronous) - { - await logger.LogDebugAsync("Executing synchronous [Before(TestDiscovery)] hook"); - - beforeDiscoveryHook.Execute(beforeContext, CancellationToken.None); - } - else - { - await logger.LogDebugAsync("Executing asynchronous [Before(TestDiscovery)] hook"); + await logger.LogDebugAsync("Executing [Before(TestDiscovery)] hook"); - await beforeDiscoveryHook.ExecuteAsync(beforeContext, CancellationToken.None); - } + await beforeDiscoveryHook.ExecuteAsync(beforeContext, CancellationToken.None); } var allDiscoveredTests = serviceProvider.TestDiscoverer.GetTests(serviceProvider.EngineCancellationToken.Token); @@ -107,18 +98,9 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) foreach (var afterDiscoveryHook in afterDiscoveryHooks) { - if (afterDiscoveryHook.IsSynchronous) - { - await logger.LogDebugAsync("Executing asynchronous [After(TestDiscovery)] hook"); - - afterDiscoveryHook.Execute(afterContext, CancellationToken.None); - } - else - { - await logger.LogDebugAsync("Executing asynchronous [After(TestDiscovery)] hook"); + await logger.LogDebugAsync("Executing [After(TestDiscovery)] hook"); - await afterDiscoveryHook.ExecuteAsync(afterContext, CancellationToken.None); - } + await afterDiscoveryHook.ExecuteAsync(afterContext, CancellationToken.None); } var filteredTests = await serviceProvider.TestDiscoverer @@ -145,18 +127,9 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) foreach (var beforeSessionHook in beforeSessionHooks) { - if (beforeSessionHook.IsSynchronous) - { - await logger.LogDebugAsync("Executing synchronous [Before(TestSession)] hook"); - - beforeSessionHook.Execute(testSessionContext, context.CancellationToken); - } - else - { - await logger.LogDebugAsync("Executing asynchronous [Before(TestSession)] hook"); + await logger.LogDebugAsync("Executing [Before(TestSession)] hook"); - await beforeSessionHook.ExecuteAsync(testSessionContext, context.CancellationToken); - } + await beforeSessionHook.ExecuteAsync(testSessionContext, context.CancellationToken); } await serviceProvider.TestsExecutor.ExecuteAsync(filteredTests, runTestExecutionRequest.Filter, @@ -169,18 +142,9 @@ await serviceProvider.TestsExecutor.ExecuteAsync(filteredTests, runTestExecution foreach (var afterSessionHook in afterSessionHooks) { - if (afterSessionHook.IsSynchronous) - { - await logger.LogDebugAsync("Executing synchronous [After(TestSession)] hook"); - - afterSessionHook.Execute(testSessionContext, context.CancellationToken); - } - else - { - await logger.LogDebugAsync("Executing asynchronous [After(TestSession)] hook"); + await logger.LogDebugAsync("Executing [After(TestSession)] hook"); - await afterSessionHook.ExecuteAsync(testSessionContext, context.CancellationToken); - } + await afterSessionHook.ExecuteAsync(testSessionContext, context.CancellationToken); } foreach (var artifact in testSessionContext.Artifacts) diff --git a/TUnit.Engine/Hooks/AssemblyHookOrchestrator.cs b/TUnit.Engine/Hooks/AssemblyHookOrchestrator.cs index 075691d407..039acc6daa 100644 --- a/TUnit.Engine/Hooks/AssemblyHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/AssemblyHookOrchestrator.cs @@ -4,11 +4,13 @@ using TUnit.Core.Data; using TUnit.Core.Extensions; using TUnit.Core.Hooks; +using TUnit.Core.Logging; +using TUnit.Engine.Logging; using TUnit.Engine.Services; namespace TUnit.Engine.Hooks; -internal class AssemblyHookOrchestrator(InstanceTracker instanceTracker, HooksCollector hooksCollector) +internal class AssemblyHookOrchestrator(InstanceTracker instanceTracker, HooksCollector hooksCollector, TUnitFrameworkLogger logger) { private readonly ConcurrentDictionary _assemblyHookContexts = new(); @@ -16,6 +18,45 @@ internal class AssemblyHookOrchestrator(InstanceTracker instanceTracker, HooksCo internal GetOnlyDictionary> PreviouslyRunBeforeHooks { get; } = new(); + public async Task> ExecuteBeforeAssemblyHooks(TestContext testContext) + { + var assemblyHookContext = GetContext(testContext.TestDetails.TestClass.Type.Assembly); + + var assemblyHooksTaskCompletionSource = PreviouslyRunBeforeHooks.GetOrAdd( + testContext.TestDetails.TestClass.Type.Assembly, _ => new TaskCompletionSource(), + out var assemblyHooksTaskPreviouslyExisted); + + if (assemblyHooksTaskPreviouslyExisted) + { + await assemblyHooksTaskCompletionSource.Task; + return assemblyHookContext.ExecutionContexts; + } + + try + { + var beforeAssemblyHooks = CollectBeforeHooks(testContext.TestDetails.TestClass.Type.Assembly); + + AssemblyHookContext.Current = assemblyHookContext; + + foreach (var beforeHook in beforeAssemblyHooks) + { + await logger.LogDebugAsync("Executing [Before(Assembly)] hook"); + + await beforeHook.ExecuteAsync(assemblyHookContext, CancellationToken.None); + } + + AssemblyHookContext.Current = null; + assemblyHooksTaskCompletionSource.SetResult(false); + } + catch (Exception e) + { + assemblyHooksTaskCompletionSource.SetException(e); + throw; + } + + return assemblyHookContext.ExecutionContexts; + } + public IEnumerable> CollectBeforeHooks(Assembly assembly) { _beforeHooksReached.GetOrAdd(assembly, true); diff --git a/TUnit.Engine/Hooks/ClassHookOrchestrator.cs b/TUnit.Engine/Hooks/ClassHookOrchestrator.cs index 604e722048..883cb51b88 100644 --- a/TUnit.Engine/Hooks/ClassHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/ClassHookOrchestrator.cs @@ -3,11 +3,13 @@ using TUnit.Core.Data; using TUnit.Core.Extensions; using TUnit.Core.Hooks; +using TUnit.Core.Logging; +using TUnit.Engine.Logging; using TUnit.Engine.Services; namespace TUnit.Engine.Hooks; -internal class ClassHookOrchestrator(InstanceTracker instanceTracker, HooksCollector hooksCollector) +internal class ClassHookOrchestrator(InstanceTracker instanceTracker, HooksCollector hooksCollector, TUnitFrameworkLogger logger) { private readonly ConcurrentDictionary _classHookContexts = new(); @@ -39,6 +41,47 @@ public IEnumerable> CollectBeforeHooks(Type t } } } + + public async Task> ExecuteBeforeClassHooks(TestContext testContext) + { + var classHookContext = GetContext(testContext.TestDetails.TestClass.Type); + + var classHooksTaskCompletionSource = PreviouslyRunBeforeHooks.GetOrAdd( + testContext.TestDetails.TestClass.Type, _ => new TaskCompletionSource(), + out var classHooksTaskPreviouslyExisted); + + if (classHooksTaskPreviouslyExisted) + { + await classHooksTaskCompletionSource.Task; + return classHookContext.ExecutionContexts; + } + + try + { + var beforeClassHooks = CollectBeforeHooks(testContext.TestDetails.TestClass.Type); + + ClassHookContext.Current = classHookContext; + + foreach (var beforeHook in beforeClassHooks) + { + { + await logger.LogDebugAsync("Executing [Before(Class)] hook"); + + await beforeHook.ExecuteAsync(classHookContext, CancellationToken.None); + } + } + + ClassHookContext.Current = null; + classHooksTaskCompletionSource.SetResult(false); + } + catch (Exception e) + { + classHooksTaskCompletionSource.SetException(e); + throw; + } + + return classHookContext.ExecutionContexts; + } public IEnumerable> CollectAfterHooks( TestContext testContext, diff --git a/TUnit.Engine/Services/SingleTestExecutor.cs b/TUnit.Engine/Services/SingleTestExecutor.cs index 5c01f2bd15..8d0cb83102 100644 --- a/TUnit.Engine/Services/SingleTestExecutor.cs +++ b/TUnit.Engine/Services/SingleTestExecutor.cs @@ -201,18 +201,9 @@ private async Task ExecuteStaticAfterHooks(DiscoveredTest test, TestContext test foreach (var afterHook in afterClassHooks) { - if (afterHook.IsSynchronous) - { - await logger.LogDebugAsync("Executing synchronous [After(Class)] hook"); + await logger.LogDebugAsync("Executing [After(Class)] hook"); - RunHelpers.RunSafely(() => afterHook.Execute(classHookContext, CancellationToken.None), cleanUpExceptions); - } - else - { - await logger.LogDebugAsync("Executing asynchronous [After(Class)] hook"); - - await RunHelpers.RunSafelyAsync(() => afterHook.ExecuteAsync(classHookContext, CancellationToken.None), cleanUpExceptions); - } + await RunHelpers.RunSafelyAsync(() => afterHook.ExecuteAsync(classHookContext, CancellationToken.None), cleanUpExceptions); } ClassHookContext.Current = null; @@ -224,18 +215,9 @@ private async Task ExecuteStaticAfterHooks(DiscoveredTest test, TestContext test foreach (var afterHook in afterAssemblyHooks) { - if (afterHook.IsSynchronous) - { - await logger.LogDebugAsync("Executing synchronous [After(Assembly)] hook"); + await logger.LogDebugAsync("Executing [After(Assembly)] hook"); - RunHelpers.RunSafely(() => afterHook.Execute(assemblyHookContext, CancellationToken.None), cleanUpExceptions); - } - else - { - await logger.LogDebugAsync("Executing asynchronous [After(Assembly)] hook"); - - await RunHelpers.RunSafelyAsync(() => afterHook.ExecuteAsync(assemblyHookContext, CancellationToken.None), cleanUpExceptions); - } + await RunHelpers.RunSafelyAsync(() => afterHook.ExecuteAsync(assemblyHookContext, CancellationToken.None), cleanUpExceptions); } AssemblyHookContext.Current = null; diff --git a/TUnit.Engine/Services/TestInvoker.cs b/TUnit.Engine/Services/TestInvoker.cs index 86775c75b1..b937d202e4 100644 --- a/TUnit.Engine/Services/TestInvoker.cs +++ b/TUnit.Engine/Services/TestInvoker.cs @@ -35,22 +35,11 @@ public async Task Invoke(DiscoveredTest discoveredTest, CancellationToken cancel foreach (var executableHook in beforeHooks) { - if (executableHook.IsSynchronous) - { - await logger.LogDebugAsync("Executing synchronous [Before(Test)] hook"); - - Timings.Record($"Before(Test): {executableHook.Name}", discoveredTest.TestContext, () => - executableHook.Execute(discoveredTest.TestContext, cancellationToken) - ); - } - else - { - await logger.LogDebugAsync("Executing asynchronous [Before(Test)] hook"); + await logger.LogDebugAsync("Executing [Before(Test)] hook"); - await Timings.Record($"Before(Test): {executableHook.Name}", discoveredTest.TestContext, () => - executableHook.ExecuteAsync(discoveredTest.TestContext, cancellationToken) - ); - } + await Timings.Record($"Before(Test): {executableHook.Name}", discoveredTest.TestContext, () => + executableHook.ExecuteAsync(discoveredTest.TestContext, cancellationToken) + ); } foreach (var testStartEventsObject in discoveredTest.TestContext.GetTestStartEventObjects()) @@ -85,17 +74,8 @@ private async ValueTask DisposeTest(TestContext testContext, List cle foreach (var executableHook in afterHooks) { - if (executableHook.IsSynchronous) - { - await logger.LogDebugAsync("Executing synchronous [After(Test)] hook"); - - Timings.Record($"After(Test): {executableHook.Name}", testContext, () => - executableHook.Execute(testContext, CancellationToken.None) - ); - } - else { - await logger.LogDebugAsync("Executing asynchronous [After(Test)] hook"); + await logger.LogDebugAsync("Executing [After(Test)] hook"); await Timings.Record($"After(Test): {executableHook.Name}", testContext, () => executableHook.ExecuteAsync(testContext, CancellationToken.None) diff --git a/TUnit.Engine/Services/TestsExecutor.cs b/TUnit.Engine/Services/TestsExecutor.cs index 66fee7dc36..0e11736025 100644 --- a/TUnit.Engine/Services/TestsExecutor.cs +++ b/TUnit.Engine/Services/TestsExecutor.cs @@ -7,7 +7,6 @@ using Polyfills; using TUnit.Core; using TUnit.Core.Helpers; -using TUnit.Core.Logging; using TUnit.Engine.CommandLineProviders; using TUnit.Engine.Hooks; using TUnit.Engine.Logging; @@ -18,7 +17,6 @@ namespace TUnit.Engine.Services; internal class TestsExecutor { private readonly SingleTestExecutor _singleTestExecutor; - private readonly TUnitFrameworkLogger _logger; private readonly ICommandLineOptions _commandLineOptions; private readonly EngineCancellationToken _engineCancellationToken; @@ -36,7 +34,6 @@ public TestsExecutor(SingleTestExecutor singleTestExecutor, EngineCancellationToken engineCancellationToken, AssemblyHookOrchestrator assemblyHookOrchestrator, ClassHookOrchestrator classHookOrchestrator) { _singleTestExecutor = singleTestExecutor; - _logger = logger; _commandLineOptions = commandLineOptions; _engineCancellationToken = engineCancellationToken; _assemblyHookOrchestrator = assemblyHookOrchestrator; @@ -111,9 +108,9 @@ await Task.Run(async delegate { if (test.TestContext.SkipReason == null) { - RestoreContexts(await ExecuteBeforeAssemblyHooks(test.TestContext)); + RestoreContexts(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); - RestoreContexts(await ExecuteBeforeClassHooks(test.TestContext)); + RestoreContexts(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); } await ProcessTest(test, filter, context, context.CancellationToken); @@ -141,9 +138,9 @@ private async Task ProcessCollection(IEnumerable queue, { if (test.TestContext.SkipReason == null) { - RestoreContexts(await ExecuteBeforeAssemblyHooks(test.TestContext)); + RestoreContexts(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); - RestoreContexts(await ExecuteBeforeClassHooks(test.TestContext)); + RestoreContexts(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); } await ProcessTest(test, filter, context, token); @@ -154,9 +151,9 @@ await queue { if (test.TestContext.SkipReason != null) { - RestoreContexts(await ExecuteBeforeAssemblyHooks(test.TestContext)); + RestoreContexts(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); - RestoreContexts(await ExecuteBeforeClassHooks(test.TestContext)); + RestoreContexts(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); } await ProcessTest(test, filter, context, context.CancellationToken); @@ -209,103 +206,6 @@ private async Task ProcessTest(DiscoveredTest test, } #endif - private async Task> ExecuteBeforeClassHooks(TestContext testContext) - { - var classHookContext = _classHookOrchestrator.GetContext(testContext.TestDetails.TestClass.Type); - - var classHooksTaskCompletionSource = _classHookOrchestrator.PreviouslyRunBeforeHooks.GetOrAdd( - testContext.TestDetails.TestClass.Type, _ => new TaskCompletionSource(), - out var classHooksTaskPreviouslyExisted); - - if (classHooksTaskPreviouslyExisted) - { - await classHooksTaskCompletionSource.Task; - return classHookContext.ExecutionContexts; - } - - try - { - var beforeClassHooks = - _classHookOrchestrator.CollectBeforeHooks(testContext.TestDetails.TestClass.Type); - - ClassHookContext.Current = classHookContext; - - foreach (var beforeHook in beforeClassHooks) - { - if (beforeHook.IsSynchronous) - { - await _logger.LogDebugAsync("Executing synchronous [Before(Class)] hook"); - - beforeHook.Execute(classHookContext, CancellationToken.None); - } - else - { - await _logger.LogDebugAsync("Executing asynchronous [Before(Class)] hook"); - - await beforeHook.ExecuteAsync(classHookContext, CancellationToken.None); - } - } - - ClassHookContext.Current = null; - classHooksTaskCompletionSource.SetResult(false); - } - catch (Exception e) - { - classHooksTaskCompletionSource.SetException(e); - throw; - } - - return classHookContext.ExecutionContexts; - } - - private async Task> ExecuteBeforeAssemblyHooks(TestContext testContext) - { - var assemblyHookContext =_assemblyHookOrchestrator.GetContext(testContext.TestDetails.TestClass.Type.Assembly); - - var assemblyHooksTaskCompletionSource = _assemblyHookOrchestrator.PreviouslyRunBeforeHooks.GetOrAdd( - testContext.TestDetails.TestClass.Type.Assembly, _ => new TaskCompletionSource(), - out var assemblyHooksTaskPreviouslyExisted); - - if (assemblyHooksTaskPreviouslyExisted) - { - await assemblyHooksTaskCompletionSource.Task; - return assemblyHookContext.ExecutionContexts; - } - - try - { - var beforeAssemblyHooks = _assemblyHookOrchestrator.CollectBeforeHooks(testContext.TestDetails.TestClass.Type.Assembly); - - AssemblyHookContext.Current = assemblyHookContext; - - foreach (var beforeHook in beforeAssemblyHooks) - { - if (beforeHook.IsSynchronous) - { - await _logger.LogDebugAsync("Executing synchronous [Before(Assembly)] hook"); - - beforeHook.Execute(assemblyHookContext, CancellationToken.None); - } - else - { - await _logger.LogDebugAsync("Executing asynchronous [Before(Assembly)] hook"); - - await beforeHook.ExecuteAsync(assemblyHookContext, CancellationToken.None); - } - } - - AssemblyHookContext.Current = null; - assemblyHooksTaskCompletionSource.SetResult(false); - } - catch (Exception e) - { - assemblyHooksTaskCompletionSource.SetException(e); - throw; - } - - return assemblyHookContext.ExecutionContexts; - } - private int GetParallelTestsLimit() { if (_commandLineOptions.TryGetOptionArgumentList(MaximumParallelTestsCommandProvider.MaximumParallelTests, diff --git a/TUnit.TestProject/Bugs/1914/AsyncHookTests.cs b/TUnit.TestProject/Bugs/1914/AsyncHookTests.cs new file mode 100644 index 0000000000..ccb19c8b4d --- /dev/null +++ b/TUnit.TestProject/Bugs/1914/AsyncHookTests.cs @@ -0,0 +1,129 @@ +using TUnit.Assertions; +using TUnit.Assertions.Extensions; + +namespace TUnit.TestProject.Bugs._1914; + +public class AsyncHookTests +{ + private static readonly AsyncLocal _0BeforeTestDiscoveryLocal = new(); + private static readonly AsyncLocal _0BeforeTestDiscoveryLocal2 = new(); + + private static readonly AsyncLocal _1BeforeTestSessionLocal = new(); + private static readonly AsyncLocal _1BeforeTestSessionLocal2 = new(); + + private static readonly AsyncLocal _2BeforeAssemblyLocal = new(); + private static readonly AsyncLocal _2BeforeAssemblyLocal2 = new(); + + private static readonly AsyncLocal _3BeforeClassLocal = new(); + private static readonly AsyncLocal _3BeforeClassLocal2 = new(); + + private static readonly AsyncLocal _4BeforeTestLocal = new(); + private static readonly AsyncLocal _4BeforeTestLocal2 = new(); + + [BeforeEvery(TestDiscovery)] + public static async Task BeforeTestDiscovery(BeforeTestDiscoveryContext context) + { + await Task.CompletedTask; + _0BeforeTestDiscoveryLocal.Value = "BeforeTestDiscovery"; + context.FlowAsyncLocalValues(); + } + + [BeforeEvery(TestDiscovery)] + public static async Task BeforeTestDiscovery2(BeforeTestDiscoveryContext context) + { + await Task.CompletedTask; + _0BeforeTestDiscoveryLocal2.Value = "BeforeTestDiscovery2"; + context.FlowAsyncLocalValues(); + } + + [BeforeEvery(TestSession)] + public static async Task BeforeTestSession(TestSessionContext context) + { + await Task.CompletedTask; + _1BeforeTestSessionLocal.Value = "BeforeTestSession"; + context.FlowAsyncLocalValues(); + } + + [BeforeEvery(TestSession)] + public static async Task BeforeTestSession2(TestSessionContext context) + { + await Task.CompletedTask; + _1BeforeTestSessionLocal2.Value = "BeforeTestSession2"; + context.FlowAsyncLocalValues(); + } + + [BeforeEvery(Assembly)] + public static async Task BeforeAssembly(AssemblyHookContext context) + { + await Task.CompletedTask; + _2BeforeAssemblyLocal.Value = "BeforeAssembly"; + context.FlowAsyncLocalValues(); + } + + [BeforeEvery(Assembly)] + public static async Task BeforeAssembly2(AssemblyHookContext context) + { + await Task.CompletedTask; + _2BeforeAssemblyLocal2.Value = "BeforeAssembly2"; + context.FlowAsyncLocalValues(); + } + + [BeforeEvery(Class)] + public static async Task BeforeClass(ClassHookContext context) + { + await Task.CompletedTask; + _3BeforeClassLocal.Value = "BeforeClass"; + context.FlowAsyncLocalValues(); + } + + [BeforeEvery(Class)] + public static async Task BeforeClass2(ClassHookContext context) + { + await Task.CompletedTask; + _3BeforeClassLocal2.Value = "BeforeClass2"; + context.FlowAsyncLocalValues(); + } + + [BeforeEvery(Test)] + public static async Task BeforeTest(TestContext context) + { + await Task.CompletedTask; + _4BeforeTestLocal.Value = "BeforeTest"; + context.FlowAsyncLocalValues(); + } + + [BeforeEvery(Test)] + public static async Task BeforeTest2(TestContext context) + { + await Task.CompletedTask; + _4BeforeTestLocal2.Value = "BeforeTest2"; + context.FlowAsyncLocalValues(); + } + + + [Test] + [Arguments(1)] + [Arguments(2)] + [Arguments(3)] + [Arguments(4)] + [Arguments(5)] + [Arguments(6)] + [Arguments(7)] + [Arguments(8)] + public async Task TestAsyncLocal(int i) + { + using var _ = Assert.Multiple(); + + await Assert.That(_0BeforeTestDiscoveryLocal.Value).IsEqualTo("BeforeTestDiscovery"); + await Assert.That(_1BeforeTestSessionLocal.Value).IsEqualTo("BeforeTestSession"); + await Assert.That(_2BeforeAssemblyLocal.Value).IsEqualTo("BeforeAssembly"); + await Assert.That(_3BeforeClassLocal.Value).IsEqualTo("BeforeClass"); + await Assert.That(_4BeforeTestLocal.Value).IsEqualTo("BeforeTest"); + + await Assert.That(_0BeforeTestDiscoveryLocal2.Value).IsEqualTo("BeforeTestDiscovery2"); + await Assert.That(_1BeforeTestSessionLocal2.Value).IsEqualTo("BeforeTestSession2"); + await Assert.That(_2BeforeAssemblyLocal2.Value).IsEqualTo("BeforeAssembly2"); + await Assert.That(_3BeforeClassLocal2.Value).IsEqualTo("BeforeClass2"); + await Assert.That(_4BeforeTestLocal2.Value).IsEqualTo("BeforeTest2"); + } +} \ No newline at end of file diff --git a/TUnit.TestProject/Bugs/1914/Tests.cs b/TUnit.TestProject/Bugs/1914/SyncHookTests.cs similarity index 99% rename from TUnit.TestProject/Bugs/1914/Tests.cs rename to TUnit.TestProject/Bugs/1914/SyncHookTests.cs index 26b0cb662c..50ed607862 100644 --- a/TUnit.TestProject/Bugs/1914/Tests.cs +++ b/TUnit.TestProject/Bugs/1914/SyncHookTests.cs @@ -3,7 +3,7 @@ namespace TUnit.TestProject.Bugs._1914; -public class Tests +public class SyncHookTests { private static readonly AsyncLocal _0BeforeTestDiscoveryLocal = new(); private static readonly AsyncLocal _0BeforeTestDiscoveryLocal2 = new(); diff --git a/docs/docs/tutorial-extras/setup.md b/docs/docs/tutorial-extras/setup.md index edab76b013..2012214f6f 100644 --- a/docs/docs/tutorial-extras/setup.md +++ b/docs/docs/tutorial-extras/setup.md @@ -82,3 +82,19 @@ public class MyTestClass } } ``` +## AsyncLocal + +If you are wanting to set AsyncLocal values within your `[Before(...)]` hooks, this is supported. + +But to propagate the values into the test framework, you must call `context.FlowAsyncLocalValues()` - Where `context` is the relevant context object injected into your hook method. + +E.g. + +```csharp + [BeforeEvery(Class)] + public static void BeforeClass(ClassHookContext context) + { + _myAsyncLocal.Value = "Some Value"; + context.FlowAsyncLocalValues(); + } +``` \ No newline at end of file From d2ccf0eb19a4eb79aa3918b2bf9825d3fd785f80 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 26 Feb 2025 22:26:51 +0000 Subject: [PATCH 08/17] Fix compilation error --- .../Writers/Hooks/TestHooksWriter.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs index 04d7b44894..8830df0258 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs @@ -56,17 +56,12 @@ public static void Execute(SourceCodeWriter sourceBuilder, HooksDataModel model) sourceBuilder.WriteTabs(); sourceBuilder.Write("MethodInfo = "); SourceInformationWriter.GenerateMethodInformation(sourceBuilder, model.Context, model.ClassType, model.Method, null, ','); - - if (model.IsVoid) + + + if (model.ClassType.IsGenericDefinition()) { - if (model.ClassType.IsGenericDefinition()) - { - sourceBuilder.WriteLine($"Body = (classInstance, context, cancellationToken) => classInstance.GetType().GetMethod(\"{model.MethodName}\", [{string.Join(", ", model.ParameterTypes.Select(x => $"typeof({x})"))}]).Invoke(classInstance, {GetArgsOrEmptyArray(model)}),"); - } - else - { - sourceBuilder.WriteLine($"Body = (classInstance, context, cancellationToken) => (({model.FullyQualifiedTypeName})classInstance).{model.MethodName}({GetArgs(model)}),"); - } + sourceBuilder.WriteLine( + $"Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => classInstance.GetType().GetMethod(\"{model.MethodName}\", [{string.Join(", ", model.ParameterTypes.Select(x => $"typeof({x})"))}]).Invoke(classInstance, {GetArgsOrEmptyArray(model)})),"); } else { From 9693ebed1c1c65aeb6b47263acf2c0264c786e79 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 26 Feb 2025 22:40:24 +0000 Subject: [PATCH 09/17] WIP --- .../Framework/TUnitServiceProvider.cs | 2 +- .../Helpers/ExecutionContextHelper.cs | 18 +++++++++++++++ TUnit.Engine/Hooks/TestHookOrchestrator.cs | 23 ++++++++++++++++++- TUnit.Engine/Services/TestInvoker.cs | 17 +------------- TUnit.Engine/Services/TestsExecutor.cs | 23 ++++++------------- TUnit.TestProject/AsyncLocalTest.cs | 1 + TUnit.TestProject/Bugs/1914/AsyncHookTests.cs | 4 +++- TUnit.TestProject/Bugs/1914/SyncHookTests.cs | 4 +++- 8 files changed, 56 insertions(+), 36 deletions(-) create mode 100644 TUnit.Engine/Helpers/ExecutionContextHelper.cs diff --git a/TUnit.Engine/Framework/TUnitServiceProvider.cs b/TUnit.Engine/Framework/TUnitServiceProvider.cs index 59e3a80a92..ea7dacb61b 100644 --- a/TUnit.Engine/Framework/TUnitServiceProvider.cs +++ b/TUnit.Engine/Framework/TUnitServiceProvider.cs @@ -88,7 +88,7 @@ public TUnitServiceProvider(IExtension extension, var classHookOrchestrator = Register(new ClassHookOrchestrator(instanceTracker, HooksCollector, Logger)); - var testHookOrchestrator = Register(new TestHookOrchestrator(HooksCollector)); + var testHookOrchestrator = Register(new TestHookOrchestrator(HooksCollector, Logger)); var testRegistrar = Register(new TestRegistrar(instanceTracker, AssemblyHookOrchestrator, classHookOrchestrator)); TestDiscoverer = Register(new TUnitTestDiscoverer(testsConstructor, testFilterService, TestGrouper, testRegistrar, TUnitMessageBus, Logger, extension)); diff --git a/TUnit.Engine/Helpers/ExecutionContextHelper.cs b/TUnit.Engine/Helpers/ExecutionContextHelper.cs new file mode 100644 index 0000000000..ba051e1308 --- /dev/null +++ b/TUnit.Engine/Helpers/ExecutionContextHelper.cs @@ -0,0 +1,18 @@ +using TUnit.Core; + +namespace TUnit.Engine.Helpers; + +internal static class ExecutionContextHelper +{ + public static void RestoreContexts(Context context) => RestoreContexts(context.ExecutionContexts); + + public static void RestoreContexts(List executionContexts) + { +#if NET + foreach (var executionContext in executionContexts) + { + ExecutionContext.Restore(executionContext); + } +#endif + } +} \ No newline at end of file diff --git a/TUnit.Engine/Hooks/TestHookOrchestrator.cs b/TUnit.Engine/Hooks/TestHookOrchestrator.cs index 8eb057407f..f31e217ed0 100644 --- a/TUnit.Engine/Hooks/TestHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/TestHookOrchestrator.cs @@ -1,5 +1,8 @@ using TUnit.Core; using TUnit.Core.Hooks; +using TUnit.Core.Logging; +using TUnit.Engine.Helpers; +using TUnit.Engine.Logging; using TUnit.Engine.Services; namespace TUnit.Engine.Hooks; @@ -7,8 +10,26 @@ namespace TUnit.Engine.Hooks; #if !DEBUG [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] #endif -internal class TestHookOrchestrator(HooksCollector hooksCollector) +internal class TestHookOrchestrator(HooksCollector hooksCollector, TUnitFrameworkLogger logger) { + public async Task> ExecuteBeforeHooks(DiscoveredTest discoveredTest, CancellationToken cancellationToken) + { + var beforeHooks = CollectBeforeHooks( + discoveredTest.TestContext.TestDetails.ClassInstance, + discoveredTest); + + foreach (var executableHook in beforeHooks) + { + await logger.LogDebugAsync("Executing [Before(Test)] hook"); + + await Timings.Record($"Before(Test): {executableHook.Name}", discoveredTest.TestContext, () => + executableHook.ExecuteAsync(discoveredTest.TestContext, cancellationToken) + ); + } + + return discoveredTest.TestContext.ExecutionContexts; + } + internal IEnumerable> CollectBeforeHooks(object classInstance, DiscoveredTest discoveredTest) { var testClassType = classInstance.GetType(); diff --git a/TUnit.Engine/Services/TestInvoker.cs b/TUnit.Engine/Services/TestInvoker.cs index b937d202e4..cc8cb7e00f 100644 --- a/TUnit.Engine/Services/TestInvoker.cs +++ b/TUnit.Engine/Services/TestInvoker.cs @@ -25,22 +25,7 @@ public async Task Invoke(DiscoveredTest discoveredTest, CancellationToken cancel await onInitializeObject.InitializeAsync(); } - // In order to set async-local values properly in a before hook, the method doing so needs to have the parents execution context. - // This is achievable by: - // - Calling it here (and not in a child/sibling method to the actual test body) as this method calls the test body so is considered a parent - // - Running synchronous hooks synchronously, so they don't generate a new child execution context which happens in async methods - var beforeHooks = testHookOrchestrator.CollectBeforeHooks( - discoveredTest.TestContext.TestDetails.ClassInstance, - discoveredTest); - - foreach (var executableHook in beforeHooks) - { - await logger.LogDebugAsync("Executing [Before(Test)] hook"); - - await Timings.Record($"Before(Test): {executableHook.Name}", discoveredTest.TestContext, () => - executableHook.ExecuteAsync(discoveredTest.TestContext, cancellationToken) - ); - } + ExecutionContextHelper.RestoreContexts(await testHookOrchestrator.ExecuteBeforeHooks(discoveredTest, cancellationToken)); foreach (var testStartEventsObject in discoveredTest.TestContext.GetTestStartEventObjects()) { diff --git a/TUnit.Engine/Services/TestsExecutor.cs b/TUnit.Engine/Services/TestsExecutor.cs index 0e11736025..f7a7e9af71 100644 --- a/TUnit.Engine/Services/TestsExecutor.cs +++ b/TUnit.Engine/Services/TestsExecutor.cs @@ -8,6 +8,7 @@ using TUnit.Core; using TUnit.Core.Helpers; using TUnit.Engine.CommandLineProviders; +using TUnit.Engine.Helpers; using TUnit.Engine.Hooks; using TUnit.Engine.Logging; using TUnit.Engine.Models; @@ -108,9 +109,9 @@ await Task.Run(async delegate { if (test.TestContext.SkipReason == null) { - RestoreContexts(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); + ExecutionContextHelper.RestoreContexts(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); - RestoreContexts(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); + ExecutionContextHelper.RestoreContexts(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); } await ProcessTest(test, filter, context, context.CancellationToken); @@ -138,9 +139,9 @@ private async Task ProcessCollection(IEnumerable queue, { if (test.TestContext.SkipReason == null) { - RestoreContexts(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); + ExecutionContextHelper.RestoreContexts(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); - RestoreContexts(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); + ExecutionContextHelper.RestoreContexts(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); } await ProcessTest(test, filter, context, token); @@ -151,9 +152,9 @@ await queue { if (test.TestContext.SkipReason != null) { - RestoreContexts(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); + ExecutionContextHelper.RestoreContexts(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); - RestoreContexts(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); + ExecutionContextHelper.RestoreContexts(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); } await ProcessTest(test, filter, context, context.CancellationToken); @@ -162,16 +163,6 @@ await queue #endif } - private void RestoreContexts(List executionContexts) - { -#if NET - foreach (var executionContext in executionContexts) - { - ExecutionContext.Restore(executionContext); - } -#endif - } - #if NET private async ValueTask ProcessTest(DiscoveredTest test, ITestExecutionFilter? filter, ExecuteRequestContext context, CancellationToken cancellationToken) diff --git a/TUnit.TestProject/AsyncLocalTest.cs b/TUnit.TestProject/AsyncLocalTest.cs index 8e8287beff..de574d06ea 100644 --- a/TUnit.TestProject/AsyncLocalTest.cs +++ b/TUnit.TestProject/AsyncLocalTest.cs @@ -11,6 +11,7 @@ public class AsyncLocalTest [Before(Test)] public void Before(TestContext context) { + context.FlowAsyncLocalValues(); _asyncLocalValue.Value = "123"; } diff --git a/TUnit.TestProject/Bugs/1914/AsyncHookTests.cs b/TUnit.TestProject/Bugs/1914/AsyncHookTests.cs index ccb19c8b4d..c23f823ed1 100644 --- a/TUnit.TestProject/Bugs/1914/AsyncHookTests.cs +++ b/TUnit.TestProject/Bugs/1914/AsyncHookTests.cs @@ -1,8 +1,10 @@ -using TUnit.Assertions; +using System.Diagnostics.CodeAnalysis; +using TUnit.Assertions; using TUnit.Assertions.Extensions; namespace TUnit.TestProject.Bugs._1914; +[SuppressMessage("Usage", "TUnit0042:Global hooks should not be mixed with test classes to avoid confusion. Place them in their own class.")] public class AsyncHookTests { private static readonly AsyncLocal _0BeforeTestDiscoveryLocal = new(); diff --git a/TUnit.TestProject/Bugs/1914/SyncHookTests.cs b/TUnit.TestProject/Bugs/1914/SyncHookTests.cs index 50ed607862..cae79df907 100644 --- a/TUnit.TestProject/Bugs/1914/SyncHookTests.cs +++ b/TUnit.TestProject/Bugs/1914/SyncHookTests.cs @@ -1,8 +1,10 @@ -using TUnit.Assertions; +using System.Diagnostics.CodeAnalysis; +using TUnit.Assertions; using TUnit.Assertions.Extensions; namespace TUnit.TestProject.Bugs._1914; +[SuppressMessage("Usage", "TUnit0042:Global hooks should not be mixed with test classes to avoid confusion. Place them in their own class.")] public class SyncHookTests { private static readonly AsyncLocal _0BeforeTestDiscoveryLocal = new(); From 2953c62e7ac777e91f21bc6fb1fa1c660bc3df65 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 26 Feb 2025 23:52:08 +0000 Subject: [PATCH 10/17] Fix for sync hook tests --- .../BeforeHookAsyncLocalAnalyzerTests.cs | 4 +- .../BeforeHookAsyncLocalAnalyzer.cs | 2 +- TUnit.Analyzers/Resources.Designer.cs | 6 +-- TUnit.Core/Context.cs | 16 ++----- .../Framework/TUnitServiceProvider.cs | 4 +- TUnit.Engine/Framework/TUnitTestFramework.cs | 20 ++------ .../Helpers/ExecutionContextHelper.cs | 11 +++-- .../Hooks/AssemblyHookOrchestrator.cs | 6 +-- TUnit.Engine/Hooks/ClassHookOrchestrator.cs | 6 +-- .../Hooks/TestDiscoveryHookOrchestrator.cs | 19 +++++++- TUnit.Engine/Hooks/TestHookOrchestrator.cs | 6 ++- .../Hooks/TestSessionHookOrchestrator.cs | 19 +++++++- TUnit.Engine/Services/TestInvoker.cs | 2 +- TUnit.Engine/Services/TestsExecutor.cs | 12 ++--- TUnit.Engine/TUnitMessageBus.cs | 4 +- TUnit.TestProject/AsyncLocalTest.cs | 2 +- TUnit.TestProject/Bugs/1914/AsyncHookTests.cs | 46 +++++++++---------- TUnit.TestProject/Bugs/1914/SyncHookTests.cs | 46 +++++++++---------- 18 files changed, 122 insertions(+), 109 deletions(-) diff --git a/TUnit.Analyzers.Tests/BeforeHookAsyncLocalAnalyzerTests.cs b/TUnit.Analyzers.Tests/BeforeHookAsyncLocalAnalyzerTests.cs index 8ad21b2f8a..1727730ee1 100644 --- a/TUnit.Analyzers.Tests/BeforeHookAsyncLocalAnalyzerTests.cs +++ b/TUnit.Analyzers.Tests/BeforeHookAsyncLocalAnalyzerTests.cs @@ -34,7 +34,7 @@ public void MyTest() } [Test] - public async Task FlowAsyncLocalValues_No_Error() + public async Task AddAsyncLocalValues_No_Error() { await Verifier .VerifyAnalyzerAsync( @@ -52,7 +52,7 @@ public class MyClass public void MyTest(ClassHookContext context) { _asyncLocal.Value = 1; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); }|} } """); diff --git a/TUnit.Analyzers/BeforeHookAsyncLocalAnalyzer.cs b/TUnit.Analyzers/BeforeHookAsyncLocalAnalyzer.cs index 553704a370..d0543cbbde 100644 --- a/TUnit.Analyzers/BeforeHookAsyncLocalAnalyzer.cs +++ b/TUnit.Analyzers/BeforeHookAsyncLocalAnalyzer.cs @@ -100,7 +100,7 @@ private void CheckMethod(OperationAnalysisContext context, IMethodBodyOperation continue; } - if (invocationOperation.TargetMethod.Name == "FlowAsyncLocalValues") + if (invocationOperation.TargetMethod.Name == "AddAsyncLocalValues") { return; } diff --git a/TUnit.Analyzers/Resources.Designer.cs b/TUnit.Analyzers/Resources.Designer.cs index 3c2fc747f4..204a803be3 100644 --- a/TUnit.Analyzers/Resources.Designer.cs +++ b/TUnit.Analyzers/Resources.Designer.cs @@ -1302,7 +1302,7 @@ internal static string TUnit0046Title { } /// - /// Looks up a localized string similar to For AsyncLocal values set in before hooks, you must call `context.FlowAsyncLocalValues` to access them within tests.. + /// Looks up a localized string similar to For AsyncLocal values set in before hooks, you must call `context.AddAsyncLocalValues` to access them within tests.. /// internal static string TUnit0047Description { get { @@ -1311,7 +1311,7 @@ internal static string TUnit0047Description { } /// - /// Looks up a localized string similar to For AsyncLocal values set in before hooks, you must call `context.FlowAsyncLocalValues` to access them within tests.. + /// Looks up a localized string similar to For AsyncLocal values set in before hooks, you must call `context.AddAsyncLocalValues` to access them within tests.. /// internal static string TUnit0047MessageFormat { get { @@ -1320,7 +1320,7 @@ internal static string TUnit0047MessageFormat { } /// - /// Looks up a localized string similar to Call `context.FlowAsyncLocalValues`. + /// Looks up a localized string similar to Call `context.AddAsyncLocalValues`. /// internal static string TUnit0047Title { get { diff --git a/TUnit.Core/Context.cs b/TUnit.Core/Context.cs index 010c96522b..063a23832c 100644 --- a/TUnit.Core/Context.cs +++ b/TUnit.Core/Context.cs @@ -30,16 +30,11 @@ internal Context() { } - internal List ExecutionContexts { get; } = []; + internal ExecutionContext? ExecutionContext { get; set; } - public void FlowAsyncLocalValues() + public void AddAsyncLocalValues() { - var executionContext = ExecutionContext.Capture(); - - if (executionContext != null) - { - ExecutionContexts.Add(executionContext); - } + ExecutionContext = ExecutionContext.Capture(); } public string GetStandardOutput() @@ -59,9 +54,6 @@ public TUnitLogger GetDefaultLogger() public void Dispose() { - foreach (var executionContext in ExecutionContexts) - { - executionContext.Dispose(); - } + ExecutionContext?.Dispose(); } } \ No newline at end of file diff --git a/TUnit.Engine/Framework/TUnitServiceProvider.cs b/TUnit.Engine/Framework/TUnitServiceProvider.cs index ea7dacb61b..436e8ae8c1 100644 --- a/TUnit.Engine/Framework/TUnitServiceProvider.cs +++ b/TUnit.Engine/Framework/TUnitServiceProvider.cs @@ -83,8 +83,8 @@ public TUnitServiceProvider(IExtension extension, AssemblyHookOrchestrator = Register(new AssemblyHookOrchestrator(instanceTracker, HooksCollector, Logger)); - TestDiscoveryHookOrchestrator = Register(new TestDiscoveryHookOrchestrator(HooksCollector, stringFilter)); - TestSessionHookOrchestrator = Register(new TestSessionHookOrchestrator(HooksCollector, AssemblyHookOrchestrator, stringFilter)); + TestDiscoveryHookOrchestrator = Register(new TestDiscoveryHookOrchestrator(HooksCollector, Logger, stringFilter)); + TestSessionHookOrchestrator = Register(new TestSessionHookOrchestrator(HooksCollector, AssemblyHookOrchestrator, Logger, stringFilter)); var classHookOrchestrator = Register(new ClassHookOrchestrator(instanceTracker, HooksCollector, Logger)); diff --git a/TUnit.Engine/Framework/TUnitTestFramework.cs b/TUnit.Engine/Framework/TUnitTestFramework.cs index 1d505fef61..fc0196175f 100644 --- a/TUnit.Engine/Framework/TUnitTestFramework.cs +++ b/TUnit.Engine/Framework/TUnitTestFramework.cs @@ -7,6 +7,7 @@ using Microsoft.Testing.Platform.Services; using TUnit.Core; using TUnit.Core.Logging; +using TUnit.Engine.Helpers; namespace TUnit.Engine.Framework; @@ -81,15 +82,7 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) { serviceProvider.EngineCancellationToken.Initialise(context.CancellationToken); - var beforeDiscoveryHooks = serviceProvider.TestDiscoveryHookOrchestrator.CollectBeforeHooks(); - var beforeContext = serviceProvider.TestDiscoveryHookOrchestrator.GetBeforeContext(); - - foreach (var beforeDiscoveryHook in beforeDiscoveryHooks) - { - await logger.LogDebugAsync("Executing [Before(TestDiscovery)] hook"); - - await beforeDiscoveryHook.ExecuteAsync(beforeContext, CancellationToken.None); - } + ExecutionContextHelper.RestoreContext(await serviceProvider.TestDiscoveryHookOrchestrator.RunBeforeTestDiscovery()); var allDiscoveredTests = serviceProvider.TestDiscoverer.GetTests(serviceProvider.EngineCancellationToken.Token); @@ -123,15 +116,8 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) TestFilter = stringFilter }; - var beforeSessionHooks = serviceProvider.TestSessionHookOrchestrator.CollectBeforeHooks(); + ExecutionContextHelper.RestoreContext(await serviceProvider.TestSessionHookOrchestrator.RunBeforeTestSession(context.CancellationToken)); - foreach (var beforeSessionHook in beforeSessionHooks) - { - await logger.LogDebugAsync("Executing [Before(TestSession)] hook"); - - await beforeSessionHook.ExecuteAsync(testSessionContext, context.CancellationToken); - } - await serviceProvider.TestsExecutor.ExecuteAsync(filteredTests, runTestExecutionRequest.Filter, context); diff --git a/TUnit.Engine/Helpers/ExecutionContextHelper.cs b/TUnit.Engine/Helpers/ExecutionContextHelper.cs index ba051e1308..acd20f4173 100644 --- a/TUnit.Engine/Helpers/ExecutionContextHelper.cs +++ b/TUnit.Engine/Helpers/ExecutionContextHelper.cs @@ -1,15 +1,18 @@ -using TUnit.Core; +using System.Runtime.CompilerServices; +using TUnit.Core; namespace TUnit.Engine.Helpers; internal static class ExecutionContextHelper { - public static void RestoreContexts(Context context) => RestoreContexts(context.ExecutionContexts); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RestoreContext(Context context) => RestoreContext(context.ExecutionContext); - public static void RestoreContexts(List executionContexts) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RestoreContext(ExecutionContext? executionContext) { #if NET - foreach (var executionContext in executionContexts) + if (executionContext != null) { ExecutionContext.Restore(executionContext); } diff --git a/TUnit.Engine/Hooks/AssemblyHookOrchestrator.cs b/TUnit.Engine/Hooks/AssemblyHookOrchestrator.cs index 039acc6daa..dfba7dadf2 100644 --- a/TUnit.Engine/Hooks/AssemblyHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/AssemblyHookOrchestrator.cs @@ -18,7 +18,7 @@ internal class AssemblyHookOrchestrator(InstanceTracker instanceTracker, HooksCo internal GetOnlyDictionary> PreviouslyRunBeforeHooks { get; } = new(); - public async Task> ExecuteBeforeAssemblyHooks(TestContext testContext) + public async Task ExecuteBeforeAssemblyHooks(TestContext testContext) { var assemblyHookContext = GetContext(testContext.TestDetails.TestClass.Type.Assembly); @@ -29,7 +29,7 @@ public async Task> ExecuteBeforeAssemblyHooks(TestContext if (assemblyHooksTaskPreviouslyExisted) { await assemblyHooksTaskCompletionSource.Task; - return assemblyHookContext.ExecutionContexts; + return assemblyHookContext.ExecutionContext; } try @@ -54,7 +54,7 @@ public async Task> ExecuteBeforeAssemblyHooks(TestContext throw; } - return assemblyHookContext.ExecutionContexts; + return assemblyHookContext.ExecutionContext; } public IEnumerable> CollectBeforeHooks(Assembly assembly) diff --git a/TUnit.Engine/Hooks/ClassHookOrchestrator.cs b/TUnit.Engine/Hooks/ClassHookOrchestrator.cs index 883cb51b88..6b6574691a 100644 --- a/TUnit.Engine/Hooks/ClassHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/ClassHookOrchestrator.cs @@ -42,7 +42,7 @@ public IEnumerable> CollectBeforeHooks(Type t } } - public async Task> ExecuteBeforeClassHooks(TestContext testContext) + public async Task ExecuteBeforeClassHooks(TestContext testContext) { var classHookContext = GetContext(testContext.TestDetails.TestClass.Type); @@ -53,7 +53,7 @@ public async Task> ExecuteBeforeClassHooks(TestContext te if (classHooksTaskPreviouslyExisted) { await classHooksTaskCompletionSource.Task; - return classHookContext.ExecutionContexts; + return classHookContext.ExecutionContext; } try @@ -80,7 +80,7 @@ public async Task> ExecuteBeforeClassHooks(TestContext te throw; } - return classHookContext.ExecutionContexts; + return classHookContext.ExecutionContext; } public IEnumerable> CollectAfterHooks( diff --git a/TUnit.Engine/Hooks/TestDiscoveryHookOrchestrator.cs b/TUnit.Engine/Hooks/TestDiscoveryHookOrchestrator.cs index 61c577b2d7..1f71553d57 100644 --- a/TUnit.Engine/Hooks/TestDiscoveryHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/TestDiscoveryHookOrchestrator.cs @@ -1,14 +1,31 @@ using TUnit.Core; using TUnit.Core.Hooks; +using TUnit.Core.Logging; +using TUnit.Engine.Logging; using TUnit.Engine.Services; namespace TUnit.Engine.Hooks; -internal class TestDiscoveryHookOrchestrator(HooksCollector hooksCollector, string? stringFilter) +internal class TestDiscoveryHookOrchestrator(HooksCollector hooksCollector, TUnitFrameworkLogger logger, string? stringFilter) { private BeforeTestDiscoveryContext? _beforeContext; private TestDiscoveryContext? _afterContext; + public async Task RunBeforeTestDiscovery() + { + var beforeDiscoveryHooks = CollectBeforeHooks(); + var beforeContext = GetBeforeContext(); + + foreach (var beforeDiscoveryHook in beforeDiscoveryHooks) + { + await logger.LogDebugAsync("Executing [Before(TestDiscovery)] hook"); + + await beforeDiscoveryHook.ExecuteAsync(beforeContext, CancellationToken.None); + } + + return beforeContext.ExecutionContext; + } + public IEnumerable> CollectBeforeHooks() { return hooksCollector.BeforeTestDiscoveryHooks diff --git a/TUnit.Engine/Hooks/TestHookOrchestrator.cs b/TUnit.Engine/Hooks/TestHookOrchestrator.cs index f31e217ed0..c4613ce276 100644 --- a/TUnit.Engine/Hooks/TestHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/TestHookOrchestrator.cs @@ -12,7 +12,7 @@ namespace TUnit.Engine.Hooks; #endif internal class TestHookOrchestrator(HooksCollector hooksCollector, TUnitFrameworkLogger logger) { - public async Task> ExecuteBeforeHooks(DiscoveredTest discoveredTest, CancellationToken cancellationToken) + public async Task ExecuteBeforeHooks(DiscoveredTest discoveredTest, CancellationToken cancellationToken) { var beforeHooks = CollectBeforeHooks( discoveredTest.TestContext.TestDetails.ClassInstance, @@ -25,9 +25,11 @@ public async Task> ExecuteBeforeHooks(DiscoveredTest disc await Timings.Record($"Before(Test): {executableHook.Name}", discoveredTest.TestContext, () => executableHook.ExecuteAsync(discoveredTest.TestContext, cancellationToken) ); + + ExecutionContextHelper.RestoreContext(discoveredTest.TestContext.ExecutionContext); } - return discoveredTest.TestContext.ExecutionContexts; + return discoveredTest.TestContext.ExecutionContext; } internal IEnumerable> CollectBeforeHooks(object classInstance, DiscoveredTest discoveredTest) diff --git a/TUnit.Engine/Hooks/TestSessionHookOrchestrator.cs b/TUnit.Engine/Hooks/TestSessionHookOrchestrator.cs index 42509e7b93..9b010b39f8 100644 --- a/TUnit.Engine/Hooks/TestSessionHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/TestSessionHookOrchestrator.cs @@ -1,13 +1,30 @@ using TUnit.Core; using TUnit.Core.Hooks; +using TUnit.Core.Logging; +using TUnit.Engine.Logging; using TUnit.Engine.Services; namespace TUnit.Engine.Hooks; -internal class TestSessionHookOrchestrator(HooksCollector hooksCollector, AssemblyHookOrchestrator assemblyHookOrchestrator, string? stringFilter) +internal class TestSessionHookOrchestrator(HooksCollector hooksCollector, AssemblyHookOrchestrator assemblyHookOrchestrator, TUnitFrameworkLogger logger, string? stringFilter) { private TestSessionContext? _context; + public async Task RunBeforeTestSession(CancellationToken cancellationToken) + { + var testSessionContext = GetContext(); + var beforeSessionHooks = CollectBeforeHooks(); + + foreach (var beforeSessionHook in beforeSessionHooks) + { + await logger.LogDebugAsync("Executing [Before(TestSession)] hook"); + + await beforeSessionHook.ExecuteAsync(testSessionContext, cancellationToken); + } + + return testSessionContext.ExecutionContext; + } + public IEnumerable> CollectBeforeHooks() { return hooksCollector.BeforeTestSessionHooks diff --git a/TUnit.Engine/Services/TestInvoker.cs b/TUnit.Engine/Services/TestInvoker.cs index cc8cb7e00f..79e50b2eb9 100644 --- a/TUnit.Engine/Services/TestInvoker.cs +++ b/TUnit.Engine/Services/TestInvoker.cs @@ -25,7 +25,7 @@ public async Task Invoke(DiscoveredTest discoveredTest, CancellationToken cancel await onInitializeObject.InitializeAsync(); } - ExecutionContextHelper.RestoreContexts(await testHookOrchestrator.ExecuteBeforeHooks(discoveredTest, cancellationToken)); + ExecutionContextHelper.RestoreContext(await testHookOrchestrator.ExecuteBeforeHooks(discoveredTest, cancellationToken)); foreach (var testStartEventsObject in discoveredTest.TestContext.GetTestStartEventObjects()) { diff --git a/TUnit.Engine/Services/TestsExecutor.cs b/TUnit.Engine/Services/TestsExecutor.cs index f7a7e9af71..21c76b6524 100644 --- a/TUnit.Engine/Services/TestsExecutor.cs +++ b/TUnit.Engine/Services/TestsExecutor.cs @@ -109,9 +109,9 @@ await Task.Run(async delegate { if (test.TestContext.SkipReason == null) { - ExecutionContextHelper.RestoreContexts(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); + ExecutionContextHelper.RestoreContext(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); - ExecutionContextHelper.RestoreContexts(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); + ExecutionContextHelper.RestoreContext(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); } await ProcessTest(test, filter, context, context.CancellationToken); @@ -139,9 +139,9 @@ private async Task ProcessCollection(IEnumerable queue, { if (test.TestContext.SkipReason == null) { - ExecutionContextHelper.RestoreContexts(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); + ExecutionContextHelper.RestoreContext(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); - ExecutionContextHelper.RestoreContexts(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); + ExecutionContextHelper.RestoreContext(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); } await ProcessTest(test, filter, context, token); @@ -152,9 +152,9 @@ await queue { if (test.TestContext.SkipReason != null) { - ExecutionContextHelper.RestoreContexts(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); + ExecutionContextHelper.RestoreContext(await _assemblyHookOrchestrator.ExecuteBeforeAssemblyHooks(test.TestContext)); - ExecutionContextHelper.RestoreContexts(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); + ExecutionContextHelper.RestoreContext(await _classHookOrchestrator.ExecuteBeforeClassHooks(test.TestContext)); } await ProcessTest(test, filter, context, context.CancellationToken); diff --git a/TUnit.Engine/TUnitMessageBus.cs b/TUnit.Engine/TUnitMessageBus.cs index 1e3c1c3ad8..fc34678991 100644 --- a/TUnit.Engine/TUnitMessageBus.cs +++ b/TUnit.Engine/TUnitMessageBus.cs @@ -204,8 +204,8 @@ private void TidyStacktrace(Exception exception) var newStackTrace = FilterStackTrace(originalStacktrace); StackTrace(exception) = null; - RemoteStackTraceString(exception) = null; - StackTraceString(exception) = newStackTrace; + StackTraceString(exception) = null; + RemoteStackTraceString(exception) = newStackTrace; if (exception.InnerException != null) { diff --git a/TUnit.TestProject/AsyncLocalTest.cs b/TUnit.TestProject/AsyncLocalTest.cs index de574d06ea..1074a4fc43 100644 --- a/TUnit.TestProject/AsyncLocalTest.cs +++ b/TUnit.TestProject/AsyncLocalTest.cs @@ -11,7 +11,7 @@ public class AsyncLocalTest [Before(Test)] public void Before(TestContext context) { - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); _asyncLocalValue.Value = "123"; } diff --git a/TUnit.TestProject/Bugs/1914/AsyncHookTests.cs b/TUnit.TestProject/Bugs/1914/AsyncHookTests.cs index c23f823ed1..aeb45cc9b5 100644 --- a/TUnit.TestProject/Bugs/1914/AsyncHookTests.cs +++ b/TUnit.TestProject/Bugs/1914/AsyncHookTests.cs @@ -22,84 +22,84 @@ public class AsyncHookTests private static readonly AsyncLocal _4BeforeTestLocal = new(); private static readonly AsyncLocal _4BeforeTestLocal2 = new(); - [BeforeEvery(TestDiscovery)] + [Before(TestDiscovery)] public static async Task BeforeTestDiscovery(BeforeTestDiscoveryContext context) { await Task.CompletedTask; _0BeforeTestDiscoveryLocal.Value = "BeforeTestDiscovery"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(TestDiscovery)] + [Before(TestDiscovery)] public static async Task BeforeTestDiscovery2(BeforeTestDiscoveryContext context) { await Task.CompletedTask; _0BeforeTestDiscoveryLocal2.Value = "BeforeTestDiscovery2"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(TestSession)] + [Before(TestSession)] public static async Task BeforeTestSession(TestSessionContext context) { await Task.CompletedTask; _1BeforeTestSessionLocal.Value = "BeforeTestSession"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(TestSession)] + [Before(TestSession)] public static async Task BeforeTestSession2(TestSessionContext context) { await Task.CompletedTask; _1BeforeTestSessionLocal2.Value = "BeforeTestSession2"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(Assembly)] + [Before(Assembly)] public static async Task BeforeAssembly(AssemblyHookContext context) { await Task.CompletedTask; _2BeforeAssemblyLocal.Value = "BeforeAssembly"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(Assembly)] + [Before(Assembly)] public static async Task BeforeAssembly2(AssemblyHookContext context) { await Task.CompletedTask; _2BeforeAssemblyLocal2.Value = "BeforeAssembly2"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(Class)] + [Before(Class)] public static async Task BeforeClass(ClassHookContext context) { await Task.CompletedTask; _3BeforeClassLocal.Value = "BeforeClass"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(Class)] + [Before(Class)] public static async Task BeforeClass2(ClassHookContext context) { await Task.CompletedTask; _3BeforeClassLocal2.Value = "BeforeClass2"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(Test)] - public static async Task BeforeTest(TestContext context) + [Before(Test)] + public async Task BeforeTest(TestContext context) { await Task.CompletedTask; _4BeforeTestLocal.Value = "BeforeTest"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(Test)] - public static async Task BeforeTest2(TestContext context) + [Before(Test)] + public async Task BeforeTest2(TestContext context) { await Task.CompletedTask; _4BeforeTestLocal2.Value = "BeforeTest2"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } @@ -114,8 +114,6 @@ public static async Task BeforeTest2(TestContext context) [Arguments(8)] public async Task TestAsyncLocal(int i) { - using var _ = Assert.Multiple(); - await Assert.That(_0BeforeTestDiscoveryLocal.Value).IsEqualTo("BeforeTestDiscovery"); await Assert.That(_1BeforeTestSessionLocal.Value).IsEqualTo("BeforeTestSession"); await Assert.That(_2BeforeAssemblyLocal.Value).IsEqualTo("BeforeAssembly"); diff --git a/TUnit.TestProject/Bugs/1914/SyncHookTests.cs b/TUnit.TestProject/Bugs/1914/SyncHookTests.cs index cae79df907..4c06d417ff 100644 --- a/TUnit.TestProject/Bugs/1914/SyncHookTests.cs +++ b/TUnit.TestProject/Bugs/1914/SyncHookTests.cs @@ -22,74 +22,74 @@ public class SyncHookTests private static readonly AsyncLocal _4BeforeTestLocal = new(); private static readonly AsyncLocal _4BeforeTestLocal2 = new(); - [BeforeEvery(TestDiscovery)] + [Before(TestDiscovery)] public static void BeforeTestDiscovery(BeforeTestDiscoveryContext context) { _0BeforeTestDiscoveryLocal.Value = "BeforeTestDiscovery"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(TestDiscovery)] + [Before(TestDiscovery)] public static void BeforeTestDiscovery2(BeforeTestDiscoveryContext context) { _0BeforeTestDiscoveryLocal2.Value = "BeforeTestDiscovery2"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(TestSession)] + [Before(TestSession)] public static void BeforeTestSession(TestSessionContext context) { _1BeforeTestSessionLocal.Value = "BeforeTestSession"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(TestSession)] + [Before(TestSession)] public static void BeforeTestSession2(TestSessionContext context) { _1BeforeTestSessionLocal2.Value = "BeforeTestSession2"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(Assembly)] + [Before(Assembly)] public static void BeforeAssembly(AssemblyHookContext context) { _2BeforeAssemblyLocal.Value = "BeforeAssembly"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(Assembly)] + [Before(Assembly)] public static void BeforeAssembly2(AssemblyHookContext context) { _2BeforeAssemblyLocal2.Value = "BeforeAssembly2"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(Class)] + [Before(Class)] public static void BeforeClass(ClassHookContext context) { _3BeforeClassLocal.Value = "BeforeClass"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(Class)] + [Before(Class)] public static void BeforeClass2(ClassHookContext context) { _3BeforeClassLocal2.Value = "BeforeClass2"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(Test)] - public static void BeforeTest(TestContext context) + [Before(Test)] + public void BeforeTest(TestContext context) { _4BeforeTestLocal.Value = "BeforeTest"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } - [BeforeEvery(Test)] - public static void BeforeTest2(TestContext context) + [Before(Test)] + public void BeforeTest2(TestContext context) { _4BeforeTestLocal2.Value = "BeforeTest2"; - context.FlowAsyncLocalValues(); + context.AddAsyncLocalValues(); } @@ -104,8 +104,6 @@ public static void BeforeTest2(TestContext context) [Arguments(8)] public async Task TestAsyncLocal(int i) { - using var _ = Assert.Multiple(); - await Assert.That(_0BeforeTestDiscoveryLocal.Value).IsEqualTo("BeforeTestDiscovery"); await Assert.That(_1BeforeTestSessionLocal.Value).IsEqualTo("BeforeTestSession"); await Assert.That(_2BeforeAssemblyLocal.Value).IsEqualTo("BeforeAssembly"); From 6308a0da035c549eaaea5d704e7e81b7298e756d Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 26 Feb 2025 23:54:07 +0000 Subject: [PATCH 11/17] Fixes --- TUnit.Engine/Hooks/AssemblyHookOrchestrator.cs | 3 +++ TUnit.Engine/Hooks/ClassHookOrchestrator.cs | 3 +++ TUnit.Engine/Hooks/TestDiscoveryHookOrchestrator.cs | 3 +++ TUnit.Engine/Hooks/TestSessionHookOrchestrator.cs | 3 +++ 4 files changed, 12 insertions(+) diff --git a/TUnit.Engine/Hooks/AssemblyHookOrchestrator.cs b/TUnit.Engine/Hooks/AssemblyHookOrchestrator.cs index dfba7dadf2..34bd7c3b4f 100644 --- a/TUnit.Engine/Hooks/AssemblyHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/AssemblyHookOrchestrator.cs @@ -5,6 +5,7 @@ using TUnit.Core.Extensions; using TUnit.Core.Hooks; using TUnit.Core.Logging; +using TUnit.Engine.Helpers; using TUnit.Engine.Logging; using TUnit.Engine.Services; @@ -43,6 +44,8 @@ internal class AssemblyHookOrchestrator(InstanceTracker instanceTracker, HooksCo await logger.LogDebugAsync("Executing [Before(Assembly)] hook"); await beforeHook.ExecuteAsync(assemblyHookContext, CancellationToken.None); + + ExecutionContextHelper.RestoreContext(assemblyHookContext.ExecutionContext); } AssemblyHookContext.Current = null; diff --git a/TUnit.Engine/Hooks/ClassHookOrchestrator.cs b/TUnit.Engine/Hooks/ClassHookOrchestrator.cs index 6b6574691a..4ff7528f12 100644 --- a/TUnit.Engine/Hooks/ClassHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/ClassHookOrchestrator.cs @@ -4,6 +4,7 @@ using TUnit.Core.Extensions; using TUnit.Core.Hooks; using TUnit.Core.Logging; +using TUnit.Engine.Helpers; using TUnit.Engine.Logging; using TUnit.Engine.Services; @@ -68,6 +69,8 @@ public IEnumerable> CollectBeforeHooks(Type t await logger.LogDebugAsync("Executing [Before(Class)] hook"); await beforeHook.ExecuteAsync(classHookContext, CancellationToken.None); + + ExecutionContextHelper.RestoreContext(classHookContext.ExecutionContext); } } diff --git a/TUnit.Engine/Hooks/TestDiscoveryHookOrchestrator.cs b/TUnit.Engine/Hooks/TestDiscoveryHookOrchestrator.cs index 1f71553d57..a20bc96134 100644 --- a/TUnit.Engine/Hooks/TestDiscoveryHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/TestDiscoveryHookOrchestrator.cs @@ -1,6 +1,7 @@ using TUnit.Core; using TUnit.Core.Hooks; using TUnit.Core.Logging; +using TUnit.Engine.Helpers; using TUnit.Engine.Logging; using TUnit.Engine.Services; @@ -21,6 +22,8 @@ internal class TestDiscoveryHookOrchestrator(HooksCollector hooksCollector, TUni await logger.LogDebugAsync("Executing [Before(TestDiscovery)] hook"); await beforeDiscoveryHook.ExecuteAsync(beforeContext, CancellationToken.None); + + ExecutionContextHelper.RestoreContext(beforeContext.ExecutionContext); } return beforeContext.ExecutionContext; diff --git a/TUnit.Engine/Hooks/TestSessionHookOrchestrator.cs b/TUnit.Engine/Hooks/TestSessionHookOrchestrator.cs index 9b010b39f8..ca75b221b0 100644 --- a/TUnit.Engine/Hooks/TestSessionHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/TestSessionHookOrchestrator.cs @@ -1,6 +1,7 @@ using TUnit.Core; using TUnit.Core.Hooks; using TUnit.Core.Logging; +using TUnit.Engine.Helpers; using TUnit.Engine.Logging; using TUnit.Engine.Services; @@ -20,6 +21,8 @@ internal class TestSessionHookOrchestrator(HooksCollector hooksCollector, Assemb await logger.LogDebugAsync("Executing [Before(TestSession)] hook"); await beforeSessionHook.ExecuteAsync(testSessionContext, cancellationToken); + + ExecutionContextHelper.RestoreContext(testSessionContext.ExecutionContext); } return testSessionContext.ExecutionContext; From 206a2efab9f31f4080497381f40f1daedb2820b7 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 26 Feb 2025 23:56:53 +0000 Subject: [PATCH 12/17] Update snaps --- .../AfterAllTests.Test.verified.txt | 28 +++++++++---------- .../AfterTests.Test.verified.txt | 28 +++++++++---------- .../AssemblyAfterTests.Test.verified.txt | 28 +++++++++---------- .../AssemblyBeforeTests.Test.verified.txt | 28 +++++++++---------- .../BeforeAllTests.Test.verified.txt | 28 +++++++++---------- .../BeforeTests.Test.verified.txt | 28 +++++++++---------- ...obalStaticAfterEachTests.Test.verified.txt | 28 +++++++++---------- ...balStaticBeforeEachTests.Test.verified.txt | 28 +++++++++---------- .../Hooks1589.Test.verified.txt | 4 +-- .../Hooks1594.Test.verified.txt | 10 +++---- .../STAThreadHooksTests.Test.verified.txt | 4 +-- .../TestDiscoveryHookTests.Test.verified.txt | 4 +-- .../Tests1899.BaseClass.verified.txt | 2 +- 13 files changed, 124 insertions(+), 124 deletions(-) diff --git a/TUnit.Core.SourceGenerator.Tests/AfterAllTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/AfterAllTests.Test.verified.txt index 0414fcb0fb..8351d7aea7 100644 --- a/TUnit.Core.SourceGenerator.Tests/AfterAllTests.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/AfterAllTests.Test.verified.txt @@ -70,7 +70,7 @@ file partial class Hooks_Base1 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.Base1.AfterAll1()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.Base1.AfterAll1()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -159,7 +159,7 @@ file partial class Hooks_Base1 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.Base1)classInstance).AfterEach1()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.Base1)classInstance).AfterEach1()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -246,7 +246,7 @@ file partial class Hooks_Base2 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.Base2.AfterAll2()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.Base2.AfterAll2()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -335,7 +335,7 @@ file partial class Hooks_Base2 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.Base2)classInstance).AfterEach2()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.Base2)classInstance).AfterEach2()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -422,7 +422,7 @@ file partial class Hooks_Base3 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.Base3.AfterAll3()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.Base3.AfterAll3()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -511,7 +511,7 @@ file partial class Hooks_Base3 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.Base3)classInstance).AfterEach3()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.Base3)classInstance).AfterEach3()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -598,7 +598,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUp()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUp()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -693,7 +693,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUpWithContext(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUpWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -788,7 +788,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUp(cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUp(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -888,7 +888,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUpWithContext(context, cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUpWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -977,7 +977,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).Cleanup()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).Cleanup()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1073,7 +1073,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).Cleanup(cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).Cleanup(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1169,7 +1169,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).CleanupWithContext(context)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).CleanupWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1270,7 +1270,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).CleanupWithContext(context, cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).CleanupWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = diff --git a/TUnit.Core.SourceGenerator.Tests/AfterTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/AfterTests.Test.verified.txt index 9a5e4fc3bf..0032008899 100644 --- a/TUnit.Core.SourceGenerator.Tests/AfterTests.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/AfterTests.Test.verified.txt @@ -70,7 +70,7 @@ file partial class Hooks_Base1 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.Base1.AfterAll1()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.Base1.AfterAll1()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -159,7 +159,7 @@ file partial class Hooks_Base1 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.Base1)classInstance).AfterEach1()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.Base1)classInstance).AfterEach1()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -246,7 +246,7 @@ file partial class Hooks_Base2 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.Base2.AfterAll2()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.Base2.AfterAll2()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -335,7 +335,7 @@ file partial class Hooks_Base2 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.Base2)classInstance).AfterEach2()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.Base2)classInstance).AfterEach2()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -422,7 +422,7 @@ file partial class Hooks_Base3 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.Base3.AfterAll3()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.Base3.AfterAll3()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -511,7 +511,7 @@ file partial class Hooks_Base3 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.Base3)classInstance).AfterEach3()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.Base3)classInstance).AfterEach3()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -598,7 +598,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUp()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUp()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -693,7 +693,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUpWithContext(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUpWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -788,7 +788,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUp(cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUp(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -888,7 +888,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUpWithContext(context, cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.CleanupTests.AfterAllCleanUpWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -977,7 +977,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).Cleanup()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).Cleanup()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1073,7 +1073,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).Cleanup(cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).Cleanup(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1169,7 +1169,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).CleanupWithContext(context)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).CleanupWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1270,7 +1270,7 @@ file partial class Hooks_CleanupTests : global::TUnit.Core.Interfaces.SourceGene Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).CleanupWithContext(context, cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.CleanupTests)classInstance).CleanupWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = diff --git a/TUnit.Core.SourceGenerator.Tests/AssemblyAfterTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/AssemblyAfterTests.Test.verified.txt index 7a1f5deb36..37d9d42e18 100644 --- a/TUnit.Core.SourceGenerator.Tests/AssemblyAfterTests.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/AssemblyAfterTests.Test.verified.txt @@ -70,7 +70,7 @@ file partial class Hooks_AssemblyBase1 : global::TUnit.Core.Interfaces.SourceGen Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyBase1.AfterAll1()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyBase1.AfterAll1()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -159,7 +159,7 @@ file partial class Hooks_AssemblyBase1 : global::TUnit.Core.Interfaces.SourceGen Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyBase1)classInstance).AfterEach1()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyBase1)classInstance).AfterEach1()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -246,7 +246,7 @@ file partial class Hooks_AssemblyBase2 : global::TUnit.Core.Interfaces.SourceGen Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyBase2.AfterAll2()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyBase2.AfterAll2()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -335,7 +335,7 @@ file partial class Hooks_AssemblyBase2 : global::TUnit.Core.Interfaces.SourceGen Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyBase2)classInstance).AfterEach2()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyBase2)classInstance).AfterEach2()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -422,7 +422,7 @@ file partial class Hooks_AssemblyBase3 : global::TUnit.Core.Interfaces.SourceGen Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyBase3.AfterAll3()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyBase3.AfterAll3()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -511,7 +511,7 @@ file partial class Hooks_AssemblyBase3 : global::TUnit.Core.Interfaces.SourceGen Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyBase3)classInstance).AfterEach3()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyBase3)classInstance).AfterEach3()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -598,7 +598,7 @@ file partial class Hooks_AssemblyCleanupTests : global::TUnit.Core.Interfaces.So Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyCleanupTests.AfterAllCleanUp()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyCleanupTests.AfterAllCleanUp()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -693,7 +693,7 @@ file partial class Hooks_AssemblyCleanupTests : global::TUnit.Core.Interfaces.So Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyCleanupTests.AfterAllCleanUpWithContext(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyCleanupTests.AfterAllCleanUpWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -788,7 +788,7 @@ file partial class Hooks_AssemblyCleanupTests : global::TUnit.Core.Interfaces.So Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyCleanupTests.AfterAllCleanUp(cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyCleanupTests.AfterAllCleanUp(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -888,7 +888,7 @@ file partial class Hooks_AssemblyCleanupTests : global::TUnit.Core.Interfaces.So Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyCleanupTests.AfterAllCleanUpWithContext(context, cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.AssemblyCleanupTests.AfterAllCleanUpWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -977,7 +977,7 @@ file partial class Hooks_AssemblyCleanupTests : global::TUnit.Core.Interfaces.So Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyCleanupTests)classInstance).Cleanup()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyCleanupTests)classInstance).Cleanup()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1073,7 +1073,7 @@ file partial class Hooks_AssemblyCleanupTests : global::TUnit.Core.Interfaces.So Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyCleanupTests)classInstance).Cleanup(cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyCleanupTests)classInstance).Cleanup(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1169,7 +1169,7 @@ file partial class Hooks_AssemblyCleanupTests : global::TUnit.Core.Interfaces.So Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyCleanupTests)classInstance).CleanupWithContext(context)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyCleanupTests)classInstance).CleanupWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1270,7 +1270,7 @@ file partial class Hooks_AssemblyCleanupTests : global::TUnit.Core.Interfaces.So Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyCleanupTests)classInstance).CleanupWithContext(context, cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.AssemblyCleanupTests)classInstance).CleanupWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = diff --git a/TUnit.Core.SourceGenerator.Tests/AssemblyBeforeTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/AssemblyBeforeTests.Test.verified.txt index 6b7103a383..887411ba45 100644 --- a/TUnit.Core.SourceGenerator.Tests/AssemblyBeforeTests.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/AssemblyBeforeTests.Test.verified.txt @@ -64,7 +64,7 @@ file partial class Hooks_AssemblyBase1 : global::TUnit.Core.Interfaces.SourceGen Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblyBase1.BeforeAll1()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblyBase1.BeforeAll1()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -153,7 +153,7 @@ file partial class Hooks_AssemblyBase1 : global::TUnit.Core.Interfaces.SourceGen Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblyBase1)classInstance).BeforeEach1()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblyBase1)classInstance).BeforeEach1()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -240,7 +240,7 @@ file partial class Hooks_AssemblyBase2 : global::TUnit.Core.Interfaces.SourceGen Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblyBase2.BeforeAll2()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblyBase2.BeforeAll2()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -329,7 +329,7 @@ file partial class Hooks_AssemblyBase2 : global::TUnit.Core.Interfaces.SourceGen Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblyBase2)classInstance).BeforeEach2()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblyBase2)classInstance).BeforeEach2()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -416,7 +416,7 @@ file partial class Hooks_AssemblyBase3 : global::TUnit.Core.Interfaces.SourceGen Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblyBase3.BeforeAll3()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblyBase3.BeforeAll3()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -505,7 +505,7 @@ file partial class Hooks_AssemblyBase3 : global::TUnit.Core.Interfaces.SourceGen Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblyBase3)classInstance).BeforeEach3()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblyBase3)classInstance).BeforeEach3()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -592,7 +592,7 @@ file partial class Hooks_AssemblySetupTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblySetupTests.BeforeAllSetUp()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblySetupTests.BeforeAllSetUp()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -687,7 +687,7 @@ file partial class Hooks_AssemblySetupTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblySetupTests.BeforeAllSetUpWithContext(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblySetupTests.BeforeAllSetUpWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -782,7 +782,7 @@ file partial class Hooks_AssemblySetupTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblySetupTests.BeforeAllSetUp(cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblySetupTests.BeforeAllSetUp(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -882,7 +882,7 @@ file partial class Hooks_AssemblySetupTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblySetupTests.BeforeAllSetUpWithContext(context, cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.AssemblySetupTests.BeforeAllSetUpWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -971,7 +971,7 @@ file partial class Hooks_AssemblySetupTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblySetupTests)classInstance).Setup()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblySetupTests)classInstance).Setup()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1067,7 +1067,7 @@ file partial class Hooks_AssemblySetupTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblySetupTests)classInstance).Setup(cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblySetupTests)classInstance).Setup(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1163,7 +1163,7 @@ file partial class Hooks_AssemblySetupTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblySetupTests)classInstance).SetupWithContext(context)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblySetupTests)classInstance).SetupWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1264,7 +1264,7 @@ file partial class Hooks_AssemblySetupTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblySetupTests)classInstance).SetupWithContext(context, cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.AssemblySetupTests)classInstance).SetupWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = diff --git a/TUnit.Core.SourceGenerator.Tests/BeforeAllTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/BeforeAllTests.Test.verified.txt index 37f1354d96..9ed959ec3d 100644 --- a/TUnit.Core.SourceGenerator.Tests/BeforeAllTests.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/BeforeAllTests.Test.verified.txt @@ -64,7 +64,7 @@ file partial class Hooks_Base1 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.Base1.BeforeAll1()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.Base1.BeforeAll1()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -153,7 +153,7 @@ file partial class Hooks_Base1 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.Base1)classInstance).BeforeEach1()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.Base1)classInstance).BeforeEach1()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -240,7 +240,7 @@ file partial class Hooks_Base2 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.Base2.BeforeAll2()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.Base2.BeforeAll2()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -329,7 +329,7 @@ file partial class Hooks_Base2 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.Base2)classInstance).BeforeEach2()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.Base2)classInstance).BeforeEach2()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -416,7 +416,7 @@ file partial class Hooks_Base3 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.Base3.BeforeAll3()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.Base3.BeforeAll3()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -505,7 +505,7 @@ file partial class Hooks_Base3 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.Base3)classInstance).BeforeEach3()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.Base3)classInstance).BeforeEach3()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -592,7 +592,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUp()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUp()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -687,7 +687,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUpWithContext(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUpWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -782,7 +782,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUp(cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUp(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -882,7 +882,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUpWithContext(context, cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUpWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -971,7 +971,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).Setup()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).Setup()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1067,7 +1067,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).Setup(cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).Setup(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1163,7 +1163,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).SetupWithContext(context)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).SetupWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1264,7 +1264,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).SetupWithContext(context, cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).SetupWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = diff --git a/TUnit.Core.SourceGenerator.Tests/BeforeTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/BeforeTests.Test.verified.txt index fdb373ab96..d41c7f96bb 100644 --- a/TUnit.Core.SourceGenerator.Tests/BeforeTests.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/BeforeTests.Test.verified.txt @@ -64,7 +64,7 @@ file partial class Hooks_Base1 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.Base1.BeforeAll1()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.Base1.BeforeAll1()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -153,7 +153,7 @@ file partial class Hooks_Base1 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.Base1)classInstance).BeforeEach1()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.Base1)classInstance).BeforeEach1()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -240,7 +240,7 @@ file partial class Hooks_Base2 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.Base2.BeforeAll2()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.Base2.BeforeAll2()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -329,7 +329,7 @@ file partial class Hooks_Base2 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.Base2)classInstance).BeforeEach2()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.Base2)classInstance).BeforeEach2()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -416,7 +416,7 @@ file partial class Hooks_Base3 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.Base3.BeforeAll3()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.Base3.BeforeAll3()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -505,7 +505,7 @@ file partial class Hooks_Base3 : global::TUnit.Core.Interfaces.SourceGenerator.I Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.Base3)classInstance).BeforeEach3()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.Base3)classInstance).BeforeEach3()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -592,7 +592,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUp()), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUp()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -687,7 +687,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUpWithContext(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUpWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -782,7 +782,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUp(cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUp(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -882,7 +882,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUpWithContext(context, cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.SetupTests.BeforeAllSetUpWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -971,7 +971,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).Setup()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).Setup()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1067,7 +1067,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).Setup(cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).Setup(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1163,7 +1163,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).SetupWithContext(context)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).SetupWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -1264,7 +1264,7 @@ file partial class Hooks_SetupTests : global::TUnit.Core.Interfaces.SourceGenera Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).SetupWithContext(context, cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.SetupTests)classInstance).SetupWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = diff --git a/TUnit.Core.SourceGenerator.Tests/GlobalStaticAfterEachTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/GlobalStaticAfterEachTests.Test.verified.txt index b87a146911..b7881d8ca1 100644 --- a/TUnit.Core.SourceGenerator.Tests/GlobalStaticAfterEachTests.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/GlobalStaticAfterEachTests.Test.verified.txt @@ -71,7 +71,7 @@ file partial class Hooks_GlobalBase1 : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalBase1)classInstance).AfterEach1()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalBase1)classInstance).AfterEach1()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -159,7 +159,7 @@ file partial class Hooks_GlobalBase2 : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalBase2)classInstance).AfterEach2()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalBase2)classInstance).AfterEach2()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -247,7 +247,7 @@ file partial class Hooks_GlobalBase3 : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalBase3)classInstance).AfterEach3()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalBase3)classInstance).AfterEach3()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -335,7 +335,7 @@ file partial class Hooks_GlobalCleanUpTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalCleanUpTests)classInstance).CleanUp()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalCleanUpTests)classInstance).CleanUp()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -431,7 +431,7 @@ file partial class Hooks_GlobalCleanUpTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalCleanUpTests)classInstance).CleanUp(cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalCleanUpTests)classInstance).CleanUp(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -527,7 +527,7 @@ file partial class Hooks_GlobalCleanUpTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalCleanUpTests)classInstance).CleanUpWithContext(context)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalCleanUpTests)classInstance).CleanUpWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -628,7 +628,7 @@ file partial class Hooks_GlobalCleanUpTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalCleanUpTests)classInstance).CleanUpWithContext(context, cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.AfterTests.GlobalCleanUpTests)classInstance).CleanUpWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -711,7 +711,7 @@ file partial class Hooks_GlobalBase1 : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalBase1.AfterAll1(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalBase1.AfterAll1(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -806,7 +806,7 @@ file partial class Hooks_GlobalBase2 : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalBase2.AfterAll2(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalBase2.AfterAll2(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -901,7 +901,7 @@ file partial class Hooks_GlobalBase3 : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalBase3.AfterAll3(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalBase3.AfterAll3(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -996,7 +996,7 @@ file partial class Hooks_GlobalCleanUpTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalCleanUpTests.AfterAllCleanUp(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalCleanUpTests.AfterAllCleanUp(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -1096,7 +1096,7 @@ file partial class Hooks_GlobalCleanUpTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalCleanUpTests.AfterAllCleanUp(context, cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalCleanUpTests.AfterAllCleanUp(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -1191,7 +1191,7 @@ file partial class Hooks_GlobalCleanUpTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalCleanUpTests.AfterAllCleanUpWithContext(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalCleanUpTests.AfterAllCleanUpWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -1291,7 +1291,7 @@ file partial class Hooks_GlobalCleanUpTests : global::TUnit.Core.Interfaces.Sour Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalCleanUpTests.AfterAllCleanUpWithContext(context, cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.AfterTests.GlobalCleanUpTests.AfterAllCleanUpWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", diff --git a/TUnit.Core.SourceGenerator.Tests/GlobalStaticBeforeEachTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/GlobalStaticBeforeEachTests.Test.verified.txt index 6a6264eae4..1b0e4f40bb 100644 --- a/TUnit.Core.SourceGenerator.Tests/GlobalStaticBeforeEachTests.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/GlobalStaticBeforeEachTests.Test.verified.txt @@ -65,7 +65,7 @@ file partial class Hooks_GlobalBase1 : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalBase1)classInstance).BeforeEach1()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalBase1)classInstance).BeforeEach1()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -153,7 +153,7 @@ file partial class Hooks_GlobalBase2 : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalBase2)classInstance).BeforeEach2()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalBase2)classInstance).BeforeEach2()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -241,7 +241,7 @@ file partial class Hooks_GlobalBase3 : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalBase3)classInstance).BeforeEach3()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalBase3)classInstance).BeforeEach3()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -329,7 +329,7 @@ file partial class Hooks_GlobalSetUpTests : global::TUnit.Core.Interfaces.Source Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalSetUpTests)classInstance).SetUp()), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalSetUpTests)classInstance).SetUp()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -425,7 +425,7 @@ file partial class Hooks_GlobalSetUpTests : global::TUnit.Core.Interfaces.Source Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalSetUpTests)classInstance).SetUp(cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalSetUpTests)classInstance).SetUp(cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -521,7 +521,7 @@ file partial class Hooks_GlobalSetUpTests : global::TUnit.Core.Interfaces.Source Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalSetUpTests)classInstance).SetUpWithContext(context)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalSetUpTests)classInstance).SetUpWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -622,7 +622,7 @@ file partial class Hooks_GlobalSetUpTests : global::TUnit.Core.Interfaces.Source Properties = [], }), }, - AsyncBody = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalSetUpTests)classInstance).SetUpWithContext(context, cancellationToken)), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.BeforeTests.GlobalSetUpTests)classInstance).SetUpWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -705,7 +705,7 @@ file partial class Hooks_GlobalBase1 : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalBase1.BeforeAll1(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalBase1.BeforeAll1(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -800,7 +800,7 @@ file partial class Hooks_GlobalBase2 : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalBase2.BeforeAll2(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalBase2.BeforeAll2(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -895,7 +895,7 @@ file partial class Hooks_GlobalBase3 : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalBase3.BeforeAll3(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalBase3.BeforeAll3(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -990,7 +990,7 @@ file partial class Hooks_GlobalSetUpTests : global::TUnit.Core.Interfaces.Source Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalSetUpTests.BeforeAllSetUp(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalSetUpTests.BeforeAllSetUp(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -1090,7 +1090,7 @@ file partial class Hooks_GlobalSetUpTests : global::TUnit.Core.Interfaces.Source Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalSetUpTests.BeforeAllSetUp(context, cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalSetUpTests.BeforeAllSetUp(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -1185,7 +1185,7 @@ file partial class Hooks_GlobalSetUpTests : global::TUnit.Core.Interfaces.Source Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalSetUpTests.BeforeAllSetUpWithContext(context)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalSetUpTests.BeforeAllSetUpWithContext(context)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", @@ -1285,7 +1285,7 @@ file partial class Hooks_GlobalSetUpTests : global::TUnit.Core.Interfaces.Source Properties = [], }), }, - AsyncBody = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalSetUpTests.BeforeAllSetUpWithContext(context, cancellationToken)), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.BeforeTests.GlobalSetUpTests.BeforeAllSetUpWithContext(context, cancellationToken)), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", diff --git a/TUnit.Core.SourceGenerator.Tests/Hooks1589.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/Hooks1589.Test.verified.txt index 9e0884f0dc..f04d853944 100644 --- a/TUnit.Core.SourceGenerator.Tests/Hooks1589.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/Hooks1589.Test.verified.txt @@ -65,7 +65,7 @@ file partial class Hooks_BaseTests : global::TUnit.Core.Interfaces.SourceGenerat Properties = [], }), }, - Body = (classInstance, context, cancellationToken) => classInstance.GetType().GetMethod("Setup", []).Invoke(classInstance, []), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => classInstance.GetType().GetMethod("Setup", []).Invoke(classInstance, [])), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -128,7 +128,7 @@ new global::TUnit.Core.SourceGeneratedPropertyInformation ], }), }, - Body = (classInstance, context, cancellationToken) => ((global::TUnit.TestProject.Bugs._1589.BaseTests)classInstance).Setup(), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.Bugs._1589.BaseTests)classInstance).Setup()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = diff --git a/TUnit.Core.SourceGenerator.Tests/Hooks1594.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/Hooks1594.Test.verified.txt index ee81d3dfaa..5410624b29 100644 --- a/TUnit.Core.SourceGenerator.Tests/Hooks1594.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/Hooks1594.Test.verified.txt @@ -72,7 +72,7 @@ file partial class Hooks_MyTests : global::TUnit.Core.Interfaces.SourceGenerator Properties = [], }), }, - Body = (classInstance, context, cancellationToken) => ((global::TUnit.TestProject.Bugs._1594.MyTests)classInstance).SetupMyTests(), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.Bugs._1594.MyTests)classInstance).SetupMyTests()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -160,7 +160,7 @@ file partial class Hooks_ParentTests : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - Body = (classInstance, context, cancellationToken) => classInstance.GetType().GetMethod("SetupParentTests", []).Invoke(classInstance, []), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => classInstance.GetType().GetMethod("SetupParentTests", []).Invoke(classInstance, [])), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -207,7 +207,7 @@ file partial class Hooks_ParentTests : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - Body = (classInstance, context, cancellationToken) => ((global::TUnit.TestProject.Bugs._1594.ParentTests)classInstance).SetupParentTests(), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.Bugs._1594.ParentTests)classInstance).SetupParentTests()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -295,7 +295,7 @@ file partial class Hooks_GrandParentTests : global::TUnit.Core.Interfaces.Source Properties = [], }), }, - Body = (classInstance, context, cancellationToken) => classInstance.GetType().GetMethod("SetupBase", []).Invoke(classInstance, []), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => classInstance.GetType().GetMethod("SetupBase", []).Invoke(classInstance, [])), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -351,7 +351,7 @@ new global::TUnit.Core.SourceGeneratedPropertyInformation ], }), }, - Body = (classInstance, context, cancellationToken) => ((global::TUnit.TestProject.Bugs._1594.GrandParentTests)classInstance).SetupBase(), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.Bugs._1594.GrandParentTests)classInstance).SetupBase()), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = diff --git a/TUnit.Core.SourceGenerator.Tests/STAThreadHooksTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/STAThreadHooksTests.Test.verified.txt index 8f8bafd2f5..c96a22fef2 100644 --- a/TUnit.Core.SourceGenerator.Tests/STAThreadHooksTests.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/STAThreadHooksTests.Test.verified.txt @@ -69,7 +69,7 @@ file partial class Hooks_STAThreadTests : global::TUnit.Core.Interfaces.SourceGe Properties = [], }), }, - Body = (classInstance, context, cancellationToken) => ((global::TUnit.TestProject.STAThreadTests)classInstance).BeforeTest(), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.STAThreadTests)classInstance).BeforeTest()), HookExecutor = new global::TUnit.Core.STAThreadExecutor(), Order = 0, MethodAttributes = @@ -171,7 +171,7 @@ file partial class Hooks_STAThreadTests : global::TUnit.Core.Interfaces.SourceGe Properties = [], }), }, - Body = (classInstance, context, cancellationToken) => ((global::TUnit.TestProject.STAThreadTests)classInstance).AfterTest(), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => ((global::TUnit.TestProject.STAThreadTests)classInstance).AfterTest()), HookExecutor = new global::TUnit.Core.STAThreadExecutor(), Order = 0, MethodAttributes = diff --git a/TUnit.Core.SourceGenerator.Tests/TestDiscoveryHookTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/TestDiscoveryHookTests.Test.verified.txt index d113791923..4c6030ee4b 100644 --- a/TUnit.Core.SourceGenerator.Tests/TestDiscoveryHookTests.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/TestDiscoveryHookTests.Test.verified.txt @@ -55,7 +55,7 @@ file partial class Hooks_TestDiscoveryHookTests : global::TUnit.Core.Interfaces. Properties = [], }), }, - Body = (context, cancellationToken) => global::TUnit.TestProject.TestDiscoveryHookTests.BeforeDiscovery(), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.TestDiscoveryHookTests.BeforeDiscovery()), HookExecutor = DefaultExecutor.Instance, Order = 5, FilePath = @"", @@ -140,7 +140,7 @@ file partial class Hooks_TestDiscoveryHookTests : global::TUnit.Core.Interfaces. Properties = [], }), }, - Body = (context, cancellationToken) => global::TUnit.TestProject.TestDiscoveryHookTests.AfterDiscovery(), + Body = (context, cancellationToken) => AsyncConvert.Convert(() => global::TUnit.TestProject.TestDiscoveryHookTests.AfterDiscovery()), HookExecutor = DefaultExecutor.Instance, Order = 0, FilePath = @"", diff --git a/TUnit.Core.SourceGenerator.Tests/Tests1899.BaseClass.verified.txt b/TUnit.Core.SourceGenerator.Tests/Tests1899.BaseClass.verified.txt index b3e91767f3..ddc2ae76dd 100644 --- a/TUnit.Core.SourceGenerator.Tests/Tests1899.BaseClass.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/Tests1899.BaseClass.verified.txt @@ -65,7 +65,7 @@ file partial class Hooks_BaseClass : global::TUnit.Core.Interfaces.SourceGenerat Properties = [], }), }, - Body = (classInstance, context, cancellationToken) => classInstance.GetType().GetMethod("Setup", []).Invoke(classInstance, []), + Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => classInstance.GetType().GetMethod("Setup", []).Invoke(classInstance, [])), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = From d0ea80218f013c0c115053f255279ca502679c02 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Thu, 27 Feb 2025 00:30:26 +0000 Subject: [PATCH 13/17] WIP --- TUnit.Core.SourceGenerator.Tests/Hooks1589.Test.verified.txt | 2 +- TUnit.Core.SourceGenerator.Tests/Hooks1594.Test.verified.txt | 4 ++-- .../Tests1899.BaseClass.verified.txt | 2 +- .../CodeGenerators/Writers/Hooks/TestHooksWriter.cs | 2 +- TUnit.Core/AsyncConvert.cs | 2 +- TUnit.Engine/Extensions/TestContextExtensions.cs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/TUnit.Core.SourceGenerator.Tests/Hooks1589.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/Hooks1589.Test.verified.txt index f04d853944..b53199f5cb 100644 --- a/TUnit.Core.SourceGenerator.Tests/Hooks1589.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/Hooks1589.Test.verified.txt @@ -65,7 +65,7 @@ file partial class Hooks_BaseTests : global::TUnit.Core.Interfaces.SourceGenerat Properties = [], }), }, - Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => classInstance.GetType().GetMethod("Setup", []).Invoke(classInstance, [])), + Body = (classInstance, context, cancellationToken) => AsyncConvert.ConvertObject(() => classInstance.GetType().GetMethod("Setup", []).Invoke(classInstance, [])), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = diff --git a/TUnit.Core.SourceGenerator.Tests/Hooks1594.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/Hooks1594.Test.verified.txt index 5410624b29..7914bbc091 100644 --- a/TUnit.Core.SourceGenerator.Tests/Hooks1594.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/Hooks1594.Test.verified.txt @@ -160,7 +160,7 @@ file partial class Hooks_ParentTests : global::TUnit.Core.Interfaces.SourceGener Properties = [], }), }, - Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => classInstance.GetType().GetMethod("SetupParentTests", []).Invoke(classInstance, [])), + Body = (classInstance, context, cancellationToken) => AsyncConvert.ConvertObject(() => classInstance.GetType().GetMethod("SetupParentTests", []).Invoke(classInstance, [])), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = @@ -295,7 +295,7 @@ file partial class Hooks_GrandParentTests : global::TUnit.Core.Interfaces.Source Properties = [], }), }, - Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => classInstance.GetType().GetMethod("SetupBase", []).Invoke(classInstance, [])), + Body = (classInstance, context, cancellationToken) => AsyncConvert.ConvertObject(() => classInstance.GetType().GetMethod("SetupBase", []).Invoke(classInstance, [])), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = diff --git a/TUnit.Core.SourceGenerator.Tests/Tests1899.BaseClass.verified.txt b/TUnit.Core.SourceGenerator.Tests/Tests1899.BaseClass.verified.txt index ddc2ae76dd..206f6bcb49 100644 --- a/TUnit.Core.SourceGenerator.Tests/Tests1899.BaseClass.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/Tests1899.BaseClass.verified.txt @@ -65,7 +65,7 @@ file partial class Hooks_BaseClass : global::TUnit.Core.Interfaces.SourceGenerat Properties = [], }), }, - Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => classInstance.GetType().GetMethod("Setup", []).Invoke(classInstance, [])), + Body = (classInstance, context, cancellationToken) => AsyncConvert.ConvertObject(() => classInstance.GetType().GetMethod("Setup", []).Invoke(classInstance, [])), HookExecutor = DefaultExecutor.Instance, Order = 0, MethodAttributes = diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs index 8830df0258..b6485e2ceb 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs @@ -61,7 +61,7 @@ public static void Execute(SourceCodeWriter sourceBuilder, HooksDataModel model) if (model.ClassType.IsGenericDefinition()) { sourceBuilder.WriteLine( - $"Body = (classInstance, context, cancellationToken) => AsyncConvert.Convert(() => classInstance.GetType().GetMethod(\"{model.MethodName}\", [{string.Join(", ", model.ParameterTypes.Select(x => $"typeof({x})"))}]).Invoke(classInstance, {GetArgsOrEmptyArray(model)})),"); + $"Body = (classInstance, context, cancellationToken) => AsyncConvert.ConvertObject(() => classInstance.GetType().GetMethod(nameof({model.FullyQualifiedTypeName}.{model.MethodName}), [{string.Join(", ", model.ParameterTypes.Select(x => $"typeof({x})"))}]).Invoke(classInstance, {GetArgsOrEmptyArray(model)})),"); } else { diff --git a/TUnit.Core/AsyncConvert.cs b/TUnit.Core/AsyncConvert.cs index dfa242f6a5..c9f5a8326b 100644 --- a/TUnit.Core/AsyncConvert.cs +++ b/TUnit.Core/AsyncConvert.cs @@ -21,7 +21,7 @@ public static async Task Convert(Func action) await action(); } - public static async ValueTask Convert(object? invoke) + public static async Task ConvertObject(object? invoke) { if (invoke is Task task) { diff --git a/TUnit.Engine/Extensions/TestContextExtensions.cs b/TUnit.Engine/Extensions/TestContextExtensions.cs index 3d55b996b0..ca59d7cd52 100644 --- a/TUnit.Engine/Extensions/TestContextExtensions.cs +++ b/TUnit.Engine/Extensions/TestContextExtensions.cs @@ -41,7 +41,7 @@ public static async Task ReregisterTestWithArguments( try { - await AsyncConvert.Convert(testMetadata.TestMethod.ReflectionInformation.Invoke(@class, args)); + await AsyncConvert.ConvertObject(testMetadata.TestMethod.ReflectionInformation.Invoke(@class, args)); } catch (TargetInvocationException e) { From 8d28cc76d1fcc9f856aba151ba02326a2c84fff1 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Thu, 27 Feb 2025 10:08:49 +0000 Subject: [PATCH 14/17] Revert --- .../CodeGenerators/Writers/Hooks/TestHooksWriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs index b6485e2ceb..9a79780408 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/Hooks/TestHooksWriter.cs @@ -61,7 +61,7 @@ public static void Execute(SourceCodeWriter sourceBuilder, HooksDataModel model) if (model.ClassType.IsGenericDefinition()) { sourceBuilder.WriteLine( - $"Body = (classInstance, context, cancellationToken) => AsyncConvert.ConvertObject(() => classInstance.GetType().GetMethod(nameof({model.FullyQualifiedTypeName}.{model.MethodName}), [{string.Join(", ", model.ParameterTypes.Select(x => $"typeof({x})"))}]).Invoke(classInstance, {GetArgsOrEmptyArray(model)})),"); + $"Body = (classInstance, context, cancellationToken) => AsyncConvert.ConvertObject(() => classInstance.GetType().GetMethod(\"{model.MethodName}\", [{string.Join(", ", model.ParameterTypes.Select(x => $"typeof({x})"))}]).Invoke(classInstance, {GetArgsOrEmptyArray(model)})),"); } else { From a4fd6618043629ada4c6cabb9763a77a82fa1d22 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Thu, 27 Feb 2025 10:43:36 +0000 Subject: [PATCH 15/17] Fix the loading of hooks for libraries WIP --- .../Hooks/TestDiscoveryHookOrchestrator.cs | 4 ++ .../Hooks/TestSessionHookOrchestrator.cs | 6 ++ TUnit.Engine/Services/HooksCollector.cs | 72 +++++++++---------- 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/TUnit.Engine/Hooks/TestDiscoveryHookOrchestrator.cs b/TUnit.Engine/Hooks/TestDiscoveryHookOrchestrator.cs index a20bc96134..cae54180c1 100644 --- a/TUnit.Engine/Hooks/TestDiscoveryHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/TestDiscoveryHookOrchestrator.cs @@ -11,9 +11,13 @@ internal class TestDiscoveryHookOrchestrator(HooksCollector hooksCollector, TUni { private BeforeTestDiscoveryContext? _beforeContext; private TestDiscoveryContext? _afterContext; + + public async Task RunBeforeTestDiscovery() { + hooksCollector.CollectDiscoveryHooks(); + var beforeDiscoveryHooks = CollectBeforeHooks(); var beforeContext = GetBeforeContext(); diff --git a/TUnit.Engine/Hooks/TestSessionHookOrchestrator.cs b/TUnit.Engine/Hooks/TestSessionHookOrchestrator.cs index ca75b221b0..a244b5b7e5 100644 --- a/TUnit.Engine/Hooks/TestSessionHookOrchestrator.cs +++ b/TUnit.Engine/Hooks/TestSessionHookOrchestrator.cs @@ -13,6 +13,8 @@ internal class TestSessionHookOrchestrator(HooksCollector hooksCollector, Assemb public async Task RunBeforeTestSession(CancellationToken cancellationToken) { + hooksCollector.CollectionTestSessionHooks(); + var testSessionContext = GetContext(); var beforeSessionHooks = CollectBeforeHooks(); @@ -24,6 +26,10 @@ internal class TestSessionHookOrchestrator(HooksCollector hooksCollector, Assemb ExecutionContextHelper.RestoreContext(testSessionContext.ExecutionContext); } + + // After Discovery and Before test session hooks are run, more chance of references assemblies + // being loaded into the AppDomain, so now we collect the test hooks which should pick up loaded libraries too + hooksCollector.CollectHooks(); return testSessionContext.ExecutionContext; } diff --git a/TUnit.Engine/Services/HooksCollector.cs b/TUnit.Engine/Services/HooksCollector.cs index 0c8f8eeee7..73bda44c32 100644 --- a/TUnit.Engine/Services/HooksCollector.cs +++ b/TUnit.Engine/Services/HooksCollector.cs @@ -5,7 +5,7 @@ namespace TUnit.Engine.Services; -internal class HooksCollector +internal class HooksCollector(string sessionId) { internal readonly List> BeforeTestDiscoveryHooks = []; internal readonly List> BeforeTestSessionHooks = []; @@ -26,54 +26,61 @@ internal class HooksCollector internal readonly List> AfterEveryAssemblyHooks = []; internal readonly List> AfterEveryClassHooks = []; internal readonly List> AfterEveryTestHooks = []; - private readonly string _sessionId; - public HooksCollector(string sessionId) - { - _sessionId = sessionId; - - CollectDiscoveryHooks(); - CollectHooks(); - } - - private void CollectDiscoveryHooks() + public void CollectDiscoveryHooks() { foreach (var hookSource in Sources.TestDiscoveryHookSources) { - foreach (var beforeHook in hookSource.CollectBeforeTestDiscoveryHooks(_sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeTestDiscoveryHooks(sessionId)) { BeforeTestDiscoveryHooks.Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterTestDiscoveryHooks(_sessionId)) + foreach (var afterHook in hookSource.CollectAfterTestDiscoveryHooks(sessionId)) { AfterTestDiscoveryHooks.Add(afterHook); } } } - private void CollectHooks() + public void CollectionTestSessionHooks() + { + foreach (var hookSource in Sources.TestSessionHookSources) + { + foreach (var beforeHook in hookSource.CollectBeforeTestSessionHooks(sessionId)) + { + BeforeTestSessionHooks.Add(beforeHook); + } + + foreach (var afterHook in hookSource.CollectAfterTestSessionHooks(sessionId)) + { + AfterTestSessionHooks.Add(afterHook); + } + } + } + + public void CollectHooks() { foreach (var hookSource in Sources.TestHookSources) { - foreach (var beforeHook in hookSource.CollectBeforeTestHooks(_sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeTestHooks(sessionId)) { var beforeList = BeforeTestHooks.GetOrAdd(beforeHook.ClassType, _ => []); beforeList.Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterTestHooks(_sessionId)) + foreach (var afterHook in hookSource.CollectAfterTestHooks(sessionId)) { var afterList = AfterTestHooks.GetOrAdd(afterHook.ClassType, _ => []); afterList.Add(afterHook); } - foreach (var beforeHook in hookSource.CollectBeforeEveryTestHooks(_sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeEveryTestHooks(sessionId)) { BeforeEveryTestHooks.Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterEveryTestHooks(_sessionId)) + foreach (var afterHook in hookSource.CollectAfterEveryTestHooks(sessionId)) { AfterEveryTestHooks.Add(afterHook); } @@ -81,24 +88,24 @@ private void CollectHooks() foreach (var hookSource in Sources.ClassHookSources) { - foreach (var beforeHook in hookSource.CollectBeforeClassHooks(_sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeClassHooks(sessionId)) { var beforeList = BeforeClassHooks.GetOrAdd(beforeHook.ClassType, _ => []); beforeList.Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterClassHooks(_sessionId)) + foreach (var afterHook in hookSource.CollectAfterClassHooks(sessionId)) { var afterList = AfterClassHooks.GetOrAdd(afterHook.ClassType, _ => []); afterList.Add(afterHook); } - foreach (var beforeHook in hookSource.CollectBeforeEveryClassHooks(_sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeEveryClassHooks(sessionId)) { BeforeEveryClassHooks.Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterEveryClassHooks(_sessionId)) + foreach (var afterHook in hookSource.CollectAfterEveryClassHooks(sessionId)) { AfterEveryClassHooks.Add(afterHook); } @@ -106,38 +113,25 @@ private void CollectHooks() foreach (var hookSource in Sources.AssemblyHookSources) { - foreach (var beforeHook in hookSource.CollectBeforeAssemblyHooks(_sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeAssemblyHooks(sessionId)) { BeforeAssemblyHooks.GetOrAdd(beforeHook.Assembly, _ => []).Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterAssemblyHooks(_sessionId)) + foreach (var afterHook in hookSource.CollectAfterAssemblyHooks(sessionId)) { AfterAssemblyHooks.GetOrAdd(afterHook.Assembly, _ => []).Add(afterHook); } - foreach (var beforeHook in hookSource.CollectBeforeEveryAssemblyHooks(_sessionId)) + foreach (var beforeHook in hookSource.CollectBeforeEveryAssemblyHooks(sessionId)) { BeforeEveryAssemblyHooks.Add(beforeHook); } - foreach (var afterHook in hookSource.CollectAfterEveryAssemblyHooks(_sessionId)) + foreach (var afterHook in hookSource.CollectAfterEveryAssemblyHooks(sessionId)) { AfterEveryAssemblyHooks.Add(afterHook); } } - - foreach (var hookSource in Sources.TestSessionHookSources) - { - foreach (var beforeHook in hookSource.CollectBeforeTestSessionHooks(_sessionId)) - { - BeforeTestSessionHooks.Add(beforeHook); - } - - foreach (var afterHook in hookSource.CollectAfterTestSessionHooks(_sessionId)) - { - AfterTestSessionHooks.Add(afterHook); - } - } } } \ No newline at end of file From 08c4d7237c938372c2a204ee973003db918ba79f Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Thu, 27 Feb 2025 11:35:19 +0000 Subject: [PATCH 16/17] Fix --- TUnit.Core/AsyncConvert.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/TUnit.Core/AsyncConvert.cs b/TUnit.Core/AsyncConvert.cs index c9f5a8326b..a6da3282fc 100644 --- a/TUnit.Core/AsyncConvert.cs +++ b/TUnit.Core/AsyncConvert.cs @@ -23,6 +23,21 @@ public static async Task Convert(Func action) public static async Task ConvertObject(object? invoke) { + if (invoke is Func syncFunc) + { + syncFunc(); + } + + if (invoke is Func asyncFunc) + { + await asyncFunc(); + } + + if (invoke is Func asyncValueFunc) + { + await asyncValueFunc(); + } + if (invoke is Task task) { await task; From 09b83b94c4f8c83541cddaee119d31b3ab567c08 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Thu, 27 Feb 2025 11:52:38 +0000 Subject: [PATCH 17/17] Fix test --- TUnit.TestProject/AsyncLocalTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TUnit.TestProject/AsyncLocalTest.cs b/TUnit.TestProject/AsyncLocalTest.cs index 1074a4fc43..63224624d0 100644 --- a/TUnit.TestProject/AsyncLocalTest.cs +++ b/TUnit.TestProject/AsyncLocalTest.cs @@ -11,8 +11,8 @@ public class AsyncLocalTest [Before(Test)] public void Before(TestContext context) { - context.AddAsyncLocalValues(); _asyncLocalValue.Value = "123"; + context.AddAsyncLocalValues(); } [After(Test)]