#![cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use std::process::Command;
use std::time::Duration;
const VS_BIN: &str = env!("CARGO_BIN_EXE_vs");
const FAKE_VS_SCRIPT: &str = r#"#!/usr/bin/env bash
printf '%s\0' "$@" > "$VS_FAKE_LOG"
"#;
fn fake_vs_bin(dir: &Path) -> std::path::PathBuf {
let bin = dir.join("fake-vs");
std::fs::write(&bin, FAKE_VS_SCRIPT).unwrap();
let mut perm = std::fs::metadata(&bin).unwrap().permissions();
perm.set_mode(0o755);
std::fs::set_permissions(&bin, perm).unwrap();
bin
}
fn capture_spawned_args(home: &Path, fake: &Path, argv_log: &Path) -> Vec<String> {
let _ = std::fs::remove_file(argv_log);
let out = Command::new(VS_BIN)
.arg(format!("--home={}", home.display()))
.arg("status")
.env("VS_DAEMON_BIN", fake)
.env("VS_FAKE_LOG", argv_log)
.output()
.expect("run vs");
let deadline = std::time::Instant::now() + Duration::from_secs(2);
while !argv_log.exists() && std::time::Instant::now() < deadline {
std::thread::sleep(Duration::from_millis(25));
}
let raw = std::fs::read(argv_log).unwrap_or_else(|e| {
panic!(
"fake never wrote argv log: {e}\nstdout: {}\nstderr: {}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
)
});
raw.split(|b| *b == 0)
.filter(|s| !s.is_empty())
.map(|s| String::from_utf8_lossy(s).into_owned())
.collect()
}
#[test]
fn auto_spawn_propagates_home_to_serve() {
let dir = tempfile::tempdir().unwrap();
let home = dir.path().join("home");
std::fs::create_dir_all(&home).unwrap();
let fake = fake_vs_bin(dir.path());
let argv_log = dir.path().join("argv.log");
let args = capture_spawned_args(&home, &fake, &argv_log);
assert_eq!(args.first().map(String::as_str), Some("serve"));
let home_flag = format!("--home={}", home.display());
assert!(
args.iter().any(|a| a == &home_flag),
"expected spawned args to include {home_flag:?}; got {args:?}",
);
}
#[test]
fn auto_spawn_skipped_with_no_spawn_flag() {
let dir = tempfile::tempdir().unwrap();
let home = dir.path().join("home");
std::fs::create_dir_all(&home).unwrap();
let fake = fake_vs_bin(dir.path());
let argv_log = dir.path().join("argv.log");
let _ = std::fs::remove_file(&argv_log);
let _ = Command::new(VS_BIN)
.arg(format!("--home={}", home.display()))
.arg("--no-spawn")
.arg("status")
.env("VS_DAEMON_BIN", &fake)
.env("VS_FAKE_LOG", &argv_log)
.output()
.expect("run vs");
std::thread::sleep(Duration::from_millis(250));
assert!(
!argv_log.exists(),
"--no-spawn must skip auto-spawn, but the fake was invoked",
);
}