psx 0.1.8

Library for developing homebrew for the Sony PlayStation 1
#![cfg(test)]

use crate::{print, println};
use const_random::const_random;
use core::any::type_name;
use num::integer::gcd;

pub const MAX_TESTS: usize = 1_000;

/// Executes `$body` `MAX_TESTS` times with one or more variables specified by
///`$name` set to random values of type `$ty`. Note the rng is seeded at
/// compile-time so `cargo psx test` runs will produce different results but
/// each test binary should have deterministic results.
#[macro_export]
macro_rules! fuzz {
    (|$($name:ident: $ty:ty),+| { $($body:tt)* }) => {
        {
            use const_random::const_random;
            use crate::sys::rng::Rng;

            let rng = Rng::new(const_random!(u32));
            for _ in 0..crate::test::MAX_TESTS {
                $(let $name = rng.rand::<$ty>();)*
                $($body)*
            }
        }
    };
}

/// Executes `$body` `MAX_TESTS` times with a slice `$name` of type `$ty` with
/// a random size and values.
#[macro_export]
macro_rules! fuzz_data {
    (|$name:ident: &[$ty:ty]| { $($body:tt)* }) => {
        {
            use const_random::const_random;
            use crate::sys::rng::Rng;

            const MAX_SIZE: usize = 1_000;
            const SIZE: usize = const_random!(usize) % MAX_SIZE;
            let mut rng = Rng::new(const_random!(u32));
            for _ in 0..crate::test::MAX_TESTS {
                let mut ar: [$ty; SIZE] = [0; SIZE];
                for n in 0..SIZE {
                    ar[n] = rng.rand::<$ty>();
                }
                let $name = &ar;
                $($body)*
            }
        }
    };
}

pub trait Test {
    fn run(&self);
}

fn index_params(n: usize) -> (usize, usize) {
    let b = const_random!(usize) % n;
    let mut a = const_random!(usize) % n;
    if a == 0 {
        a = 1;
    }
    while gcd(a, n) != 1 {
        a = a / gcd(a, n);
    }
    (a, b)
}

#[test_case]
fn test_params() {
    fuzz!(|num_tests: usize| {
        let (a, b) = index_params(num_tests);
        assert!(a != 0);
        assert!(a < num_tests);
        assert!(b < num_tests);
        assert!(gcd(a, num_tests) == 1);
    });
}

impl<T: Fn()> Test for T {
    fn run(&self) {
        print!("test {} ...", type_name::<Self>());
        self();
        println!("ok");
    }
}

pub fn runner<const N: usize>(tests: &[&dyn Test; N]) {
    let (a, b) = index_params(N);

    let mut executed_tests = [0; N];

    println!("running {} tests", N);
    for n in 0..N {
        let idx = (a * n + b) % N;
        tests[idx].run();
        executed_tests[n] = idx;
    }
    executed_tests.as_mut_slice().sort_unstable();
    let ran_all_tests = executed_tests.iter().cloned().eq(0..N);
    assert!(ran_all_tests, "Test framework failed to run all tests!");
    println!("\ntest result: ok. {} passed; 0 failed\n", N);
    loop {}
}

#[test_case]
fn sanity_check() {
    assert!(true);
}