Skip to content

Commit 7f10b23

Browse files
committed
fix: use categorizer result consistently and unwrap AggregateException at top of GetFailureStateProperty
The method was computing a category via FailureCategorizer but then ignoring it for routing, using separate type-checking logic against the original (potentially wrapped) exception. This caused two bugs: 1. AggregateException wrapping e.g. TaskCanceledException would be correctly categorized as Timeout by the categorizer, but the method's own `e is TaskCanceledException` check would fail since `e` was still the AggregateException. 2. The assertion check used a different string comparison (InvariantCulture) than the categorizer (Ordinal) and only checked for "Assertion" rather than also checking "Assert". Now AggregateException is unwrapped once at the top, the unwrapped exception is passed to the categorizer, and the FailureCategory enum drives all routing and is used for constructing state properties.
1 parent 30e0c29 commit 7f10b23

1 file changed

Lines changed: 12 additions & 6 deletions

File tree

TUnit.Engine/TUnitMessageBus.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.Testing.Platform.TestHost;
77
using TUnit.Core;
88
using TUnit.Engine.CommandLineProviders;
9+
using TUnit.Engine.Enums;
910
using TUnit.Engine.Exceptions;
1011
using TUnit.Engine.Extensions;
1112
using TUnit.Engine.Services;
@@ -143,22 +144,27 @@ public ValueTask PublishOutputUpdate(TestNode testNode)
143144

144145
private static TestNodeStateProperty GetFailureStateProperty(TestContext testContext, Exception e, TimeSpan duration)
145146
{
146-
var category = FailureCategorizer.Categorize(e);
147+
// Unwrap AggregateException once so all downstream logic sees the real cause
148+
var unwrapped = e is AggregateException { InnerExceptions.Count: > 0 } agg
149+
? agg.InnerExceptions[0]
150+
: e;
151+
152+
var category = FailureCategorizer.Categorize(unwrapped);
147153
var categoryLabel = FailureCategorizer.GetLabel(category);
148154

149-
if (testContext.Metadata.TestDetails.Timeout != null
150-
&& e is TaskCanceledException or OperationCanceledException or TimeoutException
155+
if (category == FailureCategory.Timeout
156+
&& testContext.Metadata.TestDetails.Timeout != null
151157
&& duration >= testContext.Metadata.TestDetails.Timeout.Value)
152158
{
153159
return new TimeoutTestNodeStateProperty($"[{categoryLabel}] Test timed out after {testContext.Metadata.TestDetails.Timeout.Value.TotalMilliseconds}ms");
154160
}
155161

156-
if (e.GetType().Name.Contains("Assertion", StringComparison.InvariantCulture))
162+
if (category == FailureCategory.Assertion)
157163
{
158-
return new FailedTestNodeStateProperty(e, $"[{categoryLabel}] {e.Message}");
164+
return new FailedTestNodeStateProperty(unwrapped, $"[{categoryLabel}] {unwrapped.Message}");
159165
}
160166

161-
return new ErrorTestNodeStateProperty(e, $"[{categoryLabel}] {e.Message}");
167+
return new ErrorTestNodeStateProperty(unwrapped, $"[{categoryLabel}] {unwrapped.Message}");
162168
}
163169

164170
public Task<bool> IsEnabledAsync()

0 commit comments

Comments
 (0)