Add ktest framework

This commit is contained in:
Zhang Junyang
2023-11-04 16:41:30 +08:00
committed by Tate, Hongliang Tian
parent d7cc52c615
commit b8818bb740
28 changed files with 373 additions and 297 deletions

View File

@ -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"

View File

@ -5,7 +5,6 @@ on:
push:
branches:
- main
- releases/*
jobs:
test:

25
.github/workflows/unit_test.yml vendored Normal file
View 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
View File

@ -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]]

View File

@ -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 :=
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

View File

@ -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

View File

@ -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"] }

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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!();
}
}

View File

@ -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);
}
}

View File

@ -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));

View 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"] }

View 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)
}

View File

@ -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);
}

View File

@ -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 {

View File

@ -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

View 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;

View File

@ -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();

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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!");
}
}

View File

@ -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());
}

View File

@ -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");
}

View File

@ -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));
}
}

View File

@ -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}