diff --git a/Cargo.lock b/Cargo.lock index a7a791f06..aea88db8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,7 +139,6 @@ dependencies = [ "bitvec", "component", "int-to-c-enum", - "ktest", "log", "ostd", "pod", @@ -180,7 +179,6 @@ dependencies = [ "int-to-c-enum", "intrusive-collections", "keyable-arc", - "ktest", "lazy_static", "lending-iterator", "libflate", @@ -240,7 +238,6 @@ dependencies = [ "aster-rights", "aster-rights-proc", "inherit-methods-macro", - "ktest", "ostd", "pod", "typeflags-util", @@ -797,20 +794,9 @@ version = "0.1.0" name = "ktest" version = "0.1.0" dependencies = [ - "ktest-proc-macro", "owo-colors", ] -[[package]] -name = "ktest-proc-macro" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "rand", - "syn 2.0.49", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -1131,6 +1117,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", + "rand", "syn 2.0.49", ] diff --git a/Cargo.toml b/Cargo.toml index 48e6dc111..9e47daa49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ members = [ "ostd/libs/linux-bzimage/boot-params", "ostd/libs/linux-bzimage/setup", "ostd/libs/ktest", - "ostd/libs/ktest-proc-macro", "kernel", "kernel/aster-nix", "kernel/comps/block", diff --git a/Makefile b/Makefile index 492cc0299..e9795f1cc 100644 --- a/Makefile +++ b/Makefile @@ -86,12 +86,11 @@ export # or tested without OSDK. NON_OSDK_CRATES := \ ostd/libs/align_ext \ - ostd/libs/ostd-macros \ ostd/libs/id-alloc \ ostd/libs/linux-bzimage/builder \ ostd/libs/linux-bzimage/boot-params \ ostd/libs/ktest \ - ostd/libs/ktest-proc-macro \ + ostd/libs/ostd-macros \ kernel/libs/cpio-decoder \ kernel/libs/int-to-c-enum \ kernel/libs/int-to-c-enum/derive \ diff --git a/docs/src/osdk/guide/create-project.md b/docs/src/osdk/guide/create-project.md index 028bf5a59..7c8f3732a 100644 --- a/docs/src/osdk/guide/create-project.md +++ b/docs/src/osdk/guide/create-project.md @@ -84,10 +84,6 @@ The dependency version may change over time. [dependencies.ostd] git = "https://github.com/asterinas/asterinas" branch = "main" - -[dependencies.ktest] -git = "https://github.com/asterinas/asterinas" -branch = "main" ``` OSDK will also exclude the directory diff --git a/kernel/aster-nix/Cargo.toml b/kernel/aster-nix/Cargo.toml index 530954831..9457fcda6 100644 --- a/kernel/aster-nix/Cargo.toml +++ b/kernel/aster-nix/Cargo.toml @@ -42,7 +42,6 @@ smoltcp = { version = "0.9.1", default-features = false, features = [ "socket-raw", "socket-dhcpv4", ] } -ktest = { path = "../../ostd/libs/ktest" } tdx-guest = { version = "0.1.0", optional = true } # parse elf file diff --git a/kernel/aster-nix/src/fs/exfat/mod.rs b/kernel/aster-nix/src/fs/exfat/mod.rs index fb7f194c8..640d0a5f8 100644 --- a/kernel/aster-nix/src/fs/exfat/mod.rs +++ b/kernel/aster-nix/src/fs/exfat/mod.rs @@ -21,7 +21,10 @@ mod test { bio::{BioEnqueueError, BioStatus, BioType, SubmittedBio}, BlockDevice, }; - use ostd::mm::{FrameAllocOptions, Segment, VmIo}; + use ostd::{ + mm::{FrameAllocOptions, Segment, VmIo}, + prelude::*, + }; use rand::{rngs::SmallRng, RngCore, SeedableRng}; use crate::{ diff --git a/kernel/aster-nix/src/fs/utils/channel.rs b/kernel/aster-nix/src/fs/utils/channel.rs index eddc94667..9984fa498 100644 --- a/kernel/aster-nix/src/fs/utils/channel.rs +++ b/kernel/aster-nix/src/fs/utils/channel.rs @@ -541,6 +541,8 @@ impl AsRef for (Error, T) { mod test { use alloc::sync::Arc; + use ostd::prelude::*; + use crate::fs::utils::Channel; #[ktest] diff --git a/kernel/aster-nix/src/lib.rs b/kernel/aster-nix/src/lib.rs index 0e25c93ed..e1764b326 100644 --- a/kernel/aster-nix/src/lib.rs +++ b/kernel/aster-nix/src/lib.rs @@ -41,9 +41,6 @@ extern crate alloc; extern crate lru; #[macro_use] extern crate controlled; -#[cfg(ktest)] -#[macro_use] -extern crate ktest; #[macro_use] extern crate getset; diff --git a/kernel/aster-nix/src/process/process/mod.rs b/kernel/aster-nix/src/process/process/mod.rs index fe0adfd83..2f7ccaf36 100644 --- a/kernel/aster-nix/src/process/process/mod.rs +++ b/kernel/aster-nix/src/process/process/mod.rs @@ -652,6 +652,8 @@ pub fn current() -> Arc { #[cfg(ktest)] mod test { + use ostd::prelude::*; + use super::*; fn new_process(parent: Option>) -> Arc { diff --git a/kernel/aster-nix/src/process/sync/condvar.rs b/kernel/aster-nix/src/process/sync/condvar.rs index 4d696f8f6..2f18bc2ce 100644 --- a/kernel/aster-nix/src/process/sync/condvar.rs +++ b/kernel/aster-nix/src/process/sync/condvar.rs @@ -264,7 +264,7 @@ impl Condvar { #[cfg(ktest)] mod test { - use ostd::sync::Mutex; + use ostd::{prelude::*, sync::Mutex}; use super::*; use crate::thread::{ diff --git a/kernel/aster-nix/src/taskless.rs b/kernel/aster-nix/src/taskless.rs index 74733ce82..d25801da5 100644 --- a/kernel/aster-nix/src/taskless.rs +++ b/kernel/aster-nix/src/taskless.rs @@ -189,7 +189,7 @@ fn taskless_softirq_handler( mod test { use core::sync::atomic::AtomicUsize; - use ostd::trap::enable_local; + use ostd::{prelude::*, trap::enable_local}; use super::*; diff --git a/kernel/aster-nix/src/vm/vmar/options.rs b/kernel/aster-nix/src/vm/vmar/options.rs index 0a68e527f..ac0fa7b10 100644 --- a/kernel/aster-nix/src/vm/vmar/options.rs +++ b/kernel/aster-nix/src/vm/vmar/options.rs @@ -136,7 +136,7 @@ impl VmarChildOptions { #[cfg(ktest)] mod test { use aster_rights::Full; - use ostd::mm::VmIo; + use ostd::{mm::VmIo, prelude::*}; use super::*; use crate::vm::{ diff --git a/kernel/aster-nix/src/vm/vmo/options.rs b/kernel/aster-nix/src/vm/vmo/options.rs index 03e04ca4f..0a50aea4f 100644 --- a/kernel/aster-nix/src/vm/vmo/options.rs +++ b/kernel/aster-nix/src/vm/vmo/options.rs @@ -477,7 +477,7 @@ impl VmoChildType for VmoCowChild {} #[cfg(ktest)] mod test { use aster_rights::Full; - use ostd::mm::VmIo; + use ostd::{mm::VmIo, prelude::*}; use super::*; diff --git a/kernel/comps/network/Cargo.toml b/kernel/comps/network/Cargo.toml index ffe359287..1da353f3d 100644 --- a/kernel/comps/network/Cargo.toml +++ b/kernel/comps/network/Cargo.toml @@ -13,7 +13,6 @@ bitflags = "1.3" bitvec = { version = "1.0.1", default-features = false, features = ["alloc"]} component = { path = "../../libs/comp-sys/component" } int-to-c-enum = { path = "../../libs/int-to-c-enum" } -ktest = { path = "../../../ostd/libs/ktest" } log = "0.4" ostd = { path = "../../../ostd" } pod = { git = "https://github.com/asterinas/pod", rev = "d7dba56" } diff --git a/kernel/comps/network/src/dma_pool.rs b/kernel/comps/network/src/dma_pool.rs index 3be350f6a..ad3762a55 100644 --- a/kernel/comps/network/src/dma_pool.rs +++ b/kernel/comps/network/src/dma_pool.rs @@ -9,7 +9,6 @@ use alloc::{ use core::ops::Range; use bitvec::{array::BitArray, prelude::Lsb0}; -use ktest::ktest; use ostd::{ mm::{ Daddr, DmaDirection, DmaStream, FrameAllocOptions, HasDaddr, VmReader, VmWriter, PAGE_SIZE, @@ -288,6 +287,8 @@ impl Drop for DmaSegment { mod test { use alloc::vec::Vec; + use ostd::prelude::*; + use super::*; #[ktest] diff --git a/kernel/libs/aster-util/Cargo.toml b/kernel/libs/aster-util/Cargo.toml index 4ee55838b..321d5a74a 100644 --- a/kernel/libs/aster-util/Cargo.toml +++ b/kernel/libs/aster-util/Cargo.toml @@ -12,6 +12,5 @@ typeflags-util = { path = "../typeflags-util" } aster-rights-proc = { path = "../aster-rights-proc" } aster-rights = { path = "../aster-rights" } inherit-methods-macro = { git = "https://github.com/asterinas/inherit-methods-macro", rev = "98f7e3e" } -ktest = { path = "../../../ostd/libs/ktest" } [features] diff --git a/kernel/libs/aster-util/src/coeff.rs b/kernel/libs/aster-util/src/coeff.rs index b37ed4b22..7cf1eed61 100644 --- a/kernel/libs/aster-util/src/coeff.rs +++ b/kernel/libs/aster-util/src/coeff.rs @@ -127,9 +127,10 @@ impl Mul for Coeff { #[cfg(ktest)] mod test { - use ktest::ktest; + use ostd::prelude::*; use super::*; + #[ktest] fn calculation() { let coeff = Coeff::new(23456, 56789, 1_000_000_000); diff --git a/osdk/src/commands/new/lib.template b/osdk/src/commands/new/lib.template index 7add7017e..744917421 100644 --- a/osdk/src/commands/new/lib.template +++ b/osdk/src/commands/new/lib.template @@ -1,12 +1,10 @@ #![no_std] #![deny(unsafe_code)] -#[cfg_attr(ktest, macro_use)] -extern crate ktest; -extern crate ostd; - #[cfg(ktest)] mod tests { + use ostd::prelude::*; + #[ktest] fn it_works() { let memory_regions = ostd::boot::memory_regions(); diff --git a/osdk/src/commands/new/mod.rs b/osdk/src/commands/new/mod.rs index 74dee3a20..bc321cd7f 100644 --- a/osdk/src/commands/new/mod.rs +++ b/osdk/src/commands/new/mod.rs @@ -42,8 +42,6 @@ fn add_manifest_dependencies(cargo_metadata: &serde_json::Value, crate_name: &st let ostd_dep = toml::Table::from_str(&aster_crate_dep("ostd")).unwrap(); dependencies.as_table_mut().unwrap().extend(ostd_dep); - let ktest_dep = toml::Table::from_str(&aster_crate_dep("ktest")).unwrap(); - dependencies.as_table_mut().unwrap().extend(ktest_dep); let content = toml::to_string(&manifest).unwrap(); fs::write(mainfest_path, content).unwrap(); diff --git a/osdk/tests/commands/new.rs b/osdk/tests/commands/new.rs index faa3feb34..bcf0caaa6 100644 --- a/osdk/tests/commands/new.rs +++ b/osdk/tests/commands/new.rs @@ -18,16 +18,19 @@ fn create_kernel_in_workspace() { } create_workspace(WORKSPACE_NAME, &[KERNEL_NAME]); let kernel_path = PathBuf::from(WORKSPACE_NAME).join(KERNEL_NAME); + let manifest_path = kernel_path.join("Cargo.toml"); let mut cmd = cargo_osdk(["new", "--kernel", KERNEL_NAME]); cmd.current_dir(WORKSPACE_NAME); let output = cmd.output().unwrap(); + depends_on_local_ostd(&manifest_path); assert_success(&output); remove_dir_all(&kernel_path).unwrap(); let mut cmd = cargo_osdk(["new", "-t", "kernel", KERNEL_NAME]); cmd.current_dir(WORKSPACE_NAME); let output = cmd.output().unwrap(); + depends_on_local_ostd(&manifest_path); assert_success(&output); remove_dir_all(&kernel_path).unwrap(); diff --git a/osdk/tests/commands/run.rs b/osdk/tests/commands/run.rs index 8ba87aa43..ce60c5338 100644 --- a/osdk/tests/commands/run.rs +++ b/osdk/tests/commands/run.rs @@ -21,6 +21,8 @@ mod workspace { cargo_osdk_new .ok() .expect("Failed to create kernel project"); + let manifest_path = os_dir.join("Cargo.toml"); + depends_on_local_ostd(manifest_path); } fn prepare_workspace(workspace: &str) { diff --git a/osdk/tests/examples_in_book/create_os_projects.rs b/osdk/tests/examples_in_book/create_os_projects.rs index 3c31dab45..9062e627a 100644 --- a/osdk/tests/examples_in_book/create_os_projects.rs +++ b/osdk/tests/examples_in_book/create_os_projects.rs @@ -2,7 +2,7 @@ use std::{fs, path::PathBuf}; -use crate::util::cargo_osdk; +use crate::util::{cargo_osdk, depends_on_local_ostd}; #[test] fn create_a_kernel_project() { @@ -23,6 +23,8 @@ fn create_a_kernel_project() { assert!(kernel_path.join("Cargo.toml").is_file()); assert!(kernel_path.join("rust-toolchain.toml").is_file()); + depends_on_local_ostd(kernel_path.join("Cargo.toml")); + fs::remove_dir_all(&kernel_path).unwrap(); } diff --git a/osdk/tests/examples_in_book/test_and_run_projects.rs b/osdk/tests/examples_in_book/test_and_run_projects.rs index 188a392dc..8e4d82669 100644 --- a/osdk/tests/examples_in_book/test_and_run_projects.rs +++ b/osdk/tests/examples_in_book/test_and_run_projects.rs @@ -2,7 +2,7 @@ use std::{fs, path::PathBuf}; -use crate::util::cargo_osdk; +use crate::util::{cargo_osdk, depends_on_local_ostd}; #[test] fn create_and_run_kernel() { @@ -19,6 +19,10 @@ fn create_and_run_kernel() { command.current_dir(work_dir); command.ok().unwrap(); + // Makes the kernel depend on local OSTD + let manifest_path = os_dir.join("Cargo.toml"); + depends_on_local_ostd(&manifest_path); + let mut command = cargo_osdk(&["build"]); command.current_dir(&os_dir); command.ok().unwrap(); @@ -48,6 +52,9 @@ fn create_and_test_library() { command.current_dir(work_dir); command.ok().unwrap(); + let manifest_path = module_dir.join("Cargo.toml"); + depends_on_local_ostd(manifest_path); + let mut command = cargo_osdk(&["test"]); command.current_dir(&module_dir); command.ok().unwrap(); diff --git a/osdk/tests/examples_in_book/work_in_workspace.rs b/osdk/tests/examples_in_book/work_in_workspace.rs index c1976a591..676c44d00 100644 --- a/osdk/tests/examples_in_book/work_in_workspace.rs +++ b/osdk/tests/examples_in_book/work_in_workspace.rs @@ -7,7 +7,7 @@ use std::{ path::PathBuf, }; -use crate::util::cargo_osdk; +use crate::util::{cargo_osdk, depends_on_local_ostd}; #[test] fn work_in_workspace() { @@ -46,9 +46,14 @@ fn work_in_workspace() { .unwrap(); module_src_file.flush().unwrap(); + // Make module depends on local ostd + let module_manifest_path = workspace_dir.join(module).join("Cargo.toml"); + depends_on_local_ostd(module_manifest_path); + // Add dependency to myos/Cargo.toml let kernel_manifest_path = workspace_dir.join(kernel).join("Cargo.toml"); assert!(kernel_manifest_path.is_file()); + depends_on_local_ostd(&kernel_manifest_path); let mut kernel_manifest_file = OpenOptions::new() .append(true) .open(&kernel_manifest_path) diff --git a/osdk/tests/util/mod.rs b/osdk/tests/util/mod.rs index 6f456d357..98ac7b614 100644 --- a/osdk/tests/util/mod.rs +++ b/osdk/tests/util/mod.rs @@ -10,6 +10,7 @@ use std::{ }; use assert_cmd::Command; +use toml::{Table, Value}; pub fn cargo_osdk, I: IntoIterator>(args: I) -> Command { let mut command = Command::cargo_bin("cargo-osdk").unwrap(); @@ -85,3 +86,35 @@ pub fn add_member_to_workspace(workspace: impl AsRef, new_member: &str) { let new_content = workspace_manifest.to_string(); fs::write(&path, new_content).unwrap(); } + +/// Makes crates created by `cargo ostd new` depends on ostd locally, +/// instead of ostd from local branch. +/// +/// Each crate created by `cargo ostd new` should add this patch. +pub fn depends_on_local_ostd(manifest_path: impl AsRef) { + let crate_dir = env!("CARGO_MANIFEST_DIR"); + let ostd_dir = PathBuf::from(crate_dir) + .join("..") + .join("ostd") + .canonicalize() + .unwrap() + .to_string_lossy() + .to_string(); + + // FIXME: It may be more elegant to add `patch` section instead of replacing dependency. + // But adding `patch` section does not work in my local test, which is confusing. + + let manifest_content = fs::read_to_string(&manifest_path).unwrap(); + let mut manifest: Table = toml::from_str(&manifest_content).unwrap(); + let dep = manifest + .get_mut("dependencies") + .map(Value::as_table_mut) + .flatten() + .unwrap(); + + let mut table = Table::new(); + table.insert("path".to_string(), Value::String(ostd_dir)); + dep.insert("ostd".to_string(), Value::Table(table)); + + fs::write(manifest_path, manifest.to_string().as_bytes()).unwrap(); +} diff --git a/ostd/libs/ktest-proc-macro/Cargo.toml b/ostd/libs/ktest-proc-macro/Cargo.toml deleted file mode 100644 index 91204c934..000000000 --- a/ostd/libs/ktest-proc-macro/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "ktest-proc-macro" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = "1.0.69" -quote = "1.0.33" -rand = "0.8.5" -syn = { version = "2.0.29", features = ["full"] } diff --git a/ostd/libs/ktest-proc-macro/src/lib.rs b/ostd/libs/ktest-proc-macro/src/lib.rs deleted file mode 100644 index c272e5a6e..000000000 --- a/ostd/libs/ktest-proc-macro/src/lib.rs +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 - -#![feature(proc_macro_span)] - -extern crate proc_macro2; - -use proc_macro::TokenStream; -use quote::quote; -use rand::{distributions::Alphanumeric, Rng}; -use syn::{parse_macro_input, Expr, Ident, ItemFn}; - -/// The test attribute macro to mark a test function. -#[proc_macro_attribute] -pub fn ktest(_attr: TokenStream, item: TokenStream) -> TokenStream { - // Assuming that the item has type `fn() -> ()`, otherwise panics. - let input = parse_macro_input!(item as ItemFn); - assert!( - input.sig.inputs.is_empty(), - "ktest function should have no arguments" - ); - assert!( - matches!(input.sig.output, syn::ReturnType::Default), - "ktest function should return `()`" - ); - - // Generate a random identifier to avoid name conflicts. - let fn_id: String = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(8) - .map(char::from) - .collect(); - - let fn_name = &input.sig.ident; - let fn_ktest_item_name = Ident::new( - &format!("{}_ktest_item_{}", &input.sig.ident, &fn_id), - proc_macro2::Span::call_site(), - ); - - let is_should_panic_attr = |attr: &&syn::Attribute| { - attr.path() - .segments - .iter() - .any(|segment| segment.ident == "should_panic") - }; - let mut attr_iter = input.attrs.iter(); - let should_panic = attr_iter.find(is_should_panic_attr); - let (should_panic, expectation) = match should_panic { - Some(attr) => { - assert!( - !attr_iter.any(|attr: &syn::Attribute| is_should_panic_attr(&attr)), - "multiple `should_panic` attributes" - ); - match &attr.meta { - syn::Meta::List(l) => { - let arg_err_message = "`should_panic` attribute should only have zero or one `expected` argument, with the format of `expected = \"\"`"; - let expected_assign = - syn::parse2::(l.tokens.clone()).expect(arg_err_message); - let Expr::Lit(s) = *expected_assign.right else { - panic!("{}", arg_err_message); - }; - let syn::Lit::Str(expectation) = s.lit else { - panic!("{}", arg_err_message); - }; - (true, Some(expectation)) - } - _ => (true, None), - } - } - None => (false, None), - }; - let expectation_tokens = if let Some(s) = expectation { - quote! { - Some(#s) - } - } else { - quote! { - None - } - }; - - let package_name = std::env::var("CARGO_PKG_NAME").unwrap(); - let span = proc_macro::Span::call_site(); - let source = span.source_file().path(); - let source = source.to_str().unwrap(); - let line = span.line(); - let col = span.column(); - - let register_ktest_item = quote! { - #[cfg(ktest)] - #[used] - #[link_section = ".ktest_array"] - static #fn_ktest_item_name: ktest::KtestItem = ktest::KtestItem::new( - #fn_name, - (#should_panic, #expectation_tokens), - ktest::KtestItemInfo { - module_path: module_path!(), - fn_name: stringify!(#fn_name), - package: #package_name, - source: #source, - line: #line, - col: #col, - }, - ); - }; - - let output = quote! { - #input - - #register_ktest_item - }; - - TokenStream::from(output) -} diff --git a/ostd/libs/ktest/Cargo.toml b/ostd/libs/ktest/Cargo.toml index 44e5ffb54..db397f1ad 100644 --- a/ostd/libs/ktest/Cargo.toml +++ b/ostd/libs/ktest/Cargo.toml @@ -7,4 +7,3 @@ edition = "2021" [dependencies] owo-colors = "3.5.0" -ktest-proc-macro = { path = "../ktest-proc-macro" } diff --git a/ostd/libs/ktest/src/lib.rs b/ostd/libs/ktest/src/lib.rs index 6b4605324..f193a7ac6 100644 --- a/ostd/libs/ktest/src/lib.rs +++ b/ostd/libs/ktest/src/lib.rs @@ -22,9 +22,10 @@ //! module, e.g.: //! //! ```rust -//! use ktest::ktest; //! #[cfg(ktest)] //! mod test { +//! use ostd::prelude::*; +//! //! #[ktest] //! fn trivial_assertion() { //! assert_eq!(0, 0); @@ -42,13 +43,12 @@ //! } //! ``` //! -//! And also, any crates using the ktest framework should be linked with ostd -//! and import the `ktest` crate: +//! Any crates using the ktest framework should be linked with ostd. //! //! ```toml //! # Cargo.toml //! [dependencies] -//! ktest = { path = "relative/path/to/ktest" } +//! ostd = { path = "relative/path/to/ostd" } //! ``` //! //! By the way, `#[ktest]` attribute along also works, but it hinders test control @@ -97,8 +97,6 @@ pub mod tree; extern crate alloc; use alloc::{boxed::Box, string::String}; -pub use ktest_proc_macro::ktest; - #[derive(Clone, Debug)] pub struct PanicInfo { pub message: String, diff --git a/ostd/libs/ostd-macros/Cargo.toml b/ostd/libs/ostd-macros/Cargo.toml index 81172d7ab..cc6d9946f 100644 --- a/ostd/libs/ostd-macros/Cargo.toml +++ b/ostd/libs/ostd-macros/Cargo.toml @@ -11,4 +11,5 @@ proc-macro = true [dependencies] proc-macro2 = "1.0.78" quote = "1.0.35" +rand = "0.8.5" syn = { version = "2.0.48", features = ["full"] } diff --git a/ostd/libs/ostd-macros/src/lib.rs b/ostd/libs/ostd-macros/src/lib.rs index 09f95b6c6..3020e4388 100644 --- a/ostd/libs/ostd-macros/src/lib.rs +++ b/ostd/libs/ostd-macros/src/lib.rs @@ -1,8 +1,11 @@ // SPDX-License-Identifier: MPL-2.0 +#![feature(proc_macro_span)] + use proc_macro::TokenStream; use quote::quote; -use syn::{parse_macro_input, ItemFn}; +use rand::{distributions::Alphanumeric, Rng}; +use syn::{parse_macro_input, Expr, Ident, ItemFn}; /// This macro is used to mark the kernel entry point. /// @@ -33,3 +36,152 @@ pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream { ) .into() } + +/// The test attribute macro to mark a test function. +/// +/// # Example +/// +/// For crates other than ostd, +/// this macro can be used in the following form. +/// +/// ```norun +/// use ostd::prelude::*; +/// +/// #[ktest] +/// fn test_fn() { +/// assert_eq!(1 + 1, 2); +/// } +/// ``` +/// +/// For ostd crate itself, +/// this macro can be used in the form +/// +/// ```norun +/// use crate::prelude::*; +/// +/// #[ktest] +/// fn test_fn() { +/// assert_eq!(1 + 1, 2); +/// } +/// ``` +#[proc_macro_attribute] +pub fn ktest(_attr: TokenStream, item: TokenStream) -> TokenStream { + // Assuming that the item has type `fn() -> ()`, otherwise panics. + let input = parse_macro_input!(item as ItemFn); + assert!( + input.sig.inputs.is_empty(), + "ostd::test function should have no arguments" + ); + assert!( + matches!(input.sig.output, syn::ReturnType::Default), + "ostd::test function should return `()`" + ); + + // Generate a random identifier to avoid name conflicts. + let fn_id: String = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(8) + .map(char::from) + .collect(); + + let fn_name = &input.sig.ident; + let fn_ktest_item_name = Ident::new( + &format!("{}_ktest_item_{}", &input.sig.ident, &fn_id), + proc_macro2::Span::call_site(), + ); + + let is_should_panic_attr = |attr: &&syn::Attribute| { + attr.path() + .segments + .iter() + .any(|segment| segment.ident == "should_panic") + }; + let mut attr_iter = input.attrs.iter(); + let should_panic = attr_iter.find(is_should_panic_attr); + let (should_panic, expectation) = match should_panic { + Some(attr) => { + assert!( + !attr_iter.any(|attr: &syn::Attribute| is_should_panic_attr(&attr)), + "multiple `should_panic` attributes" + ); + match &attr.meta { + syn::Meta::List(l) => { + let arg_err_message = "`should_panic` attribute should only have zero or one `expected` argument, with the format of `expected = \"\"`"; + let expected_assign = + syn::parse2::(l.tokens.clone()).expect(arg_err_message); + let Expr::Lit(s) = *expected_assign.right else { + panic!("{}", arg_err_message); + }; + let syn::Lit::Str(expectation) = s.lit else { + panic!("{}", arg_err_message); + }; + (true, Some(expectation)) + } + _ => (true, None), + } + } + None => (false, None), + }; + let expectation_tokens = if let Some(s) = expectation { + quote! { + Some(#s) + } + } else { + quote! { + None + } + }; + + let package_name = std::env::var("CARGO_PKG_NAME").unwrap(); + let span = proc_macro::Span::call_site(); + let source = span.source_file().path(); + let source = source.to_str().unwrap(); + let line = span.line(); + let col = span.column(); + + let register_ktest_item = if package_name.as_str() == "ostd" { + quote! { + #[cfg(ktest)] + #[used] + #[link_section = ".ktest_array"] + static #fn_ktest_item_name: ktest::KtestItem = ktest::KtestItem::new( + #fn_name, + (#should_panic, #expectation_tokens), + ktest::KtestItemInfo { + module_path: module_path!(), + fn_name: stringify!(#fn_name), + package: #package_name, + source: #source, + line: #line, + col: #col, + }, + ); + } + } else { + quote! { + #[cfg(ktest)] + #[used] + #[link_section = ".ktest_array"] + static #fn_ktest_item_name: ostd::ktest::KtestItem = ostd::ktest::KtestItem::new( + #fn_name, + (#should_panic, #expectation_tokens), + ostd::ktest::KtestItemInfo { + module_path: module_path!(), + fn_name: stringify!(#fn_name), + package: #package_name, + source: #source, + line: #line, + col: #col, + }, + ); + } + }; + + let output = quote! { + #input + + #register_ktest_item + }; + + TokenStream::from(output) +} diff --git a/ostd/src/lib.rs b/ostd/src/lib.rs index 542fbf97b..52d0dcdd8 100644 --- a/ostd/src/lib.rs +++ b/ostd/src/lib.rs @@ -22,9 +22,6 @@ #![warn(missing_docs)] extern crate alloc; -#[cfg(ktest)] -#[macro_use] -extern crate ktest; extern crate static_assertions; pub mod arch; @@ -109,6 +106,8 @@ fn invoke_ffi_init_funcs() { /// Simple unit tests for the ktest framework. #[cfg(ktest)] mod test { + use crate::prelude::*; + #[ktest] fn trivial_assertion() { assert_eq!(0, 0); @@ -126,3 +125,9 @@ mod test { panic!("expected panic message"); } } + +/// The module re-exports everything from the ktest crate +#[cfg(ktest)] +pub mod ktest { + pub use ktest::*; +} diff --git a/ostd/src/mm/dma/dma_coherent.rs b/ostd/src/mm/dma/dma_coherent.rs index c9bbe19f2..2b6a76439 100644 --- a/ostd/src/mm/dma/dma_coherent.rs +++ b/ostd/src/mm/dma/dma_coherent.rs @@ -190,7 +190,7 @@ mod test { use alloc::vec; use super::*; - use crate::mm::FrameAllocOptions; + use crate::{mm::FrameAllocOptions, prelude::*}; #[ktest] fn map_with_coherent_device() { diff --git a/ostd/src/mm/dma/dma_stream.rs b/ostd/src/mm/dma/dma_stream.rs index 8351b6e45..88f7beb9f 100644 --- a/ostd/src/mm/dma/dma_stream.rs +++ b/ostd/src/mm/dma/dma_stream.rs @@ -310,7 +310,7 @@ mod test { use alloc::vec; use super::*; - use crate::mm::FrameAllocOptions; + use crate::{mm::FrameAllocOptions, prelude::*}; #[ktest] fn streaming_map() { diff --git a/ostd/src/mm/page_table/boot_pt.rs b/ostd/src/mm/page_table/boot_pt.rs index d877ba3e5..22fd503bb 100644 --- a/ostd/src/mm/page_table/boot_pt.rs +++ b/ostd/src/mm/page_table/boot_pt.rs @@ -161,6 +161,9 @@ impl Drop for BootPageTable } } +#[cfg(ktest)] +use crate::prelude::*; + #[cfg(ktest)] #[ktest] fn test_boot_pt_map_protect() { diff --git a/ostd/src/mm/page_table/test.rs b/ostd/src/mm/page_table/test.rs index 7acd9c801..fcc4ac1f3 100644 --- a/ostd/src/mm/page_table/test.rs +++ b/ostd/src/mm/page_table/test.rs @@ -3,10 +3,13 @@ use core::mem::ManuallyDrop; use super::*; -use crate::mm::{ - kspace::LINEAR_MAPPING_BASE_VADDR, - page::{allocator, meta::FrameMeta}, - page_prop::{CachePolicy, PageFlags}, +use crate::{ + mm::{ + kspace::LINEAR_MAPPING_BASE_VADDR, + page::{allocator, meta::FrameMeta}, + page_prop::{CachePolicy, PageFlags}, + }, + prelude::*, }; const PAGE_SIZE: usize = 4096; diff --git a/ostd/src/prelude.rs b/ostd/src/prelude.rs index a2eb9ad80..c8951d109 100644 --- a/ostd/src/prelude.rs +++ b/ostd/src/prelude.rs @@ -10,6 +10,9 @@ pub type Result = core::result::Result; pub(crate) use alloc::{boxed::Box, sync::Arc, vec::Vec}; pub(crate) use core::any::Any; +#[cfg(ktest)] +pub use ostd_macros::ktest; + pub use crate::{ early_print as print, early_println as println, mm::{Paddr, Vaddr}, diff --git a/ostd/src/sync/atomic_bits.rs b/ostd/src/sync/atomic_bits.rs index 1b92799e8..a5fe3dfd0 100644 --- a/ostd/src/sync/atomic_bits.rs +++ b/ostd/src/sync/atomic_bits.rs @@ -293,6 +293,7 @@ impl fmt::Debug for AtomicBits { #[cfg(ktest)] mod test { use super::*; + use crate::prelude::*; #[ktest] fn new() { diff --git a/ostd/src/sync/wait.rs b/ostd/src/sync/wait.rs index 8ced4940b..430c5733a 100644 --- a/ostd/src/sync/wait.rs +++ b/ostd/src/sync/wait.rs @@ -298,7 +298,7 @@ impl Waker { #[cfg(ktest)] mod test { use super::*; - use crate::task::TaskOptions; + use crate::{prelude::*, task::TaskOptions}; fn queue_wake(wake: F) where diff --git a/ostd/src/task/task.rs b/ostd/src/task/task.rs index 5ac541cf3..036359be7 100644 --- a/ostd/src/task/task.rs +++ b/ostd/src/task/task.rs @@ -332,6 +332,8 @@ impl TaskOptions { #[cfg(ktest)] mod test { + use crate::prelude::*; + #[ktest] fn create_task() { let task = || {