#![cfg(feature = "localfs")]
#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::sync::Arc;
use kaish_kernel::vfs::{LocalFs, MemoryFs, VfsRouter};
use kaish_kernel::{Kernel, KernelBackend, KernelConfig, LocalBackend};
fn read_only_backend_kernel(dir: &std::path::Path) -> Kernel {
let mut vfs = VfsRouter::new();
vfs.mount("/", LocalFs::read_only(dir));
let backend: Arc<dyn KernelBackend> = Arc::new(LocalBackend::new(Arc::new(vfs)));
Kernel::with_backend(backend, KernelConfig::isolated(), |_| {}, |_| {})
.expect("with_backend kernel")
}
async fn run(kernel: &Kernel, script: &str) -> (String, i64) {
let result = kernel.execute(script).await.expect("kernel execute");
(result.text_out().trim().to_string(), result.code)
}
#[tokio::test]
async fn dev_null_write_succeeds_under_read_only_backend() {
let dir = tempfile::tempdir().expect("tempdir");
let kernel = read_only_backend_kernel(dir.path());
let (out, code) = run(&kernel, "echo hello > /dev/null").await;
assert_eq!(code, 0, "write to /dev/null must succeed under a read-only backend: out={out:?}");
}
#[tokio::test]
async fn dev_null_reads_empty_under_with_backend() {
let dir = tempfile::tempdir().expect("tempdir");
let kernel = read_only_backend_kernel(dir.path());
let (out, code) = run(&kernel, "cat /dev/null").await;
assert_eq!(code, 0);
assert_eq!(out, "", "reading /dev/null must be empty");
}
#[tokio::test]
async fn dev_zero_readable_under_with_backend() {
let dir = tempfile::tempdir().expect("tempdir");
let kernel = read_only_backend_kernel(dir.path());
let (out, code) = run(&kernel, "head -c 8 /dev/zero | wc -c").await;
assert_eq!(code, 0, "reading /dev/zero must succeed: out={out:?}");
assert_eq!(out, "8", "head -c 8 /dev/zero should yield exactly 8 bytes: out={out:?}");
}
#[tokio::test]
async fn ls_dev_lists_devices_under_with_backend() {
let dir = tempfile::tempdir().expect("tempdir");
let kernel = read_only_backend_kernel(dir.path());
let (out, code) = run(&kernel, "ls /dev").await;
assert_eq!(code, 0, "ls /dev must succeed: out={out:?}");
for name in ["null", "zero", "random", "urandom"] {
assert!(out.contains(name), "ls /dev should list {name}: out={out:?}");
}
}
#[tokio::test]
async fn non_virtual_write_still_delegates_to_read_only_backend() {
let dir = tempfile::tempdir().expect("tempdir");
let kernel = read_only_backend_kernel(dir.path());
let (out, code) = run(&kernel, "echo hello > /probe.txt").await;
assert_ne!(code, 0, "write to a real path under a read-only backend must fail: out={out:?}");
}
#[tokio::test]
async fn configure_vfs_can_override_dev_mount() {
let dir = tempfile::tempdir().expect("tempdir");
let mut vfs = VfsRouter::new();
vfs.mount("/", LocalFs::read_only(dir.path()));
let backend: Arc<dyn KernelBackend> = Arc::new(LocalBackend::new(Arc::new(vfs)));
let kernel = Kernel::with_backend(
backend,
KernelConfig::isolated(),
|vfs| vfs.mount("/dev", MemoryFs::new()),
|_| {},
)
.expect("with_backend kernel");
let (out, code) = run(&kernel, "cat /dev/null").await;
assert_ne!(code, 0, "overridden /dev should no longer be DevFs: out={out:?}");
}