Skip to content

fix(test): stop wall-clock and mock races failing CI on macOS and Windows - #1382

Merged
wizzomafizzo merged 3 commits into
mainfrom
fix/flaky-wallclock-tests
Sep 2, 2026
Merged

fix(test): stop wall-clock and mock races failing CI on macOS and Windows#1382
wizzomafizzo merged 3 commits into
mainfrom
fix/flaky-wallclock-tests

Conversation

@wizzomafizzo

@wizzomafizzo wizzomafizzo commented Sep 1, 2026

Copy link
Copy Markdown
Member

Closes #1379.

Three tests fail on CI for reasons unrelated to the product, and between them they make main's status close to meaningless: of the last 25 Lint and Test runs on main, one succeeded. Because the macOS and Windows jobs only run on pushes to main, none of this is visible on a pull request.

macOS, TestStreamingSource_PrefetchFillsRingWithoutTickerPacing. The test proved prefetch burst-fills its ring by giving it 2 s, since per-tick pacing would need 4 s. That 2 s was also the budget for decoding and resampling 6 s of 44.1 kHz audio in a parallel test on a shared runner, so a loaded macOS job failed it with prefetch behaving correctly — that is how 4023de5b went red. streamingSource now carries the prefetch wake-up interval as a field and the test stretches it to an hour, so a full ring can only be the result of a burst fill and runner speed cannot decide the outcome. ConcurrentPrefetchAndMix waits for the same fill on the same 2 s deadline as a precondition; it gets a deadline it cannot lose.

Windows, TestChunkedStepTiming_PacingIsNotBilledAsSQL. assert.Positive(t, timing.bounds) asks whether a small SQLite query took a measurable amount of time. Windows' timer ticks at ~15.6 ms and the query finishes inside one tick, so it read back as exactly 0s. The test now wraps the DB so the bounds lookup sleeps 40 ms, and asserts the two things actually claimed: the lookup runs once per chunk (counted, not timed) and the injected delay lands in timing.bounds.

macOS, TestHandleGenerateMedia_SystemFiltering. Separate flake found while confirming the other two. The test runs real indexing against a mocked MediaDB and cancels it; when the goroutine outruns cancellation it calls ListMediaUserData on a mock with no expectations, and testify's panic takes down the whole pkg/api/methods binary. Letting the goroutine run to completion showed RunBackgroundOptimizationWithLease is unmocked too, so covering only the first call would have moved the panic rather than removed it.

Both wall-clock fixes were checked against injected regressions rather than trusted: reinstating per-tick pacing fails the audio test, and dropping timing.bounds += time.Since(...) fails the bounds test. The mock panic reproduces exactly as it did on CI when the expectation is removed and cancellation delayed.

macOS and Windows are not reachable locally and a pull request will not run them either, so the injected-regression checks stand in for that; the real verdict lands on the post-merge run on main.

The concurrency block noted at the bottom of #1379 is deliberately not touched here — cancelling superseded runs is intended behaviour.

Summary by CodeRabbit

  • Improvements
    • Improved streaming audio prefetch behavior with configurable timing, helping playback adapt more reliably across different conditions.
    • Strengthened media processing reliability during indexing and cancellation scenarios.
  • Bug Fixes
    • Improved timing accuracy for media database operations, providing more consistent processing behavior.
  • Tests
    • Expanded coverage for audio prefetching, media processing, and database timing to help prevent regressions.

TestStreamingSource_PrefetchFillsRingWithoutTickerPacing proved prefetch
burst-fills its ring by giving it 2 s to do so: burst-filling lands well
inside that, one chunk per 100 ms tick would need 4 s. But the same 2 s
was also the budget for decoding and resampling 6 s of 44.1 kHz audio, in
a parallel test on a shared runner, so a loaded macOS job failed it with
prefetch behaving correctly. That is how 4023de5 went red on main.

streamingSource now carries the prefetch wake-up interval as a field and
the test stretches it to an hour. Reaching a full ring then requires a
goroutine that keeps decoding until the ring is full, which is the
property under test, and no machine is slow enough to fail it. Confirmed
by reinstating per-tick pacing: the test still catches it. The deadline
survives only as a hang guard.

A zero interval falls back to the 100 ms default, since newTestSource
builds a bare streamingSource and time.NewTicker(0) panics.

ConcurrentPrefetchAndMix waits for the same fill on the same 2 s
deadline. There it is a precondition rather than the subject, so it just
gets a deadline it cannot lose.
TestChunkedStepTiming_PacingIsNotBilledAsSQL asserted that timing.bounds
was positive, which asks whether a small SQLite query took a measurable
amount of time. Windows' timer ticks at ~15.6 ms and the query finishes
well inside one tick, so it read back as exactly 0s and failed there
while passing on Linux and macOS, whose clocks are finer.

The test now wraps the DB so the bounds lookup, the only QueryRowContext
sqlUpsertStagedMedia issues, sleeps 40 ms. Two claims replace positivity:
the lookup runs once per chunk, counted rather than timed, and the
injected delay lands inside timing.bounds, so the timer really does wrap
the bounds statement. The requirement stays at one delay rather than one
per call, because a coarse clock can read each measured interval up to a
tick short and scaling by the call count would put the granularity back
in. Confirmed by dropping the accumulation in sql_scan_reconcile.go: the
test fails.

The removed sqlTime assertion was tautological once bounds is
non-negative. The attribution it was reaching for, recordChunkedStep
subtracting bounds and pacing from the step, is covered by
TestReconcileStepTimingsLine_AccountsForWallTime.
TestHandleGenerateMedia_SystemFiltering runs real indexing against a
mocked MediaDB and then cancels it. Cancellation normally wins the race,
but when the indexing goroutine gets further it calls ListMediaUserData
on a MockUserDBI carrying no expectations at all, and testify's "I don't
know what to return" panic takes down the whole pkg/api/methods binary
rather than failing this test. That is what happened on macOS in
06521be.

ListMediaUserData is the only UserDB method the indexing path calls, but
letting the goroutine run to completion showed that the next call it
reaches, RunBackgroundOptimizationWithLease, is unmocked too, so covering
one alone would have moved the panic rather than removed it. Both are
expectations now, as they already are in mediascanner's own tests, and
the test passes with the goroutine running to the end.
@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 0dccb1df-aef7-4ed0-8fd3-2f61691b4259

📥 Commits

Reviewing files that changed from the base of the PR and between 4023de5 and a850063.

📒 Files selected for processing (4)
  • pkg/api/methods/methods_test.go
  • pkg/audio/playback.go
  • pkg/audio/playback_test.go
  • pkg/database/mediadb/scan_reconcile_accounting_test.go

Included review availability: Your plan provides up to 2 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Changes

Reliability fixes

Layer / File(s) Summary
Configurable audio prefetch timing
pkg/audio/playback.go, pkg/audio/playback_test.go
streamingSource now supports a configurable prefetch interval with a 100 ms default. Prefetch tests use a one-hour tick and longer hang-guard deadlines.
Deterministic database timing assertions
pkg/database/mediadb/scan_reconcile_accounting_test.go
The timing test injects delay into bounds lookups, counts calls, and verifies bounds accounting without wall-clock comparisons.
Optional API mock expectations
pkg/api/methods/methods_test.go
The media generation test adds optional expectations for user-data listing and background optimization calls.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to a8500

This change makes flaky audio, database-timing, and mock-based tests deterministic while preserving production behavior; no actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the PR's main changes: removing wall-clock and mock-race failures in macOS and Windows CI tests.
Linked Issues check ✅ Passed The PR satisfies the coding objectives in [#1379]. It removes timing-sensitive audio and database assertions and adds mock expectations for cancellation-related indexing calls. The unrelated main-run …
Out of Scope Changes check ✅ Passed All changes support the linked issue [#1379]. The test updates address audio prefetch timing, database timing attribution, and cancellation-related mock calls. No unrelated code changes are identified…
Full details: Linked Issues check

Explanation

The PR satisfies the coding objectives in [#1379]. It removes timing-sensitive audio and database assertions and adds mock expectations for cancellation-related indexing calls. The unrelated main-run cancellation configuration is intentionally unchanged.

Full details: Out of Scope Changes check

Explanation

All changes support the linked issue [#1379]. The test updates address audio prefetch timing, database timing attribution, and cancellation-related mock calls. No unrelated code changes are identified.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/flaky-wallclock-tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.50000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/audio/playback.go 87.50% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@wizzomafizzo
wizzomafizzo merged commit 7788f1a into main Sep 2, 2026
18 checks passed
@wizzomafizzo
wizzomafizzo deleted the fix/flaky-wallclock-tests branch September 2, 2026 02:12
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.

fix(test): flaky wall-clock assertions fail CI on macOS and Windows

1 participant