Skip to content

Commit 39f5f2d

Browse files
committed
test: compare sparse retention and single-source recording PCM
1 parent 6aa273f commit 39f5f2d

2 files changed

Lines changed: 89 additions & 14 deletions

File tree

‎.github/workflows/fix-instant-mixer-regression.yml‎

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Instant mixer sparse sample regression
1+
name: Instant mixer sparse and PCM identity proof
22
on:
33
push:
44
branches: [ci/fixproof-instant-mixer]
@@ -10,18 +10,29 @@ env:
1010
CARGO_INCREMENTAL: "0"
1111
jobs:
1212
compare:
13-
runs-on: blacksmith-6vcpu-macos-15
14-
timeout-minutes: 30
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
include:
17+
- os: macos
18+
runner: blacksmith-6vcpu-macos-15
19+
target: aarch64-apple-darwin
20+
- os: windows
21+
runner: blacksmith-8vcpu-windows-2025
22+
target: x86_64-pc-windows-msvc
23+
runs-on: ${{ matrix.runner }}
24+
timeout-minutes: 60
1525
defaults:
1626
run:
1727
shell: bash
1828
steps:
1929
- uses: actions/checkout@v4
2030
- uses: ./.github/actions/engine-verify-setup
2131
with:
22-
target: aarch64-apple-darwin
32+
target: ${{ matrix.target }}
2333
tier: fast
2434
- name: Link native framework
35+
if: runner.os == 'macOS'
2536
run: |
2637
mkdir -p target/ci-verify/Frameworks
2738
ln -s "$GITHUB_WORKSPACE/target/Frameworks/Spacedrive.framework" target/ci-verify/Frameworks/Spacedrive.framework
@@ -50,22 +61,39 @@ jobs:
5061
)
5162
path.write_text(source)
5263
PY
53-
- name: Compare unchanged strict sparse probe
64+
- name: Compare strict sparse probe and single-source recordings
5465
run: |
55-
set +e
5666
set -o pipefail
57-
failed=0
67+
root="$RUNNER_TEMP/mixer-regression"
5868
for arm in main candidate; do
59-
cp "$RUNNER_TEMP/mixer-regression/$arm.rs" crates/recording/src/sources/audio_mixer.rs
60-
cargo test -p cap-recording --lib --profile ci-verify --locked pcm_sparse_system_and_pause_resume_preserve_later_gaps -- --nocapture 2>&1 | tee "$RUNNER_TEMP/mixer-regression/$arm.log"
61-
status=$?
62-
echo "ARM=$arm EXIT=$status"
63-
if [ "$status" != 0 ]; then failed=1; fi
69+
cp "$root/$arm.rs" crates/recording/src/sources/audio_mixer.rs
70+
status=0
71+
cargo test -p cap-recording --lib --profile ci-verify --locked pcm_sparse_system_and_pause_resume_preserve_later_gaps -- --nocapture 2>&1 | tee "$root/$arm.log" || status=$?
72+
if [[ "$arm" == main ]]; then test "$status" = 101; else test "$status" = 0; fi
73+
cargo run -p cap-verify --example mixer-pcm-identity --profile ci-verify --locked -- "$root/$arm-recordings" > "$root/$arm-recordings.log" 2>&1
6474
done
65-
exit "$failed"
75+
python3 - <<'PYCODE'
76+
import hashlib
77+
import json
78+
import os
79+
from pathlib import Path
80+
root = Path(os.environ["RUNNER_TEMP"]) / "mixer-regression"
81+
assert "peaks=[14401, 28802, 43203]" in (root / "main.log").read_text()
82+
assert "peaks=[14400, 28800, 43200]" in (root / "candidate.log").read_text()
83+
results = {}
84+
for source in ["mic", "system"]:
85+
files = [root / f"{arm}-recordings/{source}.pcm" for arm in ["main", "candidate"]]
86+
data = [file.read_bytes() for file in files]
87+
assert data[0] == data[1] and data[0], (source, [len(value) for value in data])
88+
metadata = [json.loads((root / f"{arm}-recordings/{source}.json").read_text()) for arm in ["main", "candidate"]]
89+
assert metadata[0] == metadata[1], (source, metadata)
90+
results[source] = {"bytes": len(data[0]), "sha256": hashlib.sha256(data[0]).hexdigest(), "metadata": metadata[0]}
91+
(root / "identity.json").write_text(json.dumps(results, indent=2))
92+
print(json.dumps(results, indent=2))
93+
PYCODE
6694
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
6795
if: always()
6896
with:
69-
name: mixer-sparse-comparison
97+
name: mixer-sparse-comparison-${{ matrix.os }}
7098
path: ${{ runner.temp }}/mixer-regression
7199
retention-days: 7
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use anyhow::Context;
2+
use cap_verify::{catalog, media, pipeline, report::Tier};
3+
use std::{io::Write, path::PathBuf};
4+
5+
fn main() -> anyhow::Result<()> {
6+
let root = PathBuf::from(
7+
std::env::args()
8+
.nth(1)
9+
.context("output directory required")?,
10+
);
11+
std::fs::create_dir_all(&root)?;
12+
let runtime = tokio::runtime::Builder::new_multi_thread()
13+
.enable_all()
14+
.build()?;
15+
for system in [false, true] {
16+
let name = if system { "system" } else { "mic" };
17+
let mut spec = catalog::recorded_instant(Tier::Fast);
18+
spec.name = format!("identity-{name}");
19+
spec.duration = 2.0;
20+
spec.screen.delivery = pipeline::Delivery::Steady;
21+
spec.arm_at = None;
22+
spec.mic_delay = (!system).then_some(0.0);
23+
spec.system_audio = system;
24+
spec.system_continuous = true;
25+
let dir = root.join(name);
26+
runtime.block_on(pipeline::record_instant(&spec, &dir))?;
27+
let pcm = media::decode_audio(&dir.join("content/output.mp4"), None)?;
28+
anyhow::ensure!(pcm.channels.iter().all(|channel| !channel.is_empty()));
29+
let mut file =
30+
std::io::BufWriter::new(std::fs::File::create(root.join(format!("{name}.pcm")))?);
31+
for channel in &pcm.channels {
32+
for sample in channel {
33+
file.write_all(&sample.to_le_bytes())?;
34+
}
35+
}
36+
file.flush()?;
37+
std::fs::write(
38+
root.join(format!("{name}.json")),
39+
serde_json::to_vec_pretty(&serde_json::json!({
40+
"sample_rate": pcm.sample_rate,
41+
"start_secs": pcm.start_secs,
42+
"samples_per_channel": pcm.channels.iter().map(Vec::len).collect::<Vec<_>>(),
43+
}))?,
44+
)?;
45+
}
46+
Ok(())
47+
}

0 commit comments

Comments
 (0)