Skip to content

flake: walk_registry m664 tests share SEMANTICS_LOG static without cross-test serialization #720

Description

@mlieberman85

Observed on

PR #719 CI run 32931567792Lint + test (macos-latest) lane:

test scan_fs::walk_registry::walker::tests::walker_skips_default_noise_dirs ... FAILED
test scan_fs::walk_registry::walker::tests::walker_survives_symlink_loop   ... FAILED

thread 'scan_fs::walk_registry::walker::tests::walker_skips_default_noise_dirs' panicked at
    waybill-cli/src/scan_fs/walk_registry/walker.rs:609:9:
    top-level file should be visited; log=[]

The subsequent re-run (SHA 1b6d87a) passed the same tests — classic parallel-test flake symptom, not a real regression.

Root cause

Three tests in waybill-cli/src/scan_fs/walk_registry/walker.rs share one static log for their assertions:

static SEMANTICS_LOG: Mutex<Vec<String>> = Mutex::new(Vec::new());

Each test calls SEMANTICS_LOG.lock().unwrap().clear() at its start, populates it via a record_visit callback registered on the walker, then asserts against the collected entries.

Sites (as of dc8018d):

  • walker_survives_symlink_loop (line 488) — #[cfg(unix)]
  • walker_respects_exclusion_set (line 525)
  • walker_skips_default_noise_dirs (line 568)

Cargo runs test fns from the same target in parallel by default (--test-threads = nproc). When two of these three tests interleave, one calls .clear() after another has populated the log but before that other reads its assertions, leaving log=[] and tripping the "should be visited" assertion.

Local runs on macOS pass because scheduling happens to serialize the three tests; CI macOS-arm64 has different core counts and interleaves them differently. This is why the m664 merge lane passed and this PR's first run failed even though walk_registry/ had zero changes on this branch.

Fix options (any one)

  1. Per-test-local log: replace the static Mutex with a Vec<String> owned by each test, threaded into record_visit via the SharedWalkerContext.state payload. Cleanest — removes the shared mutable state entirely.
  2. File-scoped serial Mutex: add a SERIALIZE: Mutex<()> static and have each test acquire it BEFORE SEMANTICS_LOG.clear(). Cheap but only prevents interleaving inside this one file; other tests in the same binary that touch the log would still race.
  3. #[serial] from the serial_test crate: standard-idiomatic. Adds a dev-dep the workspace doesn't currently use. Skip unless the pattern shows up in more places.

Recommend option 1 — it's the right shape for a walker that already threads a state: Option<Box<dyn Any + Send + Sync>> through SharedWalkerContext per m664 contract C4.

Not blocking anything

Neither test is a functional gate; both are unit-tests over walker semantics. The flake surfaces intermittently on macOS lanes and is worked around by re-running the failed lane. Filing so someone can fix at leisure and so the next occurrence doesn't get root-caused a second time.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions