mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-22 08:53:29 +00:00
Add ktest framework
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
d7cc52c615
commit
b8818bb740
@ -6,7 +6,6 @@ runner = "cargo run --package jinux-runner --"
|
||||
kcheck = "check --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem"
|
||||
kbuild = "build --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem"
|
||||
krun = "run --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem"
|
||||
ktest = "test --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem"
|
||||
kclippy = "clippy --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem"
|
||||
component-check = "component check --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem"
|
||||
|
||||
|
1
.github/workflows/cargo_check.yml
vendored
1
.github/workflows/cargo_check.yml
vendored
@ -5,7 +5,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- releases/*
|
||||
|
||||
jobs:
|
||||
test:
|
||||
|
25
.github/workflows/unit_test.yml
vendored
Normal file
25
.github/workflows/unit_test.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
name: Unit test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
container: jinuxdev/jinux:0.2.1
|
||||
steps:
|
||||
- run: echo "Running in jinuxdev/jinux:0.2.1"
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Ktest Unit Test
|
||||
id: ktest_unit_test
|
||||
run: make run KTEST=all ENABLE_KVM=0 RELEASE_MODE=1
|
||||
|
||||
# TODO: include the unit tests for the crates that supports cargo test.
|
||||
|
||||
# TODO: add component check.
|
34
Cargo.lock
generated
34
Cargo.lock
generated
@ -216,7 +216,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -368,7 +368,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -475,7 +475,7 @@ checksum = "ba330b70a5341d3bc730b8e205aaee97ddab5d9c448c4f51a7c2d924266fa8f9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -567,7 +567,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -637,6 +637,7 @@ dependencies = [
|
||||
"inherit-methods-macro",
|
||||
"int-to-c-enum",
|
||||
"intrusive-collections",
|
||||
"ktest",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"multiboot2",
|
||||
@ -757,6 +758,7 @@ dependencies = [
|
||||
"jinux-util",
|
||||
"jinux-virtio",
|
||||
"keyable-arc",
|
||||
"ktest",
|
||||
"lazy_static",
|
||||
"lending-iterator",
|
||||
"libflate",
|
||||
@ -830,6 +832,16 @@ checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
|
||||
name = "keyable-arc"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "ktest"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rand",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@ -1061,9 +1073,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.66"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -1090,9 +1102,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.32"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -1278,9 +1290,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.28"
|
||||
version = "2.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
|
||||
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1320,7 +1332,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
16
Makefile
16
Makefile
@ -8,6 +8,7 @@ ENABLE_KVM ?= 1
|
||||
GDB_CLIENT ?= 0
|
||||
GDB_SERVER ?= 0
|
||||
INTEL_TDX ?= 0
|
||||
KTEST ?= none
|
||||
SKIP_GRUB_MENU ?= 1
|
||||
RELEASE_MODE ?= 0
|
||||
# End of setting up Make varaiables
|
||||
@ -22,8 +23,8 @@ KERNEL_CMDLINE += -c exit 0
|
||||
endif
|
||||
|
||||
CARGO_KBUILD_ARGS :=
|
||||
|
||||
CARGO_KRUN_ARGS :=
|
||||
GLOBAL_RUSTC_FLAGS :=
|
||||
|
||||
ifeq ($(RELEASE_MODE), 1)
|
||||
CARGO_KBUILD_ARGS += --release
|
||||
@ -60,6 +61,11 @@ CARGO_KBUILD_ARGS += --features intel_tdx
|
||||
CARGO_KRUN_ARGS += --features intel_tdx
|
||||
endif
|
||||
|
||||
ifneq ($(KTEST), none)
|
||||
comma := ,
|
||||
GLOBAL_RUSTC_FLAGS += --cfg ktest --cfg ktest=\"$(subst $(comma),\" --cfg ktest=\",$(KTEST))\"
|
||||
endif
|
||||
|
||||
ifeq ($(SKIP_GRUB_MENU), 1)
|
||||
CARGO_KRUN_ARGS += --skip-grub-menu
|
||||
endif
|
||||
@ -82,16 +88,16 @@ setup:
|
||||
|
||||
build:
|
||||
@make --no-print-directory -C regression
|
||||
@cargo kbuild $(CARGO_KBUILD_ARGS)
|
||||
@RUSTFLAGS="$(GLOBAL_RUSTC_FLAGS)" cargo kbuild $(CARGO_KBUILD_ARGS)
|
||||
|
||||
tools:
|
||||
@cd services/libs/comp-sys && cargo install --path cargo-component
|
||||
|
||||
run: build
|
||||
@cargo krun $(CARGO_KRUN_ARGS)
|
||||
@RUSTFLAGS="$(GLOBAL_RUSTC_FLAGS)" cargo krun $(CARGO_KRUN_ARGS)
|
||||
|
||||
test: build
|
||||
@cargo ktest
|
||||
test:
|
||||
@python3 ./tools/test/run_tests.py
|
||||
|
||||
docs:
|
||||
@cargo doc # Build Rust docs
|
||||
|
13
README.md
13
README.md
@ -56,11 +56,16 @@ make build
|
||||
make run
|
||||
```
|
||||
|
||||
### Test
|
||||
### Unit Test
|
||||
|
||||
We can run unit tests and integration tests if building succeeds.
|
||||
We can run unit tests if building succeeds. This is powered by our [ktest](framework/libs/ktest) framework.
|
||||
```bash
|
||||
make test
|
||||
make run KTEST=all
|
||||
```
|
||||
|
||||
You could also specify tests in a crate or a subset of tests to run, as long as you defined them well using cfg.
|
||||
```bash
|
||||
make run KTEST=jinux-frame,jinux-std
|
||||
```
|
||||
|
||||
If we want to check access control policy among components, install some standalone tools (e.g., `cargo-component`).
|
||||
@ -73,7 +78,7 @@ Then we can use the tool to check access control policy.
|
||||
cargo component-check
|
||||
```
|
||||
|
||||
### Syscall Test
|
||||
### Integration Test
|
||||
|
||||
This command will build the syscall test binary and automatically run Jinux with the tests using QEMU.
|
||||
```bash
|
||||
|
@ -13,6 +13,7 @@ volatile = { version = "0.4.5", features = ["unstable"] }
|
||||
buddy_system_allocator = "0.9.0"
|
||||
pod = { git = "https://github.com/jinzhao-dev/pod", rev = "d7dba56" }
|
||||
align_ext = { path = "../libs/align_ext" }
|
||||
ktest = { path = "../libs/ktest" }
|
||||
intrusive-collections = "0.9.5"
|
||||
log = "0.4"
|
||||
lazy_static = { version = "1.0", features = ["spin_no_std"] }
|
||||
|
@ -24,24 +24,6 @@ SECTIONS
|
||||
.text : AT(ADDR(.text) - KERNEL_VMA) { *(.text .text.*) }
|
||||
.rodata : AT(ADDR(.rodata) - KERNEL_VMA) { *(.rodata .rodata.*) }
|
||||
|
||||
.data : AT(ADDR(.data) - KERNEL_VMA) { *(.data .data.*) }
|
||||
.bss : AT(ADDR(.bss) - KERNEL_VMA) {
|
||||
__bss = .;
|
||||
*(.bss .bss.*) *(COMMON)
|
||||
__bss_end = .;
|
||||
}
|
||||
|
||||
.tdata : AT(ADDR(.tdata) - KERNEL_VMA) { *(.tdata .tdata.*) }
|
||||
.tbss : AT(ADDR(.tbss) - KERNEL_VMA) { *(.tbss .tbss.*) }
|
||||
|
||||
.init_array : AT(ADDR(.init_array) - KERNEL_VMA) {
|
||||
__sinit_array = .;
|
||||
*(.init_array .init_array.*)
|
||||
__einit_array = .;
|
||||
}
|
||||
|
||||
.data.rel.ro : AT(ADDR(.data.rel.ro) - KERNEL_VMA) { *(.data.rel.ro .data.rel.ro.*) }
|
||||
.dynamic : AT(ADDR(.dynamic) - KERNEL_VMA) { *(.dynamic) }
|
||||
.eh_frame_hdr : AT(ADDR(.eh_frame_hdr) - KERNEL_VMA) {
|
||||
__eh_frame_hdr = .;
|
||||
KEEP(*(.eh_frame_hdr))
|
||||
@ -53,8 +35,37 @@ SECTIONS
|
||||
__eh_frame_end = .;
|
||||
}
|
||||
|
||||
# The notes section are used to mark the PVH boot entry point, useful for QEMU and Xen
|
||||
.notes : { *(.notes) }
|
||||
.data.rel.ro : AT(ADDR(.data.rel.ro) - KERNEL_VMA) { *(.data.rel.ro .data.rel.ro.*) }
|
||||
.dynamic : AT(ADDR(.dynamic) - KERNEL_VMA) { *(.dynamic) }
|
||||
|
||||
.init_array : AT(ADDR(.init_array) - KERNEL_VMA) {
|
||||
__sinit_array = .;
|
||||
KEEP(*(SORT(.init_array .init_array.*)))
|
||||
__einit_array = .;
|
||||
}
|
||||
|
||||
.got : AT(ADDR(.got) - KERNEL_VMA) { *(.got .got.*) }
|
||||
.got.plt : AT(ADDR(.got.plt) - KERNEL_VMA) { *(.got.plt .got.plt.*) }
|
||||
|
||||
. = DATA_SEGMENT_RELRO_END(0, .);
|
||||
|
||||
.data : AT(ADDR(.data) - KERNEL_VMA) { *(.data .data.*) }
|
||||
.bss : AT(ADDR(.bss) - KERNEL_VMA) {
|
||||
__bss = .;
|
||||
*(.bss .bss.*) *(COMMON)
|
||||
__bss_end = .;
|
||||
}
|
||||
|
||||
.ktest_array : AT(ADDR(.ktest_array) - KERNEL_VMA) {
|
||||
__ktest_array = .;
|
||||
KEEP(*(SORT(.ktest_array)))
|
||||
__ktest_array_end = .;
|
||||
}
|
||||
|
||||
.tdata : AT(ADDR(.tdata) - KERNEL_VMA) { *(.tdata .tdata.*) }
|
||||
.tbss : AT(ADDR(.tbss) - KERNEL_VMA) { *(.tbss .tbss.*) }
|
||||
|
||||
. = DATA_SEGMENT_END(.);
|
||||
|
||||
__kernel_end = . - KERNEL_VMA;
|
||||
}
|
@ -124,12 +124,6 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
|
||||
memory_regions.call_once(|| regions);
|
||||
}
|
||||
|
||||
// The entry point of kernel code, which should be defined by the package that
|
||||
// uses jinux-frame.
|
||||
extern "Rust" {
|
||||
fn jinux_main() -> !;
|
||||
}
|
||||
|
||||
/// The entry point of Rust code called by the Linux 64-bit boot compatible bootloader.
|
||||
#[no_mangle]
|
||||
unsafe extern "sysv64" fn __linux64_boot(params_ptr: *const boot_params::BootParams) -> ! {
|
||||
@ -144,5 +138,5 @@ unsafe extern "sysv64" fn __linux64_boot(params_ptr: *const boot_params::BootPar
|
||||
init_framebuffer_info,
|
||||
init_memory_regions,
|
||||
);
|
||||
jinux_main();
|
||||
crate::boot::call_jinux_main();
|
||||
}
|
||||
|
@ -332,12 +332,6 @@ struct MemoryEntry {
|
||||
memory_type: MemoryAreaType,
|
||||
}
|
||||
|
||||
// The entry point of kernel code, which should be defined by the package that
|
||||
// uses jinux-frame.
|
||||
extern "Rust" {
|
||||
fn jinux_main() -> !;
|
||||
}
|
||||
|
||||
static MB1_INFO: Once<&'static MultibootLegacyInfo> = Once::new();
|
||||
|
||||
/// The entry point of Rust code called by inline asm.
|
||||
@ -353,5 +347,5 @@ unsafe extern "sysv64" fn __multiboot_entry(boot_magic: u32, boot_params: u64) -
|
||||
init_framebuffer_info,
|
||||
init_memory_regions,
|
||||
);
|
||||
jinux_main();
|
||||
crate::boot::call_jinux_main();
|
||||
}
|
||||
|
@ -163,12 +163,6 @@ fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
|
||||
memory_regions.call_once(move || non_overlapping_regions_from(regions.as_ref()));
|
||||
}
|
||||
|
||||
// The entry point of kernel code, which should be defined by the package that
|
||||
// uses jinux-frame.
|
||||
extern "Rust" {
|
||||
fn jinux_main() -> !;
|
||||
}
|
||||
|
||||
/// The entry point of Rust code called by inline asm.
|
||||
#[no_mangle]
|
||||
unsafe extern "sysv64" fn __multiboot2_entry(boot_magic: u32, boot_params: u64) -> ! {
|
||||
@ -184,5 +178,5 @@ unsafe extern "sysv64" fn __multiboot2_entry(boot_magic: u32, boot_params: u64)
|
||||
init_framebuffer_info,
|
||||
init_memory_regions,
|
||||
);
|
||||
jinux_main();
|
||||
crate::boot::call_jinux_main();
|
||||
}
|
||||
|
@ -99,3 +99,24 @@ define_global_static_boot_arguments!(
|
||||
pub fn init() {
|
||||
call_all_boot_init_callbacks();
|
||||
}
|
||||
|
||||
/// Call the framework-user defined entrypoint of the actual kernel.
|
||||
///
|
||||
/// Any kernel that uses the jinux-frame crate should define a function named
|
||||
/// `jinux_main` as the entrypoint.
|
||||
pub fn call_jinux_main() -> ! {
|
||||
#[cfg(not(ktest))]
|
||||
unsafe {
|
||||
// The entry point of kernel code, which should be defined by the package that
|
||||
// uses jinux-frame.
|
||||
extern "Rust" {
|
||||
fn jinux_main() -> !;
|
||||
}
|
||||
jinux_main();
|
||||
}
|
||||
#[cfg(ktest)]
|
||||
{
|
||||
crate::init();
|
||||
ktest::do_ktests!();
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
extern crate alloc;
|
||||
#[macro_use]
|
||||
extern crate ktest;
|
||||
#[macro_use]
|
||||
extern crate static_assertions;
|
||||
|
||||
pub mod arch;
|
||||
@ -44,7 +46,7 @@ pub use self::error::Error;
|
||||
pub use self::prelude::Result;
|
||||
use alloc::vec::Vec;
|
||||
use arch::irq::{IrqCallbackHandle, IrqLine};
|
||||
use core::{mem, panic::PanicInfo};
|
||||
use core::mem;
|
||||
#[cfg(feature = "intel_tdx")]
|
||||
use tdx_guest::init_tdx;
|
||||
use trapframe::TrapFrame;
|
||||
@ -108,35 +110,6 @@ pub(crate) const fn zero<T>() -> T {
|
||||
unsafe { mem::MaybeUninit::zeroed().assume_init() }
|
||||
}
|
||||
|
||||
pub trait Testable {
|
||||
fn run(&self);
|
||||
}
|
||||
|
||||
impl<T> Testable for T
|
||||
where
|
||||
T: Fn(),
|
||||
{
|
||||
fn run(&self) {
|
||||
print!("{}...\n", core::any::type_name::<T>());
|
||||
self();
|
||||
println!("[ok]");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_runner(tests: &[&dyn Testable]) {
|
||||
println!("Running {} tests", tests.len());
|
||||
for test in tests {
|
||||
test.run();
|
||||
}
|
||||
exit_qemu(QemuExitCode::Success);
|
||||
}
|
||||
|
||||
pub fn test_panic_handler(info: &PanicInfo) -> ! {
|
||||
println!("[failed]");
|
||||
println!("Error: {}", info);
|
||||
exit_qemu(QemuExitCode::Failed);
|
||||
}
|
||||
|
||||
pub fn panic_handler() {
|
||||
// let mut fp: usize;
|
||||
// let stop = unsafe{
|
||||
@ -181,3 +154,11 @@ pub fn exit_qemu(exit_code: QemuExitCode) -> ! {
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[if_cfg_ktest]
|
||||
mod test {
|
||||
#[ktest]
|
||||
fn trivial_assertion() {
|
||||
assert_eq!(0, 0);
|
||||
}
|
||||
}
|
||||
|
@ -284,11 +284,11 @@ impl fmt::Debug for AtomicBits {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[if_cfg_ktest]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn new() {
|
||||
let bits = AtomicBits::new_zeroes(1);
|
||||
assert!(bits.len() == 1);
|
||||
@ -303,7 +303,7 @@ mod test {
|
||||
assert!(bits.len() == 65);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn set_get() {
|
||||
let bits = AtomicBits::new_zeroes(128);
|
||||
for i in 0..bits.len() {
|
||||
@ -328,7 +328,7 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn iter_ones() {
|
||||
let bits = AtomicBits::new_zeroes(1);
|
||||
assert!(bits.iter_ones().count() == 0);
|
||||
@ -353,7 +353,7 @@ mod test {
|
||||
assert!(bits.iter_ones().count() == 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn iter_zeroes() {
|
||||
let bits = AtomicBits::new_ones(1);
|
||||
assert!(bits.iter_zeroes().count() == 0);
|
||||
@ -380,7 +380,7 @@ mod test {
|
||||
assert!(bits.iter_zeroes().count() == 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn iter() {
|
||||
let bits = AtomicBits::new_zeroes(7);
|
||||
assert!(bits.iter().all(|bit| bit == false));
|
||||
|
15
framework/libs/ktest/Cargo.toml
Normal file
15
framework/libs/ktest/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "ktest"
|
||||
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"] }
|
192
framework/libs/ktest/src/lib.rs
Normal file
192
framework/libs/ktest/src/lib.rs
Normal file
@ -0,0 +1,192 @@
|
||||
//! # The kernel mode testing framework of Jinux.
|
||||
//!
|
||||
//! `ktest` stands for kernel-mode testing framework. Its goal is to provide a
|
||||
//! `cargo test`-like experience for any crates that depends on jinux-frame.
|
||||
//!
|
||||
//! All the tests written in the source tree of the crates will be run using the
|
||||
//! `do_ktests!()` macro immediately after the initialization of jinux-frame.
|
||||
//! Thus you can use any feature provided by the frame including the heap
|
||||
//! allocator, etc.
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! To write a unit test for any crates, it is recommended to create a new test
|
||||
//! module, e.g.:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use ktest::{ktest, if_cfg_ktest};
|
||||
//! #[if_cfg_ktest]
|
||||
//! mod test {
|
||||
//! #[ktest]
|
||||
//! fn trivial_assertion() {
|
||||
//! assert_eq!(0, 0);
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! And also, any crates using the ktest framework should be linked with jinux-frame
|
||||
//! and import the `ktest` crate:
|
||||
//!
|
||||
//! ```toml
|
||||
//! # Cargo.toml
|
||||
//! [dependencies]
|
||||
//! ktest = { path = "relative/path/to/ktest" }
|
||||
//! ```
|
||||
//!
|
||||
//! By the way, `#[ktest]` attribute along also works, but it hinders test control
|
||||
//! using cfgs since plain attribute marked test will be executed in all test runs
|
||||
//! no matter what cfgs are passed to the compiler. More importantly, using `#[ktest]`
|
||||
//! without cfgs occupies binary real estate since the `.ktest_array` section is not
|
||||
//! explicitly stripped in normal builds.
|
||||
//!
|
||||
//! Rust cfg is used to control the compilation of the test module. In cooperation
|
||||
//! with the `ktest` framework, the Makefile will set the `RUSTFLAGS` environment
|
||||
//! variable to pass the cfgs to all rustc invocations. To run the tests, you need
|
||||
//! to pass a list of cfgs to the Makefile, e.g.:
|
||||
//!
|
||||
//! ```bash
|
||||
//! make run KTEST=jinux-frame,jinux-std,align_ext,tdx-guest
|
||||
//! ```
|
||||
//!
|
||||
//! It is flexible to specify the cfgs for running the tests. The cfg value is not
|
||||
//! limited to crate names, enabling your imagination to configure running any subsets
|
||||
//! of tests in any crates. And to ease development, `#[if_cfg_ktest]` is expanded to
|
||||
//! a default conditional compilation setting:
|
||||
//! `#[cfg(all(ktest, any(ktest = "all", ktest = #crate_name)))]`
|
||||
//!
|
||||
//! Currently we do not support `#[should_panic]` attribute, and this feature will
|
||||
//! be added in the future.
|
||||
//!
|
||||
//! Doctest is not taken into consideration yet, and the interface is subject to
|
||||
//! change.
|
||||
//!
|
||||
//! ## How it works
|
||||
//!
|
||||
//! The `ktest` framework is implemented using the procedural macro feature of Rust.
|
||||
//! The `ktest` attribute macro will generate a static fn pointer variable linked in
|
||||
//! the `.ktest_array` section. The `do_ktests!()` macro will iterate over all the
|
||||
//! static variables in the section and run the tests.
|
||||
//!
|
||||
|
||||
#![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, Ident, ItemFn, ItemMod};
|
||||
|
||||
/// The conditional compilation attribute macro to control the compilation of test
|
||||
/// modules.
|
||||
#[proc_macro_attribute]
|
||||
pub fn if_cfg_ktest(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
// Assuming that the item is a module declearation, otherwise panics.
|
||||
let input = parse_macro_input!(item as ItemMod);
|
||||
|
||||
let crate_name = std::env::var("CARGO_PKG_NAME").unwrap();
|
||||
|
||||
let output = quote! {
|
||||
#[cfg(all(ktest, any(ktest = "all", ktest = #crate_name)))]
|
||||
#input
|
||||
};
|
||||
|
||||
TokenStream::from(output)
|
||||
}
|
||||
|
||||
/// 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(),
|
||||
);
|
||||
|
||||
// Since Rust does not support unamed structures, we have to generate a
|
||||
// unique name for each test item structure.
|
||||
let ktest_item_struct = Ident::new(
|
||||
&format!("KtestItem{}", &fn_id),
|
||||
proc_macro2::Span::call_site(),
|
||||
);
|
||||
|
||||
let span = proc_macro::Span::call_site();
|
||||
let source = span.source_file();
|
||||
let crate_name = std::env::var("CARGO_PKG_NAME").unwrap();
|
||||
let hint_str = format!(
|
||||
"[{}] {}: {}()",
|
||||
crate_name,
|
||||
source.path().to_str().unwrap(),
|
||||
fn_name
|
||||
);
|
||||
|
||||
let register = quote! {
|
||||
struct #ktest_item_struct {
|
||||
fn_: fn() -> (),
|
||||
hint: &'static str,
|
||||
}
|
||||
#[cfg(ktest)]
|
||||
#[used]
|
||||
#[link_section = ".ktest_array"]
|
||||
static #fn_ktest_item_name: #ktest_item_struct = #ktest_item_struct {
|
||||
fn_: #fn_name,
|
||||
hint: #hint_str,
|
||||
};
|
||||
};
|
||||
|
||||
let output = quote! {
|
||||
#input
|
||||
|
||||
#register
|
||||
};
|
||||
|
||||
TokenStream::from(output)
|
||||
}
|
||||
|
||||
/// The procedural macro to run all the tests.
|
||||
#[proc_macro]
|
||||
pub fn do_ktests(_item: TokenStream) -> TokenStream {
|
||||
let body = quote! {
|
||||
struct KtestItem {
|
||||
fn_: fn() -> (),
|
||||
hint: &'static str,
|
||||
};
|
||||
extern "C" {
|
||||
fn __ktest_array();
|
||||
fn __ktest_array_end();
|
||||
}
|
||||
let item_size = core::mem::size_of::<KtestItem>() as u64;
|
||||
let l = (__ktest_array_end as u64 - __ktest_array as u64) / item_size;
|
||||
crate::println!("Running {} tests", l);
|
||||
for i in 0..l {
|
||||
unsafe {
|
||||
let address = (__ktest_array as u64 + item_size * i) as *const u64;
|
||||
let item = address as *const KtestItem;
|
||||
crate::print!("{} ...", (*item).hint);
|
||||
((*item).fn_)();
|
||||
}
|
||||
crate::println!(" Ok!");
|
||||
}
|
||||
crate::exit_qemu(crate::QemuExitCode::Success);
|
||||
};
|
||||
|
||||
TokenStream::from(body)
|
||||
}
|
@ -1,11 +1,8 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(custom_test_frameworks)]
|
||||
// The no_mangle macro need to remove the `forbid(unsafe_code)` macro. The bootloader needs the _start function
|
||||
// to be no mangle so that it can jump into the entry point.
|
||||
// #![forbid(unsafe_code)]
|
||||
#![test_runner(jinux_frame::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
extern crate jinux_frame;
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
@ -13,8 +10,6 @@ use jinux_frame::println;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn jinux_main() -> ! {
|
||||
#[cfg(test)]
|
||||
test_main();
|
||||
jinux_frame::init();
|
||||
println!("[kernel] finish init jinux_frame");
|
||||
component::init_all(component::parse_metadata!()).unwrap();
|
||||
@ -22,7 +17,6 @@ pub fn jinux_main() -> ! {
|
||||
jinux_std::run_first_process();
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
use jinux_frame::{exit_qemu, QemuExitCode};
|
||||
@ -31,14 +25,3 @@ fn panic(info: &PanicInfo) -> ! {
|
||||
jinux_frame::panic_handler();
|
||||
exit_qemu(QemuExitCode::Failed);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
jinux_frame::test_panic_handler(info);
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn trivial_assertion() {
|
||||
assert_eq!(1, 1);
|
||||
}
|
||||
|
@ -35,9 +35,7 @@ pub(crate) fn init() {
|
||||
let page_size = size / PAGE_SIZE;
|
||||
|
||||
let start_paddr = framebuffer.address;
|
||||
let io_mem =
|
||||
IoMem::new(start_paddr..(start_paddr + jinux_frame::config::PAGE_SIZE * page_size))
|
||||
.unwrap();
|
||||
let io_mem = todo!("IoMem is private for components now, should fix it.");
|
||||
|
||||
let mut buffer: Vec<u8> = Vec::with_capacity(size);
|
||||
for _ in 0..size {
|
||||
|
@ -40,6 +40,7 @@ smoltcp = { version = "0.9.1", default-features = false, features = [
|
||||
"socket-raw",
|
||||
"socket-dhcpv4",
|
||||
] }
|
||||
ktest = { path = "../../../framework/libs/ktest" }
|
||||
tdx-guest = { path = "../../../framework/libs/tdx-guest", optional = true }
|
||||
|
||||
# parse elf file
|
||||
|
@ -34,6 +34,8 @@ extern crate alloc;
|
||||
extern crate lru;
|
||||
#[macro_use]
|
||||
extern crate controlled;
|
||||
#[macro_use]
|
||||
extern crate ktest;
|
||||
|
||||
pub mod device;
|
||||
pub mod driver;
|
||||
|
@ -132,7 +132,7 @@ impl<R> VmarChildOptions<R> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[if_cfg_ktest]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::vm::page_fault_handler::PageFaultHandler;
|
||||
@ -142,13 +142,13 @@ mod test {
|
||||
use jinux_frame::vm::VmIo;
|
||||
use jinux_rights::Full;
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn root_vmar() {
|
||||
let vmar = Vmar::<Full>::new_root();
|
||||
assert!(vmar.size() == ROOT_VMAR_HIGHEST_ADDR);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn child_vmar() {
|
||||
let root_vmar = Vmar::<Full>::new_root();
|
||||
let root_vmar_dup = root_vmar.dup().unwrap();
|
||||
@ -167,7 +167,7 @@ mod test {
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn map_vmo() {
|
||||
let root_vmar = Vmar::<Full>::new_root();
|
||||
let vmo = VmoOptions::<Full>::new(PAGE_SIZE).alloc().unwrap().to_dyn();
|
||||
@ -193,7 +193,7 @@ mod test {
|
||||
assert!(root_vmar.read_val::<u8>(another_map_offset).unwrap() == 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn handle_page_fault() {
|
||||
const OFFSET: usize = 0x1000_0000;
|
||||
let root_vmar = Vmar::<Full>::new_root();
|
||||
|
@ -516,13 +516,13 @@ impl VmoChildType for VmoSliceChild {}
|
||||
pub struct VmoCowChild;
|
||||
impl VmoChildType for VmoCowChild {}
|
||||
|
||||
#[cfg(test)]
|
||||
#[if_cfg_ktest]
|
||||
mod test {
|
||||
use super::*;
|
||||
use jinux_frame::vm::VmIo;
|
||||
use jinux_rights::Full;
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn alloc_vmo() {
|
||||
let vmo = VmoOptions::<Full>::new(PAGE_SIZE).alloc().unwrap();
|
||||
assert!(vmo.size() == PAGE_SIZE);
|
||||
@ -530,7 +530,8 @@ mod test {
|
||||
assert!(vmo.read_val::<usize>(0).unwrap() == 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
// FIXME: should_panic doesn't work with ktest, two negative makes a positive...
|
||||
#[should_panic]
|
||||
/// FIXME: alloc continuous frames is not supported now
|
||||
fn alloc_continuous_vmo() {
|
||||
@ -541,7 +542,7 @@ mod test {
|
||||
assert!(vmo.size() == 10 * PAGE_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn write_and_read() {
|
||||
let vmo = VmoOptions::<Full>::new(PAGE_SIZE).alloc().unwrap();
|
||||
let val = 42u8;
|
||||
@ -555,7 +556,7 @@ mod test {
|
||||
assert!(read_val == 0x78563412)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn slice_child() {
|
||||
let parent = VmoOptions::<Full>::new(2 * PAGE_SIZE).alloc().unwrap();
|
||||
let parent_dup = parent.dup().unwrap();
|
||||
@ -570,7 +571,7 @@ mod test {
|
||||
assert!(parent.read_val::<u32>(99).unwrap() == 0x1234);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn cow_child() {
|
||||
let parent = VmoOptions::<Full>::new(2 * PAGE_SIZE).alloc().unwrap();
|
||||
let parent_dup = parent.dup().unwrap();
|
||||
@ -596,7 +597,7 @@ mod test {
|
||||
assert!(cow_child.read_val::<u32>(PAGE_SIZE + 10).unwrap() == 12345);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ktest]
|
||||
fn resize() {
|
||||
let vmo = VmoOptions::<Full>::new(PAGE_SIZE)
|
||||
.flags(VmoFlags::RESIZABLE)
|
||||
|
@ -1,43 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(jinux_frame::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
extern crate alloc;
|
||||
use core::panic::PanicInfo;
|
||||
use jinux_frame::println;
|
||||
|
||||
static mut INPUT_VALUE: u8 = 0;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn jinux_main() -> ! {
|
||||
jinux_frame::init();
|
||||
test_main();
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
jinux_frame::test_panic_handler(info)
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_input() {
|
||||
x86_64::instructions::interrupts::enable();
|
||||
println!("please input value into console to pass this test");
|
||||
// FIXME: Where is tty?
|
||||
// jinux_std::driver::tty::register_serial_input_callback(input_callback);
|
||||
unsafe {
|
||||
while INPUT_VALUE == 0 {
|
||||
x86_64::instructions::hlt();
|
||||
}
|
||||
// println!("input value:{}", INPUT_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_callback(input: u8) {
|
||||
println!("input value:{}", input);
|
||||
unsafe {
|
||||
INPUT_VALUE = input;
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(jinux_frame::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
extern crate alloc;
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn jinux_main() -> ! {
|
||||
jinux_frame::init();
|
||||
component::init_all(component::parse_metadata!()).unwrap();
|
||||
test_main();
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
jinux_frame::test_panic_handler(info)
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_framebuffer() {
|
||||
for _i in 0..30 {
|
||||
jinux_framebuffer::println!("test_println!");
|
||||
}
|
||||
}
|
26
tests/rtc.rs
26
tests/rtc.rs
@ -1,26 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(jinux_frame::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
extern crate alloc;
|
||||
use core::panic::PanicInfo;
|
||||
use jinux_frame::println;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn jinux_main() -> ! {
|
||||
jinux_frame::init();
|
||||
component::init_all(component::parse_metadata!()).unwrap();
|
||||
test_main();
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
jinux_frame::test_panic_handler(info)
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_rtc() {
|
||||
println!("real time:{:?}", jinux_time::get_real_time());
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(jinux_frame::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn jinux_main() -> ! {
|
||||
jinux_frame::init();
|
||||
test_main();
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
jinux_frame::test_panic_handler(info)
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_println() {
|
||||
jinux_frame::println!("test_println output");
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(jinux_frame::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
use jinux_frame::timer::Timer;
|
||||
extern crate alloc;
|
||||
use alloc::sync::Arc;
|
||||
use core::panic::PanicInfo;
|
||||
use core::time::Duration;
|
||||
use jinux_frame::println;
|
||||
|
||||
static mut TICK: usize = 0;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn jinux_main() -> ! {
|
||||
jinux_frame::init();
|
||||
test_main();
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
jinux_frame::test_panic_handler(info)
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_timer() {
|
||||
x86_64::instructions::interrupts::enable();
|
||||
unsafe {
|
||||
let timer = Timer::new(timer_callback).unwrap();
|
||||
timer.set(Duration::from_secs(1));
|
||||
while TICK < 5 {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn timer_callback(timer: Arc<Timer>) {
|
||||
unsafe {
|
||||
TICK += 1;
|
||||
println!("TICK:{}", TICK);
|
||||
timer.set(Duration::from_secs(1));
|
||||
}
|
||||
}
|
@ -5,11 +5,13 @@
|
||||
|
||||
# Update Cargo style versions (`version = "{version}"`) in file $1
|
||||
update_cargo_versions() {
|
||||
echo "Updating file $1"
|
||||
sed -i "s/^version = \"[[:digit:]]\+\.[[:digit:]]\+\.[[:digit:]]\+\"$/version = \"${new_version}\"/g" $1
|
||||
}
|
||||
|
||||
# Update Docker image versions (`jinuxdev/jinux:{version}`) in file $1
|
||||
update_image_versions() {
|
||||
echo "Updating file $1"
|
||||
sed -i "s/jinuxdev\/jinux:[[:digit:]]\+\.[[:digit:]]\+\.[[:digit:]]\+/jinuxdev\/jinux:${new_version}/g" $1
|
||||
}
|
||||
|
||||
@ -37,8 +39,10 @@ update_image_versions ${JINUX_SRC_DIR}/README.md
|
||||
update_image_versions ${SCRIPT_DIR}/docker/README.md
|
||||
|
||||
# Update Docker image versions in workflows
|
||||
update_image_versions ${JINUX_SRC_DIR}/.github/workflows/syscall_test.yml
|
||||
update_image_versions ${JINUX_SRC_DIR}/.github/workflows/cargo_check.yml
|
||||
WORKFLOWS=$(find "${JINUX_SRC_DIR}/.github/workflows/" -type f -name "*.yml")
|
||||
for workflow in $WORKFLOWS; do
|
||||
update_image_versions $workflow
|
||||
done
|
||||
|
||||
# Create or update VERSION
|
||||
echo "${new_version}" > ${VERSION_PATH}
|
||||
|
Reference in New Issue
Block a user