From 80ae717dd9af1e27e9846cc21923cf47e8e17474 Mon Sep 17 00:00:00 2001 From: funky Date: Sun, 24 Nov 2024 11:21:39 +1100 Subject: [PATCH] Introduced kernel testing system ktest This commit also fixed a small issue with panic handler formatting --- Cargo.lock | 9 ++++++++ kernel/Cargo.toml | 3 +++ kernel/ktest_macro/Cargo.toml | 11 ++++++++++ kernel/ktest_macro/src/lib.rs | 29 +++++++++++++++++++++++++ kernel/lds/x86_64.ld | 7 ++++++ kernel/src/kmain.rs | 15 +++++++------ kernel/src/ktest.rs | 38 +++++++++++++++++++++++++++++++++ kernel/src/lib.rs | 19 ++++++----------- repbuild/src/main.rs | 40 ++++++++++++++++++++++++++++++++--- 9 files changed, 149 insertions(+), 22 deletions(-) create mode 100644 kernel/ktest_macro/Cargo.toml create mode 100644 kernel/ktest_macro/src/lib.rs create mode 100644 kernel/src/ktest.rs diff --git a/Cargo.lock b/Cargo.lock index 8dc4baa1..baaf1a60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -392,6 +392,7 @@ dependencies = [ "derive_more", "hashbrown", "hbvm", + "ktest_macro", "limine", "log", "sbi", @@ -404,6 +405,14 @@ dependencies = [ "xml", ] +[[package]] +name = "ktest_macro" +version = "0.1.0" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "lazy_static" version = "1.5.0" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 9cbe8956..9cee4de7 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -3,12 +3,15 @@ edition = "2021" name = "kernel" version = "0.2.0" +[features] +ktest = [] [dependencies] # embedded-graphics = "0.8" hbvm = { git = "https://git.ablecorp.us/AbleOS/holey-bytes.git", features = [ "nightly", ] } +ktest_macro = { path = "ktest_macro" } log = "0.4" spin = "0.9" slab = { version = "0.4", default-features = false } diff --git a/kernel/ktest_macro/Cargo.toml b/kernel/ktest_macro/Cargo.toml new file mode 100644 index 00000000..5c85e6cc --- /dev/null +++ b/kernel/ktest_macro/Cargo.toml @@ -0,0 +1,11 @@ +[package] +edition = "2021" +name = "ktest_macro" +version = "0.1.0" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0.37" +syn = { version = "2.0.89", features = ["full"] } \ No newline at end of file diff --git a/kernel/ktest_macro/src/lib.rs b/kernel/ktest_macro/src/lib.rs new file mode 100644 index 00000000..c1c3a8a3 --- /dev/null +++ b/kernel/ktest_macro/src/lib.rs @@ -0,0 +1,29 @@ +extern crate proc_macro; +extern crate quote; +extern crate syn; +use { + proc_macro::TokenStream, + quote::quote, + syn::{parse_macro_input, ItemFn} +}; + +#[proc_macro_attribute] +pub fn ktest(_attr: TokenStream, item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as ItemFn); + let test_name = &input.sig.ident; + let static_var_name = syn::Ident::new( + &format!("__ktest_{}", test_name), + test_name.span(), + ); + let out = quote! { + // #[cfg(feature = "ktest")] + #input + + // #[cfg(feature = "ktest")] + #[unsafe(link_section = ".note.ktest")] + #[used] + pub static #static_var_name: fn() = #test_name; + }; + + TokenStream::from(out) +} \ No newline at end of file diff --git a/kernel/lds/x86_64.ld b/kernel/lds/x86_64.ld index 19ee79f6..84e8c4d7 100644 --- a/kernel/lds/x86_64.ld +++ b/kernel/lds/x86_64.ld @@ -40,6 +40,13 @@ SECTIONS *(.data .data.*) *(.got .got.*) } :data + + /* Add the .ktest section for test functions */ + .note.ktest : { + __ktest_start = .; /* Mark the beginning of the section */ + *(.note.ktest) /* Include all items in the .ktest section */ + __ktest_end = .; /* Mark the end of the section */ + } .bss : { *(COMMON) diff --git a/kernel/src/kmain.rs b/kernel/src/kmain.rs index ec87c6f8..625b9cd7 100644 --- a/kernel/src/kmain.rs +++ b/kernel/src/kmain.rs @@ -22,6 +22,14 @@ use { pub fn kmain(_cmdline: &str, boot_modules: BootModules) -> ! { debug!("Entered kmain"); + #[cfg(feature = "ktest")] { + use crate::ktest; + debug!("TESTING"); + ktest::test_main(); + + loop {}; + } + // let kcmd = build_cmd("Kernel Command Line", cmdline); // trace!("Cmdline: {kcmd:?}"); @@ -148,10 +156,3 @@ pub static IPC_BUFFERS: Lazy> = Lazy::new(|| { Mutex::new(bufs) }); - -#[test_case] -fn trivial_assertion() { - trace!("trivial assertion... "); - assert_eq!(1, 1); - info!("[ok]"); -} diff --git a/kernel/src/ktest.rs b/kernel/src/ktest.rs new file mode 100644 index 00000000..43a2a11a --- /dev/null +++ b/kernel/src/ktest.rs @@ -0,0 +1,38 @@ +pub use ktest_macro::ktest; +use log::debug; + +extern "C" { + static __ktest_start: fn(); + static __ktest_end: fn(); +} + +// TODO: Get test_fn linker name (may require no_mangle in macro) +// More info on tests (run the rest even if panic) +// Implement ktest for arm and riscv (Later problems, see below) +// Allow for arch specific tests (Leave for now) +// Allow for ktest test name attr +// Usefull message at the end of testing +pub fn test_main() { + unsafe { + let mut current_test = &__ktest_start as *const fn(); + let mut current = 1; + let test_end = &__ktest_end as *const fn(); + + while current_test < test_end { + let test_fn = *current_test; + + debug!("Running test {}", current); + + test_fn(); + debug!("Test {} passed", current); + + current_test = current_test.add(1); + current += 1; + } + } +} + +#[ktest] +pub fn trivial_assertion() { + assert_eq!(1, 1); +} \ No newline at end of file diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index f5bf86f8..52956ecd 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -2,6 +2,7 @@ //! Named akern. //! Akern is woefully undersupported at the moment but we are looking to add support improve hardware discovery and make our lives as kernel and operating system developers easier and better #![no_std] +#![no_main] #![feature( slice_split_once, exclusive_wrapper, @@ -10,11 +11,9 @@ lazy_get, alloc_error_handler, ptr_sub_ptr, - custom_test_frameworks, naked_functions, - pointer_is_aligned_to + pointer_is_aligned_to, )] -#![test_runner(crate::test_runner)] #![allow(dead_code, internal_features, static_mut_refs)] extern crate alloc; @@ -33,6 +32,10 @@ mod memory; mod task; mod utils; +// #[cfg(feature = "tests")] +mod ktest; + +use alloc::string::ToString; use versioning::Version; /// Kernel's version @@ -56,15 +59,7 @@ fn panic(info: &core::panic::PanicInfo) -> ! { )); } - let msg = info.message(); + let msg = info.message().to_string().replace("\n", "\r\n"); let _ = crate::arch::log(format_args!("{msg}\r\n")); loop {} } - -#[cfg(test)] -fn test_runner(tests: &[&dyn Fn()]) { - println!("Running {} tests", tests.len()); - for test in tests { - test(); - } -} diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index 2741ef52..5a80bf49 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -27,6 +27,7 @@ fn main() -> Result<(), Error> { let mut release = false; let mut debuginfo = false; let mut target = Target::X86_64; + let mut tests = false; for arg in args { if arg == "-r" || arg == "--release" { release = true; @@ -38,17 +39,42 @@ fn main() -> Result<(), Error> { target = Target::Aarch64; } else if arg == "avx2" { target = Target::X86_64Avx2; + } else if arg == "--ktest" { + tests = true; } else { return Err(report!(Error::InvalidSubCom)); } } - build(release, target, debuginfo).change_context(Error::Build) + build(release, target, debuginfo, tests).change_context(Error::Build) } + // Some("test" | "t") => { + // let mut release = false; + // let mut debuginfo = false; + // let mut target = Target::X86_64; + // for arg in args { + // if arg == "-r" || arg == "--release" { + // release = true; + // } else if arg == "-d" || arg == "--debuginfo" { + // debuginfo = true; + // } else if arg == "rv64" || arg == "riscv64" || arg == "riscv64-virt" { + // target = Target::Riscv64Virt; + // } else if arg == "arm64" || arg == "aarch64" || arg == "aarch64-virt" { + // target = Target::Aarch64; + // } else if arg == "avx2" { + // target = Target::X86_64Avx2; + // } else { + // return Err(report!(Error::InvalidSubCom)); + // } + // } + + // test(release, target, debuginfo).change_context(Error::Build) + // } Some("run" | "r") => { let mut release = false; let mut debuginfo = false; let mut target = Target::X86_64; + let mut tests = false; let mut do_accel = true; for arg in args { if arg == "-r" || arg == "--release" { @@ -63,12 +89,14 @@ fn main() -> Result<(), Error> { do_accel = false; } else if arg == "avx2" { target = Target::X86_64Avx2; + } else if arg == "--ktest" { + tests = true; } else { return Err(report!(Error::InvalidSubCom)); } } - build(release, target, debuginfo)?; + build(release, target, debuginfo, tests)?; run(release, target, do_accel) } Some("help" | "h") => { @@ -82,6 +110,7 @@ fn main() -> Result<(), Error> { " -r / --release: build in release mode\n", " -d / --debuginfo: build with debug info\n", " --noaccel: run without acceleration (e.g, no kvm)\n", + " --ktest: Enables tests via ktest\n", "[ rv64 / riscv64 / riscv64-virt / aarch64 / arm64 / aarch64-virt / avx2 ]: sets target" ),); Ok(()) @@ -310,7 +339,7 @@ fn copy_file_to_img(fpath: &str, fs: &FileSystem) { .expect("Copy failed"); } -fn build(release: bool, target: Target, debuginfo: bool) -> Result<(), Error> { +fn build(release: bool, target: Target, debuginfo: bool, tests: bool) -> Result<(), Error> { let fs = get_fs().change_context(Error::Io)?; let mut com = Command::new("cargo"); com.current_dir("kernel"); @@ -322,6 +351,10 @@ fn build(release: bool, target: Target, debuginfo: bool) -> Result<(), Error> { com.env("RUSTFLAGS", "-Cdebug-assertions=true"); } + if tests { + com.args(["--features", "ktest"]); + } + if target == Target::Riscv64Virt { com.args(["--target", "targets/riscv64-virt-ableos.json"]); } @@ -473,6 +506,7 @@ fn run(release: bool, target: Target, do_accel: bool) -> Result<(), Error> { } } + fn fetch_ovmf(target: Target) -> Result { let (ovmf_url, ovmf_path) = match target { Target::X86_64 | Target::X86_64Avx2 => (