Skip to content

Tests stop sharing WPF threads, Postgres roles and runner CPU - #5604

Merged
erikdarlingdata merged 13 commits into
devfrom
fix/5602-test-isolation
Oct 9, 2026
Merged

erikdarlingdata merged 13 commits into
devfrom
fix/5602-test-isolation

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Fixes #5602
Fixes #5600

Test classes were sharing WPF threads, Postgres cluster objects and runner CPU, and that sharing produced a new flake every day. Each lane owns one section below.

CHANGELOG
SECTION: None
ENTRY: - Test isolation ([#5602]) - tests only, with no product change.

WPF (lane a)

Every WPF test body now runs on ONE shared background STA thread per test process, instead of each of 54 test files starting its own thread behind WpfStaGate.

What changed

  • Darling/Darling.Tests/StaTestThread.cs (linked into Lite.Tests; the filter in build.yml and Lite.Tests.csproj follow): StaTestThread.Run(Action), Run<T>(Func<T>) and Run(Func<Task>). The thread starts lazily and runs Dispatcher.Run().
  • One body at a time, end to end: the CALLER takes a process-wide gate (SemaphoreSlim) before it queues the body and releases it after the body and its drain return, so a body that pumps (PushFrame) can never let a second one in (Lite test flake: ScreenReaderNamesFollowUpTests hits a WPF dictionary from two threads #5559). The gate replaces WpfStaGate.
  • Drain after each body: any window the body left open is closed, then the dispatcher runs to application-idle. An exception thrown by queued work (a timer tick, a Loaded handler) no longer kills the thread; it fails the test whose body or drain ran it.
  • Exceptions come back with their original stack (ExceptionDispatchInfo).
  • Hang guard: a body that does not return in 60 s fails its own test, naming it. The shared thread is then stuck, and every later WPF test in the process fails at once with a message naming the stuck test (the wait for the gate is not charged to the 60 s).
  • A synchronous body runs with no synchronization context, like it did on a plain thread. Without that, a body that blocks on .GetAwaiter().GetResult() of a task whose awaits capture the dispatcher's context would wait for the thread it is blocking. Async bodies (Run(Func<Task>)) keep the dispatcher's context.
  • All 54 files converted (28 Darling.Tests, 26 Lite.Tests): the private OnStaThread copies, WpfStaGate.cs and both WpfStaGateCensusTests are removed. A few files keep a small wrapper because they set something per body: ViewerCollectorDurationHoverTests.InEnglish (sets and restores the thread's culture, which the shared thread would otherwise keep), ViewerCollectionHealthLoadTests.OnTabDispatcher (display mode UTC, async body), StandalonePlanViewerPlusTabTests.OnRig (builds the rig on the thread; the old InvokeShutdown in its finally is gone, a shared dispatcher must not be shut down). The two live files that ran their own Dispatcher.Run() or thread (ViewerCollectionLogDataStartLiveTests, ViewerQueryGridsDataStartLiveTests) call StaTestThread.Run directly.
  • StaThreadCensusTests (both projects, Stage=Guard) replaces WpfStaGateCensusTests. It reads code only (through CSharpSourceWalker) and fails, naming the file and the rule, when a test file outside the allow list calls SetApartmentState(ApartmentState.STA), defines an OnStaThread-style helper, or calls Dispatcher.Run(). It also needs at least 20 files that use the host (anti-vacuity), and every allow-list entry needs a reason.
  • StaTestThreadTests (Darling.Tests, in the timing collection because it judges 200 to 300 ms limits): bodies run in order on one background STA thread, an exception keeps its stack and the thread survives it, a pumping body keeps a second caller out (3 callers x 3 bodies, never more than 1 inside), the hang guard names the test and later calls fail at once (on a host of its own, so the shared thread is never left stuck), the drain runs queued work and closes a window left open, an async body resumes on the shared thread, an exception from queued work reaches the test, EnterExclusive keeps a body out.

Allow list

  • Darling.Tests: the host StaTestThread.cs, its own tests StaTestThreadTests.cs, the census itself, and WpfExitSwitchTests.cs (Test processes no longer pay 0.3 s per WPF STA thread at exit, which failed green Darling runs (#5596) #5598: it needs an STA thread that has ENDED, which the shared thread never does). That file is not on this branch yet; it must take StaTestThread.EnterExclusive() around its own thread (the census checks that once the file exists).
  • Lite.Tests: no entry other than the census itself.
  • For whoever merges dev into this branch: WpfExitSwitchTests.cs still calls WpfStaGate.Enter(). Change that one call to StaTestThread.EnterExclusive(); nothing else needs touching.

RED-plant. A planted file with an own-thread helper, an ApartmentState.STA start and a Dispatcher.Run() fails the census at my head in both projects, once per rule, naming the file. On the origin/dev tree (scanned with a Python port of the same rules, comment lines dropped, not by building dev) the census would flag Darling.Tests: 28 files starting a thread, 26 own helpers, 2 Dispatcher.Run(); Lite.Tests: 26, 26, 0.

Tests (Total / Failed, at the pushed head; live classes skip with no Postgres rig)

  • StaTestThreadTests, both StaThreadCensusTests, TimingTestCensusTests: 16 / 0 (Darling.Tests).
  • Every converted Darling.Tests class: 368 / 0 (44 skipped, the live Postgres ones).
  • Every converted Lite.Tests class: 545 / 0.
  • *Census* + *Ratchet* + CommentFilterAdoptionTests + DarlingReadsTraitGuardTests + CrossAppGuardCiGateTests: Darling.Tests 518 / 0 (1 skipped), Lite.Tests *Census* + *Ratchet* 67 / 0.
  • -trait Stage=Guard -failSkips: Darling.Tests 1438 / 0, Lite.Tests 450 / 0.
  • 0 warnings in both builds.
  • NOT run: the full suites (CI decides), and the live Postgres classes whose helpers I converted (ViewerCollectionLogDataStartLiveTests drill and banner paths, ViewerQueryGridsDataStartLiveTests.ReadBanner, ViewerBlockedProcessReportsCoverageLiveTests, ViewerJobHistoryDataStartLiveTests, OverviewBlockingLaneDataStartLiveTests): they skip here, CI runs them.

Check on CI: the async drill path in ViewerCollectionLogDataStartLiveTests is the one conversion that changed an STA shape (it ran Dispatcher.Run() on its own thread; it now awaits on the shared dispatcher).

Postgres (lane b)

What changed

  • One collection, pg-cluster-roles, declared with DisableParallelization (Darling.Tests/PgClusterRolesCollection.cs). xunit runs it alone, after the parallel classes. It has no collection fixture: its members build their own scratch database or change only cluster settings, and a second LivePostgresStoreFixture would run its residue check while parallel collections still write the store.
  • ScratchPostgres isolates tables, not the cluster's catalogs. The product provisions FIXED role names (admin, viewer, mcp), so any test that runs its provisioning batch, or creates, drops or alters those roles, shares them with every other such test whatever database it aims at.
  • Moved into pg-cluster-roles:
    • fixed roles: PasswordKeyTablesLiveTests, DarlingSecuritySplitLiveTests, ProvisioningBatchLockedTransactionLiveTests (the second class in ProvisioningSerializationTests.cs), StoreServerPasswordRulesLiveTests, GetReadLatencyLiveTests (it had no collection at all), ComposeStatementTimeoutV147MigrationLiveTests (no collection either).
    • cluster settings: DarlingStoreHostProfileLiveTests (ALTER SYSTEM SET max_wal_size), PgServerConfigPendingRestartLiveTests (ALTER SYSTEM on shared_buffers), PgSettingRedactionLivePostgresTests (ALTER SYSTEM SET primary_conninfo).
  • A leak this audit found and fixed: the V147 re-assertion fact created viewer and mcp and never dropped them. The classes that provision the managed roles skip themselves when those names already exist, so after V147 ran they quietly skipped (24 role-exists skips on the first run on a fresh rig, none after the fix). The fact now records which of the two it made and drops only those, through LiveStoreCleanup.RunAsync.
  • StoreStatementStatsLiveTests created roles pgss_reader, pgss_outsider, pgss_plainowner and the database pgss_plain under fixed names. They now carry a per-run suffix.
  • Every other test that makes a role already uses a per-run GUID suffix or the scratch name (aws_role_, srv_add_, tag_core_, dismiss_only_, host_mcp_, sec_admin_ and the like, Darling.Tests: a full local run leaves state that fails later runs #4981), so they need nothing. LivePostgresCollectionHygieneTests now accepts [Collection("pg-cluster-roles")] as a recorded decision (it runs alone, which is stronger than live-postgres).
  • PgClusterObjectCensusTests (Darling.Tests; Lite.Tests has no Postgres tests). It reads each class that reaches the DARLING_TEST_PG cluster, strips comments, and fails on CREATE/ALTER/DROP ROLE with a literal name, DROP OWNED BY, GRANT ... TO / REVOKE ... FROM a literal role, ALTER SYSTEM SET/RESET, ALTER DATABASE x SET, ON DATABASE x with a literal name, the provisioning entry points, and the DarlingManagedPostgres.*RoleName constants, unless the class is in pg-cluster-roles or on the allow list. A stale allow-list entry fails too. A line with Assert. or Regex is skipped (text the test only checks).

Audit (every test that creates, alters, grants, revokes or drops a role, membership, database grant, role setting or cluster setting)

  • Own-cluster classes (start a managed cluster on a free port, nothing shared): ComposeStoreRolesLiveTests, ManagedConfVerdictsLiveTests, ScramVerifierLiveTests, DarlingManagedPostgresTests, DarlingStoreUpgradeTests. The census does not reach them.
  • Per-run names, nothing to change: AdminServersRouteLiveTests, AwsPerServerRoleLiveTests, CustomAlertResolveGrantLiveTests, DarlingMcpStoreHostToolsLiveTests, DarlingPasswordKeyStoreLiveTests, DiagnosticsBundleLeakLiveTests, PgReadBinaryFileCapabilityLiveTests, StoreInstallIdRefusedClusterLiveTests, ValidateConfigOldFormatPasswordLiveTests, ServerEditViewerRoleLiveTests.
  • Allow list (PgClusterObjectCensusTests.AllowList, each entry has its reason): MigrationUpgradeLadderLiveTests (CREATE ROLE darling only if missing; on a CI cluster it exists), AlertHistoryDismissTests, ViewerStoreLoginRawReadLiveTests, ServerAddViewerRoleLiveTests, ServerEditLiveTests, ServerTagMcpRoleLiveTests, ServerTagViewerRoleLiveTests, ComposeTempFileLimitLiveTests (shipped GRANT or role text retargeted at a per-run role, never the fixed roles), PgStatementTextScrubLiveTests, StatementAnalysisReadCutLiveTests, StoreLogRemaskLiveTests, StoreStatementStatsLiveTests (sample statement text fed to a scrubber as data).
  • Known gap in the census: the allow list is per class, so a new literal-role statement added to an allow-listed class is not caught. The four data-only entries are the weakest; the rest only retarget shipped text.

RED-plants

  • At the head: a planted class that reads DARLING_TEST_PG and holds CREATE ROLE planted_literal LOGIN outside the collection fails the census with the file, line, class and rule. Removed afterwards.
  • At the start point of this branch (dev at 5ee741f, before the moves): the census listed the 9 classes above, plus the unsuffixed StoreStatementStatsLiveTests.

Test plan

  • Build: 0 warnings, 0 errors, after merging both other lanes' work.
  • Moved live classes on a throwaway rig (PostgreSQL runtime with TimescaleDB and pg_stat_statements, UTC, a database named darling), 12 classes in one run: Total 89, Failed 0, Skipped 1 (the compressed-hypertable fact skips because the test database had no TimescaleDB extension; the live collection's fixture normally enables it). Roles left on the rig afterwards: none.
  • StoreStatementStatsLiveTests + StoreStatementStatsTests: Total 41, Failed 0.
  • *Census*, *Ratchet* and *Hygiene* classes: Total 599, Failed 0, Skipped 1 (existing).
  • -trait Stage=Guard -failSkips: Total 1441, Failed 0, Skipped 0.
  • The full suite (CI decides), and the before/after "whole-tree guards" and shard exit times the issue asks for (measured on CI, not on this laptop).

Handoff notes for the seat

  • Cost: nine classes (about 89 tests, 150 s here) now run one after another at the end instead of in parallel. DarlingSecuritySplitLiveTests has 10 tests but only one uses the fixed roles; splitting it would shave a little.
  • GuardStageScanner only knows live-postgres as a live collection. Every moved class also reads DARLING_TEST_PG, which it already catches, so nothing is lost today.

Timing (lane c)

What changed

  • One collection, timing, declared with DisableParallelization (Darling.Tests/TimingCollection.cs, Lite.Tests/TimingCollection.cs). xunit runs it alone, after the parallel classes. It replaces pg-setting-redactor-timing and statement-filter-warm-up: both old definitions are deleted and their classes (PgSettingRedactorTests, StatementFilterWarmUpWaitTests) joined timing. The warm-up class stages a process-wide fake warm-up; running alone in timing keeps it apart from every other class, which is all its own collection did.
  • Darling.Tests classes moved into timing (each judges a real upper time bound and owns its store or needs none): DarlingCloudIdentityProbeTests, PgLogEventsPipelineTests, PgSensitiveStatementFilterLiveTests, SensitiveStatementsParityLiveTests, SensitiveStatementAutoParamTests, StatementFilterAlertTests, StatementFilterWebTests, WebhookPostTimeoutTests, ScratchPostgresExitDrainTests, ScratchPostgresQuiesceLiveTests, TuningHourlyCreateLiveTests, RawRetentionPurgeJobLiveTests, BoundedCompressionDdlTests, QueryStoreIntervalBrinLiveTests.
  • Lite.Tests classes moved into timing: CollectionHealthReadBehaviourTests, StatusBarSizeReadLockTests, DetachedCollectorGateTests, QueryStoreServerGateTests.
  • The cloud probe budget test fails again in the whole-suite guards job #5600: DarlingCloudIdentityProbeTests takes the return time from a synchronous continuation on the probe task, so the hop back into the test is no longer counted, and the class runs in timing. The stall-watcher thread from The cloud probe budget test stops counting machine stalls against the probe (#5600) #5601 is not taken: the bar stays 1000 ms with no allowance. The cloud probe budget test stops counting machine stalls against the probe (#5600) #5601 is to be closed in favour of this.
  • TimingTestCensusTests in each project, on a shared scanner (Darling.Tests/TimingTestScanner.cs, linked into Lite.Tests; the path is in build.yml's lite_linked_shard). It fails when a class starts a clock (Stopwatch.StartNew/GetTimestamp/GetElapsedTime, new Stopwatch, DateTime.UtcNow - started, Environment.TickCount; comments and string literals are blanked first, so source pins that quote the calls do not count) outside [Collection("timing")] and off the allow list. The rule is wider than "asserts on the time" on purpose: a diagnostic has to say it is one. Allow-list entries are per file with a reason, a stale entry fails (the list only shrinks), and the census checks the collection is DisableParallelization and that the scan still sees at least 15 (Darling) / 2 (Lite) members.
  • The "judge the fastest of three" part: the redactor, SensitiveStatementOutputFilterTests (best of three, through TimingClaim) and the sensitive-statement perf tests already do. I added no new repeat to the wait-based budget tests (the probe, the webhook post, the drains): they wait real budgets of 200 ms to several seconds, so three runs would add minutes, and running alone is what removes the competition.

Audit: every test file that starts a clock

Moved: the 18 classes above. Allow list (reasons are in TimingTestCensusTests.s_allowed, 31 Darling files and 6 Lite files). In brief:

  • hang guard (ceiling of 30 s to 2 min, not a timing check): ComposeTimeoutPhaseLive, QueryStoreIntervalPurgeRowCapped, StatementScrubSnapshot, TuningDropLockRecoveryLive, ProvisioningSerialization (lower bounds plus 20 s / 90 s ceilings), TestCpuAccountant (Lite).
  • lower bound only (load can only lengthen it): CaggGroupIndexDropUpgradeLive, MoveDirectoryOnceReleased, ReadScopeStatementCaptureLive, CollectionResetGate (Lite).
  • diagnostic or throughput record, nothing asserts on the time: PlanRegressionDailyTriggerCostRig, QueryStoreBackfillCutChunkLive, QueryStoreIntervalConvergenceLive, SensitiveStatementJson/Session/Xml, StatementScrubBlocking, TrendStaleStatsLive, WebExceptionTextCensus, QuerySnapshotPlanOnDemand and QueryWindowTruncation (Lite).
  • bound is TimingClaim.AtMost, which asserts only with DARLING_TIMING_TESTS=1: SensitiveStatementOutputFilterTests, SensitiveStatementsTests.
  • poll loops or a clock that only sets the workload: DarlingManagedPostgres, RollupBackfillLive, RdsLogStateConcurrency, PerDatabaseFaultPathSplit, CollectionLogDrainForensicsStore.
  • Lite classes already in CollectionResetGate (DisableParallelization for process-wide state): DuckDbSentinelConnection, PostCollectionCheckpointBudget, CollectionResetGate.
  • live-postgres members (LivePostgresCollectionHygieneTests ties them to the shared migrated store and a class has one collection): MigrationLockWaitContention (750 ms against a 1 s poll sleep), CollectionLogSegmentByLive (30 s), PgWaitSamplerLive, StatementFilterBlockingReadsLive (10 s), StatementFilterPlanReadsLive (15 s), StoreCopyPhaseLivePostgres, TuningReaderBudgetLive (half the command deadline).

Left for a successor

The live-postgres members above still run in parallel with non-live classes. Their bounds are wide (10 s and up, or a server-side timeout with headroom) except MigrationLockWaitContentionTests (750 ms) and StoreCopyPhaseLivePostgresTests. Moving them needs either a copy of the shared-store fixture into timing or assertions on the work (the lock was taken on the first attempt, the deadline fired on its own clock). I did not do either here.

Numbers (item 5)

BEFORE, from the last three green dev Build runs:

  • The "Darling whole-tree guards" job took 20, 22 and 21 s. It did not run the suite in those runs.
  • Guard tests job: 398, 446 and 454 s (build 194 to 239 s).
  • Per-shard job times: Darling PG shards 457 to 759 s, Lite shards 337 to 1230 s (Lite shard 2 was the longest in all three runs: 1230, 1167, 761 s).
  • Gap between a shard's last test summary line and the end of its test step: 0.0 to 6.5 s (Darling PG shard 0: 5.8, 5.8, 6.5 s; Lite shard 2: 0.2, 2.7, 4.4 s).
    AFTER: to come from this PR's own run (not measured here; the laptop does not run the suite). The timing classes now run in one serial phase at the end of each shard, so a shard's wall time can rise by the sum of those classes' times (a few minutes at most) while the per-class variance falls.

Tests run (laptop, build 0 warnings in both projects)

Darling.Tests: TimingTestCensusTests 5/0 (Total/Failed), DarlingCloudIdentityProbe 20/0, PgSettingRedactorTests 139/0, StatementFilterWarmUpWaitTests 18/0, WebhookPostTimeoutTests 7/0, StatementFilterAlertTests 19/0, SensitiveStatementAutoParamTests 44/0, StatementFilterWebTests 14/0, PgLogEventsPipelineTests 94/0, BoundedCompressionDdlTests 5/0 (2 skipped, no store), ScratchPostgresExitDrainTests 9/0 (2 skipped), all *Census* 490/0, all *Ratchet* 15/0, LivePostgresCollectionHygiene 11/0, -trait Stage=Guard -failSkips 1437/0.
Lite.Tests: TimingTestCensusTests 3/0, CollectionHealthReadBehaviourTests 26/0, StatusBarSizeReadLockTests 1/0, DetachedCollectorGateTests 7/0, QueryStoreServerGateTests 13/0, GuardStageCensusTests 9/0, all *Census* 66/0, CrossAppGuardCiGate 19/0, -trait Stage=Guard -failSkips 449/0.
Not run: the live Postgres classes that got only an attribute (no rig, by the brief); the full suites (CI decides).

RED plants

  • At this head: a class with a bare Stopwatch.StartNew() added to each project. Darling's census failed naming ZzTimingPlantTests.cs (5 run, 1 failed); Lite's failed the same way (3 run, 1 failed). Both plants removed.
  • At dev (the new scanner, census and collection copied onto a clean checkout of dev): Darling's census failed naming DarlingCloudIdentityProbeTests, PgLogEventsPipelineTests, WebhookPostTimeoutTests and the rest of the unmoved classes; Lite's failed naming CollectionHealthReadBehaviourTests, DetachedCollectorGateTests and StatusBarSizeReadLockTests.

Merge (lane d)

dev was merged into this branch twice (the second time after the time range picker change landed). Branch head after this lane: see the PR.

What the censuses caught from dev, and what I did

  • WpfExitSwitchTests (from Test processes no longer pay 0.3 s per WPF STA thread at exit, which failed green Darling runs (#5596) #5598) still called the removed WpfStaGate.Enter(): now StaTestThread.EnterExclusive(). The timing census then caught it (it starts a clock): it now carries [Collection("timing")], since it judges the fastest of three exit times. Its Lite.Tests link and build.yml filter entry were merged keeping both sides (the old WpfStaGate.cs entries are gone, the new StaTestThread.cs, TimingTestScanner.cs and WpfExitSwitchTests.cs entries stay).
  • The time range picker change (Time range picker: more presets, calendar periods and typed ranges #5570) added TimeRangePickerControlTests and ViewerTimeRangeGapFixTests, each with its own OnStaThread helper that took WpfStaGate. That broke the build, so both now call StaTestThread.Run(...) and the helpers are deleted, as lane a did elsewhere. The STA census passes with no allow-list entry.
  • PgClusterObjectCensusTests, the Lite STA census and the Lite timing census caught nothing else from dev.

Live classes run on a throwaway server (TimescaleDB and pg_stat_statements loaded, UTC, a darling database present)

Total/Failed: ViewerCollectionLogDataStartLive 9/0 (the riskiest conversion: its drill path now awaits on the shared dispatcher), ViewerPlanCorrectionsDataStartLive 5/0 (the class in the ViewerQueryGridsDataStartLiveTests file), ViewerBlockedProcessReportsCoverageLive 17/0, ViewerJobHistoryDataStartLive 5/0, Lite OverviewBlockingLaneDataStartLiveTests 12/0, PgLogEventsPipeline 94/0, PgSensitiveStatementFilterLive 2/0, SensitiveStatementsParityLive 5/0, ScratchPostgresExitDrain 9/0, ScratchPostgresQuiesceLive 4/0, TuningHourlyCreateLive 5/0, RawRetentionPurgeJobLive 2/0, BoundedCompressionDdl 5/0, QueryStoreIntervalBrinLive 31/0, DarlingCloudIdentityProbe 20/0. No failure, so nothing from this branch to fix.

The two tight live-postgres timing checks

  • MigrationLockWaitContentionTests: the uncontended-acquire fact (750 ms against the one-second poll sleep) cannot drop its clock. The bug it guards is a poll sleep moved above the first attempt, which keeps the attempt count at 1, so no count of work tells it apart. It moved to a new class, MigrationLockUncontendedAcquireTimingTests, in timing, on its own scratch database (the migration lock is per database, so nothing there to contend with). The bar is unchanged. The three contention facts stay in live-postgres, and their clock is gone, so that allow-list entry is deleted.
  • StoreCopyPhaseLivePostgresTests.TheStartPhaseIsBounded...: the upper bound (the connection's command timeout) is dropped, because the exact fault type and exact message assertions already tell the sweep deadline from the connection's own timeout (the latter is an NpgsqlException). The lower bound stays (deadline less a second; load can only lengthen a wait). Its allow-list entry now says lower bound only.
  • RED plants (each reverted): a poll sleep before the first attempt failed the moved fact ("took 1002ms, over the 750ms ceiling"); the start-phase deadline built in milliseconds failed the lower bound ("gave up after 0.0s"); the start-phase deadline made a day long (so only the connection's timeout ends the stall) failed on the exact type (expected TimeoutException, got NpgsqlException). The old upper bound caught only the third, and the type check still does.
  • The wide ones stay in live-postgres: CollectionLogSegmentByLive, PgWaitSamplerLive, StatementFilterBlockingReadsLive, StatementFilterPlanReadsLive, TuningReaderBudgetLive. The shared reason in the timing census now says why: a second copy of the shared-store fixture in timing would read the process-wide cleanup ledger again and report every live class's residue twice, and their bounds are 10 s and up against milliseconds of work.

Cluster-object census

  • The four sample-text allow entries (PgStatementTextScrubLiveTests, StatementAnalysisReadCutLiveTests, StoreLogRemaskLiveTests, StoreStatementStatsLiveTests) now pin the exact matching code lines (PinnedSampleText; 1, 1, 1 and 4 lines). A new matching line in one of those classes fails ("NOT PINNED"), and a pinned line that stops matching fails (EveryPinnedSampleStatement_StillMatchesInItsClass). RED plant: a DROP ROLE literal added to PgStatementTextScrubLiveTests failed the census naming the file and line; reverted.
  • DarlingSecuritySplitLiveTests is NOT split. Only one fact uses the fixed admin/viewer/mcp roles, but the other facts run ALTER DEFAULT PRIVILEGES FOR ROLE <owner> IN SCHEMA collect ... and grant and drop roles on the shared store. The default-privileges row is shared by every test run by that owner, so moving the class out of pg-cluster-roles would put those writes back in parallel with other classes, which is the kind of overlap that produced "tuple concurrently updated" (Darling test flake: viewer-role grant test fails with 'tuple concurrently updated' #5560). Leaving the whole class in the collection keeps them alone.

Tests (laptop; the full suites were not run, CI decides). 0 build warnings in both projects.

Darling.Tests: all *Census* 495/0 (1 skipped, as before), all *Ratchet* 15/0, all *Hygiene* 90/0, StaThreadCensus 3/0, TimingTestCensus 5/0, PgClusterObjectCensus 4/0, WpfExitSwitch 5/0, TimeRangePickerControl 8/0, ViewerTimeRangeGapFix 25/0 (1 skipped), MigrationLock 4/0, StoreCopyPhase 15/0, LivePostgresCollectionHygiene 11/0, -trait Stage=Guard -failSkips 1444/0.
Lite.Tests: all *Census* 67/0, CrossAppGuardCiGate 19/0, StaThreadCensus 3/0, TimingTestCensus 3/0, WpfExitSwitch 3/0, -trait Stage=Guard -failSkips 450/0.
The throwaway server was stopped and its directory deleted; only the bootstrap role existed, no test role or scratch database was left.

Not done: AggregateJobQuiesceLiveTests (#5603, another seat).

A shared background STA thread runs every WPF test body, one at a time, behind a caller-held gate, and drains the dispatcher after each body. A hung body fails its own test by name and the later ones fail fast. The 54 per-file STA helpers and WpfStaGate are gone, and StaThreadCensusTests (both test projects) keeps them from coming back.
…re (#5602, #5600)

Classes that time their own work join the one non-parallel timing collection (it replaces the redactor and warm-up collections). The cloud identity probe test stamps the probe's return where it completes. TimingTestCensusTests in both test projects fails a class that starts a clock outside the collection unless it is on an allow list with its reason.
…run alone, and a census keeps it that way (#5602)

Roles, grants and ALTER SYSTEM are cluster-wide, so scratch databases do not isolate them. Classes that
provision or alter the fixed admin/viewer/mcp roles, or change cluster settings, move into one
pg-cluster-roles collection that runs with DisableParallelization. The V147 re-assertion fact now drops the
viewer/mcp roles it made (they made later classes skip). StoreStatementStatsLiveTests names its roles and
database per run. PgClusterObjectCensusTests fails when a live class does otherwise.
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.

1 participant