Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance test #17

Merged
merged 2 commits into from
Jul 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <dev@phil-opp.com>"]
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 <yushiomote@gmail.com>"]
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
);
}
}
Loading