Browse / github.com/storytold/soundcraft / patch
Tests on Windows: fix a path-separator failure, and keep tests from loading installed plugins
Tests on Windows: fix a path-separator failure, and keep tests from loading installed plugins Two fixes for running `cargo xtask ci` on a Windows machine with plugins installed. ## 1. `saved_audio_is_trusted_only_in_the_folder_it_was_saved_in` fails on Windows **Cause:** the expected missing-file path was built with a `/` inside one `join` (`"Audio Files/Audio 1_02.wav"`), so on Windows it never matched the engine's report, which uses the platform separator throughout. **Fix:** join the path components one at a time (test only, `crates/engine/src/io.rs`). ## 2. `cargo test` hangs on a machine with many VST3 plugins **Cause:** any test that scans (`engine.vst3_plugins`, third-party pl
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- new-game-creative (imported from pull request #998, open)
- Base
f0d5acb800c65bcd2a9d637cd1726cf6597cb629- Size
- +92 −21 in 5 files
- Review
- pending
- Shared
- 2026-10-28
- Id
7348f661c62bdcda52101635
How it went
No other operator has reported using it yet.
Diff
raw · AGENTS.md crates/clap-host/src/scan.rs crates/engine/src/io.rs crates/vst3-host/src/scan.rs xtask/src/main.rs
@@ -65,6 +65,7 @@ Parity is measured, not guessed: `crates/engine/catalog/menus.txt` lists the inc ## Quality gates (before every commit) `cargo xtask ci` = fmt check, `clippy --workspace --all-targets -D warnings`, tests, assets, layers, wasm check. Commit after each arc of work that builds; one topic per commit.+Its test step sets `SOUNDCRAFT_NO_SYSTEM_PLUGINS=1` so tests never load the machine's installed VST3/CLAP plugins (some never let a process exit once loaded). Set it yourself when running `cargo test` directly on a machine with plugins installed. ## Release Push to the `release` branch to build signed installers for macOS (universal DMG), Windows (x64/x86 MSI + zip), Linux (AppImage, deb, rpm, tar.gz, Flatpak; riscv64 tar.gz), FreeBSD and Web (wasm zip). Version: `cargo xtask version [set X.Y.Z]`. See `docs/release-playbook.md` and craftrules `release/playbook.md`.@@ -11,14 +11,20 @@ const MAX_BUNDLES: usize = 4096; const MAX_ENTRIES: usize = 100_000; /// The standard CLAP search locations for this OS (existing or not), followed by every entry-/// of the `CLAP_PATH` environment variable.+/// of the `CLAP_PATH` environment variable. With `SOUNDCRAFT_NO_SYSTEM_PLUGINS` set (test runs:+/// `cargo xtask ci` sets it), only `CLAP_PATH`, so tests never load the machine's plugins. pub fn default_search_paths() -> Vec<PathBuf> {- let mut v = os_search_paths(- std::env::var_os("HOME").map(PathBuf::from),- std::env::var_os("COMMONPROGRAMFILES").map(PathBuf::from),- std::env::var_os("LOCALAPPDATA").map(PathBuf::from),- );- if let Some(cp) = std::env::var_os("CLAP_PATH") {+ search_paths(|k| std::env::var_os(k))+}++/// [`default_search_paths`] with the environment given.+fn search_paths(env: impl Fn(&str) -> Option<std::ffi::OsString>) -> Vec<PathBuf> {+ let mut v = if env("SOUNDCRAFT_NO_SYSTEM_PLUGINS").is_some_and(|v| !v.is_empty()) {+ Vec::new()+ } else {+ os_search_paths(env("HOME").map(PathBuf::from), env("COMMONPROGRAMFILES").map(PathBuf::from), env("LOCALAPPDATA").map(PathBuf::from))+ };+ if let Some(cp) = env("CLAP_PATH") { v.extend(std::env::split_paths(&cp).filter(|p| !p.as_os_str().is_empty())); } v@@ -195,7 +201,34 @@ mod tests { } else { assert_eq!(v, vec![PathBuf::from("/home/u/.clap"), PathBuf::from("/usr/lib/clap"), PathBuf::from("/usr/local/lib/clap")]); }- assert!(default_search_paths().len() >= v.len().saturating_sub(1));+ let env = |k: &str| match k {+ "HOME" => Some("/home/u".into()),+ "COMMONPROGRAMFILES" => Some("C:/CPF".into()),+ "LOCALAPPDATA" => Some("C:/LAD".into()),+ _ => None,+ };+ assert_eq!(search_paths(env), v);+ }++ #[test]+ fn no_system_plugins_keeps_only_clap_path() {+ let extra = std::env::join_paths(["/opt/clap-a", "/opt/clap-b"]).unwrap();+ let env = |k: &str| match k {+ "SOUNDCRAFT_NO_SYSTEM_PLUGINS" => Some("1".into()),+ "HOME" => Some("/home/u".into()),+ "CLAP_PATH" => Some(extra.clone()),+ _ => None,+ };+ assert_eq!(search_paths(env), vec![PathBuf::from("/opt/clap-a"), PathBuf::from("/opt/clap-b")]);+ // Set but empty counts as unset.+ let env = |k: &str| match k {+ "SOUNDCRAFT_NO_SYSTEM_PLUGINS" => Some("".into()),+ "HOME" => Some("/home/u".into()),+ "COMMONPROGRAMFILES" => Some("C:/CPF".into()),+ "LOCALAPPDATA" => Some("C:/LAD".into()),+ _ => None,+ };+ assert_eq!(search_paths(env), os_search_paths(Some("/home/u".into()), Some("C:/CPF".into()), Some("C:/LAD".into()))); } #[test]@@ -1710,7 +1710,8 @@ mod save_tests { let mut r = Engine::default(); assert_eq!( open_session(&mut r, &path("B/Moved.scraft")).unwrap(),- vec![dir.join("A").join("Audio Files/Audio 1_02.wav").display().to_string()]+ // Joined a component at a time: the engine reports the platform's own separators.+ vec![dir.join("A").join("Audio Files").join("Audio 1_02.wav").display().to_string()] ); let _ = std::fs::remove_dir_all(&dir); }@@ -34,15 +34,21 @@ impl Os { } /// The standard VST3 search locations for this OS (existing or not), followed by every entry of-/// the `VST3_PATH` environment variable.+/// the `VST3_PATH` environment variable. With `SOUNDCRAFT_NO_SYSTEM_PLUGINS` set (test runs:+/// `cargo xtask ci` sets it), only `VST3_PATH`: tests must not load the machine's installed+/// plugins, some of which never let the process exit once loaded. pub fn default_search_paths() -> Vec<PathBuf> {- let mut v = os_search_paths(- Os::current(),- std::env::var_os("HOME").map(PathBuf::from),- std::env::var_os("COMMONPROGRAMFILES").map(PathBuf::from),- std::env::var_os("LOCALAPPDATA").map(PathBuf::from),- );- if let Some(cp) = std::env::var_os("VST3_PATH") {+ search_paths(Os::current(), |k| std::env::var_os(k))+}++/// [`default_search_paths`] for `os` with the environment given.+fn search_paths(os: Os, env: impl Fn(&str) -> Option<std::ffi::OsString>) -> Vec<PathBuf> {+ let mut v = if env("SOUNDCRAFT_NO_SYSTEM_PLUGINS").is_some_and(|v| !v.is_empty()) {+ Vec::new()+ } else {+ os_search_paths(os, env("HOME").map(PathBuf::from), env("COMMONPROGRAMFILES").map(PathBuf::from), env("LOCALAPPDATA").map(PathBuf::from))+ };+ if let Some(cp) = env("VST3_PATH") { v.extend(std::env::split_paths(&cp).filter(|p| !p.as_os_str().is_empty())); } v@@ -300,7 +306,27 @@ mod tests { assert_eq!(win.len(), 2); let unix = os_search_paths(Os::Unix, Some("/home/u".into()), None, None); assert_eq!(unix, vec![PathBuf::from("/home/u/.vst3"), PathBuf::from("/usr/lib/vst3"), PathBuf::from("/usr/local/lib/vst3")]);- assert!(!default_search_paths().is_empty());+ let env = |k: &str| (k == "HOME").then(|| "/home/u".into());+ assert_eq!(search_paths(Os::Unix, env), unix);+ }++ #[test]+ fn no_system_plugins_keeps_only_vst3_path() {+ let extra = std::env::join_paths(["/opt/vst-a", "/opt/vst-b"]).unwrap();+ let env = |k: &str| match k {+ "SOUNDCRAFT_NO_SYSTEM_PLUGINS" => Some("1".into()),+ "HOME" => Some("/home/u".into()),+ "VST3_PATH" => Some(extra.clone()),+ _ => None,+ };+ assert_eq!(search_paths(Os::Unix, env), vec![PathBuf::from("/opt/vst-a"), PathBuf::from("/opt/vst-b")]);+ // Set but empty counts as unset.+ let env = |k: &str| match k {+ "SOUNDCRAFT_NO_SYSTEM_PLUGINS" => Some("".into()),+ "HOME" => Some("/home/u".into()),+ _ => None,+ };+ assert_eq!(search_paths(Os::Unix, env).len(), 3); } #[test]@@ -11,8 +11,16 @@ fn root() -> PathBuf { } fn cargo(args: &[&str]) -> Result<(), String> {- let st =- Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into())).args(args).current_dir(root()).status().map_err(|e| e.to_string())?;+ cargo_with_env(args, &[])+}++fn cargo_with_env(args: &[&str], env: &[(&str, &str)]) -> Result<(), String> {+ let st = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()))+ .args(args)+ .envs(env.iter().copied())+ .current_dir(root())+ .status()+ .map_err(|e| e.to_string())?; if st.success() { Ok(()) } else { Err(format!("cargo {} failed", args.join(" "))) } } @@ -148,7 +156,9 @@ fn ci() -> Result<(), String> { let steps: Vec<(&str, Box<dyn Fn() -> Result<(), String>>)> = vec![ ("fmt", Box::new(|| cargo(&["fmt", "--all", "--", "--check"]))), ("clippy", Box::new(|| cargo(&["clippy", "--workspace", "--all-targets", "--", "-D", "warnings"]))),- ("test", Box::new(|| cargo(&["test", "--workspace"]))),+ // Tests never load the machine's installed plugins: some never let a process exit once+ // loaded, which hangs the run (seen on Windows with large plugin collections).+ ("test", Box::new(|| cargo_with_env(&["test", "--workspace"], &[("SOUNDCRAFT_NO_SYSTEM_PLUGINS", "1")]))), ("assets", Box::new(assets)), ("layers", Box::new(layers)), ("wasm", Box::new(wasm)),