use nixvm::vm::Vm;
fn drain(vm: &mut Vm) -> String {
let mut out = Vec::new();
for _ in 0..64 {
let step = vm.pump().expect("pump");
out.extend_from_slice(&step.stdout);
out.extend_from_slice(&step.stderr);
if step.exit_code.is_some() {
break;
}
if step.stdout.is_empty() && step.stderr.is_empty() {
break;
}
}
String::from_utf8_lossy(&out).into_owned()
}
#[test]
fn boots_alpine_and_runs_shell_commands() {
let Ok(tar_path) = std::env::var("NIXVM_ALPINE_TAR") else {
eprintln!("NIXVM_ALPINE_TAR not set; skipping live Alpine boot test");
return;
};
let tar = std::fs::read(&tar_path).expect("read Alpine tar");
let mut vm = Vm::boot(
&tar,
vec!["/bin/busybox".to_string(), "sh".to_string()],
256 * 1024 * 1024,
)
.expect("boot Alpine busybox sh");
let boot = drain(&mut vm);
eprintln!("--- boot output ---\n{boot}");
vm.write_stdin(b"echo hello-from-alpine\n");
let out = drain(&mut vm);
eprintln!("--- after echo ---\n{out}");
assert!(
out.contains("hello-from-alpine"),
"shell should echo the command output, got: {out:?}"
);
vm.write_stdin(b"uname -m\n");
let out2 = drain(&mut vm);
eprintln!("--- after uname ---\n{out2}");
assert!(out2.contains("aarch64"), "uname -m should print aarch64");
vm.write_stdin(b"exit\n");
let _ = drain(&mut vm);
assert!(vm.exit_code().is_some(), "shell should exit on `exit`");
}
#[cfg(feature = "fstool")]
#[test]
fn boots_alpine_from_in_memory_squashfs_overlay() {
let Ok(tar_path) = std::env::var("NIXVM_ALPINE_TAR") else {
eprintln!("NIXVM_ALPINE_TAR not set; skipping squashfs-overlay boot test");
return;
};
let tar = std::fs::read(&tar_path).expect("read Alpine tar");
let mut vm = Vm::boot_squashfs(
&tar,
vec!["/bin/busybox".to_string(), "sh".to_string()],
256 * 1024 * 1024,
)
.expect("boot from in-memory squashfs overlay");
let _ = drain(&mut vm);
vm.write_stdin(b"cat /etc/alpine-release; echo squashfs-overlay-ok\n");
let out = drain(&mut vm);
eprintln!("--- squashfs overlay ---\n{out}");
assert!(
out.contains("squashfs-overlay-ok"),
"shell runs from the squashfs-overlay root, got: {out:?}"
);
vm.write_stdin(b"echo hi > /tmp/x; cat /tmp/x\n");
let out2 = drain(&mut vm);
assert!(
out2.contains("hi"),
"tmpfs upper is writable, got: {out2:?}"
);
}
#[cfg(feature = "targz")]
#[test]
fn boots_alpine_from_targz_via_compcol() {
let Ok(gz_path) = std::env::var("NIXVM_ALPINE_TARGZ") else {
eprintln!("NIXVM_ALPINE_TARGZ not set; skipping compcol .tar.gz boot test");
return;
};
let gz = std::fs::read(&gz_path).expect("read Alpine .tar.gz");
let tar = nixvm::fs::tar::gunzip(&gz, 512 * 1024 * 1024).expect("compcol gunzip");
let mut vm = Vm::boot(
&tar,
vec!["/bin/busybox".to_string(), "sh".to_string()],
256 * 1024 * 1024,
)
.expect("boot from compcol-decompressed rootfs");
let _ = drain(&mut vm);
vm.write_stdin(b"echo compcol-decompressed-ok\n");
let out = drain(&mut vm);
assert!(
out.contains("compcol-decompressed-ok"),
"shell runs from the compcol-decompressed rootfs, got: {out:?}"
);
}