Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 12 additions & 17 deletions tests/guest/rust-tests/src/bin/volume_step.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
//! One step of the master level on soundserver's `volume` port and back,
//! each answer the codec's own readback: the step reads back one position
//! down and unmuted, and the step back leaves the level at the top.
//! from the bottom one position up and down again, each answer the codec's
//! own readback; nothing plays, and the master is left muted.

use toyos::audio::{Volume, VolumeStep, MSG_VOLUME, MSG_VOLUME_STEP};

fn step(volume: &toyos::Connection, delta: i32) -> Volume {
volume.send(MSG_VOLUME_STEP, &VolumeStep { delta }).expect("the step is sent");
let header = volume.recv_header().expect("soundserver answers the step");
assert_eq!(header.msg_type, MSG_VOLUME, "soundserver answered the step with message {}", header.msg_type);
volume.recv_payload(&header).expect("the answer is a volume")
}
#[path = "../master.rs"]
mod master;

fn main() {
let volume = toyos::endow::service("volume").expect("this job holds soundserver's volume port");
let down = step(&volume, 1);
println!("step down: position {} of {}, muted {}", down.position, down.bottom, down.muted);
assert_eq!((down.position, down.muted), (1, 0), "one step down reads back one position below the top");
let top = step(&volume, -1);
println!("step up: position {} of {}, muted {}", top.position, top.bottom, top.muted);
assert_eq!((top.position, top.muted), (0, 0), "the step back reads back the top");
let volume = master::connect();
let bottom = master::silence(&volume).bottom;
assert!(bottom > 0, "a master with one position has no step to take");
let up = master::step(&volume, -1);
assert_eq!((up.position, up.muted), (bottom - 1, 0), "one step up reads back one position above the bottom");
let down = master::step(&volume, 1);
assert_eq!((down.position, down.muted), (bottom, 0), "the step back reads back the bottom");
master::silence(&volume);
}
33 changes: 33 additions & 0 deletions tests/guest/rust-tests/src/master.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//! soundserver's master on its `volume` port, each answer the codec's own
//! readback.

use toyos::audio::{Volume, VolumeStep, MSG_VOLUME, MSG_VOLUME_MUTE, MSG_VOLUME_STEP};

pub fn connect() -> toyos::Connection {
toyos::endow::service("volume").expect("this job holds soundserver's volume port")
}

fn answer(volume: &toyos::Connection) -> Volume {
let header = volume.recv_header().expect("soundserver answers the request");
assert_eq!(header.msg_type, MSG_VOLUME, "soundserver answered with message {}", header.msg_type);
let read: Volume = volume.recv_payload(&header).expect("the answer is a volume");
println!("master: position {} of {}, muted {}", read.position, read.bottom, read.muted);
read
}

/// Move the master by `delta` positions; a step unmutes it.
pub fn step(volume: &toyos::Connection, delta: i32) -> Volume {
volume.send(MSG_VOLUME_STEP, &VolumeStep { delta }).expect("the step is sent");
answer(volume)
}

/// Mute the master at its bottom, whatever it was: the step there unmutes,
/// so the toggle after it mutes.
pub fn silence(volume: &toyos::Connection) -> Volume {
let bottom = step(volume, i32::MAX);
assert_eq!((bottom.position, bottom.muted), (bottom.bottom, 0), "the master reads back at its bottom, unmuted");
volume.send_bytes(MSG_VOLUME_MUTE, &[]).expect("the mute is sent");
let muted = answer(volume);
assert_eq!((muted.position, muted.muted), (bottom.bottom, 1), "the master reads back muted at its bottom");
muted
}
16 changes: 10 additions & 6 deletions tests/guest/rust-tests/src/tone.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
//! Shared tone playback: a deterministic sine.
//! Shared tone playback: a deterministic sine, into a muted master.

use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Arc;

use cpal::traits::{DeviceTrait, StreamTrait};

#[path = "master.rs"]
mod master;

const FREQ_HZ: f64 = 440.0;
const DURATION_SECS: f64 = 3.0;
/// Loud enough that any dropout is unambiguous in the capture, with headroom
/// so mixing can never clip.
const AMPLITUDE: f64 = 16000.0;
/// -42 dBFS, so a mute that failed silently would still be almost inaudible.
const AMPLITUDE: f64 = 256.0;

/// Play a deterministic 440Hz sine on `device` at `config` and block until it
/// has drained.
/// Play a deterministic 440Hz sine on `device` at `config` with the master
/// muted, and block until it has drained. The rows judge soundserver's
/// stream and counters, which the mute leaves intact.
///
/// The sample value is derived from the absolute sample index, not from
/// per-callback state, so the generated signal is identical regardless of
/// how the callback invocations are sized or timed.
pub fn play(device: &cpal::Device, config: cpal::StreamConfig) {
master::silence(&master::connect());
let sample_rate = config.sample_rate as f64;
let channels = config.channels as usize;
let total_samples = (sample_rate * DURATION_SECS) as u64;
Expand Down
3 changes: 2 additions & 1 deletion tests/images/testcases.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ syscap = ["rt"]
# without it would make that arm vacuous rather than red.
# `counters` and `trace` because `counters_read` reads every counter there is,
# and narrows each away to prove its refusal.
# `volume` because `volume_step` steps soundserver's master and back.
# `volume` because the tone jobs mute soundserver's master and `volume_step`
# steps it.
# `log` because `counters_metal` measures its idle second only once the log
# holds its own line, read back off logkeeper.
[programs.test-runner]
Expand Down
9 changes: 6 additions & 3 deletions tests/toyos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,9 @@ const METAL: &[(&str, metal::Metal)] = &[
judge: |b| b[0].job_passed("test_rs_wake_storm_cost"),
},
),
// The owner's directive (2026-10-14): no audible sound tests on the bench
// laptops, so these rows run with soundserver's master muted, which none
// of their claims needs to hear (`tests/guest/rust-tests/src/master.rs`).
(
// A tone through soundserver's HDA path. The T14 and the AMD bench.
"hda_tone",
Expand All @@ -540,9 +543,9 @@ const METAL: &[(&str, metal::Metal)] = &[
},
),
(
// One step of the codec's master and back, each read back from the
// codec, on the HDA path soundserver configured. The T14 and the AMD
// bench.
// One step of the codec's master up from its bottom and back, each
// read back from the codec, on the HDA path soundserver configured,
// and the master muted again. The T14 and the AMD bench.
"hda_volume_step",
metal::Metal {
arms: TESTCASES,
Expand Down