Skip to content

fix: audio test hardening - #52

Merged
Horuse merged 41 commits into
devfrom
fix/audio-test-hardening
Sep 27, 2026
Merged

Horuse merged 41 commits into
devfrom
fix/audio-test-hardening

Conversation

@Horuse

@Horuse Horuse commented Sep 20, 2026 •

Copy link
Copy Markdown
Owner

What does this PR do?

Expands regression coverage across the audio engine and frontend, fixes the production defects the new tests exposed, and removes timing flakiness from the suite.

Production fixes

  • Waveform append capture wrote at the wrong absolute offset.
  • Unity-gain EQ now reconstructs sample-accurately instead of losing energy around crossovers.
  • Non-finite samples are contained at the runtime-effect boundary so one bad sample cannot poison stateful effects (deterministic, no alloc/lock/syscall on the RT path).
  • Encoders: MP3 final-frame loss, Opus pre-skip/end trimming and empty streams, mono AAC frame accounting; every file encoder now rejects partial interleaved frames. File reader reports correct Opus duration.
  • Pipeline storage: history snapshots are migrated before restore and stamped with the current schema; persisted writes are serialized; nested recording-format defaults are restored; duplicate graph IDs are rejected; an invalid overwrite config no longer deletes the existing recording.
  • Pending audio-node reconnection retries after transient backend failures and no longer overlaps.

Coverage

  • Rust: effects, DAG routing, resampling, file readers/encoders, network audio, WebRTC, plugins, pipeline lifecycle. New in this pass:
    • DSP worker graph hot-swap: the old graph returns to main instead of dropping on the RT thread; full swap queue errors instead of blocking.
    • Plugin ParamRing: ordering, multi-reader broadcast, lagging-reader catch-up, no torn events under concurrent writers.
    • Net audio: seq wraparound 65535 -> 0, gap limit, malformed datagram rejection, sender loopback (per-channel seq, headers, payload).
    • WebRTC ctrl messages (P/Q/M/B) and signaling server wire format (field names, omitted nulls, junk frames).
    • Biquad LPF/HPF response, TPDF dither bounds.
  • Frontend: formatting, Markdown parser, announcements, pipeline utils and migrations (every step, v0 to current, snapshots), persistence, updater flows, reconnect, channel slot routing (deriveSlots, freeRunFrom).
  • Property tests (proptest) only for stable invariants (finite output, ceiling, bounded gain); curves and timing use deterministic examples. Compressor regression seed is committed.

Test stability

  • Fixed the macOS CI failure in netaudio::receiver: the receiver binds its socket in a spawned task, so the single early datagram could be lost. The test now resends until counted.
  • Replaced fixed sleeps with deadline polling in speaker/monitor worker tests; offload tests wait for the worker's return instead of sleeping 30 ms.
  • playback_pulls_samples needs a specific ALSA sink; marked #[ignore] and removed the --skip from ci.yml.
  • Tests loading real CLAP/VST3/AU plugins are serialized (concurrent native loading can deadlock in plugin global state).
  • Updater test restores globalThis.fetch; dropped a hardcoded PIPELINE_VERSION assertion.

Small refactor

  • WebRTC ctrl message parsing extracted from the data-channel closure into parse_ctrl so it can be tested. Behavior unchanged.

Tooling

  • Unified bun run test / bun run check run frontend and Rust concurrently.
  • cargo clippy --all-targets is clean (test sines use TAU).

New dev dependencies: proptest (DSP invariants, persisted failing cases) and Tauri's test feature (app test harness, not enabled in release).

The final suite contains 447 Rust library tests, 3 Rust integration tests, and 83 frontend tests.

Checklist

  • Diff is limited to the change — no unrelated edits
  • bun run test passes
  • bun run check passes
  • bun run format leaves the tree clean
  • Generated TS types are committed with the Rust change (not applicable — no #[derive(TS)] contract changed)
  • No new dependency without a reason in the PR description
  • I read the RT audio path section of docs/CONCEPT.md and confirmed this change adds no allocations, locks, or syscalls to cpal / SCK callbacks or DspWorker::run

Platform coverage

  • Developed on: macOS 14
  • Tested on: macOS 14
  • What I did to test:
    • bun run test: all frontend and Rust tests pass, including real AU/CLAP/VST3 plugins and stereo/mono AAC round trips
    • cargo llvm-cov --lib --summary-only: 71.75% line coverage (measured before the last round of added tests)
    • bun run check: 0 errors, 21 pre-existing Svelte warnings
    • cargo clippy --all-targets: clean
  • Not tested: Linux, Windows (locally; CI covers build and tests)

Platform-neutral pipeline/input and pipeline/output changed; CoreAudio, PipeWire and WASAPI backends did not. The AAC fix is macOS-only (AVFoundation).

@Horuse
Horuse changed the base branch from main to dev September 27, 2026 18:57
@Horuse
Horuse merged commit 4ee3093 into dev Sep 27, 2026
4 checks passed
@Horuse
Horuse deleted the fix/audio-test-hardening branch September 27, 2026 20:35
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