Skip to content

tests: wait for the host stream, not the clock, in the audio card tests - #740

Open
jamesbraid wants to merge 2 commits into
mainfrom
audio/mace-tests
Open

jamesbraid wants to merge 2 commits into
mainfrom
audio/mace-tests

Conversation

@jamesbraid

Copy link
Copy Markdown
Collaborator

Dominik's both_outputs_play_at_their_own_rates_into_one_stream failed on Test (windows-latest) with "sound in the file": the [audio] file writer plays on its own thread by the host's clock, and a loaded runner held it past the test's fixed 0.2 s wait, so the file closed empty. It is racy on main too: holding the file thread for 1.5 s makes main's fixture fail 5 of 5 with the same panic.

These two test-only commits make the host-output tests wait for the stream rather than the clock:

  • The two-output test closes the file only once both outputs have played everything out (giving up after 30 s), with a 300 ms prebuffer so the ring holds the whole 0.7 s while the writer is held up. It checks that each output took every frame with none dropped or skipped, and that each side holds that much sound at its own pitch. A new test holds the file thread for 1.5 s.
  • The underrun tests of MACE, RAD1 and the ICE1724 wait for the stream to run dry after the pause and at the end, instead of sleeping 0.3 s. RAD1's real-clock test waits on frames moved, not wall time, and drops its lower bound on the clock, since a held-up timer only makes frames late.

Verification: an injected 0.8 s writer hold failed the three underrun tests 18 of 18 times before and 0 of 18 after. Ten loops under four busy loops on two CPUs pass for the MACE, HAL2, RAD1, ICE1724 and host_audio tests.

Known and unchanged: with a timer held for 1 s, RAD1's real-clock test still fails, because past two laps of the ring the card credits frames it missed without playing them.

both_outputs_play_at_their_own_rates_into_one_stream fed both outputs by
wall time for 0.7 s, slept 0.2 s and closed the file. The `[audio] file`
takes frames on a thread of its own by the host's clock, and a loaded
host can hold that thread up for longer than the test lasts. The file is
then closed with nothing in it: Test (windows-latest) failed with "sound
in the file", the WAV holding no loud stretch at all, while the check
that neither output had run dry passed, as it must for a stream whose
renderer had not yet played anything.

The file is now closed only once both outputs have played everything
out, giving up after 30 s. That wait is the fix. The prebuffer goes to
300 ms so the ring ceiling, three prebuffers, holds all 0.7 s while the
writer is held up; the default 100 ms ceiling would skip most of it.

A ring that runs dry because the writer was held up is the host's
real-time behaviour, so the check that neither output ran dry goes. In
its place each output must have taken every frame given, with none
dropped or skipped, and each side must hold that much sound at its own
pitch, counted without the silence a held-up writer leaves. A gap inside
the sound is therefore not caught by the file check, only lost frames.

A new test holds the file's thread for 1.5 s before it starts its clock.
With the old fixed 200 ms wait it failed 5 of 5 runs with no sound in
the file.
Two more host-output tests failed on a loaded host. The MACE
tone-through-an-underrun test failed 1 of 10 runs pinned to two CPUs
beside four busy loops, on its check that the host stream ran dry while
the output was stopped. The RAD1 real-clock test failed once in a full
run under load and then passed 2 of 3 runs on its own, on its check that
the card moved at least half the frames the wall time allowed.

The underrun tests of MACE, RAD1 and the ICE1724 slept 0.3 s while the
output was stopped, read the underrun count, and slept 0.3 s more before
closing the file. The `[audio] file` plays on a thread of its own by the
host's clock, and a loaded host can hold that thread up past both
sleeps: the count has not risen, or the end of the sound is still queued
when the file closes. Each now waits after the pause, and at the end,
until the stream has run dry since a count taken just before the stop,
giving up after 10 s (60 s for RAD1, its tests' usual limit).

MACE and RAD1 also take a 300 ms prebuffer, so the ring's ceiling of
three prebuffers holds what the card plays while the writer is held up
instead of skipping it, which would cut the sound around the pause
short. With that prebuffer the stream still holds 300 ms when the output
stops, so a 0.3 s sleep only lets it play out; the pause the capture
must show is the 300 ms the restart spends refilling the prebuffer after
the drain. Their sleep goes, and the drain is what makes the pause:
without it the restart lands on frames still queued and the capture has
no silence. The ICE1724 keeps the default 20 ms prebuffer and its 0.3 s
sleep, which is its pause.

RAD1's timer thread moves the frames, and a busy host can give it no
time for most of the test. Its real-clock test now waits until 0.6 s of
frames have moved before the mute and 0.3 s more after it, then until
the stream has played the tone and 0.1 s past it, and checks that no
more frames moved than the host's clock allowed. The lower bound on the
clock goes: a held-up timer only makes frames late, and the card's rate
is checked against a test clock elsewhere. Its bound on what the file
holds now allows 0.5% more, which the file's clock follower gives when
it plays slow; 4 of 100 runs on an otherwise idle host heard 50 to 120
frames more than the card played, past the 48 allowed. That the follower
is the cause is inferred, not measured.

A writer held for 0.8 s from 0.55 s or 0.65 s into the run, injected in
a local copy of the file backend, failed the three underrun tests in 18
of 18 runs before this change and in 0 of 18 after. Leaving out the
drain after the pause fails MACE and RAD1 3 of 3 runs with no pause
heard. A timer held for 1 s failed the RAD1 real-clock test's clock
check 3 of 3 runs. It still fails after this change, on the check that
the file holds the tone: past two laps of the ring the card credits the
frames it missed without playing them.
@jamesbraid
jamesbraid added this pull request to stack #741 November 14, 2026 20:52
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