unit test support
This commit is contained in:
parent
221e9bd8f6
commit
cc2eabd500
2 changed files with 116 additions and 3 deletions
20
src/main.rs
20
src/main.rs
|
@ -1,12 +1,17 @@
|
||||||
// from https://github.com/rust-console/gba/blob/main/examples/hello.rs
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
#![cfg_attr(test, feature(custom_test_frameworks))]
|
||||||
|
#![cfg_attr(test, test_runner(test_harness::test_runner))]
|
||||||
|
#![cfg_attr(test, reexport_test_harness_main = "test_main")]
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_harness;
|
||||||
|
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
use gba::prelude::*;
|
use gba::prelude::*;
|
||||||
|
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
#[unsafe(no_mangle)]
|
#[cfg_attr(not(test), unsafe(no_mangle))]
|
||||||
#[instruction_set(arm::t32)]
|
#[instruction_set(arm::t32)]
|
||||||
extern "C" fn main() -> ! {
|
extern "C" fn main() -> ! {
|
||||||
BG_PALETTE.index(0).write(Color(0x7F2B)); // blue
|
BG_PALETTE.index(0).write(Color(0x7F2B)); // blue
|
||||||
|
@ -33,7 +38,8 @@ extern "C" fn main() -> ! {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[panic_handler]
|
#[cfg_attr(not(test), panic_handler)]
|
||||||
|
#[cfg_attr(test, allow(unused))]
|
||||||
fn panic_handler(panic_info: &PanicInfo) -> ! {
|
fn panic_handler(panic_info: &PanicInfo) -> ! {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
if let Ok(mut log) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Fatal) {
|
if let Ok(mut log) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Fatal) {
|
||||||
|
@ -48,3 +54,11 @@ fn panic_handler(panic_info: &PanicInfo) -> ! {
|
||||||
VBlankIntrWait();
|
VBlankIntrWait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test_case]
|
||||||
|
fn it_works() {
|
||||||
|
assert_eq!(1 + 1, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
99
src/test_harness.rs
Normal file
99
src/test_harness.rs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
use core::fmt::Write;
|
||||||
|
use gba::{bios, mem, mgba, prelude::*};
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
DISPSTAT.write(DisplayStatus::new().with_irq_vblank(true));
|
||||||
|
BG_PALETTE.index(0).write(Color::from_rgb(25, 10, 5));
|
||||||
|
IE.write(IrqBits::VBLANK);
|
||||||
|
IME.write(true);
|
||||||
|
VBlankIntrWait();
|
||||||
|
VBlankIntrWait();
|
||||||
|
VBlankIntrWait();
|
||||||
|
|
||||||
|
// the Fatal one kills emulation after one line / 256 bytes
|
||||||
|
// so emit all the information as Error first
|
||||||
|
if let Ok(mut log) = mgba::MgbaBufferedLogger::try_new(mgba::MgbaMessageLevel::Error) {
|
||||||
|
writeln!(log, "[failed]").ok();
|
||||||
|
write!(log, "{}", info).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut log) = mgba::MgbaBufferedLogger::try_new(mgba::MgbaMessageLevel::Fatal) {
|
||||||
|
if let Some(loc) = info.location() {
|
||||||
|
write!(log, "panic at {loc}! see mgba error log for details.").ok();
|
||||||
|
} else {
|
||||||
|
write!(log, "panic! see mgba error log for details.").ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IE.write(IrqBits::new());
|
||||||
|
bios::IntrWait(true, IrqBits::new());
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait UnitTest {
|
||||||
|
fn run(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Fn()> UnitTest for T {
|
||||||
|
fn run(&self) {
|
||||||
|
if let Ok(mut log) = mgba::MgbaBufferedLogger::try_new(mgba::MgbaMessageLevel::Info) {
|
||||||
|
write!(log, "{}...", core::any::type_name::<T>()).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
self();
|
||||||
|
|
||||||
|
if let Ok(mut log) = mgba::MgbaBufferedLogger::try_new(mgba::MgbaMessageLevel::Info) {
|
||||||
|
writeln!(log, "[ok]").ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn test_runner(tests: &[&dyn UnitTest]) {
|
||||||
|
if let Ok(mut log) = mgba::MgbaBufferedLogger::try_new(mgba::MgbaMessageLevel::Info) {
|
||||||
|
write!(log, "Running {} tests", tests.len()).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
for test in tests {
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
if let Ok(mut log) = mgba::MgbaBufferedLogger::try_new(mgba::MgbaMessageLevel::Info) {
|
||||||
|
write!(log, "Tests finished successfully").ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
extern "C" fn main() {
|
||||||
|
DISPCNT.write(DisplayControl::new().with_video_mode(VideoMode::_0));
|
||||||
|
BG_PALETTE.index(0).write(Color::new());
|
||||||
|
|
||||||
|
crate::test_main();
|
||||||
|
|
||||||
|
BG_PALETTE.index(0).write(Color::from_rgb(5, 15, 25));
|
||||||
|
BG_PALETTE.index(1).write(Color::new());
|
||||||
|
BG0CNT.write(
|
||||||
|
BackgroundControl::new()
|
||||||
|
.with_charblock(0)
|
||||||
|
.with_screenblock(31),
|
||||||
|
);
|
||||||
|
DISPCNT.write(
|
||||||
|
DisplayControl::new()
|
||||||
|
.with_video_mode(VideoMode::_0)
|
||||||
|
.with_show_bg0(true),
|
||||||
|
);
|
||||||
|
|
||||||
|
// some niceties for people without mgba-test-runner
|
||||||
|
let tsb = TEXT_SCREENBLOCKS.get_frame(31).unwrap();
|
||||||
|
unsafe {
|
||||||
|
mem::set_u32x80_unchecked(tsb.into_block::<1024>().as_mut_ptr().cast(), 0, 12);
|
||||||
|
}
|
||||||
|
Cga8x8Thick.bitunpack_4bpp(CHARBLOCK0_4BPP.as_region(), 0);
|
||||||
|
|
||||||
|
let row = tsb.get_row(9).unwrap().iter().skip(6);
|
||||||
|
for (addr, ch) in row.zip(b"all tests passed!") {
|
||||||
|
addr.write(TextEntry::new().with_tile(*ch as u16));
|
||||||
|
}
|
||||||
|
|
||||||
|
DISPSTAT.write(DisplayStatus::new());
|
||||||
|
bios::IntrWait(true, IrqBits::new());
|
||||||
|
}
|
Loading…
Reference in a new issue