Loc.Current (src/ClaudeDo.Ui/Localization/Loc.cs) is a process-wide mutable static; which localizer is active depended on xUnit's parallel test scheduling. Ui.Tests: disable collection parallelization (cheap, pure VM suite) and install the real localizer in a module initializer so no test can ever observe the uninitialized key-echo default. Worker.Tests: install the same module initializer but leave parallelization on — disabling it measured a 27% wall-clock regression on its real-SQLite/git suite, and the initializer alone already removes the race. AgentConfigEditorViewModelTests now installs its own localizer instead of relying on ambient state.
41 lines
1.9 KiB
C#
41 lines
1.9 KiB
C#
using System.Runtime.CompilerServices;
|
|
using ClaudeDo.Localization;
|
|
using ClaudeDo.Ui.Localization;
|
|
using ClaudeDo.Ui.Services;
|
|
using Xunit;
|
|
|
|
// Loc.Current (src/ClaudeDo.Ui/Localization/Loc.cs) is a process-wide mutable static that many
|
|
// ViewModel test constructors assign — parallel collections race on which localizer is active.
|
|
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
|
|
|
namespace ClaudeDo.Ui.Tests;
|
|
|
|
internal static class TestSetup
|
|
{
|
|
// Runs once when this test assembly loads, before any test executes. Without it, every test
|
|
// that constructs a real DetailsIslandViewModel/TasksIslandViewModel/WorkerClient and calls a
|
|
// method instrumented with OperationTiming.Shared.Record writes straight into the live app's
|
|
// ~/.todo-app/logs/operation-timing.ndjson — a single Ui.Tests run added 50-100 cancelled
|
|
// "DetailsIsland.BindAsync:?" lines to that file, dwarfing real usage and making the churn
|
|
// look like a UI bug.
|
|
[ModuleInitializer]
|
|
internal static void RedirectOperationTimingAwayFromRealAppData()
|
|
{
|
|
var path = Path.Combine(Path.GetTempPath(), "claudedo-ui-tests", $"operation-timing-{Environment.ProcessId}.ndjson");
|
|
OperationTiming.Shared = new OperationTiming(path);
|
|
}
|
|
|
|
// Installs the real localizer before any test runs, so a class that reads Loc.T/Loc.Current
|
|
// without setting it up itself (e.g. via a ViewModel error message) doesn't fall back to
|
|
// Loc's key-echo default depending on which test class happens to run first.
|
|
[ModuleInitializer]
|
|
internal static void InstallRealLocalizer()
|
|
{
|
|
var dir = AppContext.BaseDirectory;
|
|
while (dir is not null && !Directory.Exists(Path.Combine(dir, "src", "ClaudeDo.Localization", "locales")))
|
|
dir = Path.GetDirectoryName(dir);
|
|
Loc.Current = new Localizer(
|
|
LocaleStore.Load(Path.Combine(dir!, "src", "ClaudeDo.Localization", "locales")), "en");
|
|
}
|
|
}
|