Skip to content

Hand the test logger a resolved config, not the container it outlives - #716

Merged
rbuergi merged 3 commits into
mainfrom
fix/test-logger-no-container-resolve
Jul 29, 2026
Merged

rbuergi merged 3 commits into
mainfrom
fix/test-logger-no-container-resolve

Conversation

@rbuergi

@rbuergi rbuergi commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Salvages the third commit of #708 (99577caba), which landed on that branch ~2 minutes after the PR was merged and therefore never reached main.

The defect

Teardown logs AFTER the DI container is disposed. XUnitFileLogger held the IServiceProvider and resolved IConfiguration on first log per category, so anything logging from a Dispose path threw:

MonolithMeshTestBase.DisposeAsync → AsyncDisposeQueue.DrainAsync
  → CompilationCacheService.Dispose        (logs during teardown)
    → XUnitFileLogger.GetMinLogLevel → GetService<IConfiguration>
      → disposed Autofac LifetimeScope → ObjectDisposedException

Measured on Acme.Test via the native xUnit host (the way CI runs it): 23 first-chance ObjectDisposedExceptions per run, every one this chain. They were caught (GetMinLogLevel swallows and defaults to Information), so they never failed a test — they burned 23 throws + 23 first-chance stack captures on the teardown path and buried teardown-stragglers.log in fault-shaped noise. That log is the working diagnostic for the #613 teardown crashes (it is how the #715 hub-construction chain was found), so keeping it signal-only has direct value.

The fix

XUnitFileLoggerProvider resolves the configuration object once, in its constructor, while the container is guaranteed alive, and hands that object to every logger. IConfiguration is a plain object once resolved — the reference stays valid after the scope that produced it is gone — so this removes the post-disposal container access rather than guarding it.

Verified: Acme.Test 76/76 exit 0 via the native host, stragglers 23 → 0; MeshWeaver.Fixture builds clean under -c Release -warnaserror on current main.

Explicitly NOT claimed: that this fixes the transient CI exit=2 (those exceptions were caught; attempt 2 of the same commit ran clean with no code change).

No What's New entry: test-infrastructure only.

🤖 Generated with Claude Code

Teardown logs AFTER the DI container is disposed. XUnitFileLogger held the
IServiceProvider and resolved IConfiguration on first log per category, so
anything logging from a Dispose path reached Autofac's already-disposed
LifetimeScope and threw.

Measured on Acme.Test (native xUnit host, the way CI runs it): 23 first-chance
ObjectDisposedExceptions per run, every one this chain —

    MonolithMeshTestBase.DisposeAsync -> AsyncDisposeQueue.DrainAsync
      -> CompilationCacheService.Dispose        (logs during teardown)
        -> XUnitFileLogger.GetMinLogLevel -> GetService<IConfiguration>
          -> disposed LifetimeScope -> throw

They were caught (GetMinLogLevel swallows and defaults to Information), so they
never failed a test. They burned 23 throws plus 23 first-chance stack captures
on the teardown path and buried teardown-stragglers.log in fault-shaped noise
that reads exactly like a real defect — which is worth removing on its own, and
doubly so while that log is the diagnostic for the #613 teardown crashes.

The provider now resolves the configuration OBJECT once, in its constructor,
while the container is guaranteed alive, and hands that object to every logger.
IConfiguration is a plain object once resolved, so the reference stays valid
after the scope that produced it is gone — this removes the post-disposal
container access rather than guarding it.

Verified: Acme.Test 76/76 exit 0 via the native host, stragglers 23 -> 0.

NOT claimed: that this fixes the CI exit=2. Those exceptions were caught, and
exit=2 has never reproduced locally even with CI's exact invocation. AI.Test's
19 stragglers are a DIFFERENT chain (post-dispose hub construction, #613) and
are untouched by this.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 29, 2026 12:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes teardown-time logging noise in the test infrastructure by ensuring XUnitFileLogger no longer resolves configuration via a DI container that may already be disposed during test teardown.

Changes:

  • Resolve and store an IConfiguration instance once in XUnitFileLoggerProvider (while the container is alive) instead of storing IServiceProvider.
  • Pass the resolved IConfiguration to each XUnitFileLogger, removing post-disposal container access in GetMinLogLevel.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions

github-actions Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

959 tests  ±0   953 ✅ ±0   4m 2s ⏱️ -41s
  5 suites ±0     6 💤 ±0 
  5 files   ±0     0 ❌ ±0 

Results for commit a0263fb. ± Comparison against base commit d875505.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

   12 files  ±0     12 suites  ±0   3m 38s ⏱️ +17s
  977 tests ±0    973 ✅ ±0  4 💤 ±0  0 ❌ ±0 
1 041 runs  ±0  1 037 ✅ ±0  4 💤 ±0  0 ❌ ±0 

Results for commit a0263fb. ± Comparison against base commit d875505.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

2 100 tests  ±0   1 912 ✅ ±0   4m 51s ⏱️ -10s
   12 suites ±0     188 💤 ±0 
   12 files   ±0       0 ❌ ±0 

Results for commit a0263fb. ± Comparison against base commit d875505.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

1 129 tests  ±0   1 026 ✅ ±0   5m 7s ⏱️ +2s
   10 suites ±0     103 💤 ±0 
   10 files   ±0       0 ❌ ±0 

Results for commit a0263fb. ± Comparison against base commit d875505.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

1 164 tests  ±0   1 163 ✅ ±0   5m 13s ⏱️ -48s
   12 suites ±0       1 💤 ±0 
   12 files   ±0       0 ❌ ±0 

Results for commit a0263fb. ± Comparison against base commit d875505.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

1 202 tests  ±0   1 201 ✅ ±0   6m 49s ⏱️ -13s
   10 suites ±0       1 💤 ±0 
   10 files   ±0       0 ❌ ±0 

Results for commit a0263fb. ± Comparison against base commit d875505.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

   61 files  ±0     61 suites  ±0   29m 43s ⏱️ - 1m 33s
7 531 tests ±0  7 228 ✅ ±0  303 💤 ±0  0 ❌ ±0 
7 595 runs  ±0  7 292 ✅ ±0  303 💤 ±0  0 ❌ ±0 

Results for commit a0263fb. ± Comparison against base commit d875505.

♻️ This comment has been updated with latest results.

rbuergi and others added 2 commits July 29, 2026 14:44
From the Copilot review, and correct per this repo's own no-swallow rule: the
provider constructor's catch-all could mask a real DI failure (misconfigured
container, faulting IConfiguration factory) as a silent log-level downgrade.
The stated intent is only to tolerate a provider built from an already-disposed
scope, so catch ObjectDisposedException specifically and let everything else
surface loudly.

Acme.Test 76/76; Fixture builds clean under -c Release -warnaserror.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rbuergi

rbuergi commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Copilot's finding taken in a0263fbb1: the catch-all could mask a real DI failure as a silent log-level downgrade, against this repo's own no-swallow rule. Now catches ObjectDisposedException only — the one case the constructor is meant to tolerate (a provider built from an already-disposed scope); anything else surfaces loudly. Acme.Test 76/76, Fixture clean under -warnaserror.

@rbuergi
rbuergi merged commit 394c6ef into main Jul 29, 2026
15 checks passed
@rbuergi
rbuergi deleted the fix/test-logger-no-container-resolve branch August 5, 2026 10:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants