Skip to content

Fix unit stress flakes in JumpList and QueryCache threading tests#394

Merged
codemonkeychris merged 2 commits into
mainfrom
fix/stress-flakes-jumplist-querycache
May 24, 2026
Merged

Fix unit stress flakes in JumpList and QueryCache threading tests#394
codemonkeychris merged 2 commits into
mainfrom
fix/stress-flakes-jumplist-querycache

Conversation

@codemonkeychris
Copy link
Copy Markdown
Collaborator

Summary

Two 1-in-1000 unit-stress flakes surfaced in the latest CI Stress run (#26345012652):

  1. JumpListUpdateValidationTests.UpdateAsync_Separator_With_Empty_Title_Is_Allowed — real isolation bug. Static JumpList.AppUserModelId is mutated by both this class and JumpListStateTests, and both call JumpList.ResetForTests() (which clears the static). xUnit's default parallelizes test classes, so a concurrent ResetForTests() from JumpListStateTests could wipe the AumiD between this test's setter (= "Test.App") and its UpdateAsync call, tripping the unpackaged-AumiD guard and throwing InvalidOperationException. Fix: put both classes into [Collection(\"JumpListGlobals\")], matching the pattern already used for PersistedStateCache, UnobservedTaskException, and DockingGlobals.

  2. QueryCacheThreadingTests.Concurrent_Subscribe_Unsubscribe_Converges_To_Zero — environmental. Hit the 60s DrainTimeout. Static analysis: the workload is 8 threads × 1000 lock-bound bookkeeping ops on one slot; eviction physically cannot fire within 60s given the 5-minute default CacheTime, so this is scheduler-induced (ThreadPool ramp-up / hosted-VM noise), not a deadlock. Bump the drain ceiling to 120s without weakening the deadlock guarantee.

Test plan

  • dotnet build clean
  • dotnet test --filter ~JumpList — 32/32 pass
  • Re-run CI Stress (1000x unit) and confirm both flakes are gone

🤖 Generated with Claude Code

Two flakes hit in the 1000x unit stress run:

* JumpListUpdateValidationTests.UpdateAsync_Separator_With_Empty_Title_Is_Allowed
  raced on static JumpList.AppUserModelId with JumpListStateTests, which
  also mutates the same statics and calls ResetForTests(). Without a
  shared collection, xUnit runs the two classes in parallel; a concurrent
  ResetForTests() clears AppUserModelId between the test's setter and its
  UpdateAsync call, tripping the unpackaged-AppUserModelId guard.
  Putting both classes in a [Collection("JumpListGlobals")] serializes
  them, matching the pattern this repo already uses for PersistedStateCache,
  UnobservedTaskException, and DockingGlobals.

* QueryCacheThreadingTests.Concurrent_Subscribe_Unsubscribe_Converges_To_Zero
  hit the 60s DrainTimeout once. The workload is pure lock-bound
  bookkeeping; eviction cannot fire in 60s given the 5-minute default
  CacheTime, so this is scheduler-induced rather than a deadlock. Bump
  the drain ceiling to 120s without weakening the deadlock guarantee.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR targets rare CI stress flakes in the Reactor test suite by improving test isolation for JumpList global state and by loosening an overly tight timeout in a threading stress test.

Changes:

  • Serialize JumpList tests that mutate JumpList static state by placing them in the same xUnit collection (JumpListGlobals).
  • Increase QueryCacheThreadingTests’ global drain timeout from 60s to 120s to reduce scheduler-noise flakes without changing the test’s deadlock-detection intent.
Show a summary per file
File Description
tests/Reactor.Tests/JumpListUpdateValidationTests.cs Adds JumpListGlobals collection usage and documents why JumpList tests must be serialized.
tests/Reactor.Tests/JumpListItemTests.cs Places JumpListStateTests into the same JumpListGlobals collection to prevent concurrent static resets.
tests/Reactor.Tests/Core/QueryCacheThreadingTests.cs Bumps DrainTimeout from 60s to 120s and expands rationale in comments.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 3/3 changed files
  • Comments generated: 2

Comment on lines +18 to 22
/// <c>ResetForTests()</c> in another class.
/// </summary>
[Collection("JumpListGlobals")]
public class JumpListUpdateValidationTests : IDisposable
{
Comment on lines 182 to 186
/// Spec 036 §11.3 — JumpList static state. We don't exercise the live shell
/// here (that's a selftest fixture); only the public state surface.
/// </summary>
[Collection("JumpListGlobals")]
public class JumpListStateTests
Addresses CR feedback: the other shared-state collections in this repo
(ConsoleTests, UnobservedTaskException, PersistedStateCache, DockingGlobals)
all have explicit [CollectionDefinition(..., DisableParallelization = true)]
markers. Match the convention with a JumpListGlobalsCollection marker so the
serialization intent is grep-able and the collection is also serialized
against unrelated collections that may transitively touch the same statics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codemonkeychris codemonkeychris merged commit d655f1d into main May 24, 2026
37 of 39 checks passed
@codemonkeychris codemonkeychris deleted the fix/stress-flakes-jumplist-querycache branch May 24, 2026 02:11
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.

2 participants