| pub mod array; |
| |
| #[cfg(target_arch = "wasm32")] |
| pub mod wasm; |
| |
| #[macro_use] |
| pub mod biteq; |
| |
| /// Specifies the default strategy for testing a type. |
| /// |
| /// This strategy should be what "makes sense" to test. |
| pub trait DefaultStrategy { |
| type Strategy: proptest::strategy::Strategy<Value = Self>; |
| fn default_strategy() -> Self::Strategy; |
| } |
| |
| macro_rules! impl_num { |
| { $type:tt } => { |
| impl DefaultStrategy for $type { |
| type Strategy = proptest::num::$type::Any; |
| fn default_strategy() -> Self::Strategy { |
| proptest::num::$type::ANY |
| } |
| } |
| } |
| } |
| |
| impl_num! { i8 } |
| impl_num! { i16 } |
| impl_num! { i32 } |
| impl_num! { i64 } |
| impl_num! { isize } |
| impl_num! { u8 } |
| impl_num! { u16 } |
| impl_num! { u32 } |
| impl_num! { u64 } |
| impl_num! { usize } |
| impl_num! { f32 } |
| impl_num! { f64 } |
| |
| impl<T> DefaultStrategy for *const T { |
| type Strategy = proptest::strategy::Map<proptest::num::isize::Any, fn(isize) -> *const T>; |
| fn default_strategy() -> Self::Strategy { |
| fn map<T>(x: isize) -> *const T { |
| x as _ |
| } |
| use proptest::strategy::Strategy; |
| proptest::num::isize::ANY.prop_map(map) |
| } |
| } |
| |
| impl<T> DefaultStrategy for *mut T { |
| type Strategy = proptest::strategy::Map<proptest::num::isize::Any, fn(isize) -> *mut T>; |
| fn default_strategy() -> Self::Strategy { |
| fn map<T>(x: isize) -> *mut T { |
| x as _ |
| } |
| use proptest::strategy::Strategy; |
| proptest::num::isize::ANY.prop_map(map) |
| } |
| } |
| |
| #[cfg(not(target_arch = "wasm32"))] |
| impl DefaultStrategy for u128 { |
| type Strategy = proptest::num::u128::Any; |
| fn default_strategy() -> Self::Strategy { |
| proptest::num::u128::ANY |
| } |
| } |
| |
| #[cfg(not(target_arch = "wasm32"))] |
| impl DefaultStrategy for i128 { |
| type Strategy = proptest::num::i128::Any; |
| fn default_strategy() -> Self::Strategy { |
| proptest::num::i128::ANY |
| } |
| } |
| |
| #[cfg(target_arch = "wasm32")] |
| impl DefaultStrategy for u128 { |
| type Strategy = crate::wasm::u128::Any; |
| fn default_strategy() -> Self::Strategy { |
| crate::wasm::u128::ANY |
| } |
| } |
| |
| #[cfg(target_arch = "wasm32")] |
| impl DefaultStrategy for i128 { |
| type Strategy = crate::wasm::i128::Any; |
| fn default_strategy() -> Self::Strategy { |
| crate::wasm::i128::ANY |
| } |
| } |
| |
| impl<T: core::fmt::Debug + DefaultStrategy, const LANES: usize> DefaultStrategy for [T; LANES] { |
| type Strategy = crate::array::UniformArrayStrategy<T::Strategy, Self>; |
| fn default_strategy() -> Self::Strategy { |
| Self::Strategy::new(T::default_strategy()) |
| } |
| } |
| |
| #[cfg(not(miri))] |
| pub fn make_runner() -> proptest::test_runner::TestRunner { |
| Default::default() |
| } |
| #[cfg(miri)] |
| pub fn make_runner() -> proptest::test_runner::TestRunner { |
| // Only run a few tests on Miri |
| proptest::test_runner::TestRunner::new(proptest::test_runner::Config::with_cases(4)) |
| } |
| |
| /// Test a function that takes a single value. |
| pub fn test_1<A: core::fmt::Debug + DefaultStrategy>( |
| f: &dyn Fn(A) -> proptest::test_runner::TestCaseResult, |
| ) { |
| let mut runner = make_runner(); |
| runner.run(&A::default_strategy(), f).unwrap(); |
| } |
| |
| /// Test a function that takes two values. |
| pub fn test_2<A: core::fmt::Debug + DefaultStrategy, B: core::fmt::Debug + DefaultStrategy>( |
| f: &dyn Fn(A, B) -> proptest::test_runner::TestCaseResult, |
| ) { |
| let mut runner = make_runner(); |
| runner |
| .run(&(A::default_strategy(), B::default_strategy()), |(a, b)| { |
| f(a, b) |
| }) |
| .unwrap(); |
| } |
| |
| /// Test a function that takes two values. |
| pub fn test_3< |
| A: core::fmt::Debug + DefaultStrategy, |
| B: core::fmt::Debug + DefaultStrategy, |
| C: core::fmt::Debug + DefaultStrategy, |
| >( |
| f: &dyn Fn(A, B, C) -> proptest::test_runner::TestCaseResult, |
| ) { |
| let mut runner = make_runner(); |
| runner |
| .run( |
| &( |
| A::default_strategy(), |
| B::default_strategy(), |
| C::default_strategy(), |
| ), |
| |(a, b, c)| f(a, b, c), |
| ) |
| .unwrap(); |
| } |
| |
| /// Test a unary vector function against a unary scalar function, applied elementwise. |
| #[inline(never)] |
| pub fn test_unary_elementwise<Scalar, ScalarResult, Vector, VectorResult, const LANES: usize>( |
| fv: &dyn Fn(Vector) -> VectorResult, |
| fs: &dyn Fn(Scalar) -> ScalarResult, |
| check: &dyn Fn([Scalar; LANES]) -> bool, |
| ) where |
| Scalar: Copy + core::fmt::Debug + DefaultStrategy, |
| ScalarResult: Copy + biteq::BitEq + core::fmt::Debug + DefaultStrategy, |
| Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy, |
| VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, |
| { |
| test_1(&|x: [Scalar; LANES]| { |
| proptest::prop_assume!(check(x)); |
| let result_1: [ScalarResult; LANES] = fv(x.into()).into(); |
| let result_2: [ScalarResult; LANES] = x |
| .iter() |
| .copied() |
| .map(fs) |
| .collect::<Vec<_>>() |
| .try_into() |
| .unwrap(); |
| crate::prop_assert_biteq!(result_1, result_2); |
| Ok(()) |
| }); |
| } |
| |
| /// Test a unary vector function against a unary scalar function, applied elementwise. |
| #[inline(never)] |
| pub fn test_unary_mask_elementwise<Scalar, Vector, Mask, const LANES: usize>( |
| fv: &dyn Fn(Vector) -> Mask, |
| fs: &dyn Fn(Scalar) -> bool, |
| check: &dyn Fn([Scalar; LANES]) -> bool, |
| ) where |
| Scalar: Copy + core::fmt::Debug + DefaultStrategy, |
| Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy, |
| Mask: Into<[bool; LANES]> + From<[bool; LANES]> + Copy, |
| { |
| test_1(&|x: [Scalar; LANES]| { |
| proptest::prop_assume!(check(x)); |
| let result_1: [bool; LANES] = fv(x.into()).into(); |
| let result_2: [bool; LANES] = { |
| let mut result = [false; LANES]; |
| for (i, o) in x.iter().zip(result.iter_mut()) { |
| *o = fs(*i); |
| } |
| result |
| }; |
| crate::prop_assert_biteq!(result_1, result_2); |
| Ok(()) |
| }); |
| } |
| |
| /// Test a binary vector function against a binary scalar function, applied elementwise. |
| #[inline(never)] |
| pub fn test_binary_elementwise< |
| Scalar1, |
| Scalar2, |
| ScalarResult, |
| Vector1, |
| Vector2, |
| VectorResult, |
| const LANES: usize, |
| >( |
| fv: &dyn Fn(Vector1, Vector2) -> VectorResult, |
| fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult, |
| check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES]) -> bool, |
| ) where |
| Scalar1: Copy + core::fmt::Debug + DefaultStrategy, |
| Scalar2: Copy + core::fmt::Debug + DefaultStrategy, |
| ScalarResult: Copy + biteq::BitEq + core::fmt::Debug + DefaultStrategy, |
| Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy, |
| Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy, |
| VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, |
| { |
| test_2(&|x: [Scalar1; LANES], y: [Scalar2; LANES]| { |
| proptest::prop_assume!(check(x, y)); |
| let result_1: [ScalarResult; LANES] = fv(x.into(), y.into()).into(); |
| let result_2: [ScalarResult; LANES] = x |
| .iter() |
| .copied() |
| .zip(y.iter().copied()) |
| .map(|(x, y)| fs(x, y)) |
| .collect::<Vec<_>>() |
| .try_into() |
| .unwrap(); |
| crate::prop_assert_biteq!(result_1, result_2); |
| Ok(()) |
| }); |
| } |
| |
| /// Test a binary vector-scalar function against a binary scalar function, applied elementwise. |
| #[inline(never)] |
| pub fn test_binary_scalar_rhs_elementwise< |
| Scalar1, |
| Scalar2, |
| ScalarResult, |
| Vector, |
| VectorResult, |
| const LANES: usize, |
| >( |
| fv: &dyn Fn(Vector, Scalar2) -> VectorResult, |
| fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult, |
| check: &dyn Fn([Scalar1; LANES], Scalar2) -> bool, |
| ) where |
| Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy, |
| Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy, |
| ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy, |
| Vector: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy, |
| VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, |
| { |
| test_2(&|x: [Scalar1; LANES], y: Scalar2| { |
| proptest::prop_assume!(check(x, y)); |
| let result_1: [ScalarResult; LANES] = fv(x.into(), y).into(); |
| let result_2: [ScalarResult; LANES] = { |
| let mut result = [ScalarResult::default(); LANES]; |
| for (i, o) in x.iter().zip(result.iter_mut()) { |
| *o = fs(*i, y); |
| } |
| result |
| }; |
| crate::prop_assert_biteq!(result_1, result_2); |
| Ok(()) |
| }); |
| } |
| |
| /// Test a binary vector-scalar function against a binary scalar function, applied elementwise. |
| #[inline(never)] |
| pub fn test_binary_scalar_lhs_elementwise< |
| Scalar1, |
| Scalar2, |
| ScalarResult, |
| Vector, |
| VectorResult, |
| const LANES: usize, |
| >( |
| fv: &dyn Fn(Scalar1, Vector) -> VectorResult, |
| fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult, |
| check: &dyn Fn(Scalar1, [Scalar2; LANES]) -> bool, |
| ) where |
| Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy, |
| Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy, |
| ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy, |
| Vector: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy, |
| VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, |
| { |
| test_2(&|x: Scalar1, y: [Scalar2; LANES]| { |
| proptest::prop_assume!(check(x, y)); |
| let result_1: [ScalarResult; LANES] = fv(x, y.into()).into(); |
| let result_2: [ScalarResult; LANES] = { |
| let mut result = [ScalarResult::default(); LANES]; |
| for (i, o) in y.iter().zip(result.iter_mut()) { |
| *o = fs(x, *i); |
| } |
| result |
| }; |
| crate::prop_assert_biteq!(result_1, result_2); |
| Ok(()) |
| }); |
| } |
| |
| /// Test a ternary vector function against a ternary scalar function, applied elementwise. |
| #[inline(never)] |
| pub fn test_ternary_elementwise< |
| Scalar1, |
| Scalar2, |
| Scalar3, |
| ScalarResult, |
| Vector1, |
| Vector2, |
| Vector3, |
| VectorResult, |
| const LANES: usize, |
| >( |
| fv: &dyn Fn(Vector1, Vector2, Vector3) -> VectorResult, |
| fs: &dyn Fn(Scalar1, Scalar2, Scalar3) -> ScalarResult, |
| check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES], [Scalar3; LANES]) -> bool, |
| ) where |
| Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy, |
| Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy, |
| Scalar3: Copy + Default + core::fmt::Debug + DefaultStrategy, |
| ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy, |
| Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy, |
| Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy, |
| Vector3: Into<[Scalar3; LANES]> + From<[Scalar3; LANES]> + Copy, |
| VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, |
| { |
| test_3( |
| &|x: [Scalar1; LANES], y: [Scalar2; LANES], z: [Scalar3; LANES]| { |
| proptest::prop_assume!(check(x, y, z)); |
| let result_1: [ScalarResult; LANES] = fv(x.into(), y.into(), z.into()).into(); |
| let result_2: [ScalarResult; LANES] = { |
| let mut result = [ScalarResult::default(); LANES]; |
| for ((i1, (i2, i3)), o) in |
| x.iter().zip(y.iter().zip(z.iter())).zip(result.iter_mut()) |
| { |
| *o = fs(*i1, *i2, *i3); |
| } |
| result |
| }; |
| crate::prop_assert_biteq!(result_1, result_2); |
| Ok(()) |
| }, |
| ); |
| } |
| |
| #[doc(hidden)] |
| #[macro_export] |
| macro_rules! test_lanes_helper { |
| ($($(#[$meta:meta])* $fn_name:ident $lanes:literal;)+) => { |
| $( |
| #[test] |
| $(#[$meta])* |
| fn $fn_name() { |
| implementation::<$lanes>(); |
| } |
| )+ |
| }; |
| ( |
| $(#[$meta:meta])+; |
| $($(#[$meta_before:meta])+ $fn_name_before:ident $lanes_before:literal;)* |
| $fn_name:ident $lanes:literal; |
| $($fn_name_rest:ident $lanes_rest:literal;)* |
| ) => { |
| $crate::test_lanes_helper!( |
| $(#[$meta])+; |
| $($(#[$meta_before])+ $fn_name_before $lanes_before;)* |
| $(#[$meta])+ $fn_name $lanes; |
| $($fn_name_rest $lanes_rest;)* |
| ); |
| }; |
| ( |
| $(#[$meta_ignored:meta])+; |
| $($(#[$meta:meta])+ $fn_name:ident $lanes:literal;)+ |
| ) => { |
| $crate::test_lanes_helper!($($(#[$meta])+ $fn_name $lanes;)+); |
| }; |
| } |
| |
| /// Expand a const-generic test into separate tests for each possible lane count. |
| #[macro_export] |
| macro_rules! test_lanes { |
| { |
| $(fn $test:ident<const $lanes:ident: usize>() $body:tt)* |
| } => { |
| $( |
| mod $test { |
| use super::*; |
| |
| fn implementation<const $lanes: usize>() |
| where |
| core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, |
| $body |
| |
| #[cfg(target_arch = "wasm32")] |
| wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); |
| |
| $crate::test_lanes_helper!( |
| #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; |
| lanes_1 1; |
| lanes_2 2; |
| lanes_4 4; |
| ); |
| |
| #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow |
| $crate::test_lanes_helper!( |
| #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; |
| lanes_8 8; |
| lanes_16 16; |
| lanes_32 32; |
| lanes_64 64; |
| ); |
| |
| #[cfg(feature = "all_lane_counts")] |
| $crate::test_lanes_helper!( |
| // test some odd and even non-power-of-2 lengths on miri |
| #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; |
| lanes_3 3; |
| lanes_5 5; |
| lanes_6 6; |
| ); |
| |
| #[cfg(feature = "all_lane_counts")] |
| #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow |
| $crate::test_lanes_helper!( |
| #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; |
| lanes_7 7; |
| lanes_9 9; |
| lanes_10 10; |
| lanes_11 11; |
| lanes_12 12; |
| lanes_13 13; |
| lanes_14 14; |
| lanes_15 15; |
| lanes_17 17; |
| lanes_18 18; |
| lanes_19 19; |
| lanes_20 20; |
| lanes_21 21; |
| lanes_22 22; |
| lanes_23 23; |
| lanes_24 24; |
| lanes_25 25; |
| lanes_26 26; |
| lanes_27 27; |
| lanes_28 28; |
| lanes_29 29; |
| lanes_30 30; |
| lanes_31 31; |
| lanes_33 33; |
| lanes_34 34; |
| lanes_35 35; |
| lanes_36 36; |
| lanes_37 37; |
| lanes_38 38; |
| lanes_39 39; |
| lanes_40 40; |
| lanes_41 41; |
| lanes_42 42; |
| lanes_43 43; |
| lanes_44 44; |
| lanes_45 45; |
| lanes_46 46; |
| lanes_47 47; |
| lanes_48 48; |
| lanes_49 49; |
| lanes_50 50; |
| lanes_51 51; |
| lanes_52 52; |
| lanes_53 53; |
| lanes_54 54; |
| lanes_55 55; |
| lanes_56 56; |
| lanes_57 57; |
| lanes_58 58; |
| lanes_59 59; |
| lanes_60 60; |
| lanes_61 61; |
| lanes_62 62; |
| lanes_63 63; |
| ); |
| } |
| )* |
| } |
| } |
| |
| /// Expand a const-generic `#[should_panic]` test into separate tests for each possible lane count. |
| #[macro_export] |
| macro_rules! test_lanes_panic { |
| { |
| $(fn $test:ident<const $lanes:ident: usize>() $body:tt)* |
| } => { |
| $( |
| mod $test { |
| use super::*; |
| |
| fn implementation<const $lanes: usize>() |
| where |
| core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, |
| $body |
| |
| $crate::test_lanes_helper!( |
| #[should_panic]; |
| lanes_1 1; |
| lanes_2 2; |
| lanes_4 4; |
| ); |
| |
| #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow |
| $crate::test_lanes_helper!( |
| #[should_panic]; |
| lanes_8 8; |
| lanes_16 16; |
| lanes_32 32; |
| lanes_64 64; |
| ); |
| |
| #[cfg(feature = "all_lane_counts")] |
| $crate::test_lanes_helper!( |
| // test some odd and even non-power-of-2 lengths on miri |
| #[should_panic]; |
| lanes_3 3; |
| lanes_5 5; |
| lanes_6 6; |
| ); |
| |
| #[cfg(feature = "all_lane_counts")] |
| #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow |
| $crate::test_lanes_helper!( |
| #[should_panic]; |
| lanes_7 7; |
| lanes_9 9; |
| lanes_10 10; |
| lanes_11 11; |
| lanes_12 12; |
| lanes_13 13; |
| lanes_14 14; |
| lanes_15 15; |
| lanes_17 17; |
| lanes_18 18; |
| lanes_19 19; |
| lanes_20 20; |
| lanes_21 21; |
| lanes_22 22; |
| lanes_23 23; |
| lanes_24 24; |
| lanes_25 25; |
| lanes_26 26; |
| lanes_27 27; |
| lanes_28 28; |
| lanes_29 29; |
| lanes_30 30; |
| lanes_31 31; |
| lanes_33 33; |
| lanes_34 34; |
| lanes_35 35; |
| lanes_36 36; |
| lanes_37 37; |
| lanes_38 38; |
| lanes_39 39; |
| lanes_40 40; |
| lanes_41 41; |
| lanes_42 42; |
| lanes_43 43; |
| lanes_44 44; |
| lanes_45 45; |
| lanes_46 46; |
| lanes_47 47; |
| lanes_48 48; |
| lanes_49 49; |
| lanes_50 50; |
| lanes_51 51; |
| lanes_52 52; |
| lanes_53 53; |
| lanes_54 54; |
| lanes_55 55; |
| lanes_56 56; |
| lanes_57 57; |
| lanes_58 58; |
| lanes_59 59; |
| lanes_60 60; |
| lanes_61 61; |
| lanes_62 62; |
| lanes_63 63; |
| ); |
| } |
| )* |
| } |
| } |