From 95cf948d594eca4c5f68013d3e0f6e09705c99a8 Mon Sep 17 00:00:00 2001 From: funky Date: Wed, 27 Nov 2024 19:09:15 +1100 Subject: [PATCH] Ktest major improvements --- kernel/ktest_macro/src/lib.rs | 71 +++++++++++++++++++++++++++++++---- kernel/lds/aarch64-qemu.ld | 5 +++ kernel/src/kmain.rs | 7 +++- kernel/src/ktest.rs | 49 +++++++++++++++--------- kernel/src/lib.rs | 1 - 5 files changed, 105 insertions(+), 28 deletions(-) diff --git a/kernel/ktest_macro/src/lib.rs b/kernel/ktest_macro/src/lib.rs index c1c3a8a..ebff040 100644 --- a/kernel/ktest_macro/src/lib.rs +++ b/kernel/ktest_macro/src/lib.rs @@ -1,28 +1,85 @@ extern crate proc_macro; extern crate quote; extern crate syn; + use { proc_macro::TokenStream, quote::quote, - syn::{parse_macro_input, ItemFn} + syn::{parse::Parse, parse_macro_input, Expr, ItemFn, Token} }; +struct KtestInput { + lhs: Expr, + _comma: Token![,], + rhs: Expr, +} + +impl Parse for KtestInput { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + Ok(Self { + lhs: input.parse()?, + _comma: input.parse()?, + rhs: input.parse()?, + }) + } +} + +#[proc_macro] +pub fn ktest_eq(item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as KtestInput); + + let lhs = input.lhs; + let rhs = input.rhs; + + let out = quote! { + if #lhs != #rhs { + return Err(name); + } + }; + TokenStream::from(out) +} + +#[proc_macro] +pub fn ktest_neq(item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as KtestInput); + + let lhs = input.lhs; + let rhs = input.rhs; + + let out = quote! { + if #lhs == #rhs { + return Err(name); + } + }; + TokenStream::from(out) +} + #[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 test_string = test_name.to_string(); let static_var_name = syn::Ident::new( - &format!("__ktest_{}", test_name), + &format!("__ktest_{}", test_name).to_uppercase(), test_name.span(), ); - let out = quote! { - // #[cfg(feature = "ktest")] - #input - // #[cfg(feature = "ktest")] + let block = &input.block; + let out = quote! { + #[cfg(feature = "ktest")] + fn #test_name() -> Result { + use crate::alloc::string::ToString; + let name = #test_string.to_string(); + + #block + + return Ok(name); + } + + #[cfg(feature = "ktest")] #[unsafe(link_section = ".note.ktest")] #[used] - pub static #static_var_name: fn() = #test_name; + pub static #static_var_name: fn() -> Result = #test_name; }; TokenStream::from(out) diff --git a/kernel/lds/aarch64-qemu.ld b/kernel/lds/aarch64-qemu.ld index dbcbacf..adf13fb 100644 --- a/kernel/lds/aarch64-qemu.ld +++ b/kernel/lds/aarch64-qemu.ld @@ -6,6 +6,11 @@ SECTIONS .text.boot : { *(.text.boot) } .text : { *(.text) } .data : { *(.data) } + .note.ktest : { + __ktest_start = .; + *(.note.ktest) + __ktest_end = .; + } .rodata : { *(.rodata) } .bss : { *(COMMON) diff --git a/kernel/src/kmain.rs b/kernel/src/kmain.rs index 928dbe5..8d9d398 100644 --- a/kernel/src/kmain.rs +++ b/kernel/src/kmain.rs @@ -24,8 +24,11 @@ pub fn kmain(_cmdline: &str, boot_modules: BootModules) -> ! { #[cfg(feature = "ktest")] { - use crate::ktest; - debug!("TESTING"); + use { + crate::ktest, + log::info, + }; + info!("Running tests"); ktest::test_main(); loop {} diff --git a/kernel/src/ktest.rs b/kernel/src/ktest.rs index bd5ec7f..c194445 100644 --- a/kernel/src/ktest.rs +++ b/kernel/src/ktest.rs @@ -1,38 +1,51 @@ -pub use ktest_macro::ktest; -use log::debug; +pub use ktest_macro::*; + +use { + alloc::string::String, + log::{error, info}, +}; extern "C" { - static __ktest_start: fn(); - static __ktest_end: fn(); + static __ktest_start: fn() -> Result; + static __ktest_end: fn() -> Result; } -// 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) +// TODO: 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 +// Should panic tests +// Test specific panic handler 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(); + let mut current_test = &__ktest_start as *const fn() -> Result; + let test_end = &__ktest_end as *const fn() -> Result; + + let mut pass = 0; + let mut fail = 0; while current_test < test_end { let test_fn = *current_test; - debug!("Running test {}", current); - - test_fn(); - debug!("Test {} passed", current); + let test_name = test_fn(); + match test_name { + Ok(name) => { + info!("Test: {} passed", name); + pass += 1; + }, + Err(name) => { + error!("Test: {} failed", name); + fail += 1; + } + } current_test = current_test.add(1); - current += 1; } + + info!("{}/{} tests passed", pass, pass + fail); } } #[ktest] pub fn trivial_assertion() { - assert_eq!(1, 1); + ktest_eq!(1, 1); + ktest_neq!(0, 1); } diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 3f2be11..b0005d8 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -34,7 +34,6 @@ mod memory; mod task; mod utils; -// #[cfg(feature = "tests")] #[allow(improper_ctypes, non_upper_case_globals)] mod ktest;