| use std::sync::LazyLock; |
| |
| // All (negative) integers which are at or near a power of two to test |
| // boundary conditions. We use strings so we can convert to any type using |
| // parsing, while still being able to use the *position* in ORDERED_VALS for |
| // comparisons. |
| static ORDERED_VALS: LazyLock<Vec<String>> = LazyLock::new(|| { |
| let mut pos_int_vals = Vec::new(); |
| for exp in 0..=127 { |
| let val = 1_u128 << exp; |
| pos_int_vals.push(val.saturating_sub(2)); |
| pos_int_vals.push(val.saturating_sub(1)); |
| pos_int_vals.push(val); |
| pos_int_vals.push(val.saturating_add(1)); |
| pos_int_vals.push(val.saturating_add(2)); |
| } |
| pos_int_vals.sort(); |
| pos_int_vals.dedup(); |
| |
| let mut pos_str_vals: Vec<_> = pos_int_vals.iter().map(|i| i.to_string()).collect(); |
| |
| // These are manual because the upper ones overflow even u128. |
| pos_str_vals.push("340282366920938463463374607431768211454".to_owned()); // 2**128 - 2 |
| pos_str_vals.push("340282366920938463463374607431768211455".to_owned()); // 2**128 - 1 |
| pos_str_vals.push("340282366920938463463374607431768211456".to_owned()); // 2**128 |
| pos_str_vals.push("340282366920938463463374607431768211457".to_owned()); // 2**128 + 1 |
| pos_str_vals.push("340282366920938463463374607431768211458".to_owned()); // 2**128 + 2 |
| |
| let mut out = Vec::new(); |
| for val in pos_str_vals[1..].iter().rev() { |
| out.push(format!("-{val}")); |
| } |
| out.extend(pos_str_vals); |
| out |
| }); |
| |
| macro_rules! make_checked_cast_test { |
| ($Src:ident as [$($Dst:ident),*]) => {$( |
| #[test] |
| #[allow(non_snake_case)] |
| fn ${concat(test_checked_cast_, $Src, _to_, $Dst)}() { |
| for val in ORDERED_VALS.iter() { |
| if let Some(src) = val.parse::<$Src>().ok() { |
| let dst: Option<$Dst> = val.parse().ok(); |
| assert_eq!(src.checked_cast::<$Dst>(), dst); |
| } |
| } |
| } |
| )*} |
| } |
| |
| macro_rules! make_bounded_cast_test { |
| (|$src:ident| $raw:expr, $Src:ident as [$($Dst:ident),*]) => {$( |
| #[test] |
| #[allow(non_snake_case)] |
| fn ${concat(test_bounded_cast_, $Src, _to_, $Dst)}() { |
| let ord_idx = |s| ORDERED_VALS.iter().position(|v| *v == s).unwrap(); |
| let dst_min_idx = ord_idx(<$Dst>::MIN.to_string()); |
| let dst_max_idx = ord_idx(<$Dst>::MAX.to_string()); |
| for (val_idx, val) in ORDERED_VALS.iter().enumerate() { |
| if let Some($src) = val.parse::<$Src>().ok() { |
| let dst: Option<$Dst> = val.parse().ok(); |
| |
| assert_eq!($src.wrapping_cast::<$Dst>(), $raw as $Dst); |
| |
| if val_idx > dst_max_idx { |
| assert_eq!($src.saturating_cast::<$Dst>(), <$Dst>::MAX); |
| } else if val_idx < dst_min_idx { |
| assert_eq!($src.saturating_cast::<$Dst>(), <$Dst>::MIN); |
| } else { |
| assert_eq!($src.saturating_cast::<$Dst>(), dst.unwrap()); |
| } |
| } |
| } |
| } |
| )*} |
| } |
| |
| macro_rules! make_tests_for_src { |
| (|$src:ident| $raw:expr, [$($Src:ident),*]) => {$( |
| make_checked_cast_test!( $Src as [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]); |
| make_bounded_cast_test!(|$src| $raw, $Src as [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]); |
| |
| // NonZero types are not (yet) implemented. |
| // make_checked_cast_test!($Src as [ |
| // NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, |
| // NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize |
| // ]); |
| )*} |
| } |
| |
| make_tests_for_src!(|x| x, [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize]); |
| |
| // NonZero types are not (yet) implemented. |
| // make_tests_for_src!( |
| // |x| x.get(), |
| // [ |
| // NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, |
| // NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize |
| // ] |
| // ); |