Files
ScadaBridge/tests/ZB.MOM.WW.ScadaBridge.SiteCallAudit.Tests/ZB.MOM.WW.ScadaBridge.SiteCallAudit.Tests.csproj
T
Joseph Doherty d2d4db492b test(infra): close register row 96 — per-suite xunit parallelism caps for the resource-heavy suites
maxParallelThreads was unset everywhere and only 3 of ~20 test projects carried
an xunit.runner.json at all, so xunit ran Environment.ProcessorCount collections
at once (14 on the dev box) in every suite. That is the general load multiplier
behind the row-82/91/95 flake family.

Policy: per-suite caps, NOT a global one. Pure unit suites stay uncapped for
speed; a suite gets a config only when a test CLASS costs a real external or
OS-level resource, so ProcessorCount of them at once is a resource question
rather than a CPU question. Four suites qualify, all capped at
maxParallelThreads: 4.

- ConfigurationDatabase.Tests, AuditLog.Tests, SiteCallAudit.Tests — verified as
  the three that actually take MsSqlMigrationFixture (the latter two by project
  reference). Every MSSQL-backed test class means a CREATE DATABASE plus the
  ~100-migration chain against the one shared dev SQL Server. 4 is chosen to
  MATCH the default slot count of row 82's cross-process sp_getapplock migrate
  semaphore, so the in-process and cross-process limits agree rather than
  fighting.
- Host.Tests — classes boot real ASP.NET Core hosts and real ActorSystems;
  Akka.Remote binds real sockets, Kestrel binds real ports, gRPC dials them.

Threads are capped, collections are NOT serialized. IntegrationTests
(parallelizeAssembly/parallelizeTestCollections false) and
CentralUI.PlaywrightTests (maxParallelThreads 1) already carry stricter
pre-existing settings and are left alone — the Playwright suite drives a browser
against a live rig, where shared UI state, not CPU, is the constraint.

Each capped csproj gains
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />;
the SDK does not copy it automatically and without it the file is silently
ignored. Verified by moving the copied file out of bin/ to take an uncapped
baseline: ConfigurationDatabase.Tests ran at 216 % CPU uncapped vs 145 % capped,
so the cap demonstrably takes effect.

Cost is close to nil — these suites are latency-bound on an external resource,
not CPU-bound (14-25 % utilisation for the two slowest). Standalone, uncapped ->
capped: AuditLog 383 tests 1m52s -> 1m51s; Host 520 tests 4m05s -> 4m08s;
ConfigurationDatabase 396 tests 10s -> 12s; SiteCallAudit 66 tests 8s -> 8s. All
green.

docs/test_infra/test_infra.md gains a Test Parallelism Policy section: the two
axes (in-process collection threads = this cap; cross-process solution-level
dotnet test = unbounded in general, bounded on the DB-migration path by the
sp_getapplock semaphore), the per-suite table with reasons, the rules (cap
threads rather than serialize; a cap is not a substitute for test isolation; the
Content copy is required), and the default for new projects (no config until the
project acquires a per-class external resource). test_infra_db.md cross-links it
from the semaphore bullet.
2026-08-17 05:27:52 -04:00

67 lines
3.0 KiB
XML

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Akka.TestKit.Xunit2" />
<PackageReference Include="coverlet.collector" />
<!--
MSSQL-backed SiteCallAuditActor tests use the MsSqlMigrationFixture
(reused from ConfigurationDatabase.Tests). Pinning 6.1.1 here mirrors
AuditLog.Tests: EF SqlServer 10.0.7 needs >= 6.1.1 but the central pin
is 6.0.2 (production ExternalSystemGateway). Override is test-only.
-->
<PackageReference Include="Microsoft.Data.SqlClient" VersionOverride="6.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
<!--
SkippableFact pattern (xunit 2.9.x has no native Assert.Skip) — used by
the MSSQL-backed SiteCallAuditActor tests to report Skipped when the dev
MSSQL container is not reachable.
-->
<PackageReference Include="Xunit.SkippableFact" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<!--
Deferred-work register row 96 — per-suite in-process parallelism cap.
This suite's actor tests take an MsSqlMigrationFixture per test CLASS: a
CREATE DATABASE plus the full migration chain on the ONE shared dev SQL
Server, alongside an Akka TestKit ActorSystem. Uncapped, xunit runs
Environment.ProcessorCount collections at once. The cap is threads only —
collections stay parallel — and 4 matches the default slot count of the
cross-process sp_getapplock migrate semaphore (row 82), so the in-process
and cross-process limits agree. Policy: docs/test_infra/test_infra.md.
The json must be copied to the output directory to take effect.
-->
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../src/ZB.MOM.WW.ScadaBridge.SiteCallAudit/ZB.MOM.WW.ScadaBridge.SiteCallAudit.csproj" />
<!--
The actor tests use the real SiteCallAuditRepository against a per-test
MSSQL database via MsSqlMigrationFixture. The fixture lives in
ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests; we reference that test project so
the fixture + EF migrations come along without duplicating them
(same pattern as ZB.MOM.WW.ScadaBridge.AuditLog.Tests' Bundle D2).
-->
<ProjectReference Include="../ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests.csproj" />
</ItemGroup>
</Project>