ggmath_testing 0.3.0

testing tool for custom ggmath scalars
Documentation
#[allow(unused_imports)]
use std::{
    fmt::Debug,
    mem::forget,
    panic::{catch_unwind, set_hook, take_hook},
};
use std::{
    fmt::{self, Formatter},
    mem::MaybeUninit,
    panic::UnwindSafe,
};

use ggmath::{scalar::Scalar, vector::*};

use crate::{TestableScalar, TestingError};

pub fn test_scalar<T: TestableScalar>() -> Result<(), TestingError> {
    set_hook(Box::new(|_| {}));

    test_scalar_n_a::<2, T, VecAligned>()?;
    test_scalar_n_a::<2, T, VecPacked>()?;
    test_scalar_n_a::<3, T, VecAligned>()?;
    test_scalar_n_a::<3, T, VecPacked>()?;
    test_scalar_n_a::<4, T, VecAligned>()?;
    test_scalar_n_a::<4, T, VecPacked>()?;

    let _ = take_hook();

    Ok(())
}

enum MaybePanic<T: UnwindSafe> {
    Value(T),
    Panic,
}
impl<T: UnwindSafe> MaybePanic<T> {
    fn from_fn(f: impl FnOnce() -> T + UnwindSafe) -> Self {
        match catch_unwind(f) {
            Ok(ok) => Self::Value(ok),
            Err(_) => Self::Panic,
        }
    }
}
impl<T: UnwindSafe + Debug> Debug for MaybePanic<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::Value(value) => write!(f, "{value:?}"),
            Self::Panic => write!(f, "panic"),
        }
    }
}
impl<T: UnwindSafe + PartialEq<Rhs>, Rhs: UnwindSafe> PartialEq<MaybePanic<Rhs>> for MaybePanic<T> {
    fn eq(&self, other: &MaybePanic<Rhs>) -> bool {
        match self {
            Self::Value(value) => match other {
                MaybePanic::Value(other) => value == other,
                MaybePanic::Panic => false,
            },
            Self::Panic => match other {
                MaybePanic::Value(_) => false,
                MaybePanic::Panic => true,
            },
        }
    }
}

fn assert_lhs_fn<
    const N: usize,
    T: TestableScalar,
    A: VecAlignment,
    V: PartialEq<E> + Debug,
    E: Debug,
>(
    vector_fn: &'static str,
    value: V,
    expected_value: E,
    inputs: &[String],
) -> Result<(), TestingError>
where
    ScalarCount<N>: VecLen,
{
    if value == expected_value {
        Ok(())
    } else {
        Err(TestingError::vector::<N, T, A>(
            vector_fn,
            &format!(
                "{}\nexpected `{expected_value:?}`\nfound `{value:?}`\n",
                inputs.join("")
            ),
        ))
    }
}
macro_rules! assert_lhs {
    ($vector_fn:ident: $value:expr, $expected_value:expr $(, $input:ident)* $(,)?) => {
        assert_lhs_fn::<N, T, A, _, _>(
            stringify!($vector_fn),
            MaybePanic::from_fn(|| $value),
            MaybePanic::from_fn(|| $expected_value),
            &[$(format!(" * {} = {:?} \n", stringify!($input), $input)), *],
        )?;
    };
}

fn get_n<const N: usize, const N_OUTPUT: usize, T: Scalar, A: VecAlignment>(
    vector: Vector<N, T, A>,
    indicies: [usize; N_OUTPUT],
) -> Option<Vector<N_OUTPUT, T, A>>
where
    ScalarCount<N>: VecLen,
    ScalarCount<N_OUTPUT>: VecLen,
{
    let mut output = unsafe { MaybeUninit::<[T; N_OUTPUT]>::uninit().assume_init() };

    for index in 0..N_OUTPUT {
        match vector.get(indicies[index]) {
            Some(item) => output[index] = item,
            None => return None,
        }
    }

    Some(Vector::from_array(output))
}

fn test_scalar_n_a<const N: usize, T: TestableScalar, A: VecAlignment>() -> Result<(), TestingError>
where
    ScalarCount<N>: VecLen,
{
    test_vector_get::<N, T, A>()?;

    Ok(())
}

fn test_vector_get<const N: usize, T: TestableScalar, A: VecAlignment>() -> Result<(), TestingError>
where
    ScalarCount<N>: VecLen,
{
    let vector = Vector::<N, T, A>::from_array(T::n_values(0));

    for i in 0..=4 {
        assert_lhs!(get: vector.get(i), vector.as_array().get(i).map(|some| *some), vector, i);

        for i_1 in 0..=4 {
            assert_lhs!(get_1_1: vector.get_1_1([i, i_1]), get_n::<N, 2, _, _>(vector, [i, i_1]), vector, i, i_1);

            for i_2 in 0..=4 {
                assert_lhs!(get_1_1_1: vector.get_1_1_1([i, i_1, i_2]), get_n::<N, 3, _, _>(vector, [i, i_1, i_2]), vector, i, i_1, i_2);

                for i_3 in 0..=4 {
                    assert_lhs!(get_1_1_1_1: vector.get_1_1_1_1([i, i_1, i_2, i_3]), get_n::<N, 4, _, _>(vector, [i, i_1, i_2, i_3]), vector, i, i_1, i_2, i_3);
                }
            }
        }
    }

    for i in 0..N {
        assert_lhs!(get_unchecked: unsafe { vector.get_unchecked(i) }, vector[i], vector, i);

        for i_1 in 0..N {
            assert_lhs!(get_1_1_unchecked: unsafe { vector.get_1_1_unchecked([i, i_1]) }, vector.get_1_1([i, i_1]).unwrap(), vector, i, i_1);

            for i_2 in 0..N {
                assert_lhs!(get_1_1_1_unchecked: unsafe { vector.get_1_1_1_unchecked([i, i_1, i_2]) }, vector.get_1_1_1([i, i_1, i_2]).unwrap(), vector, i, i_1, i_2);

                for i_3 in 0..N {
                    assert_lhs!(get_1_1_1_1_unchecked: unsafe { vector.get_1_1_1_1_unchecked([i, i_1, i_2, i_3]) }, vector.get_1_1_1_1([i, i_1, i_2, i_3]).unwrap(), vector, i, i_1, i_2, i_3);
                }
            }
        }
    }

    Ok(())
}