diff --git a/Cargo.lock b/Cargo.lock index 449dd30..bf03a5d 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" @@ -581,6 +590,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/fs.rs b/usertest/src/fs.rs index 8c6491c..a3f6254 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}; @@ -587,10 +587,19 @@ 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"); } -register_test!(test_rust_file, "Testing rust file operations"); +register_test!(test_rust_file); fn test_rust_dir() { use std::fs; @@ -602,4 +611,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..a13581b 100644 --- a/usertest/src/main.rs +++ b/usertest/src/main.rs @@ -1,3 +1,4 @@ +use colored::Colorize; use std::{ io::{Write, stdout}, sync::{Arc, Barrier, Mutex}, @@ -20,8 +21,8 @@ macro_rules! register_test { ($name:ident) => { // Add to inventory inventory::submit! { - crate::Test { - test_text: stringify!($name), + $crate::Test { + test_text: concat!(module_path!(), "::", stringify!($name)), test_fn: $name, } } @@ -29,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, } @@ -43,7 +44,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 +57,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 +75,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 +83,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 +117,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 +135,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 +148,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,9 +192,9 @@ fn test_mincore() { } } -register_test!(test_mincore, "Testing mincore syscall"); +register_test!(test_mincore); -fn run_test(test_fn: fn()) { +fn run_test(test_fn: fn()) -> Result<(), i32> { // Fork a new process to run the test unsafe { let pid = libc::fork(); @@ -204,17 +202,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: {} (this might be incorrect)", - std::io::Error::last_os_error() - ); + Err(status) + } else { + Ok(()) } } } @@ -223,12 +231,26 @@ fn run_test(test_fn: fn()) { 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); - stdout().flush(); - run_test(test.test_fn); - println!(" OK"); + let _ = stdout().flush(); + 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); + 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()); } 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);