move tests to conditionally compiled modules

also only enable custom test framework feature in test mode
This commit is contained in:
Corwin Kuiper 2022-01-06 19:47:30 +00:00
parent 039bc0acb1
commit 222efe9122
7 changed files with 308 additions and 280 deletions

View file

@ -61,17 +61,22 @@ fn get_data_end() -> usize {
(unsafe { &__ewram_data_end }) as *const _ as usize (unsafe { &__ewram_data_end }) as *const _ as usize
} }
#[test_case] #[cfg(test)]
fn should_return_data_end_somewhere_in_ewram(_gba: &mut crate::Gba) { mod tests {
let data_end = get_data_end(); use super::*;
assert!( #[test_case]
0x0200_0000 <= data_end, fn should_return_data_end_somewhere_in_ewram(_gba: &mut crate::Gba) {
"data end should be bigger than 0x0200_0000, got {}", let data_end = get_data_end();
data_end
); assert!(
assert!( 0x0200_0000 <= data_end,
0x0204_0000 > data_end, "data end should be bigger than 0x0200_0000, got {}",
"data end should be smaller than 0x0203_0000" data_end
); );
assert!(
0x0204_0000 > data_end,
"data end should be smaller than 0x0203_0000"
);
}
} }

View file

@ -22,33 +22,36 @@ impl<const N: usize> Bitarray<N> {
self.a[index / 32] = self.a[index / 32] & !mask | value_mask self.a[index / 32] = self.a[index / 32] & !mask | value_mask
} }
} }
#[cfg(test)]
#[test_case] mod tests {
fn write_and_read(_gba: &mut crate::Gba) { use super::*;
let mut a: Bitarray<2> = Bitarray::new(); #[test_case]
assert_eq!(a.get(55), Some(false), "expect unset values to be false"); fn write_and_read(_gba: &mut crate::Gba) {
a.set(62, true);
assert_eq!(a.get(62), Some(true), "expect set value to be true");
assert_eq!(a.get(120), None, "expect out of range to give None");
}
#[test_case]
fn test_everything(_gba: &mut crate::Gba) {
for i in 0..64 {
let mut a: Bitarray<2> = Bitarray::new(); let mut a: Bitarray<2> = Bitarray::new();
a.set(i, true); assert_eq!(a.get(55), Some(false), "expect unset values to be false");
for j in 0..64 { a.set(62, true);
let expected = i == j; assert_eq!(a.get(62), Some(true), "expect set value to be true");
assert_eq!( assert_eq!(a.get(120), None, "expect out of range to give None");
a.get(j).unwrap(), }
expected,
"set index {} and read {}, expected {} but got {}. u32 of this is {:#b}", #[test_case]
i, fn test_everything(_gba: &mut crate::Gba) {
j, for i in 0..64 {
expected, let mut a: Bitarray<2> = Bitarray::new();
a.get(j).unwrap(), a.set(i, true);
a.a[j / 32], for j in 0..64 {
); let expected = i == j;
assert_eq!(
a.get(j).unwrap(),
expected,
"set index {} and read {}, expected {} but got {}. u32 of this is {:#b}",
i,
j,
expected,
a.get(j).unwrap(),
a.a[j / 32],
);
}
} }
} }
} }

View file

@ -19,12 +19,16 @@ pub fn display_logo(gfx: &mut BackgroundDistributor) {
back.show(); back.show();
back.commit(); back.commit();
} }
#[cfg(test)]
mod tests {
use super::*;
#[test_case] #[test_case]
fn logo_display(gba: &mut crate::Gba) { fn logo_display(gba: &mut crate::Gba) {
let mut gfx = gba.display.video.tiled0(); let mut gfx = gba.display.video.tiled0();
display_logo(&mut gfx); display_logo(&mut gfx);
crate::assert_image_output("gfx/test_logo.png"); crate::assert_image_output("gfx/test_logo.png");
}
} }

View file

@ -270,37 +270,6 @@ macro_rules! add_interrupt_handler {
}; };
} }
#[test_case]
fn test_vblank_interrupt_handler(_gba: &mut crate::Gba) {
{
let counter = Mutex::new(0);
let counter_2 = Mutex::new(0);
add_interrupt_handler!(Interrupt::VBlank, |key| *counter.lock_with_key(&key) += 1);
add_interrupt_handler!(Interrupt::VBlank, |_| *counter_2.lock() += 1);
let vblank = VBlank::get();
while *counter.lock() < 100 || *counter_2.lock() < 100 {
vblank.wait_for_vblank();
}
}
assert_eq!(
interrupt_to_root(Interrupt::VBlank).next.get(),
core::ptr::null(),
"expected the interrupt table for vblank to be empty"
);
}
#[test_case]
fn test_interrupt_table_length(_gba: &mut crate::Gba) {
assert_eq!(
unsafe { INTERRUPT_TABLE.len() },
Interrupt::Gamepak as usize + 1,
"interrupt table should be able to store gamepak interrupt"
);
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum MutexState { enum MutexState {
Unlocked, Unlocked,
@ -409,3 +378,39 @@ impl Drop for VBlank {
interrupt_to_root(Interrupt::VBlank).reduce(); interrupt_to_root(Interrupt::VBlank).reduce();
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test_case]
fn test_vblank_interrupt_handler(_gba: &mut crate::Gba) {
{
let counter = Mutex::new(0);
let counter_2 = Mutex::new(0);
add_interrupt_handler!(Interrupt::VBlank, |key| *counter.lock_with_key(&key) += 1);
add_interrupt_handler!(Interrupt::VBlank, |_| *counter_2.lock() += 1);
let vblank = VBlank::get();
while *counter.lock() < 100 || *counter_2.lock() < 100 {
vblank.wait_for_vblank();
}
}
assert_eq!(
interrupt_to_root(Interrupt::VBlank).next.get(),
core::ptr::null(),
"expected the interrupt table for vblank to be empty"
);
}
#[test_case]
fn test_interrupt_table_length(_gba: &mut crate::Gba) {
assert_eq!(
unsafe { INTERRUPT_TABLE.len() },
Interrupt::Gamepak as usize + 1,
"interrupt table should be able to store gamepak interrupt"
);
}
}

View file

@ -1,11 +1,12 @@
#![no_std] #![no_std]
// This appears to be needed for testing to work // This appears to be needed for testing to work
#![cfg_attr(test, no_main)] #![cfg_attr(test, no_main)]
#![cfg_attr(test, feature(custom_test_frameworks))]
#![cfg_attr(test, test_runner(crate::test_runner))]
#![cfg_attr(test, reexport_test_harness_main = "test_main")]
#![deny(clippy::all)] #![deny(clippy::all)]
#![feature(custom_test_frameworks)]
#![feature(alloc_error_handler)] #![feature(alloc_error_handler)]
#![test_runner(crate::test_runner)]
#![reexport_test_harness_main = "test_main"]
//! # agb //! # agb
//! `agb` is a library for making games on the Game Boy Advance using the Rust //! `agb` is a library for making games on the Game Boy Advance using the Rust
//! programming language. It attempts to be a high level abstraction over the //! programming language. It attempts to be a high level abstraction over the

View file

@ -334,55 +334,6 @@ impl<const N: usize> Num<i32, N> {
} }
} }
#[test_case]
fn sqrt(_gba: &mut crate::Gba) {
for x in 1..1024 {
let n: Num<i32, 8> = Num::new(x * x);
assert_eq!(n.sqrt(), x.into());
}
}
#[test_case]
fn test_macro_conversion(_gba: &mut super::Gba) {
fn test_positive<A: FixedWidthUnsignedInteger, const B: usize>() {
let a: Num<A, B> = num!(1.5);
let one = A::one() << B;
let b = Num::from_raw(one + (one >> 1));
assert_eq!(a, b);
}
fn test_negative<A: FixedWidthSignedInteger, const B: usize>() {
let a: Num<A, B> = num!(-1.5);
let one = A::one() << B;
let b = Num::from_raw(one + (one >> 1));
assert_eq!(a, -b);
}
fn test_base<const B: usize>() {
test_positive::<i32, B>();
test_positive::<u32, B>();
test_negative::<i32, B>();
if B < 16 {
test_positive::<u16, B>();
test_positive::<i16, B>();
test_negative::<i16, B>();
}
}
// some nice powers of two
test_base::<8>();
test_base::<4>();
test_base::<16>();
// not a power of two
test_base::<10>();
// an odd number
test_base::<9>();
// and a prime
test_base::<11>();
}
impl<I: FixedWidthSignedInteger, const N: usize> Num<I, N> { impl<I: FixedWidthSignedInteger, const N: usize> Num<I, N> {
#[must_use] #[must_use]
pub fn abs(self) -> Self { pub fn abs(self) -> Self {
@ -415,129 +366,6 @@ impl<I: FixedWidthSignedInteger, const N: usize> Num<I, N> {
} }
} }
#[test_case]
fn test_numbers(_gba: &mut super::Gba) {
// test addition
let n: Num<i32, 8> = 1.into();
assert_eq!(n + 2, 3.into(), "testing that 1 + 2 == 3");
// test multiplication
let n: Num<i32, 8> = 5.into();
assert_eq!(n * 3, 15.into(), "testing that 5 * 3 == 15");
// test division
let n: Num<i32, 8> = 30.into();
let p: Num<i32, 8> = 3.into();
assert_eq!(n / 20, p / 2, "testing that 30 / 20 == 3 / 2");
assert_ne!(n, p, "testing that 30 != 3");
}
#[test_case]
fn test_division_by_one(_gba: &mut super::Gba) {
let one: Num<i32, 8> = 1.into();
for i in -40..40 {
let n: Num<i32, 8> = i.into();
assert_eq!(n / one, n);
}
}
#[test_case]
fn test_division_and_multiplication_by_16(_gba: &mut super::Gba) {
let sixteen: Num<i32, 8> = 16.into();
for i in -40..40 {
let n: Num<i32, 8> = i.into();
let m = n / sixteen;
assert_eq!(m * sixteen, n);
}
}
#[test_case]
fn test_division_by_2_and_15(_gba: &mut super::Gba) {
let two: Num<i32, 8> = 2.into();
let fifteen: Num<i32, 8> = 15.into();
let thirty: Num<i32, 8> = 30.into();
for i in -128..128 {
let n: Num<i32, 8> = i.into();
assert_eq!(n / two / fifteen, n / thirty);
assert_eq!(n / fifteen / two, n / thirty);
}
}
#[test_case]
fn test_change_base(_gba: &mut super::Gba) {
let two: Num<i32, 9> = 2.into();
let three: Num<i32, 4> = 3.into();
assert_eq!(two + three.change_base(), 5.into());
assert_eq!(three + two.change_base(), 5.into());
}
#[test_case]
fn test_rem_returns_sensible_values_for_integers(_gba: &mut super::Gba) {
for i in -50..50 {
for j in -50..50 {
if j == 0 {
continue;
}
let i_rem_j_normally = i % j;
let i_fixnum: Num<i32, 8> = i.into();
assert_eq!(i_fixnum % j, i_rem_j_normally.into());
}
}
}
#[test_case]
fn test_rem_returns_sensible_values_for_non_integers(_gba: &mut super::Gba) {
let one: Num<i32, 8> = 1.into();
let third = one / 3;
for i in -50..50 {
for j in -50..50 {
if j == 0 {
continue;
}
// full calculation in the normal way
let x: Num<i32, 8> = third + i;
let y: Num<i32, 8> = j.into();
let truncated_division: Num<i32, 8> = (x / y).trunc().into();
let remainder = x - truncated_division * y;
assert_eq!(x % y, remainder);
}
}
}
#[test_case]
fn test_rem_euclid_is_always_positive_and_sensible(_gba: &mut super::Gba) {
let one: Num<i32, 8> = 1.into();
let third = one / 3;
for i in -50..50 {
for j in -50..50 {
if j == 0 {
continue;
}
let x: Num<i32, 8> = third + i;
let y: Num<i32, 8> = j.into();
let rem_euclid = x.rem_euclid(y);
assert!(rem_euclid > 0.into());
}
}
}
impl<I: FixedWidthUnsignedInteger, const N: usize> Display for Num<I, N> { impl<I: FixedWidthUnsignedInteger, const N: usize> Display for Num<I, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut integral = self.0 >> N; let mut integral = self.0 >> N;
@ -643,15 +471,6 @@ where
} }
} }
#[test_case]
fn test_vector_multiplication_and_division(_gba: &mut super::Gba) {
let a: Vector2D<i32> = (1, 2).into();
let b = a * 5;
let c = b / 5;
assert_eq!(b, (5, 10).into());
assert_eq!(a, c);
}
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[cfg(test)] #[cfg(test)]
mod formatting_tests { mod formatting_tests {
@ -759,15 +578,6 @@ impl<const N: usize> Vector2D<Num<i32, N>> {
} }
} }
#[test_case]
fn magnitude_accuracy(_gba: &mut crate::Gba) {
let n: Vector2D<Num<i32, 16>> = (3, 4).into();
assert!((n.magnitude() - 5).abs() < num!(0.1));
let n: Vector2D<Num<i32, 8>> = (3, 4).into();
assert!((n.magnitude() - 5).abs() < num!(0.1));
}
impl<T: Number, P: Number + Into<T>> From<(P, P)> for Vector2D<T> { impl<T: Number, P: Number + Into<T>> From<(P, P)> for Vector2D<T> {
fn from(f: (P, P)) -> Self { fn from(f: (P, P)) -> Self {
Vector2D::new(f.0.into(), f.1.into()) Vector2D::new(f.0.into(), f.1.into())
@ -934,12 +744,207 @@ impl<T: Number> Vector2D<T> {
} }
} }
#[test_case] #[cfg(test)]
fn test_vector_changing(_gba: &mut super::Gba) { mod tests {
let v1: Vector2D<FixedNum<8>> = Vector2D::new(1.into(), 2.into()); use super::*;
let v2 = v1.trunc(); #[test_case]
assert_eq!(v2.get(), (1, 2)); fn sqrt(_gba: &mut crate::Gba) {
for x in 1..1024 {
let n: Num<i32, 8> = Num::new(x * x);
assert_eq!(n.sqrt(), x.into());
}
}
assert_eq!(v1 + v1, (v2 + v2).into()); #[test_case]
fn test_macro_conversion(_gba: &mut crate::Gba) {
fn test_positive<A: FixedWidthUnsignedInteger, const B: usize>() {
let a: Num<A, B> = num!(1.5);
let one = A::one() << B;
let b = Num::from_raw(one + (one >> 1));
assert_eq!(a, b);
}
fn test_negative<A: FixedWidthSignedInteger, const B: usize>() {
let a: Num<A, B> = num!(-1.5);
let one = A::one() << B;
let b = Num::from_raw(one + (one >> 1));
assert_eq!(a, -b);
}
fn test_base<const B: usize>() {
test_positive::<i32, B>();
test_positive::<u32, B>();
test_negative::<i32, B>();
if B < 16 {
test_positive::<u16, B>();
test_positive::<i16, B>();
test_negative::<i16, B>();
}
}
// some nice powers of two
test_base::<8>();
test_base::<4>();
test_base::<16>();
// not a power of two
test_base::<10>();
// an odd number
test_base::<9>();
// and a prime
test_base::<11>();
}
#[test_case]
fn test_numbers(_gba: &mut crate::Gba) {
// test addition
let n: Num<i32, 8> = 1.into();
assert_eq!(n + 2, 3.into(), "testing that 1 + 2 == 3");
// test multiplication
let n: Num<i32, 8> = 5.into();
assert_eq!(n * 3, 15.into(), "testing that 5 * 3 == 15");
// test division
let n: Num<i32, 8> = 30.into();
let p: Num<i32, 8> = 3.into();
assert_eq!(n / 20, p / 2, "testing that 30 / 20 == 3 / 2");
assert_ne!(n, p, "testing that 30 != 3");
}
#[test_case]
fn test_division_by_one(_gba: &mut crate::Gba) {
let one: Num<i32, 8> = 1.into();
for i in -40..40 {
let n: Num<i32, 8> = i.into();
assert_eq!(n / one, n);
}
}
#[test_case]
fn test_division_and_multiplication_by_16(_gba: &mut crate::Gba) {
let sixteen: Num<i32, 8> = 16.into();
for i in -40..40 {
let n: Num<i32, 8> = i.into();
let m = n / sixteen;
assert_eq!(m * sixteen, n);
}
}
#[test_case]
fn test_division_by_2_and_15(_gba: &mut crate::Gba) {
let two: Num<i32, 8> = 2.into();
let fifteen: Num<i32, 8> = 15.into();
let thirty: Num<i32, 8> = 30.into();
for i in -128..128 {
let n: Num<i32, 8> = i.into();
assert_eq!(n / two / fifteen, n / thirty);
assert_eq!(n / fifteen / two, n / thirty);
}
}
#[test_case]
fn test_change_base(_gba: &mut crate::Gba) {
let two: Num<i32, 9> = 2.into();
let three: Num<i32, 4> = 3.into();
assert_eq!(two + three.change_base(), 5.into());
assert_eq!(three + two.change_base(), 5.into());
}
#[test_case]
fn test_rem_returns_sensible_values_for_integers(_gba: &mut crate::Gba) {
for i in -50..50 {
for j in -50..50 {
if j == 0 {
continue;
}
let i_rem_j_normally = i % j;
let i_fixnum: Num<i32, 8> = i.into();
assert_eq!(i_fixnum % j, i_rem_j_normally.into());
}
}
}
#[test_case]
fn test_rem_returns_sensible_values_for_non_integers(_gba: &mut crate::Gba) {
let one: Num<i32, 8> = 1.into();
let third = one / 3;
for i in -50..50 {
for j in -50..50 {
if j == 0 {
continue;
}
// full calculation in the normal way
let x: Num<i32, 8> = third + i;
let y: Num<i32, 8> = j.into();
let truncated_division: Num<i32, 8> = (x / y).trunc().into();
let remainder = x - truncated_division * y;
assert_eq!(x % y, remainder);
}
}
}
#[test_case]
fn test_rem_euclid_is_always_positive_and_sensible(_gba: &mut crate::Gba) {
let one: Num<i32, 8> = 1.into();
let third = one / 3;
for i in -50..50 {
for j in -50..50 {
if j == 0 {
continue;
}
let x: Num<i32, 8> = third + i;
let y: Num<i32, 8> = j.into();
let rem_euclid = x.rem_euclid(y);
assert!(rem_euclid > 0.into());
}
}
}
#[test_case]
fn test_vector_multiplication_and_division(_gba: &mut crate::Gba) {
let a: Vector2D<i32> = (1, 2).into();
let b = a * 5;
let c = b / 5;
assert_eq!(b, (5, 10).into());
assert_eq!(a, c);
}
#[test_case]
fn magnitude_accuracy(_gba: &mut crate::Gba) {
let n: Vector2D<Num<i32, 16>> = (3, 4).into();
assert!((n.magnitude() - 5).abs() < num!(0.1));
let n: Vector2D<Num<i32, 8>> = (3, 4).into();
assert!((n.magnitude() - 5).abs() < num!(0.1));
}
#[test_case]
fn test_vector_changing(_gba: &mut crate::Gba) {
let v1: Vector2D<FixedNum<8>> = Vector2D::new(1.into(), 2.into());
let v2 = v1.trunc();
assert_eq!(v2.get(), (1, 2));
assert_eq!(v1 + v1, (v2 + v2).into());
}
} }

View file

@ -151,12 +151,17 @@ pub fn affine_matrix(
result result
} }
#[test_case] #[cfg(test)]
fn affine(_gba: &mut crate::Gba) { mod tests {
// expect identity matrix use super::*;
let one: Num<i16, 8> = 1.into();
let aff = affine_matrix(one, one, 0); #[test_case]
assert_eq!(aff.p_a, one.to_raw()); fn affine(_gba: &mut crate::Gba) {
assert_eq!(aff.p_d, one.to_raw()); // expect identity matrix
let one: Num<i16, 8> = 1.into();
let aff = affine_matrix(one, one, 0);
assert_eq!(aff.p_a, one.to_raw());
assert_eq!(aff.p_d, one.to_raw());
}
} }