Add testing framework
Based on os.phil-opp.com ideas it includes running test framework in qemu semihosting mode so that tests can indicate pass or fail to calling process. GitHub Actions are configured to run these tests and validate acceptance. Add test-runner target to perform tests in qemu. Add -nographic for qemu running tests. Drop serial for qemu tests as well.
This commit is contained in:
parent
33722e895f
commit
af1cc83530
|
@ -8,3 +8,4 @@ rustflags = [
|
|||
"-C", "target-cpu=cortex-a53",
|
||||
"-C", "embed-bitcode=yes",
|
||||
]
|
||||
runner = "cargo make test-runner"
|
||||
|
|
|
@ -46,7 +46,18 @@ jobs:
|
|||
run: rustup component add rust-src llvm-tools-preview
|
||||
|
||||
- name: "Install build tools"
|
||||
run: cargo install cargo-make
|
||||
run: cargo install cargo-make cargo-binutils
|
||||
|
||||
- name: "Validate rust-lld"
|
||||
run: |
|
||||
which rust-lld || echo "Not found"
|
||||
otool -L ~/.cargo/bin/rust-lld
|
||||
if: runner.os == 'macOS'
|
||||
|
||||
- name: "Print Tools Version"
|
||||
run: |
|
||||
cargo make --version
|
||||
cargo objcopy --version
|
||||
|
||||
- name: "Deny Warnings"
|
||||
run: cargo make build
|
||||
|
@ -55,6 +66,8 @@ jobs:
|
|||
|
||||
- name: Install QEMU (Linux)
|
||||
run: |
|
||||
sudo apt install software-properties-common
|
||||
sudo add-apt-repository ppa:jacob/virtualisation
|
||||
sudo apt update
|
||||
sudo apt install qemu-system-aarch64
|
||||
if: runner.os == 'Linux'
|
||||
|
@ -85,7 +98,8 @@ jobs:
|
|||
- name: 'Build kernel'
|
||||
run: cargo make build
|
||||
|
||||
# TODO: add tests runner
|
||||
- name: 'Run tests'
|
||||
run: cargo make test
|
||||
|
||||
check_formatting:
|
||||
name: "Check Formatting"
|
||||
|
|
|
@ -29,7 +29,8 @@ QEMU_CONTAINER_CMD = "qemu-system-aarch64"
|
|||
#
|
||||
# -d in_asm,unimp,int
|
||||
QEMU_OPTS = "-M raspi3 -d int -semihosting"
|
||||
QEMU_SERIAL = "-serial null -serial stdio"
|
||||
QEMU_SERIAL_OPTS = "-serial null -serial stdio"
|
||||
QEMU_TESTS_OPTS = "-nographic"
|
||||
QEMU = "qemu-system-aarch64"
|
||||
|
||||
# For gdb connection:
|
||||
|
|
|
@ -47,6 +47,12 @@ To build kernel for Raspberry Pi and copy it to SDCard mounted at `/Volumes/BOOT
|
|||
just device
|
||||
```
|
||||
|
||||
To run tests (tests require QEMU):
|
||||
|
||||
```
|
||||
just test
|
||||
```
|
||||
|
||||
On the device boot SD card you'll need a configuration file instructing RasPi to launch in 64-bit mode.
|
||||
|
||||
```
|
||||
|
|
|
@ -9,10 +9,26 @@ script = [
|
|||
"${OBJCOPY} ${OBJCOPY_PARAMS} ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/${DEFAULT_TARGET}/release/vesper ${KERNEL_BIN}"
|
||||
]
|
||||
|
||||
[tasks.custom-binary]
|
||||
script = [
|
||||
"cp ${@} ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/`basename ${@}`.elf",
|
||||
"${OBJCOPY} ${OBJCOPY_PARAMS} ${@} ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/`basename ${@}`.bin"
|
||||
]
|
||||
|
||||
[tasks.build]
|
||||
env = { "TARGET_FEATURES" = "" }
|
||||
args = ["build", "${BUILD_STD}", "--target=${TARGET_JSON}", "--release", "--features=${TARGET_FEATURES}"]
|
||||
|
||||
[tasks.test]
|
||||
env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" }
|
||||
args = ["test", "${BUILD_STD}", "--target=${TARGET_JSON}", "--features=${TARGET_FEATURES}"]
|
||||
|
||||
[tasks.test-runner]
|
||||
dependencies = ["custom-binary"]
|
||||
script = [
|
||||
"${QEMU} ${QEMU_OPTS} ${QEMU_TESTS_OPTS} -dtb ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/targets/bcm2710-rpi-3-b-plus.dtb -kernel ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/`basename ${@}`.bin"
|
||||
]
|
||||
|
||||
[tasks.build-qemu]
|
||||
env = { "TARGET_FEATURES" = "${QEMU_FEATURES}" }
|
||||
command = "cargo"
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
*/
|
||||
|
||||
// https://doc.rust-lang.org/src/std/macros.rs.html
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ($crate::macros::_print(format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
// https://doc.rust-lang.org/src/std/macros.rs.html
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => (print!("\n"));
|
||||
($($arg:tt)*) => ({
|
||||
$crate::macros::_print(format_args_nl!($($arg)*));
|
||||
})
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(any(test, qemu)))]
|
||||
pub fn _print(_args: core::fmt::Arguments) {
|
||||
// @todo real system implementation
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(any(test, qemu))] // qemu feature not enabled here?? we pass --features=qemu to cargo test
|
||||
pub fn _print(args: core::fmt::Arguments) {
|
||||
use crate::{qemu, write_to};
|
||||
let mut buf = [0u8; 512];
|
||||
qemu::semihosting_sys_write0_call(write_to::c_show(&mut buf, args).unwrap());
|
||||
}
|
|
@ -3,13 +3,26 @@
|
|||
*/
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(asm)]
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(crate::tests::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
use architecture_not_supported_sorry;
|
||||
|
||||
extern crate rlibc; // To enable linking memory intrinsics.
|
||||
|
||||
#[macro_use]
|
||||
pub mod arch;
|
||||
pub use arch::*;
|
||||
mod macros;
|
||||
#[cfg(feature = "qemu")]
|
||||
mod qemu;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod write_to;
|
||||
|
||||
entry!(kmain);
|
||||
|
||||
|
@ -17,6 +30,9 @@ entry!(kmain);
|
|||
// arch crate is responsible for calling this
|
||||
#[inline]
|
||||
pub fn kmain() -> ! {
|
||||
#[cfg(test)]
|
||||
test_main();
|
||||
|
||||
endless_sleep()
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#[cfg(test)]
|
||||
pub fn semihosting_sys_write0_call(text: &str) {
|
||||
// SAFETY: text must be \0-terminated!
|
||||
unsafe {
|
||||
asm!(
|
||||
"mov w0, #0x04
|
||||
hlt #0xF000"
|
||||
, in("x1") text.as_ptr() as u64
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
//============================================================================
|
||||
// Testing environment
|
||||
//============================================================================
|
||||
use crate::println;
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test_runner(tests: &[&dyn Fn()]) {
|
||||
println!("Running {} tests", tests.len());
|
||||
for test in tests {
|
||||
test();
|
||||
}
|
||||
println!("[success]");
|
||||
qemu_exit::aarch64::exit_success();
|
||||
}
|
Loading…
Reference in New Issue