Skip to content

Commit 099ebc9

Browse files
authored
Page 04: Testing (Partial Impl) (#7)
* Impl rough testing framework * Partial implementation of page 04
1 parent d9c491c commit 099ebc9

File tree

11 files changed

+193
-12
lines changed

11 files changed

+193
-12
lines changed

Cargo.lock

Lines changed: 43 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,13 @@ publish = false
88
[workspace]
99
members = [ "kernel" ]
1010

11-
[profile.release]
12-
panic = "abort"
13-
14-
[profile.dev]
15-
panic = "abort"
16-
17-
1811
[build-dependencies]
1912
rust-os-kernel = { path = "kernel", artifact = "bin", target = "x86_64-unknown-none" }
2013
bootloader = "0.11.3"
2114

2215
[dependencies]
2316
clap = { version = "4.2.4", features = ["derive"] }
2417
ovmf-prebuilt = "0.1.0-alpha.1"
18+
19+
[features]
20+
testing = [ ]

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,16 @@ $ cargo run --release -- uefi
6262

6363
$ cargo run --release -- bios
6464
> ... # QEMU Window should pop up launching our kernel's BIOS image
65+
```
66+
67+
## Testing
68+
69+
Due to the way this currently setup, testing is a bit funky. You can run the following commands to launch the kernel in test mode:
70+
71+
```bash
72+
# This can be set to any value, as long as its present the kernel will be compiled in test mode.
73+
$ export KERNEL_TEST_MODE="true"
74+
75+
$ cargo run --release -F testing -- <uefi||bios>
76+
> ...
6577
```

kernel/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ publish = false
77

88
[dependencies]
99
lazy_static = { version = "1.4.0", features = [ "spin_no_std" ] }
10+
uart_16550 = { version = "0.2.18", features = ["nightly"] }
1011
noto-sans-mono-bitmap = "0.2.0"
1112
bootloader_api = "0.11.3"
1213
fixed-vectors = "3.2.1"
14+
x86_64 = "0.14.10"
1315
spin = "0.9.8"
14-
libm = "0.2.6"
16+
libm = "0.2.6"

kernel/build.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() {
2+
if std::env::var_os("KERNEL_TEST_MODE").is_some() {
3+
println!("cargo:rustc-cfg=test");
4+
}
5+
}

kernel/src/macros/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use core::ops::DerefMut;
33

44
use crate::FRAME_BUFFER_WRITER;
55

6+
pub mod serial;
7+
68

79
#[macro_export]
810
macro_rules! print {

kernel/src/macros/serial.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use core::fmt::{self, Write};
2+
3+
use crate::SERIAL_ONE;
4+
5+
6+
#[macro_export]
7+
macro_rules! serial_print {
8+
($($arg: tt)*) => {
9+
$crate::macros::serial::_print(
10+
format_args!( $($arg)* )
11+
);
12+
};
13+
}
14+
15+
16+
#[macro_export]
17+
macro_rules! serial_println {
18+
() => {
19+
$crate::serial_print!("\n");
20+
};
21+
22+
($($arg: tt)*) => {
23+
$crate::serial_print!("{}\n", format_args!($($arg)*));
24+
}
25+
}
26+
27+
28+
#[doc(hidden)]
29+
pub fn _print(args: fmt::Arguments) {
30+
let mut lock = SERIAL_ONE.lock();
31+
32+
lock.write_fmt(args)
33+
.expect("In `_print()`, the `write_fmt()` function is expected not to fail");
34+
}

kernel/src/main.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,64 @@
22
#![no_main]
33
#![no_std]
44

5+
#![test_runner(tests::_empty_test_runner)]
6+
#![feature(custom_test_frameworks)]
7+
58
use bootloader_api::info::Optional;
69
use bootloader_api::BootInfo;
710

811
use lazy_static::lazy_static;
912
use fixed_vectors::Vector2;
13+
use uart_16550::SerialPort;
1014
use spin::Mutex;
1115

1216
use core::ops::DerefMut;
13-
#[cfg(not(test))]
14-
use core::panic::PanicInfo;
1517

1618
pub mod raytracer;
1719
pub mod writer;
1820
pub mod macros;
21+
pub mod qemu;
22+
23+
#[cfg(test)]
24+
mod tests;
1925

2026

2127
lazy_static!{
2228
pub static ref FRAME_BUFFER_WRITER: Mutex<Option<writer::FrameBufferWriter<'static>>> = Mutex::new(
2329
// The `FRAME_BUFFER_WRITER` needs to be initialized in `main` with the `info.framebuffer`
2430
None
2531
);
32+
33+
pub static ref SERIAL_ONE: Mutex<SerialPort> = {
34+
let mut serial_port = unsafe { SerialPort::new(0x3F8) };
35+
serial_port.init();
36+
37+
Mutex::new(serial_port)
38+
};
2639
}
2740

2841

2942
#[panic_handler]
3043
#[cfg(not(test))]
31-
fn panic(info: &PanicInfo) -> ! {
44+
fn panic(info: &core::panic::PanicInfo) -> ! {
3245
println!("PANIC: {}", info);
3346

3447
loop { }
3548
}
3649

3750

51+
#[panic_handler]
52+
#[cfg(test)]
53+
fn panic(info: &core::panic::PanicInfo) -> ! {
54+
serial_println!("[failed]\n");
55+
56+
serial_println!("Error: {}\n", info);
57+
qemu::exit_qemu(qemu::QemuExitCode::Failed);
58+
59+
loop { }
60+
}
61+
62+
3863
fn main(info: &'static mut BootInfo) -> ! {
3964
// Initialize `FRAME_BUFFER_WRITER` with `info.framebuffer`
4065
if let Optional::Some(buffer) = &mut info.framebuffer {
@@ -49,6 +74,10 @@ fn main(info: &'static mut BootInfo) -> ! {
4974
*lock = Some(writer);
5075
}
5176

77+
#[cfg(test)] {
78+
tests::_testing_main(); loop { }
79+
}
80+
5281
let mut lock = FRAME_BUFFER_WRITER.lock();
5382
let Some(writer) = lock.deref_mut() else { unreachable!() };
5483

kernel/src/qemu.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use x86_64::instructions::port::Port;
2+
3+
4+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5+
#[repr(u32)]
6+
pub enum QemuExitCode {
7+
Success = 0x10,
8+
Failed = 0x11,
9+
}
10+
11+
pub fn exit_qemu(exit_code: QemuExitCode) {
12+
13+
unsafe {
14+
let mut port = Port::new(0xf4);
15+
port.write(exit_code as u32);
16+
}
17+
}

kernel/src/tests/mod.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
use lazy_static::lazy_static;
3+
use spin::Lazy;
4+
5+
use crate::qemu::{QemuExitCode, exit_qemu};
6+
use crate::{serial_println, serial_print};
7+
8+
9+
lazy_static!{
10+
pub static ref TESTS: Lazy<&'static [(&'static str, fn())]> = Lazy::new(|| &[
11+
("Basic Assertion", || assert_eq!(2 + 2, 4)),
12+
]);
13+
}
14+
15+
16+
#[doc(hidden)]
17+
pub fn _empty_test_runner() { unimplemented!() }
18+
19+
20+
#[doc(hidden)]
21+
pub fn _testing_main() {
22+
for (name, func) in TESTS.iter() {
23+
serial_print!("Running `{}`... ", name);
24+
func();
25+
serial_println!("[ok]");
26+
}
27+
28+
exit_qemu(QemuExitCode::Success);
29+
}

src/main.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#![feature(custom_test_frameworks)]
2+
#![test_runner(test_runner)]
3+
14
use std::io::Error;
25
use std::process;
36

@@ -39,8 +42,17 @@ fn main() -> Result<(), Error> {
3942
},
4043
}
4144

45+
#[cfg(feature = "testing")] {
46+
command.arg("-device")
47+
.arg("isa-debug-exit,iobase=0xf4,iosize=0x04");
48+
command.arg("-serial")
49+
.arg("stdio");
50+
command.arg("-display")
51+
.arg("none");
52+
}
53+
4254
let mut child_process = command.spawn()?;
4355
child_process.wait()?;
4456

45-
Ok(())
57+
Ok(())
4658
}

0 commit comments

Comments
 (0)