Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion TUnit.Engine/Framework/TUnitServiceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public ITestExecutionFilter? Filter
public CancellationTokenSource FailFastCancellationSource { get; }
public ParallelLimitLockProvider ParallelLimitLockProvider { get; }
public ObjectLifecycleService ObjectLifecycleService { get; }
public bool AfterSessionHooksFailed { get; set; }
public bool SessionFailed { get; set; }

[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Reflection mode is not used in AOT/trimmed scenarios")]
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "Reflection mode is not used in AOT scenarios")]
Expand Down
26 changes: 12 additions & 14 deletions TUnit.Engine/Framework/TUnitTestFramework.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,24 +69,23 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context)
}
catch (Exception e) when (IsCancellationException(e))
{
// Check if this is a normal cancellation or fail-fast cancellation
if (context.CancellationToken.IsCancellationRequested)
{
await GetOrCreateServiceProvider(context).Logger.LogErrorAsync("The test run was cancelled.");
}
else
{
// This is likely a fail-fast cancellation
await GetOrCreateServiceProvider(context).Logger.LogErrorAsync("Test execution stopped due to fail-fast.");
}
var message = context.CancellationToken.IsCancellationRequested
? "The test run was cancelled."
: "Test execution stopped due to fail-fast.";
await GetOrCreateServiceProvider(context).Logger.LogErrorAsync(message);

// Re-throw is safe here — MTP handles OperationCanceledException specially.
throw;
}
catch (Exception e)
{
await GetOrCreateServiceProvider(context).Logger.LogErrorAsync(e);
var serviceProvider = GetOrCreateServiceProvider(context);
await serviceProvider.Logger.LogErrorAsync(e);
await ReportUnhandledException(context, e);
throw;

// Do NOT re-throw — MTP hosts expect errors via CloseTestSessionResult,
// not propagated exceptions. Re-throwing breaks JSON-RPC transports (#5263).
serviceProvider.SessionFailed = true;
}
finally
{
Expand All @@ -100,8 +99,7 @@ public async Task<CloseTestSessionResult> CloseTestSessionAsync(CloseTestSession

if (_serviceProvidersPerSession.TryRemove(context.SessionUid.Value, out var serviceProvider))
{
// Check if After(TestSession) hooks failed
if (serviceProvider.AfterSessionHooksFailed)
if (serviceProvider.SessionFailed)
{
isSuccess = false;
}
Expand Down
3 changes: 1 addition & 2 deletions TUnit.Engine/TestSessionCoordinator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,9 @@ private async Task ExecuteTestsCore(List<AbstractExecutableTest> testList, Cance
// Schedule and execute tests (batch approach to preserve ExecutionContext)
var success = await _testScheduler.ScheduleAndExecuteAsync(testList, linkedCts.Token);

// Track whether After(TestSession) hooks failed
if (!success)
{
_serviceProvider.AfterSessionHooksFailed = true;
_serviceProvider.SessionFailed = true;
}
}

Expand Down
Loading