#[test] fn sort_bool() { let mut actual = [ true, false, true, true, true, false, true, true, false, false, false, ]; let mut expected = actual; radsort::sort(&mut actual); expected.sort(); assert_eq!(actual, expected); } #[test] fn sort_char() { #[rustfmt::skip] let mut actual = [ '\u{0}', '\u{1}', '\u{F}', '\u{7F}', // 1-byte sequence '\u{80}', '\u{81}', '\u{FF}', '\u{7FF}', // 2-byte sequence '\u{800}', '\u{801}', '\u{FFF}', '\u{FFFF}', // 3-byte sequence '\u{10000}', '\u{10001}', '\u{FFFFF}', '\u{10FFFF}' // 4-byte sequence ]; actual.reverse(); let mut expected = actual; radsort::sort(&mut actual); expected.sort(); assert_eq!(actual, expected); } #[test] #[allow(clippy::cognitive_complexity)] fn sort_integer() { macro_rules! implement { ($($t:ident)*) => ($( let mut actual = [ $t::MIN, $t::MIN+1, $t::MIN / 2, $t::MAX, $t::MAX-1, $t::MAX / 2, -1i8 as $t, 0, 1, ]; let mut expected = actual.clone(); expected.sort(); radsort::sort(&mut actual); assert_eq!(actual, expected); )*) } implement! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } } #[test] fn sort_float() { macro_rules! implement { ($($t:ident)*) => ($( let mut actual = [ 0.0, -0.0, 1.0, -1.0, $t::MIN, $t::MAX, $t::MIN_POSITIVE, -$t::MIN_POSITIVE, $t::EPSILON, -$t::EPSILON, $t::INFINITY, $t::NEG_INFINITY, ]; let mut expected = actual.clone(); expected.sort_by(|a, b| a.partial_cmp(b).unwrap()); radsort::sort(&mut actual); assert_eq!(actual, expected); )*) } implement! { f32 f64 } } #[test] #[allow(clippy::cognitive_complexity)] fn sort_cached() { macro_rules! implement { ($($t:ident)*) => ($( let mut actual = [ $t::MIN, $t::MIN+1, $t::MIN / 2, $t::MAX, $t::MAX-1, $t::MAX / 2, -1i8 as $t, 0, 1, ]; let mut expected = actual.clone(); expected.sort(); radsort::sort_by_cached_key(&mut actual, |t| *t); assert_eq!(actual, expected); )*) } implement! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } } #[test] fn sort_struct() { #[derive(PartialEq, Debug, Clone)] struct Data(u32); let source: Vec<_> = (0..512).map(Data).collect(); { // Sorting references let mut actual: Vec<&Data> = source.iter().collect(); let mut expected = actual.clone(); radsort::sort_by_key(&mut actual, |d| d.0); expected.sort_by_key(|d| d.0); assert_eq!(actual, expected); } { // Sorting actual values let mut actual = source.clone(); let mut expected = source; radsort::sort_by_key(&mut actual, |d| d.0); expected.sort_by_key(|d| d.0); assert_eq!(actual, expected); } } /// Test sorting by multiple keys in a tuple. #[test] fn sort_compound() { let mut actual = Vec::new(); for a in 0..10 { for b in 0..10 { for c in 0..10 { for d in 0..10 { actual.push([a, b, c, d]); } } } } actual.reverse(); let mut expected = actual.clone(); radsort::sort_by_key(&mut expected, |a| a[0]); radsort::sort_by_key(&mut actual, |a| (a[0],)); assert_eq!(actual, expected); radsort::sort_by_key(&mut expected, |a| a[1]); radsort::sort_by_key(&mut expected, |a| a[0]); radsort::sort_by_key(&mut actual, |a| (a[0], a[1])); assert_eq!(actual, expected); radsort::sort_by_key(&mut expected, |a| a[2]); radsort::sort_by_key(&mut expected, |a| a[1]); radsort::sort_by_key(&mut expected, |a| a[0]); radsort::sort_by_key(&mut actual, |a| (a[0], a[1], a[2])); assert_eq!(actual, expected); radsort::sort_by_key(&mut expected, |a| a[3]); radsort::sort_by_key(&mut expected, |a| a[2]); radsort::sort_by_key(&mut expected, |a| a[1]); radsort::sort_by_key(&mut expected, |a| a[0]); radsort::sort_by_key(&mut actual, |a| (a[0], a[1], a[2], a[3])); assert_eq!(actual, expected); } #[test] fn sort_zst() { let mut actual = [(); 10]; let mut expected = actual; expected.sort(); radsort::sort_by_key(&mut actual, |_| 0); assert_eq!(actual, expected); } /// Tests that unreliable key function gets detected #[test] #[should_panic( expected = "The key function is not reliable: when called repeatedly, \ it returned different keys for the same element." )] fn unreliable_key_function() { let mut key_fn_call_count = 0; let mut data: Vec = (200..300).collect(); radsort::sort_by_key(&mut data, |v| { key_fn_call_count += 1; if key_fn_call_count == 250 { !v // flip all the bits, changing the element bucket } else { *v } }); } /// Tests that the slice is left in a consistent state after a panic. #[test] fn exception_safety() { // Crossing u8::MAX boundary to make sure that values differ in both bytes // and a digit won't be skipped. let mut actual: Vec = (200..300).collect(); let expected = actual.clone(); actual.reverse(); let mut wrapper = std::panic::AssertUnwindSafe(actual.as_mut_slice()); let e = std::panic::catch_unwind(move || { let mut key_fn_call_count = 0; radsort::sort_by_key(*wrapper, |v| { key_fn_call_count += 1; if key_fn_call_count == 250 { // Second sorting pass, the slice is being written to panic!("panic in the key function"); } else { *v } }); }) .expect_err("panic was not thrown"); assert_eq!( *e.downcast::<&'static str>().expect("unexpected error"), "panic in the key function" ); actual.sort(); assert_eq!(expected, actual); }