Skip to content

Commit

Permalink
Merge pull request #17 from YushiOMOTE/enhance-test
Browse files Browse the repository at this point in the history
Enhance test
  • Loading branch information
YushiOMOTE authored Jul 13, 2024
2 parents 7f99d4c + 0879bdf commit 2fcbe14
Show file tree
Hide file tree
Showing 12 changed files with 270 additions and 87 deletions.
3 changes: 0 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ jobs:
rustup target add x86_64-unknown-none
rustup component add rust-src
rustup component add llvm-tools-preview
- name: Install bootimage
run: |
cargo install bootimage
- run: cargo test --verbose

fmt:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
**/*.rs.bk
Cargo.lock
/testing/target
10 changes: 4 additions & 6 deletions testing/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
[build]
target = "x86_64-unknown-none"
rustflags = ["-Crelocation-model=static"]

[target.'cfg(target_os = "none")']
runner = "bootimage runner"
[unstable]
# enable the unstable artifact-dependencies feature, see
# https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#artifact-dependencies
bindeps = true
28 changes: 8 additions & 20 deletions testing/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,26 +1,14 @@
[package]
name = "testing"
version = "0.1.0"
authors = ["Philipp Oppermann <[email protected]>"]
edition = "2018"
edition = "2021"

[[test]]
name = "basic"
harness = false
[workspace]
members = ["kernel"]

[dependencies]
bootloader = "0.9.10"
x86_64 = "0.15.1"
com_logger = { path = ".." }
log = "0.4.22"

[dependencies.lazy_static]
version = "1.3.0"
features = ["spin_no_std"]

[package.metadata.bootimage]
test-args = [
"-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "stdio",
"-display", "none"
]
test-success-exit-code = 33
ovmf-prebuilt = "0.1.0-alpha.1"
bootloader = "0.11.7"
kernel = { path = "kernel", artifact = "bin", target = "x86_64-unknown-none" }
wait-timeout = "0.2.0"
serial_test = "3.1.1"
24 changes: 24 additions & 0 deletions testing/kernel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "kernel"
version = "0.1.0"
authors = ["Yushi OMOTE <[email protected]>"]
edition = "2021"

[dependencies]
bootloader_api = "0.11.7"
com_logger = { path = "../../" }
log = "0.4.22"
x86_64 = "0.15.1"

[[bin]]
name = "basic"
path = "src/basic.rs"

[[bin]]
name = "format"
path = "src/format.rs"

[[bin]]
name = "multi"
path = "src/multi.rs"

20 changes: 20 additions & 0 deletions testing/kernel/src/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#![no_std]
#![no_main]

use bootloader_api::{entry_point, BootInfo};
use log::info;

entry_point!(kernel_main);

fn kernel_main(_info: &'static mut BootInfo) -> ! {
com_logger::init();

info!("Hello world!");

kernel::success()
}

#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
kernel::panic(info)
}
22 changes: 22 additions & 0 deletions testing/kernel/src/format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#![no_std]
#![no_main]

use bootloader_api::{entry_point, BootInfo};
use log::info;

entry_point!(kernel_main);

fn kernel_main(_info: &'static mut BootInfo) -> ! {
com_logger::builder()
.formatter(|buf, record| writeln!(buf, "**** {} ****", record.args()))
.setup();

info!("Hello world!");

kernel::success()
}

#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
kernel::panic(info)
}
32 changes: 32 additions & 0 deletions testing/kernel/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#![no_std]
#![no_main]

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
enum QemuExitCode {
Success = 0x10,
Failed = 0x11,
}

fn exit_qemu(exit_code: QemuExitCode) {
use x86_64::instructions::port::Port;

unsafe {
let mut port = Port::new(0xf4);
port.write(exit_code as u32);
}
}

pub fn success() -> ! {
exit_qemu(QemuExitCode::Success);
loop {}
}

pub fn failed() {
exit_qemu(QemuExitCode::Failed);
}

pub fn panic(_info: &core::panic::PanicInfo) -> ! {
exit_qemu(QemuExitCode::Failed);
loop {}
}
24 changes: 24 additions & 0 deletions testing/kernel/src/multi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![no_std]
#![no_main]

use bootloader_api::{entry_point, BootInfo};
use log::info;

entry_point!(kernel_main);

fn kernel_main(_info: &'static mut BootInfo) -> ! {
com_logger::builder()
.formatter(|buf, record| writeln!(buf, "{}", record.args()))
.setup();

for i in 0..100 {
info!("Hello world! {}", i);
}

kernel::success()
}

#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
kernel::panic(info)
}
109 changes: 67 additions & 42 deletions testing/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,54 +1,79 @@
#![feature(custom_test_frameworks)]
#![test_runner(crate::test_runner)]
#![reexport_test_harness_main = "test_main"]
#![no_std]
#![no_main]

use core::panic::PanicInfo;
use log::error;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum QemuExitCode {
Success = 0x10,
Failed = 0x11,
use std::{io::Read, path::PathBuf, process::Stdio, time::Duration};
use wait_timeout::ChildExt;

#[macro_export]
macro_rules! test_kernel {
($test_name:expr, $mode:expr) => {
$crate::test_kernel_internal(
env!(concat!("CARGO_BIN_FILE_KERNEL_", $test_name)),
env!("CARGO_TARGET_TMPDIR"),
$mode,
)
};
}

pub fn exit() {
exit_qemu(QemuExitCode::Success)
pub enum Mode {
Uefi,
Bios,
}

pub fn exit_qemu(exit_code: QemuExitCode) {
use x86_64::instructions::port::PortWriteOnly;
pub fn test_kernel_internal(kernel_path: &str, tmp_dir: &str, mode: Mode) -> Vec<String> {
let out_dir = PathBuf::from(tmp_dir);

println!("Found test kernels: {}", kernel_path);

let kernel = PathBuf::from(kernel_path);

unsafe {
let mut port = PortWriteOnly::new(0xf4);
port.write(exit_code as u32);
let mut cmd = std::process::Command::new("qemu-system-x86_64");
match mode {
Mode::Uefi => {
// create an UEFI disk image (optional)
let uefi_path = out_dir.join("uefi.img");
bootloader::UefiBoot::new(&kernel)
.create_disk_image(&uefi_path)
.unwrap();

cmd.arg("-bios").arg(ovmf_prebuilt::ovmf_pure_efi());
cmd.arg("-drive")
.arg(format!("format=raw,file={}", uefi_path.display()));
}
Mode::Bios => {
// create a BIOS disk image
let bios_path = out_dir.join("bios.img");
bootloader::BiosBoot::new(&kernel)
.create_disk_image(&bios_path)
.unwrap();

cmd.arg("-drive")
.arg(format!("format=raw,file={}", bios_path.display()));
}
}
}
cmd.arg("-device")
.arg("isa-debug-exit,iobase=0xf4,iosize=0x04");
cmd.arg("-serial").arg("stdio");
cmd.arg("-display").arg("none");
cmd.stdout(Stdio::piped());
let mut child = cmd.spawn().unwrap();

pub fn test_runner(tests: &[&dyn Fn()]) {
for test in tests {
test();
match child.wait_timeout(Duration::from_secs(30)).unwrap() {
Some(status) => assert_eq!(status.code(), Some(33)),
None => panic!("Test timed out"),
}
exit_qemu(QemuExitCode::Success);
}

pub fn test_panic_handler(info: &PanicInfo) -> ! {
error!("{:#?}", info);
exit_qemu(QemuExitCode::Failed);
loop {}
}
println!("Finish");

#[cfg(test)]
#[no_mangle]
pub extern "C" fn _start() -> ! {
test_main();
loop {}
}
let stdout = child.stdout.as_mut().unwrap();
let mut output = String::default();

stdout.read_to_string(&mut output).unwrap();

println!("Finish: {}", output);

#[cfg(test)]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
test_panic_handler(info)
let mut vec: Vec<_> = output
.trim_end_matches('\n')
.split('\n')
.map(|s| s.into())
.collect();
vec.reverse();
vec
}
16 changes: 0 additions & 16 deletions testing/tests/basic.rs

This file was deleted.

68 changes: 68 additions & 0 deletions testing/tests/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use serial_test::serial;
use testing::{test_kernel, Mode};

const BASIC_EXPECTED_PREFIX: &'static str = " INFO: Hello world! (basic";

#[test]
#[serial]
fn basic_bios() {
let output = test_kernel!("basic", Mode::Bios);

assert!(output[0].starts_with(BASIC_EXPECTED_PREFIX), "{:?}", output);
}

#[test]
#[serial]
fn basic_uefi() {
let output = test_kernel!("basic", Mode::Uefi);

assert!(output[0].starts_with(BASIC_EXPECTED_PREFIX), "{:?}", output);
}

const FORMAT_EXPECTED: &'static str = "**** Hello world! ****";

#[test]
#[serial]
fn custom_format_uefi() {
let output = test_kernel!("format", Mode::Uefi);

assert_eq!(output[0], FORMAT_EXPECTED, "{:?}", output);
}

#[test]
#[serial]
fn custom_format_bios() {
let output = test_kernel!("format", Mode::Bios);

assert_eq!(output[0], FORMAT_EXPECTED, "{:?}", output);
}

#[test]
#[serial]
fn multi_line_uefi() {
let output = test_kernel!("multi", Mode::Uefi);

for i in 0..100 {
assert_eq!(
output[i],
format!("Hello world! {}", 99 - i),
"{:?}",
output
);
}
}

#[test]
#[serial]
fn multi_line_bios() {
let output = test_kernel!("multi", Mode::Bios);

for i in 0..100 {
assert_eq!(
output[i],
format!("Hello world! {}", 99 - i),
"{:?}",
output
);
}
}

0 comments on commit 2fcbe14

Please sign in to comment.