Skip to content

Commit 40d3e70

Browse files
Copilotthomhurst
andauthored
Address PR review: extract disposal helper, add error recording, fix stale comment
- Extract disposal span logic into DisposeTestInstanceWithSpanAsync() helper to reduce #if NET nesting in the finally block (review issue #1) - Add TUnitActivitySource.RecordException() calls on disposal span when OnDispose or DisposeTestInstance throws (review issue #2) - Fix stale comment in TestInitializer.cs referencing "data source initialization" instead of "test object initialization" (review issue #4) Agent-Logs-Url: https://github.com/thomhurst/TUnit/sessions/3802acb9-a7fe-4976-8c3f-5bb246c098d2 Co-authored-by: thomhurst <30480171+thomhurst@users.noreply.github.com>
1 parent 4d27995 commit 40d3e70

2 files changed

Lines changed: 55 additions & 41 deletions

File tree

TUnit.Engine/Services/TestExecution/TestCoordinator.cs

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -304,55 +304,69 @@ private async ValueTask ExecuteTestLifecycleAsync(AbstractExecutableTest test, C
304304
}
305305
finally
306306
{
307+
await DisposeTestInstanceWithSpanAsync(test).ConfigureAwait(false);
308+
}
309+
}
310+
311+
/// <summary>
312+
/// Disposes the test instance and fires OnDispose callbacks, wrapped in an OpenTelemetry
313+
/// activity span for trace timeline visibility.
314+
/// Parented under the class activity because the test case activity has already been stopped
315+
/// by this point (disposal runs after TestExecutor.ExecuteAsync completes).
316+
/// </summary>
317+
private async ValueTask DisposeTestInstanceWithSpanAsync(AbstractExecutableTest test)
318+
{
307319
#if NET
308-
Activity? disposalActivity = null;
309-
if (TUnitActivitySource.Source.HasListeners())
310-
{
311-
// Parented under the class activity because the test case activity has already
312-
// been stopped by this point (disposal runs after TestExecutor.ExecuteAsync completes).
313-
// The initialization activity is a child of the test case span since it runs within it.
314-
disposalActivity = TUnitActivitySource.StartActivity(
315-
"test instance disposal",
316-
ActivityKind.Internal,
317-
test.Context.ClassContext.Activity?.Context ?? default,
318-
[new("tunit.test.id", test.Context.Id)]);
319-
}
320-
try
321-
{
320+
Activity? disposalActivity = null;
321+
if (TUnitActivitySource.Source.HasListeners())
322+
{
323+
disposalActivity = TUnitActivitySource.StartActivity(
324+
"test instance disposal",
325+
ActivityKind.Internal,
326+
test.Context.ClassContext.Activity?.Context ?? default,
327+
[new("tunit.test.id", test.Context.Id)]);
328+
}
329+
try
330+
{
322331
#endif
323-
// Dispose test instance and fire OnDispose after each attempt
324-
// This ensures each retry gets a fresh instance
325-
var onDispose = test.Context.InternalEvents.OnDispose;
326-
if (onDispose?.InvocationList != null)
332+
// Dispose test instance and fire OnDispose after each attempt
333+
// This ensures each retry gets a fresh instance
334+
var onDispose = test.Context.InternalEvents.OnDispose;
335+
if (onDispose?.InvocationList != null)
336+
{
337+
foreach (var invocation in onDispose.InvocationList)
327338
{
328-
foreach (var invocation in onDispose.InvocationList)
339+
try
329340
{
330-
try
331-
{
332-
await invocation.InvokeAsync(test.Context, test.Context).ConfigureAwait(false);
333-
}
334-
catch (Exception disposeEx)
335-
{
336-
await _logger.LogErrorAsync($"Error during OnDispose for {test.TestId}: {disposeEx}").ConfigureAwait(false);
337-
}
341+
await invocation.InvokeAsync(test.Context, test.Context).ConfigureAwait(false);
342+
}
343+
catch (Exception disposeEx)
344+
{
345+
#if NET
346+
TUnitActivitySource.RecordException(disposalActivity, disposeEx);
347+
#endif
348+
await _logger.LogErrorAsync($"Error during OnDispose for {test.TestId}: {disposeEx}").ConfigureAwait(false);
338349
}
339350
}
351+
}
340352

341-
try
342-
{
343-
await TestExecutor.DisposeTestInstance(test).ConfigureAwait(false);
344-
}
345-
catch (Exception disposeEx)
346-
{
347-
await _logger.LogErrorAsync($"Error disposing test instance for {test.TestId}: {disposeEx}").ConfigureAwait(false);
348-
}
353+
try
354+
{
355+
await TestExecutor.DisposeTestInstance(test).ConfigureAwait(false);
356+
}
357+
catch (Exception disposeEx)
358+
{
349359
#if NET
350-
}
351-
finally
352-
{
353-
TUnitActivitySource.StopActivity(disposalActivity);
354-
}
360+
TUnitActivitySource.RecordException(disposalActivity, disposeEx);
355361
#endif
362+
await _logger.LogErrorAsync($"Error disposing test instance for {test.TestId}: {disposeEx}").ConfigureAwait(false);
363+
}
364+
#if NET
356365
}
366+
finally
367+
{
368+
TUnitActivitySource.StopActivity(disposalActivity);
369+
}
370+
#endif
357371
}
358372
}

TUnit.Engine/TestInitializer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public async ValueTask InitializeTestObjectsAsync(AbstractExecutableTest test, C
3434
{
3535
// Data source initialization now runs inside the test case span, so any spans it
3636
// creates (container startup, auth calls, connection pools, etc.) will appear nested
37-
// inside the test's trace timeline via the "data source initialization" child activity
37+
// inside the test's trace timeline via the "test object initialization" child activity
3838
// created by TestExecutor.
3939
await _objectLifecycleService.InitializeTestObjectsAsync(test.Context, cancellationToken);
4040
}

0 commit comments

Comments
 (0)