Skip to content

ConsoleRunner.Run returns without disposing MessageBus.ReporterWorker — test EXE lingers after run completes #3582

Description

@ratneshkaswa

ConsoleRunner.Run returns without disposing MessageBus.ReporterWorker — test EXE lingers after run completes

Versions

  • xunit.v3 3.2.2 (xunit.v3.core.mtp-v1 3.2.2 transitively)
  • xunit.runner.visualstudio (latest)
  • Microsoft.NET.Test.Sdk + Microsoft.Testing.Platform.MSBuild present
  • .NET 10.0.300 SDK
  • Windows 11 25H2, x64
  • Test project: <OutputType>Exe</OutputType> + <UseWPF>true</UseWPF>

Symptom

After dotnet test reports Passed! and a zero exit code, the test EXE
process (the xUnit v3 auto-generated self-host) stays alive 20 to 220 seconds
before the OS reaps it. The lingering EXE holds an exclusive lock on the
test assembly DLL in tests/bin/Release/, so the next dotnet test build
fails with MSB3027 trying to copy the apphost over the locked EXE.

The linger time is non-deterministic and varies with which test class ran:

Test selection Test runtime Process linger
1 trivial pure-data test 32ms 0s
~77 pure-data assertion tests in one class 889ms 0s
Any 1 test from a class whose nested types reference EF Core + SQLite 45ms 20-60s
Full suite (~3700 tests) 9 min 20-220s

The trigger correlates with classes whose type graph references EF Core +
SQLite, but the actual hang is in the runner, not in those tests — even a
pure-data static-method test in such a class triggers the linger.

Root cause (confirmed via dotnet-dump)

Main thread blocked at:

XunitAutoGeneratedEntryPoint.Main (line 13)
  → TestPlatformTestFramework.RunAsync(...).GetAwaiter().GetResult()
    → Task.SpinThenBlockingWait → Monitor.Wait

The Task being waited on never completes because a foreground thread is
stuck at:

Xunit.Internal.MessageBus.ReporterWorker() line 91
  → WaitHandle.WaitOne()

MessageBus.ReporterWorker is not a background thread, so the .NET runtime
won't let the process exit while it's running. ConsoleRunner.Run
(and TestPlatformTestFramework.RunAsync underneath) report results and
return the exit code without signalling the reporter worker to shut down or
disposing the MessageBus.

Reproduction

Minimal repro shape (have not extracted a standalone project, but should be
reproducible with any xunit.v3 3.2.2 self-host EXE that touches EF Core
SQLite from a test):

  1. <OutputType>Exe</OutputType> test project referencing xunit.v3 3.2.2.
  2. One test that constructs an IDbContextFactory<TContext> backed by
    SQLite and calls db.Database.EnsureCreated() inside a using.
  3. dotnet test --filter <ThatOneTest>.
  4. Tests pass cleanly. Observe the test EXE stays alive in Task Manager for
    20+ seconds after dotnet test returns.

Confirmed on both code paths:

  • Auto-generated entry point in MTP server mode (--server arg present)
  • Custom entry point calling ConsoleRunner.Run directly (MTP bypassed)

Both stop in the same place — the bus reporter worker is shared.

Workaround attempts and their downsides

  1. <UseMicrosoftTestingPlatformRunner>false</UseMicrosoftTestingPlatformRunner> in csproj
    — no effect; the entry-point cache wasn't invalidated and the regenerated
    Main still branched on --server.

  2. Custom Program.cs with ConsoleRunner.Run only (suppressing the
    auto-generated entry via <XunitAutoGeneratedEntryPoint>false</...>)
    — hang still occurs at the same place in MessageBus.ReporterWorker.

  3. Environment.Exit(exitCode) after ConsoleRunner.Run returns
    — eliminates the linger but dotnet test reports "Test host process
    crashed" → "Test Run Aborted" even on green test runs, breaking CI.

Asks

  • Confirm whether MessageBus.ReporterWorker should be marked background, or
    whether ConsoleRunner.Run should explicitly dispose the MessageBus before
    returning.
  • A fix in xunit.v3 itself would let consumers drop pre-build orphan-kill
    workarounds.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs ReproThis issue needs a repro project

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions