mirror of
https://github.com/storytold/soundcraft.git
synced 2026-10-27 12:06:51 -06:00
Surround: - main and bus formats up to 9.1.6 and Ambisonics; pairwise constant-power surround panner (divergence, centre, LFE, height) per track and send; ITU fold-down; multichannel bounce with correct WAV channel masks; multichannel device output; surround panner and multichannel meters in the Mix window; setup.main_format, mix.surround_pan, mix.send_surround_pan VST3: - soundcraft-vst3-host: isolated unsafe crate (vst3 bindings, MIT), scans standard folders, hosts components/processors/controllers, params, latency, notes; VST3 menu; engine.vst3_plugins; e2e tests with a fixture Hardening: - positions from parameters clamped; offline renders capped and bounded to track material; loop/transport limits; hostile-parameter property test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
105 lines
2.5 KiB
Rust
105 lines
2.5 KiB
Rust
//! Every command survives hostile parameters (what an agent or a buggy script might send).
|
|
|
|
use proptest::prelude::*;
|
|
use serde_json::{Value, json};
|
|
|
|
const KEYS: &[&str] = &[
|
|
"track",
|
|
"tracks",
|
|
"start",
|
|
"end",
|
|
"at",
|
|
"length",
|
|
"clip",
|
|
"clips",
|
|
"db",
|
|
"delta_db",
|
|
"value",
|
|
"pan",
|
|
"count",
|
|
"ratio",
|
|
"bpm",
|
|
"format",
|
|
"slot",
|
|
"param",
|
|
"plugin",
|
|
"mode",
|
|
"tool",
|
|
"kind",
|
|
"name",
|
|
"index",
|
|
"number",
|
|
"semitones",
|
|
"by",
|
|
"to",
|
|
"grid",
|
|
"height",
|
|
"view",
|
|
"color",
|
|
"params",
|
|
"process",
|
|
"group",
|
|
"bus",
|
|
"input",
|
|
"output",
|
|
"numerator",
|
|
"denominator",
|
|
"factor",
|
|
"sensitivity",
|
|
"rating",
|
|
"direction",
|
|
"seconds",
|
|
"frames",
|
|
"samples",
|
|
"notes",
|
|
"pitch",
|
|
"velocity",
|
|
"flag",
|
|
];
|
|
|
|
fn hostile_value() -> impl Strategy<Value = Value> {
|
|
prop_oneof![
|
|
Just(json!(null)),
|
|
Just(json!(-1)),
|
|
Just(json!(0)),
|
|
Just(json!(i64::MAX)),
|
|
Just(json!(i64::MIN)),
|
|
Just(json!(1.0e308)),
|
|
Just(json!(-1.0e308)),
|
|
Just(json!(0.5)),
|
|
Just(json!("")),
|
|
Just(json!("Kick")),
|
|
Just(json!("bars_beats:9999999|99|9999")),
|
|
Just(json!("\u{0}\u{fffd}")),
|
|
Just(json!([])),
|
|
Just(json!([1, "Kick", null, -5])),
|
|
Just(json!({})),
|
|
Just(json!({"seconds": -1e12})),
|
|
Just(json!({"a": 1})),
|
|
Just(json!(true)),
|
|
]
|
|
}
|
|
|
|
proptest! {
|
|
#![proptest_config(ProptestConfig { cases: 6, .. ProptestConfig::default() })]
|
|
#[test]
|
|
fn no_command_panics_on_hostile_params(fields in proptest::collection::vec((0usize..KEYS.len(), hostile_value()), 0..6)) {
|
|
let mut params = serde_json::Map::new();
|
|
for (k, v) in fields {
|
|
params.insert(KEYS[k].to_string(), v);
|
|
}
|
|
let params = Value::Object(params);
|
|
for spec in soundcraft_engine::command_specs() {
|
|
// Commands that write files take paths; keep the test hermetic.
|
|
if spec.params.contains("path") || spec.params.contains("dir") || spec.id == "app.quit" {
|
|
continue;
|
|
}
|
|
let mut e = soundcraft_engine::demo::demo_engine();
|
|
let _ = e.execute("edit.select", &json!({"tracks": ["Kick", "Keys"], "start": 100_000, "end": 600_000}));
|
|
if let Err(soundcraft_engine::EngineError::Internal(id, msg)) = e.execute(spec.id, ¶ms) {
|
|
prop_assert!(false, "{id} panicked with {params}: {msg}");
|
|
}
|
|
}
|
|
}
|
|
}
|