From e009cd22a887773a7231d3c81220f25ccd22c0ae Mon Sep 17 00:00:00 2001 From: clizia Date: Sun, 31 Aug 2025 13:07:59 +0200 Subject: [PATCH 1/2] added serial ports communication + Testable trait --- Cargo.toml | 11 ++++++++++- src/main.rs | 38 ++++++++++++++++++++++++++++++++------ src/serial.rs | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 src/serial.rs diff --git a/Cargo.toml b/Cargo.toml index d9de3f6..408fd74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ bootloader = "0.9" volatile = "0.2.6" spin = "0.5.2" x86_64 = "0.14.2" +uart_16550 = "0.2.0" [dependencies.lazy_static] version = "1.0" @@ -26,5 +27,13 @@ features = ["spin_no_std"] # this enables exiting qemu from the guest system [package.metadata.bootimage] -test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04"] +test-args = [ + "-device", + "isa-debug-exit,iobase=0xf4,iosize=0x04", + "-serial", + "stdio", + "-display", + "none", +] test-success-exit-code = 33 # (0x10 << 1) | 1 = 33 +test-timeout = "300" # in seconds diff --git a/src/main.rs b/src/main.rs index cc96953..3a03d35 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,22 @@ use core::panic::PanicInfo; mod vga_buffer; +mod serial; + +pub trait Testable { + fn run(&self) -> (); +} + +impl Testable for T +where + T: Fn(), +{ + fn run(&self) -> () { + serial_print!("{}...\t", core::any::type_name::()); + self(); + serial_println!("[ok]"); + } +} static HELLO: &[u8] = b"Hello toto :3"; @@ -38,7 +54,8 @@ pub extern "C" fn _start() -> ! { loop {} } -// called on panic +// panic handler +#[cfg(not(test))] #[panic_handler] fn panic(info: &PanicInfo) -> ! { println!("{}", info); @@ -46,12 +63,23 @@ fn panic(info: &PanicInfo) -> ! { loop {} } +// panic handler in test mode #[cfg(test)] -pub fn test_runner(tests: &[&dyn Fn()]) { - println!("Running {} tests", tests.len()); +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + serial_println!("[failed]\n"); + serial_println!("Error: {}\n", info); + exit_qemu(QemuExitCode::Failed); + + loop {} +} + +#[cfg(test)] +pub fn test_runner(tests: &[&dyn Testable]) { + serial_println!("Running {} tests", tests.len()); for test in tests { - test(); + test.run(); } exit_qemu(QemuExitCode::Success); @@ -59,7 +87,5 @@ pub fn test_runner(tests: &[&dyn Fn()]) { #[test_case] fn trivial_assertion() { - print!("asserzione triviale... "); assert_eq!(1, 1); - println!("[ok]"); } diff --git a/src/serial.rs b/src/serial.rs new file mode 100644 index 0000000..66e6ef3 --- /dev/null +++ b/src/serial.rs @@ -0,0 +1,35 @@ +use uart_16550::SerialPort; +use spin::Mutex; +use lazy_static::lazy_static; + +lazy_static! { + pub static ref SERIAL1: Mutex = { + let mut serial_port = unsafe { SerialPort::new(0x3F8) }; + serial_port.init(); + Mutex::new(serial_port) + }; +} + +#[doc(hidden)] +pub fn _print(args: ::core::fmt::Arguments) { + use core::fmt::Write; + SERIAL1.lock().write_fmt(args).expect("Printing to serial failed :("); +} + +// prints to the host through the serial interface +#[macro_export] +macro_rules! serial_print { + ($($arg:tt)*) => { + $crate::serial::_print(format_args!($($arg)*)); + }; +} + +// prints to the host through the serial interface +// appending a newline +#[macro_export] +macro_rules! serial_println { + () => ($crate::serial_print!("\n")); + ($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => ($crate::serial_print!( + concat!($fmt, "\n"), $($arg)*)); +} From 386522208e62769bfc22ce1038b49d6920d26010 Mon Sep 17 00:00:00 2001 From: clizia Date: Sun, 31 Aug 2025 20:22:13 +0200 Subject: [PATCH 2/2] unit testing for vga --- src/vga_buffer.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index b99723d..3d7dcff 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -155,3 +155,28 @@ impl Writer { } } +#[test_case] +fn test_println_simple() { + println!("test_println_simple output"); +} + +#[test_case] +fn test_println_many() { + for _ in 0..200 { + println!("test_println_many output"); + } +} + +#[test_case] +fn test_println_output() { + let s = "loss and gain is the same"; + println!("{}", s); + + for (i, c) in s.chars().enumerate() { + let screen_char = WRITER.lock() + .buffer + .chars[BUFFER_HEIGHT - 2][i] + .read(); + assert_eq!(char::from(screen_char.ascii_carachter), c); + } +}