Compare commits
5 commits
Author | SHA1 | Date | |
---|---|---|---|
de817e2c4d | |||
d52f14ee1f | |||
4cd7a76ee7 | |||
f3a4a6b4f5 | |||
f5aac570ee |
|
@ -4,25 +4,81 @@ extern crate syn;
|
||||||
use {
|
use {
|
||||||
proc_macro::TokenStream,
|
proc_macro::TokenStream,
|
||||||
quote::quote,
|
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<Self> {
|
||||||
|
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]
|
#[proc_macro_attribute]
|
||||||
pub fn ktest(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
pub fn ktest(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let input = parse_macro_input!(item as ItemFn);
|
let input = parse_macro_input!(item as ItemFn);
|
||||||
let test_name = &input.sig.ident;
|
let test_name = &input.sig.ident;
|
||||||
|
let test_string = test_name.to_string();
|
||||||
let static_var_name = syn::Ident::new(
|
let static_var_name = syn::Ident::new(
|
||||||
&format!("__ktest_{}", test_name),
|
&format!("__ktest_{}", test_name).to_uppercase(),
|
||||||
test_name.span(),
|
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<String, String> {
|
||||||
|
use crate::alloc::string::ToString;
|
||||||
|
let name = #test_string.to_string();
|
||||||
|
|
||||||
|
#block
|
||||||
|
|
||||||
|
return Ok(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ktest")]
|
||||||
#[unsafe(link_section = ".note.ktest")]
|
#[unsafe(link_section = ".note.ktest")]
|
||||||
#[used]
|
#[used]
|
||||||
pub static #static_var_name: fn() = #test_name;
|
pub static #static_var_name: fn() -> Result<String, String> = #test_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
TokenStream::from(out)
|
TokenStream::from(out)
|
||||||
|
|
|
@ -7,6 +7,11 @@ SECTIONS
|
||||||
.text : { *(.text) }
|
.text : { *(.text) }
|
||||||
.data : { *(.data) }
|
.data : { *(.data) }
|
||||||
.rodata : { *(.rodata) }
|
.rodata : { *(.rodata) }
|
||||||
|
.note.ktest : {
|
||||||
|
__ktest_start = .;
|
||||||
|
*(.note.ktest)
|
||||||
|
__ktest_end = .;
|
||||||
|
}
|
||||||
.bss : {
|
.bss : {
|
||||||
*(COMMON)
|
*(COMMON)
|
||||||
*(.bss .bss.*)
|
*(.bss .bss.*)
|
||||||
|
|
|
@ -23,11 +23,14 @@ pub fn kmain(_cmdline: &str, boot_modules: BootModules) -> ! {
|
||||||
debug!("Entered kmain");
|
debug!("Entered kmain");
|
||||||
|
|
||||||
#[cfg(feature = "ktest")] {
|
#[cfg(feature = "ktest")] {
|
||||||
use crate::ktest;
|
use {
|
||||||
debug!("TESTING");
|
crate::ktest::test_main,
|
||||||
ktest::test_main();
|
log::info,
|
||||||
|
};
|
||||||
|
info!("Running tests");
|
||||||
|
test_main();
|
||||||
|
|
||||||
loop {};
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// let kcmd = build_cmd("Kernel Command Line", cmdline);
|
// let kcmd = build_cmd("Kernel Command Line", cmdline);
|
||||||
|
|
|
@ -1,38 +1,53 @@
|
||||||
pub use ktest_macro::ktest;
|
use {
|
||||||
use log::debug;
|
alloc::string::String,
|
||||||
|
log::{info, error},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use ktest_macro::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[allow(improper_ctypes)]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
static __ktest_start: fn();
|
static __ktest_start: fn() -> Result<String, String>;
|
||||||
static __ktest_end: fn();
|
static __ktest_end: fn() -> Result<String, String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Get test_fn linker name (may require no_mangle in macro)
|
// TODO: Implement ktest for arm and riscv (Later problems, see below)
|
||||||
// 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 arch specific tests (Leave for now)
|
||||||
// Allow for ktest test name attr
|
// Should panic tests
|
||||||
// Usefull message at the end of testing
|
// Test specific panic handler
|
||||||
pub fn test_main() {
|
pub fn test_main() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut current_test = &__ktest_start as *const fn();
|
let mut current_test = &__ktest_start as *const fn() -> Result<String, String>;
|
||||||
let mut current = 1;
|
let test_end = &__ktest_end as *const fn() -> Result<String, String>;
|
||||||
let test_end = &__ktest_end as *const fn();
|
|
||||||
|
let mut pass = 0;
|
||||||
|
let mut fail = 0;
|
||||||
|
|
||||||
while current_test < test_end {
|
while current_test < test_end {
|
||||||
let test_fn = *current_test;
|
let test_fn = *current_test;
|
||||||
|
|
||||||
debug!("Running test {}", current);
|
let test_name = test_fn();
|
||||||
|
match test_name {
|
||||||
test_fn();
|
Ok(name) => {
|
||||||
debug!("Test {} passed", current);
|
info!("Test: {} passed", name);
|
||||||
|
pass += 1;
|
||||||
|
},
|
||||||
|
Err(name) => {
|
||||||
|
error!("Test: {} failed", name);
|
||||||
|
fail += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
current_test = current_test.add(1);
|
current_test = current_test.add(1);
|
||||||
current += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info!("{}/{} tests passed", pass, pass + fail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ktest]
|
#[ktest]
|
||||||
pub fn trivial_assertion() {
|
fn trivial_assertion() {
|
||||||
assert_eq!(1, 1);
|
ktest_eq!(1, 1);
|
||||||
|
ktest_neq!(0, 1);
|
||||||
}
|
}
|
|
@ -27,15 +27,12 @@ mod handle;
|
||||||
mod holeybytes;
|
mod holeybytes;
|
||||||
mod ipc;
|
mod ipc;
|
||||||
mod kmain;
|
mod kmain;
|
||||||
|
mod ktest;
|
||||||
mod logger;
|
mod logger;
|
||||||
mod memory;
|
mod memory;
|
||||||
mod task;
|
mod task;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
// #[cfg(feature = "tests")]
|
|
||||||
mod ktest;
|
|
||||||
|
|
||||||
use alloc::string::ToString;
|
|
||||||
use versioning::Version;
|
use versioning::Version;
|
||||||
|
|
||||||
/// Kernel's version
|
/// Kernel's version
|
||||||
|
@ -48,6 +45,8 @@ pub const VERSION: Version = Version {
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
#[cfg(target_os = "none")]
|
#[cfg(target_os = "none")]
|
||||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
use crate::alloc::string::ToString;
|
||||||
|
|
||||||
arch::register_dump();
|
arch::register_dump();
|
||||||
|
|
||||||
if let Some(loc) = info.location() {
|
if let Some(loc) = info.location() {
|
||||||
|
|
Loading…
Reference in a new issue