From ac20045a08d5afc301e0eab0d343c112ebe00db1 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Fri, 30 Jan 2026 15:27:13 -0800 Subject: [PATCH 1/5] use module path --- usertest/src/fs.rs | 40 ++++++++++++++++++++-------------------- usertest/src/futex.rs | 4 ++-- usertest/src/main.rs | 28 +++++++++++----------------- usertest/src/signals.rs | 12 +++--------- 4 files changed, 36 insertions(+), 48 deletions(-) diff --git a/usertest/src/fs.rs b/usertest/src/fs.rs index 8c6491c..a2369c5 100644 --- a/usertest/src/fs.rs +++ b/usertest/src/fs.rs @@ -14,7 +14,7 @@ fn test_opendir() { } } -register_test!(test_opendir, "Testing opendir syscall"); +register_test!(test_opendir); fn test_readdir() { let path = CString::new("/").unwrap(); @@ -38,7 +38,7 @@ fn test_readdir() { } } -register_test!(test_readdir, "Testing readdir syscall"); +register_test!(test_readdir); fn test_chdir() { let path = CString::new("/dev").unwrap(); @@ -61,7 +61,7 @@ fn test_chdir() { } } -register_test!(test_chdir, "Testing chdir syscall"); +register_test!(test_chdir); fn test_fchdir() { let path = CString::new("/dev").unwrap(); @@ -89,7 +89,7 @@ fn test_fchdir() { } } -register_test!(test_fchdir, "Testing fchdir syscall"); +register_test!(test_fchdir); fn test_chroot() { let file = "/bin/busybox"; @@ -107,7 +107,7 @@ fn test_chroot() { } } -register_test!(test_chroot, "Testing chroot syscall"); +register_test!(test_chroot); fn test_chmod() { let dir_path = "/tmp/chmod_test"; @@ -131,7 +131,7 @@ fn test_chmod() { fs::remove_dir(dir_path).expect("Failed to delete directory"); } -register_test!(test_chmod, "Testing chmod syscall"); +register_test!(test_chmod); fn test_fchmod() { let dir_path = "/tmp/fchmod_test"; @@ -160,7 +160,7 @@ fn test_fchmod() { fs::remove_dir(dir_path).expect("Failed to delete directory"); } -register_test!(test_fchmod, "Testing fchmod syscall"); +register_test!(test_fchmod); fn test_chown() { let dir_path = "/tmp/chown_test"; @@ -184,7 +184,7 @@ fn test_chown() { fs::remove_dir(dir_path).expect("Failed to delete directory"); } -register_test!(test_chown, "Testing chown syscall"); +register_test!(test_chown); fn test_fchown() { let dir_path = "/tmp/fchown_test"; @@ -213,7 +213,7 @@ fn test_fchown() { fs::remove_dir(dir_path).expect("Failed to delete directory"); } -register_test!(test_fchown, "Testing fchown syscall"); +register_test!(test_fchown); fn test_read() { let file = "/dev/zero"; @@ -233,7 +233,7 @@ fn test_read() { } } -register_test!(test_read, "Testing read syscall"); +register_test!(test_read); fn test_write() { let file = "/dev/null"; @@ -252,7 +252,7 @@ fn test_write() { } } -register_test!(test_write, "Testing write syscall"); +register_test!(test_write); fn test_link() { let path = "/tmp/link_test"; @@ -289,7 +289,7 @@ fn test_link() { fs::remove_file(link).expect("Failed to delete link"); } -register_test!(test_link, "Testing link syscall"); +register_test!(test_link); fn test_symlink() { use std::fs::{self, File}; @@ -330,7 +330,7 @@ fn test_symlink() { fs::remove_file(link).expect("Failed to delete link"); } -register_test!(test_symlink, "Testing symlink syscall"); +register_test!(test_symlink); fn test_rename() { use std::fs::{self, File}; @@ -366,7 +366,7 @@ fn test_rename() { fs::remove_file(new_path).expect("Failed to delete file"); } -register_test!(test_rename, "Testing rename syscall"); +register_test!(test_rename); fn test_truncate() { use std::fs::{self, File}; @@ -392,7 +392,7 @@ fn test_truncate() { fs::remove_file(path).expect("Failed to delete file"); } -register_test!(test_truncate, "Testing truncate syscall"); +register_test!(test_truncate); fn test_ftruncate() { let file = "/tmp/ftruncate_test.txt"; @@ -423,7 +423,7 @@ fn test_ftruncate() { fs::remove_file(file).expect("Failed to delete file"); } -register_test!(test_ftruncate, "Testing ftruncate syscall"); +register_test!(test_ftruncate); fn test_utimens() { let file = "/tmp/utimens_test"; @@ -481,7 +481,7 @@ fn test_utimens() { fs::remove_file(file).expect("Failed to delete file"); } -register_test!(test_utimens, "Testing utimens syscall"); +register_test!(test_utimens); fn test_statx() { #[repr(C)] @@ -568,7 +568,7 @@ fn test_statx() { fs::remove_file(file).expect("Failed to delete file"); } -register_test!(test_statx, "Testing statx syscall"); +register_test!(test_statx); fn test_rust_file() { use std::fs::{self, File}; @@ -590,7 +590,7 @@ fn test_rust_file() { fs::remove_file(path).expect("Failed to delete file"); } -register_test!(test_rust_file, "Testing rust file operations"); +register_test!(test_rust_file); fn test_rust_dir() { use std::fs; @@ -602,4 +602,4 @@ fn test_rust_dir() { fs::remove_dir(dir_path).expect("Failed to delete directory"); } -register_test!(test_rust_dir, "Testing rust directory operations"); +register_test!(test_rust_dir); diff --git a/usertest/src/futex.rs b/usertest/src/futex.rs index c0a0c98..a1a241f 100644 --- a/usertest/src/futex.rs +++ b/usertest/src/futex.rs @@ -42,7 +42,7 @@ fn test_futex() { } } -register_test!(test_futex, "Testing futex"); +register_test!(test_futex); fn test_futex_bitset() { // Wait on bit 1, Wake on bit 1 @@ -214,4 +214,4 @@ fn test_futex_bitset() { } } -register_test!(test_futex_bitset, "Testing futex bitset"); +register_test!(test_futex_bitset); diff --git a/usertest/src/main.rs b/usertest/src/main.rs index 270c0e0..3e7c16d 100644 --- a/usertest/src/main.rs +++ b/usertest/src/main.rs @@ -21,7 +21,7 @@ macro_rules! register_test { // Add to inventory inventory::submit! { crate::Test { - test_text: stringify!($name), + test_text: concat!(module_path!(), "::", stringify!($name)), test_fn: $name, } } @@ -43,7 +43,7 @@ fn test_sync() { } } -register_test!(test_sync, "Testing sync syscall"); +register_test!(test_sync); fn test_clock_sleep() { use std::thread::sleep; @@ -56,7 +56,7 @@ fn test_clock_sleep() { assert!(now.elapsed() >= SLEEP_LEN); } -register_test!(test_clock_sleep, "Testing clock sleep"); +register_test!(test_clock_sleep); fn test_fork() { unsafe { @@ -74,7 +74,7 @@ fn test_fork() { } } -register_test!(test_fork, "Testing fork syscall"); +register_test!(test_fork); fn test_rust_thread() { let handle = thread::spawn(|| 24); @@ -82,7 +82,7 @@ fn test_rust_thread() { assert_eq!(handle.join().unwrap(), 24); } -register_test!(test_rust_thread, "Testing rust threads"); +register_test!(test_rust_thread); fn test_rust_mutex() { const THREADS: usize = 32; @@ -116,7 +116,7 @@ fn test_rust_mutex() { assert_eq!(final_val, THREADS * ITERS); } -register_test!(test_rust_mutex, "Testing rust mutex"); +register_test!(test_rust_mutex); fn test_parking_lot_mutex_timeout() { use parking_lot::Mutex; @@ -134,10 +134,7 @@ fn test_parking_lot_mutex_timeout() { drop(guard); } -register_test!( - test_parking_lot_mutex_timeout, - "Testing parking_lot mutex with timeout" -); +register_test!(test_parking_lot_mutex_timeout); fn test_thread_with_name() { let handle = thread::Builder::new() @@ -150,7 +147,7 @@ fn test_thread_with_name() { handle.join().unwrap(); } -register_test!(test_thread_with_name, "Testing thread with name"); +register_test!(test_thread_with_name); fn test_mincore() { use std::ptr; @@ -194,7 +191,7 @@ fn test_mincore() { } } -register_test!(test_mincore, "Testing mincore syscall"); +register_test!(test_mincore); fn run_test(test_fn: fn()) { // Fork a new process to run the test @@ -211,10 +208,7 @@ fn run_test(test_fn: fn()) { let mut status = 0; libc::waitpid(pid, &mut status, 0); if !libc::WIFEXITED(status) || libc::WEXITSTATUS(status) != 0 { - panic!( - "Test failed in child process: {} (this might be incorrect)", - std::io::Error::last_os_error() - ); + panic!("Test failed in child process with status {status}"); } } } @@ -225,7 +219,7 @@ fn main() { let start = std::time::Instant::now(); for test in inventory::iter:: { print!("{} ...", test.test_text); - stdout().flush(); + let _ = stdout().flush(); run_test(test.test_fn); println!(" OK"); } diff --git a/usertest/src/signals.rs b/usertest/src/signals.rs index acdf20f..db167b1 100644 --- a/usertest/src/signals.rs +++ b/usertest/src/signals.rs @@ -72,10 +72,7 @@ fn test_interruptible_nanosleep() { } } -register_test!( - test_interruptible_nanosleep, - "Testing interruptible nanosleep" -); +register_test!(test_interruptible_nanosleep); fn test_interruptible_read_pipe() { register_handler(libc::SIGALRM, false); @@ -114,10 +111,7 @@ fn test_interruptible_read_pipe() { } } -register_test!( - test_interruptible_read_pipe, - "Testing interruptible read (pipe)" -); +register_test!(test_interruptible_read_pipe); fn test_interruptible_waitpid() { register_handler(libc::SIGALRM, false); @@ -162,4 +156,4 @@ fn test_interruptible_waitpid() { } } -register_test!(test_interruptible_waitpid, "Testing interruptible waitpid"); +register_test!(test_interruptible_waitpid); From 01237ddf6f71c85c11fcdf76bfff49c8bfef4970 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Fri, 30 Jan 2026 15:34:56 -0800 Subject: [PATCH 2/5] better error reporting --- Cargo.lock | 10 ++++++++++ usertest/Cargo.toml | 1 + usertest/src/main.rs | 37 +++++++++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9103dd8..01eb39c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,6 +58,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "colored" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "crc" version = "3.4.0" @@ -600,6 +609,7 @@ checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" name = "usertest" version = "0.1.0" dependencies = [ + "colored", "inventory", "libc", "parking_lot", diff --git a/usertest/Cargo.toml b/usertest/Cargo.toml index e27af84..21a7f47 100644 --- a/usertest/Cargo.toml +++ b/usertest/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +colored = "3" inventory = "0.3" libc = "0.2" parking_lot = "0.12" diff --git a/usertest/src/main.rs b/usertest/src/main.rs index 3e7c16d..64b04a6 100644 --- a/usertest/src/main.rs +++ b/usertest/src/main.rs @@ -3,6 +3,7 @@ use std::{ sync::{Arc, Barrier, Mutex}, thread, }; +use colored::Colorize; mod fs; mod futex; @@ -193,7 +194,13 @@ fn test_mincore() { register_test!(test_mincore); -fn run_test(test_fn: fn()) { +fn failing_test() { + panic!("This test is supposed to fail"); +} + +register_test!(failing_test); + +fn run_test(test_fn: fn()) -> Result<(), i32> { // Fork a new process to run the test unsafe { let pid = libc::fork(); @@ -201,14 +208,27 @@ fn run_test(test_fn: fn()) { panic!("fork failed"); } else if pid == 0 { // Child process - test_fn(); - libc::_exit(0); + let result = std::panic::catch_unwind(|| { + test_fn(); + }); + let exit_code = if let Err(e) = result { + // Get the panic info + eprintln!("Test panicked: {:?}", e); + let error = std::io::Error::last_os_error(); + eprintln!("Last OS error: {}", error); + 1 + } else { + 0 + }; + libc::_exit(exit_code); } else { // Parent process let mut status = 0; libc::waitpid(pid, &mut status, 0); if !libc::WIFEXITED(status) || libc::WEXITSTATUS(status) != 0 { - panic!("Test failed in child process with status {status}"); + Err(status) + } else { + Ok(()) } } } @@ -220,8 +240,13 @@ fn main() { for test in inventory::iter:: { print!("{} ...", test.test_text); let _ = stdout().flush(); - run_test(test.test_fn); - println!(" OK"); + match run_test(test.test_fn) { + Ok(()) => println!("{}", " OK".green()), + Err(code) => { + println!(" {}", "FAILED".red()); + eprintln!("Test '{}' failed with exit code {}", test.test_text, code); + } + } } let end = std::time::Instant::now(); println!("All tests passed in {} ms", (end - start).as_millis()); From d0427591265ef85196e65b22815cfff194f7b9af Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Fri, 30 Jan 2026 15:35:34 -0800 Subject: [PATCH 3/5] more fs testing --- usertest/src/fs.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/usertest/src/fs.rs b/usertest/src/fs.rs index a2369c5..5264e09 100644 --- a/usertest/src/fs.rs +++ b/usertest/src/fs.rs @@ -587,6 +587,12 @@ fn test_rust_file() { .expect("Failed to read from file"); assert_eq!(contents, "Hello, Rust!"); } + fs::hard_link(path, "/tmp/rust_fs_test_link.txt").expect("Failed to create hard link"); + let metadata = fs::metadata(path).expect("Failed to get metadata"); + assert_eq!(metadata.len(), 12); + fs::rename("/tmp/rust_fs_test_link.txt", "/tmp/rust_fs_test_renamed.txt") + .expect("Failed to rename file"); + fs::remove_file("/tmp/rust_fs_test_renamed.txt").expect("Failed to delete renamed file"); fs::remove_file(path).expect("Failed to delete file"); } From 4409aeddf589f30e10ed6ba3b2df003b1450265f Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Fri, 30 Jan 2026 15:37:53 -0800 Subject: [PATCH 4/5] fix failures leading to pass --- usertest/src/main.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/usertest/src/main.rs b/usertest/src/main.rs index 64b04a6..359747c 100644 --- a/usertest/src/main.rs +++ b/usertest/src/main.rs @@ -194,12 +194,6 @@ fn test_mincore() { register_test!(test_mincore); -fn failing_test() { - panic!("This test is supposed to fail"); -} - -register_test!(failing_test); - fn run_test(test_fn: fn()) -> Result<(), i32> { // Fork a new process to run the test unsafe { @@ -237,6 +231,7 @@ fn run_test(test_fn: fn()) -> Result<(), i32> { fn main() { println!("Running userspace tests ..."); let start = std::time::Instant::now(); + let mut failures = 0; for test in inventory::iter:: { print!("{} ...", test.test_text); let _ = stdout().flush(); @@ -245,9 +240,14 @@ fn main() { Err(code) => { println!(" {}", "FAILED".red()); eprintln!("Test '{}' failed with exit code {}", test.test_text, code); + failures += 1; } } } let end = std::time::Instant::now(); + if failures > 0 { + eprintln!("{failures} tests failed in {} ms", (end - start).as_millis()); + std::process::exit(1); + } println!("All tests passed in {} ms", (end - start).as_millis()); } From ec0ffbfb250d4d4972062b74d29ebf655b95a6d4 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Fri, 30 Jan 2026 19:46:12 -0800 Subject: [PATCH 5/5] formatting --- usertest/src/fs.rs | 7 +++++-- usertest/src/main.rs | 11 +++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/usertest/src/fs.rs b/usertest/src/fs.rs index 5264e09..a3f6254 100644 --- a/usertest/src/fs.rs +++ b/usertest/src/fs.rs @@ -590,8 +590,11 @@ fn test_rust_file() { fs::hard_link(path, "/tmp/rust_fs_test_link.txt").expect("Failed to create hard link"); let metadata = fs::metadata(path).expect("Failed to get metadata"); assert_eq!(metadata.len(), 12); - fs::rename("/tmp/rust_fs_test_link.txt", "/tmp/rust_fs_test_renamed.txt") - .expect("Failed to rename file"); + fs::rename( + "/tmp/rust_fs_test_link.txt", + "/tmp/rust_fs_test_renamed.txt", + ) + .expect("Failed to rename file"); fs::remove_file("/tmp/rust_fs_test_renamed.txt").expect("Failed to delete renamed file"); fs::remove_file(path).expect("Failed to delete file"); } diff --git a/usertest/src/main.rs b/usertest/src/main.rs index 359747c..a13581b 100644 --- a/usertest/src/main.rs +++ b/usertest/src/main.rs @@ -1,9 +1,9 @@ +use colored::Colorize; use std::{ io::{Write, stdout}, sync::{Arc, Barrier, Mutex}, thread, }; -use colored::Colorize; mod fs; mod futex; @@ -21,7 +21,7 @@ macro_rules! register_test { ($name:ident) => { // Add to inventory inventory::submit! { - crate::Test { + $crate::Test { test_text: concat!(module_path!(), "::", stringify!($name)), test_fn: $name, } @@ -30,7 +30,7 @@ macro_rules! register_test { ($name:ident, $text:expr) => { // Add to inventory inventory::submit! { - crate::Test { + $crate::Test { test_text: $text, test_fn: $name, } @@ -246,7 +246,10 @@ fn main() { } let end = std::time::Instant::now(); if failures > 0 { - eprintln!("{failures} tests failed in {} ms", (end - start).as_millis()); + eprintln!( + "{failures} tests failed in {} ms", + (end - start).as_millis() + ); std::process::exit(1); } println!("All tests passed in {} ms", (end - start).as_millis());