diff --git a/TUnit.Engine/Services/EventReceiverOrchestrator.cs b/TUnit.Engine/Services/EventReceiverOrchestrator.cs index 8e03ef52ad..050d4e4ec1 100644 --- a/TUnit.Engine/Services/EventReceiverOrchestrator.cs +++ b/TUnit.Engine/Services/EventReceiverOrchestrator.cs @@ -317,6 +317,11 @@ public ValueTask InvokeFirstTestInSessionEventReceiversAsync( return default; } + if (_firstTestInSessionTasks.TryGetValue("session", out var existingTask)) + { + return new ValueTask(existingTask); + } + var task = _firstTestInSessionTasks.GetOrAdd("session", _ => InvokeFirstTestInSessionEventReceiversCoreAsync(context, sessionContext, cancellationToken)); return new ValueTask(task); @@ -347,6 +352,12 @@ public ValueTask InvokeFirstTestInAssemblyEventReceiversAsync( } var assemblyName = assemblyContext.Assembly.GetName().FullName ?? ""; + + if (_firstTestInAssemblyTasks.TryGetValue(assemblyName, out var existingTask)) + { + return new ValueTask(existingTask); + } + var task = _firstTestInAssemblyTasks.GetOrAdd(assemblyName, _ => InvokeFirstTestInAssemblyEventReceiversCoreAsync(context, assemblyContext, cancellationToken)); return new ValueTask(task); @@ -377,6 +388,12 @@ public ValueTask InvokeFirstTestInClassEventReceiversAsync( } var classType = classContext.ClassType; + + if (_firstTestInClassTasks.TryGetValue(classType, out var existingTask)) + { + return new ValueTask(existingTask); + } + var task = _firstTestInClassTasks.GetOrAdd(classType, _ => InvokeFirstTestInClassEventReceiversCoreAsync(context, classContext, cancellationToken)); return new ValueTask(task); @@ -448,7 +465,12 @@ public ValueTask InvokeLastTestInAssemblyEventReceiversAsync( var assemblyName = assemblyContext.Assembly.GetName().FullName ?? ""; - var assemblyCount = _assemblyTestCounts.GetOrAdd(assemblyName, static _ => new Counter()).Decrement(); + if (!_assemblyTestCounts.TryGetValue(assemblyName, out var assemblyCounter)) + { + assemblyCounter = _assemblyTestCounts.GetOrAdd(assemblyName, static _ => new Counter()); + } + + var assemblyCount = assemblyCounter.Decrement(); if (assemblyCount == 0) { @@ -491,7 +513,12 @@ public ValueTask InvokeLastTestInClassEventReceiversAsync( var classType = classContext.ClassType; - var classCount = _classTestCounts.GetOrAdd(classType, static _ => new Counter()).Decrement(); + if (!_classTestCounts.TryGetValue(classType, out var classCounter)) + { + classCounter = _classTestCounts.GetOrAdd(classType, static _ => new Counter()); + } + + var classCount = classCounter.Decrement(); if (classCount == 0) { @@ -536,13 +563,21 @@ public void InitializeTestCounts(IEnumerable allTestContexts) foreach (var group in contexts.GroupBy(c => c.ClassContext.AssemblyContext.Assembly.GetName().FullName)) { - var counter = _assemblyTestCounts.GetOrAdd(group.Key, static _ => new Counter()); + if (!_assemblyTestCounts.TryGetValue(group.Key, out var counter)) + { + counter = _assemblyTestCounts.GetOrAdd(group.Key, static _ => new Counter()); + } + counter.Add(group.Count()); } foreach (var group in contexts.GroupBy(c => c.ClassContext.ClassType)) { - var counter = _classTestCounts.GetOrAdd(group.Key, static _ => new Counter()); + if (!_classTestCounts.TryGetValue(group.Key, out var counter)) + { + counter = _classTestCounts.GetOrAdd(group.Key, static _ => new Counter()); + } + counter.Add(group.Count()); } } diff --git a/TUnit.Engine/Services/HookDelegateBuilder.cs b/TUnit.Engine/Services/HookDelegateBuilder.cs index e367e7a347..6f78f1a99a 100644 --- a/TUnit.Engine/Services/HookDelegateBuilder.cs +++ b/TUnit.Engine/Services/HookDelegateBuilder.cs @@ -50,7 +50,7 @@ public HookDelegateBuilder(EventReceiverOrchestrator eventReceiverOrchestrator, private static Type GetCachedGenericTypeDefinition(Type type) { - return _genericTypeDefinitionCache.GetOrAdd(type, t => t.GetGenericTypeDefinition()); + return _genericTypeDefinitionCache.GetOrAdd(type, static t => t.GetGenericTypeDefinition()); } public async ValueTask InitializeAsync() diff --git a/TUnit.Engine/Services/TestExecution/TestExecutionGuard.cs b/TUnit.Engine/Services/TestExecution/TestExecutionGuard.cs index 78126112b6..06ac0c688c 100644 --- a/TUnit.Engine/Services/TestExecution/TestExecutionGuard.cs +++ b/TUnit.Engine/Services/TestExecution/TestExecutionGuard.cs @@ -12,8 +12,14 @@ internal sealed class TestExecutionGuard public ValueTask TryStartExecutionAsync(string testId, Func executionFunc) { + // Fast path: check if test is already executing without allocating a TCS + if (_executingTests.TryGetValue(testId, out var existingTcs)) + { + return new ValueTask(WaitForExistingExecutionAsync(existingTcs)); + } + var tcs = new TaskCompletionSource(); - var existingTcs = _executingTests.GetOrAdd(testId, tcs); + existingTcs = _executingTests.GetOrAdd(testId, tcs); if (existingTcs != tcs) {