Enable usermode unit test for specific crates

This commit is contained in:
Zhang Junyang
2023-11-04 17:01:12 +08:00
committed by Tate, Hongliang Tian
parent b8818bb740
commit bb0560530f
13 changed files with 203 additions and 74 deletions

View File

@ -20,6 +20,8 @@ jobs:
id: ktest_unit_test id: ktest_unit_test
run: make run KTEST=all ENABLE_KVM=0 RELEASE_MODE=1 run: make run KTEST=all ENABLE_KVM=0 RELEASE_MODE=1
# TODO: include the unit tests for the crates that supports cargo test. - name: Usermode Unit test
id: usermode_unit_test
run: make test
# TODO: add component check. # TODO: add component check.

17
Cargo.lock generated
View File

@ -216,7 +216,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.38", "syn 2.0.29",
] ]
[[package]] [[package]]
@ -368,7 +368,7 @@ dependencies = [
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.38", "syn 2.0.29",
] ]
[[package]] [[package]]
@ -475,7 +475,7 @@ checksum = "ba330b70a5341d3bc730b8e205aaee97ddab5d9c448c4f51a7c2d924266fa8f9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.38", "syn 2.0.29",
] ]
[[package]] [[package]]
@ -567,7 +567,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.38", "syn 2.0.29",
] ]
[[package]] [[package]]
@ -839,7 +839,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rand", "rand",
"syn 2.0.38", "syn 2.0.29",
] ]
[[package]] [[package]]
@ -1290,9 +1290,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.38" version = "2.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1332,7 +1332,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.38", "syn 2.0.29",
] ]
[[package]] [[package]]
@ -1405,6 +1405,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 1.0.109",
"typeflags-util",
] ]
[[package]] [[package]]

View File

@ -24,22 +24,65 @@ members = [
"framework/jinux-frame", "framework/jinux-frame",
"framework/jinux-frame/src/arch/x86/boot/linux_boot/setup", "framework/jinux-frame/src/arch/x86/boot/linux_boot/setup",
"framework/libs/align_ext", "framework/libs/align_ext",
"services/comps/virtio", "framework/libs/ktest",
"services/comps/input", "framework/libs/tdx-guest",
"services/comps/block", "services/comps/block",
"services/comps/network",
"services/comps/framebuffer", "services/comps/framebuffer",
"services/comps/input",
"services/comps/network",
"services/comps/time", "services/comps/time",
"services/libs/jinux-std", "services/comps/virtio",
"services/libs/jinux-rights-proc",
"services/libs/typeflags",
"services/libs/typeflags-util",
"services/libs/jinux-util",
"services/libs/cpio-decoder", "services/libs/cpio-decoder",
"services/libs/int-to-c-enum", "services/libs/int-to-c-enum",
"services/libs/int-to-c-enum/derive",
"services/libs/jinux-rights",
"services/libs/jinux-rights-proc",
"services/libs/jinux-std",
"services/libs/jinux-util",
"services/libs/keyable-arc",
"services/libs/typeflags",
"services/libs/typeflags-util",
] ]
exclude = ["services/libs/comp-sys/controlled", "services/libs/comp-sys/cargo-component"] exclude = [
"services/libs/comp-sys/cargo-component",
"services/libs/comp-sys/component",
"services/libs/comp-sys/component-macro",
"services/libs/comp-sys/controlled",
]
[workspace.metadata]
usermode_testable = [
"runner",
"framework/libs/align_ext",
"framework/libs/ktest",
"services/libs/cpio-decoder",
"services/libs/int-to-c-enum",
"services/libs/int-to-c-enum/derive",
"services/libs/jinux-rights",
"services/libs/jinux-rights-proc",
"services/libs/keyable-arc",
"services/libs/typeflags",
"services/libs/typeflags-util",
]
ktest_testable = [
"framework/jinux-frame",
"framework/libs/tdx-guest",
"services/comps/block",
"services/comps/framebuffer",
"services/comps/input",
"services/comps/network",
"services/comps/time",
"services/comps/virtio",
"services/libs/jinux-std",
"services/libs/jinux-util",
]
untestable = [
"framework/jinux-frame/src/arch/x86/boot/linux_boot/setup",
]
[features] [features]
intel_tdx = ["jinux-frame/intel_tdx", "jinux-std/intel_tdx"] intel_tdx = ["jinux-frame/intel_tdx", "jinux-std/intel_tdx"]

View File

@ -58,7 +58,20 @@ make run
### Unit Test ### Unit Test
We can run unit tests if building succeeds. This is powered by our [ktest](framework/libs/ktest) framework. #### User mode unit test
Many of our crates does not require running on bare metal environment and can be tested through the standard Cargo testing framework. A specific list of which crates can be tested with `cargo test` is listed in the `[workspace.metadata.usermode_testable]` entry in the `Cargo.toml` file of the root workspace.
There is a tool `./tools/test/run_tests.py` to run all the user mode tests, and can be invoked through Make.
```bash
make test
```
Nevertheless, you could enter the directory of a specific crate and invoke `cargo test` to perform user mode unit tests and doctests.
#### Kernel mode unit test
We can run unit tests in kernel mode for crates like `jinux-frame` or `jinux-std`. This is powered by our [ktest](framework/libs/ktest) framework.
```bash ```bash
make run KTEST=all make run KTEST=all
``` ```
@ -68,6 +81,8 @@ You could also specify tests in a crate or a subset of tests to run, as long as
make run KTEST=jinux-frame,jinux-std make run KTEST=jinux-frame,jinux-std
``` ```
#### Component check
If we want to check access control policy among components, install some standalone tools (e.g., `cargo-component`). If we want to check access control policy among components, install some standalone tools (e.g., `cargo-component`).
``` bash ``` bash
make tools make tools

View File

@ -46,7 +46,7 @@ pub use self::error::Error;
pub use self::prelude::Result; pub use self::prelude::Result;
use alloc::vec::Vec; use alloc::vec::Vec;
use arch::irq::{IrqCallbackHandle, IrqLine}; use arch::irq::{IrqCallbackHandle, IrqLine};
use core::mem; use core::{mem, panic::PanicInfo};
#[cfg(feature = "intel_tdx")] #[cfg(feature = "intel_tdx")]
use tdx_guest::init_tdx; use tdx_guest::init_tdx;
use trapframe::TrapFrame; use trapframe::TrapFrame;
@ -110,7 +110,22 @@ pub(crate) const fn zero<T>() -> T {
unsafe { mem::MaybeUninit::zeroed().assume_init() } unsafe { mem::MaybeUninit::zeroed().assume_init() }
} }
pub fn panic_handler() { /// The panic handler provided by Jinux Frame.
///
/// The definition of the real panic handler is located at the kernel binary
/// crate with the `#[panic_handler]` attribute. This function provides a
/// default implementation of the panic handler, which can forwarded to by the
/// kernel binary crate.
///
/// ```rust
/// extern crate jinux_frame;
/// #[panic_handler]
/// fn panic(info: &PanicInfo) -> ! {
/// jinux_frame::panic_handler(info);
/// }
/// ```
pub fn panic_handler(info: &PanicInfo) -> ! {
println!("[panic]:{:#?}", info);
// let mut fp: usize; // let mut fp: usize;
// let stop = unsafe{ // let stop = unsafe{
// Task::current().kstack.get_top() // Task::current().kstack.get_top()
@ -130,6 +145,7 @@ pub fn panic_handler() {
// } // }
// println!("---END BACKTRACE---"); // println!("---END BACKTRACE---");
// } // }
exit_qemu(QemuExitCode::Failed);
} }
/// The exit code of x86 QEMU isa debug device. In `qemu-system-x86_64` the /// The exit code of x86 QEMU isa debug device. In `qemu-system-x86_64` the

View File

@ -1,4 +1,4 @@
#![no_std] #![cfg_attr(not(test), no_std)]
/// An extension trait for Rust integer types, including `u8`, `u16`, `u32`, /// An extension trait for Rust integer types, including `u8`, `u16`, `u32`,
/// `u64`, and `usize`, to provide methods to make integers aligned to a /// `u64`, and `usize`, to provide methods to make integers aligned to a
@ -17,10 +17,11 @@ pub trait AlignExt {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// assert!(align_up(12, 2), 12); /// use crate::align_ext::AlignExt;
/// assert!(align_up(12, 4), 12); /// assert_eq!(12usize.align_up(2), 12);
/// assert!(align_up(12, 8), 16); /// assert_eq!(12usize.align_up(4), 12);
/// assert!(align_up(12, 16), 16); /// assert_eq!(12usize.align_up(8), 16);
/// assert_eq!(12usize.align_up(16), 16);
/// ``` /// ```
fn align_up(self, power_of_two: Self) -> Self; fn align_up(self, power_of_two: Self) -> Self;
@ -34,10 +35,11 @@ pub trait AlignExt {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// assert!(align_down(12, 2), 12); /// use crate::align_ext::AlignExt;
/// assert!(align_down(12, 4), 12); /// assert_eq!(12usize.align_down(2), 12);
/// assert!(align_down(12, 8), 8); /// assert_eq!(12usize.align_down(4), 12);
/// assert!(align_down(12, 16), 0); /// assert_eq!(12usize.align_down(8), 8);
/// assert_eq!(12usize.align_down(16), 0);
/// ``` /// ```
fn align_down(self, power_of_two: Self) -> Self; fn align_down(self, power_of_two: Self) -> Self;
} }

View File

@ -1,7 +1,7 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
// The no_mangle macro need to remove the `forbid(unsafe_code)` macro. The bootloader needs the _start function // The `no_mangle`` attribute for the `jinux_main` entrypoint requires the removal of safety check.
// to be no mangle so that it can jump into the entry point. // Please be aware that the kernel is not allowed to introduce any other unsafe operations.
// #![forbid(unsafe_code)] // #![forbid(unsafe_code)]
extern crate jinux_frame; extern crate jinux_frame;
@ -19,9 +19,5 @@ pub fn jinux_main() -> ! {
#[panic_handler] #[panic_handler]
fn panic(info: &PanicInfo) -> ! { fn panic(info: &PanicInfo) -> ! {
use jinux_frame::{exit_qemu, QemuExitCode}; jinux_frame::panic_handler(info);
println!("[panic]:{:#?}", info);
jinux_frame::panic_handler();
exit_qemu(QemuExitCode::Failed);
} }

View File

@ -6,10 +6,13 @@ use lending_iterator::LendingIterator;
fn test_decoder() { fn test_decoder() {
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
let manifest_path = std::env::var("CARGO_MANIFEST_DIR").unwrap();
let manifest_path = std::path::Path::new(manifest_path.as_str());
// Prepare the cpio buffer // Prepare the cpio buffer
let buffer = { let buffer = {
let mut find_process = Command::new("find") let mut find_process = Command::new("find")
.arg(".") .arg(manifest_path.as_os_str())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
.expect("find command is not started"); .expect("find command is not started");
@ -26,38 +29,35 @@ fn test_decoder() {
}; };
let mut decoder = CpioDecoder::new(buffer.as_slice()); let mut decoder = CpioDecoder::new(buffer.as_slice());
// 1st entry // 1st entry must be the root entry
let entry = { let entry = {
let entry_result = decoder.next().unwrap(); let entry_result = decoder.next().unwrap();
entry_result.unwrap() entry_result.unwrap()
}; };
assert!(entry.name() == "."); assert_eq!(entry.name(), manifest_path.as_os_str());
assert!(entry.metadata().file_type() == FileType::Dir);
assert!(entry.metadata().ino() > 0);
// 2nd entry
let entry = {
let entry_result = decoder.next().unwrap();
entry_result.unwrap()
};
assert!(entry.name() == "src");
assert!(entry.metadata().file_type() == FileType::Dir); assert!(entry.metadata().file_type() == FileType::Dir);
assert!(entry.metadata().ino() > 0); assert!(entry.metadata().ino() > 0);
// 3rd entry // Other entries
let mut entry = { while let Some(decode_result) = decoder.next() {
let entry_result = decoder.next().unwrap(); let mut entry = decode_result.unwrap();
entry_result.unwrap()
};
assert!(
entry.name() == "src/lib.rs"
|| entry.name() == "src/test.rs"
|| entry.name() == "src/error.rs"
);
assert!(entry.metadata().file_type() == FileType::File);
assert!(entry.metadata().ino() > 0); assert!(entry.metadata().ino() > 0);
if entry.name() == manifest_path.join("src").as_os_str() {
assert!(entry.metadata().file_type() == FileType::Dir);
assert!(entry.metadata().ino() > 0);
} else if entry.name() == manifest_path.join("src").join("lib.rs").as_os_str()
|| entry.name() == manifest_path.join("src").join("test.rs").as_os_str()
|| entry.name() == manifest_path.join("src").join("error.rs").as_os_str()
|| entry.name() == manifest_path.join("Cargo.toml").as_os_str()
{
assert!(entry.metadata().file_type() == FileType::File);
assert!(entry.metadata().size() > 0); assert!(entry.metadata().size() > 0);
let mut buffer: Vec<u8> = Vec::new(); let mut buffer: Vec<u8> = Vec::new();
assert!(entry.read_all(&mut buffer).is_ok()); assert!(entry.read_all(&mut buffer).is_ok());
} else {
panic!("unexpected entry: {:?}", entry.name());
}
}
} }
#[test] #[test]

View File

@ -35,7 +35,7 @@
//! ``` //! ```
//! //!
#![no_std] #![cfg_attr(not(test), no_std)]
/// Error type for TryFromInt derive macro /// Error type for TryFromInt derive macro
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]

View File

@ -57,6 +57,8 @@ pub type WriteOp = TRights![Write];
/// Example: /// Example:
/// ///
/// ```rust /// ```rust
/// use jinux_rights::{Rights, TRights, TRightSet};
///
/// pub struct Vmo<R=Rights>(R); /// pub struct Vmo<R=Rights>(R);
/// ///
/// impl<R:TRights> Vmo<TRightSet<R>>{ /// impl<R:TRights> Vmo<TRightSet<R>>{

View File

@ -9,7 +9,8 @@ edition = "2021"
proc-macro = true proc-macro = true
[dependencies] [dependencies]
itertools = "0.10.5"
proc-macro2 = "1.0" proc-macro2 = "1.0"
quote = "1.0" quote = "1.0"
syn = {version = "1.0.90"} syn = { version = "1.0.90" }
itertools = "0.10.5" typeflags-util = { path = "../typeflags-util" }

View File

@ -6,25 +6,24 @@
//! typeflags is used to define another declarive macro to define type set. //! typeflags is used to define another declarive macro to define type set.
//! It can be used as the following example. //! It can be used as the following example.
//! ```rust //! ```rust
//! use typeflags::typeflags;
//! typeflags! { //! typeflags! {
//! pub trait RightSet: u32 { //! pub trait RightSet: u32 {
//! struct Read = 1 << 1; //! struct Read = 1 << 1;
//! struct Write = 1 << 2; //! struct Write = 1 << 2;
//! } //! }
//! } //! }
//! ``` //!
//! The code will generate a macro with the name as RightSet, we can use this macro to define typesets with different types. //! // The above code will generate a macro with the name as RightSet, we can use this macro to define typesets with different types.
//! Usage example: //! // Usage example:
//! ```rust
//! type O = RightSet![]; // Nil //! type O = RightSet![]; // Nil
//! type R = RightSet![Read]; // Cons<Read, Nil> //! type R = RightSet![Read]; // Cons<Read, Nil>
//! type W = RightSet![Write]; // Cons<Write, Nil> //! type W = RightSet![Write]; // Cons<Write, Nil>
//! type RW = RightSet![Read, Write]; // Cons<Write, Cons<Read, Nil>> //! type RW = RightSet![Read, Write]; // Cons<Write, Cons<Read, Nil>>
//! type WR = RightSet![Write, Read]; // Cons<Write, Cons<Read, Nil>> //! type WR = RightSet![Write, Read]; // Cons<Write, Cons<Read, Nil>>
//! ```
//! //!
//! Test Example //! // Test Example
//! ```rust //! extern crate typeflags_util;
//! use typeflags_util::*; //! use typeflags_util::*;
//! assert_eq!(O::BITS, 0); //! assert_eq!(O::BITS, 0);
//! assert_eq!(R::BITS, 2); //! assert_eq!(R::BITS, 2);

52
tools/test/run_tests.py Normal file
View File

@ -0,0 +1,52 @@
#!/usr/bin/python3
# Use cargo metadata to get the manifest in json format.
def get_manifest():
import json
import subprocess
manifest = subprocess.check_output(
["cargo", "metadata", "--no-deps", "--format-version", "1"]
)
return json.loads(manifest)
# Run the user mode tests for the crates and exit if any test fails.
def run_usermode_tests(crates):
import os
import subprocess
for crate in crates:
print("Running tests for", crate)
result = subprocess.check_call(["cargo", "test", "--manifest-path", crate + "/Cargo.toml"])
if result != 0:
print("Test failed for", crate)
os.exit(result)
# The member id returned by the cargo metadata command is
# `<package name> <package name> (path+file:///<absolute path to member>)`.
# We need a relative path as we specify them in `Cargo.toml`.
def member_id_to_crate_rel_path(member_id):
import os
annotation = member_id.split(" ")[2]
abs_path = annotation \
.replace("(", "") \
.replace(")", "") \
.replace("path+file://", "")
return os.path.relpath(abs_path, os.getcwd())
def main():
import os
manifest = get_manifest()
usermode_testables = manifest["metadata"]["usermode_testable"]
ktest_testables = manifest["metadata"]["ktest_testable"]
untestables = manifest["metadata"]["untestable"]
# A sanity check to make sure we have registered all crates.
all_members = sorted([member_id_to_crate_rel_path(p["id"]) for p in manifest["packages"]])
test_members = sorted(usermode_testables + ktest_testables + untestables + ["."])
if (all_members != test_members):
print("Test members does not match all the workspace members in Cargo.toml. "
"Please setup the testablity of all the crates in Cargo.toml correctly.")
os._exit(1)
run_usermode_tests(usermode_testables)
if __name__ == "__main__":
main()