Context
Both Windows test projects currently pin xUnit v2:
windows/Ghostty.Tests/Ghostty.Tests.csproj -> xunit 2.9.2, xunit.runner.visualstudio 2.8.2
dist/windows/IconGen.Tests/IconGen.Tests.csproj -> xunit 2.9.2, xunit.runner.visualstudio 2.8.2 (added in # 206)
xUnit v3 is the active line. The CsWin32 review that informs the modern .NET reviewer skill explicitly notes v3 is the standard for actively maintained libraries on .NET 8+. The v2 runner model relies on shadow copy and an app-domain host that is awkward on .NET 8/9, and the v3 Exe host model sidesteps both.
Scope
Bundle both test projects into a single PR so the move is atomic and the CI / dotnet test story does not temporarily diverge between them. Touches:
- csproj package references: drop
xunit + xunit.runner.visualstudio, add xunit.v3 + (if still needed) xunit.v3.runner.visualstudio
- csproj properties:
OutputType=Exe on each test project (v3 host model)
- runner config: move any v2 runner settings to
xunit.runner.json (e.g. \"shadowCopy\": false) if we ever need to change defaults
- source code: tighten assertion shapes that the v3 analyzers flag more aggressively than v2 (e.g.
Assert.Contains(x, p) over Assert.NotEmpty(x.Where(p)), Assert.Fail(msg) over Assert.False(true, msg))
Why not now
Review on # 206 (branding + strip context menus) explicitly flagged this and deferred it. Doing the migration inside a feature PR would make that PR harder to review and would leave windows/Ghostty.Tests on v2 with a one-project inconsistency. Better as its own change.
Ordering
Land this after # 204 (AOT / trim / single-file analyzer triad) so the analyzer surface is stable before the test-host change, and any xUnit v3 analyzer warnings are attributable to the migration rather than to the trim/AOT opt-in.
Acceptance
Context
Both Windows test projects currently pin xUnit v2:
windows/Ghostty.Tests/Ghostty.Tests.csproj->xunit 2.9.2,xunit.runner.visualstudio 2.8.2dist/windows/IconGen.Tests/IconGen.Tests.csproj->xunit 2.9.2,xunit.runner.visualstudio 2.8.2(added in # 206)xUnit v3 is the active line. The CsWin32 review that informs the modern .NET reviewer skill explicitly notes v3 is the standard for actively maintained libraries on .NET 8+. The v2 runner model relies on shadow copy and an app-domain host that is awkward on .NET 8/9, and the v3 Exe host model sidesteps both.
Scope
Bundle both test projects into a single PR so the move is atomic and the CI /
dotnet teststory does not temporarily diverge between them. Touches:xunit+xunit.runner.visualstudio, addxunit.v3+ (if still needed)xunit.v3.runner.visualstudioOutputType=Exeon each test project (v3 host model)xunit.runner.json(e.g.\"shadowCopy\": false) if we ever need to change defaultsAssert.Contains(x, p)overAssert.NotEmpty(x.Where(p)),Assert.Fail(msg)overAssert.False(true, msg))Why not now
Review on # 206 (branding + strip context menus) explicitly flagged this and deferred it. Doing the migration inside a feature PR would make that PR harder to review and would leave
windows/Ghostty.Testson v2 with a one-project inconsistency. Better as its own change.Ordering
Land this after # 204 (AOT / trim / single-file analyzer triad) so the analyzer surface is stable before the test-host change, and any xUnit v3 analyzer warnings are attributable to the migration rather than to the trim/AOT opt-in.
Acceptance
dotnet test windows/Ghostty.Tests/Ghostty.Tests.csprojpasses under v3dotnet test dist/windows/IconGen.Tests/IconGen.Tests.csprojpasses under v3, all existing tests still greenxunit(v2) package references anywhere in the solutionjust test-win(if it exists) still works end to end