use crate::borrow::ToOwned; use crate::rc::Rc; use crate::string::ToString; use crate::test_helpers::test_rng; use crate::vec::Vec; use core::cell::Cell; use core::cmp::Ordering::{self, Equal, Greater, Less}; use core::convert::identity; use core::fmt; use core::mem; use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; use rand::{distributions::Standard, prelude::*, Rng, RngCore}; use std::panic; macro_rules! do_test { ($input:ident, $func:ident) => { let len = $input.len(); // Work out the total number of comparisons required to sort // this array... let mut count = 0usize; $input.to_owned().$func(|a, b| { count += 1; a.cmp(b) }); // ... and then panic on each and every single one. for panic_countdown in 0..count { // Refresh the counters. VERSIONS.store(0, Relaxed); for i in 0..len { DROP_COUNTS[i].store(0, Relaxed); } let v = $input.to_owned(); let _ = std::panic::catch_unwind(move || { let mut v = v; let mut panic_countdown = panic_countdown; v.$func(|a, b| { if panic_countdown == 0 { SILENCE_PANIC.with(|s| s.set(true)); panic!(); } panic_countdown -= 1; a.cmp(b) }) }); // Check that the number of things dropped is exactly // what we expect (i.e., the contents of `v`). for (i, c) in DROP_COUNTS.iter().enumerate().take(len) { let count = c.load(Relaxed); assert!(count == 1, "found drop count == {} for i == {}, len == {}", count, i, len); } // Check that the most recent versions of values were dropped. assert_eq!(VERSIONS.load(Relaxed), 0); } }; } const MAX_LEN: usize = 80; static DROP_COUNTS: [AtomicUsize; MAX_LEN] = [ // FIXME(RFC 1109): AtomicUsize is not Copy. AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), ]; static VERSIONS: AtomicUsize = AtomicUsize::new(0); #[derive(Clone, Eq)] struct DropCounter { x: u32, id: usize, version: Cell, } impl PartialEq for DropCounter { fn eq(&self, other: &Self) -> bool { self.partial_cmp(other) == Some(Ordering::Equal) } } impl PartialOrd for DropCounter { fn partial_cmp(&self, other: &Self) -> Option { self.version.set(self.version.get() + 1); other.version.set(other.version.get() + 1); VERSIONS.fetch_add(2, Relaxed); self.x.partial_cmp(&other.x) } } impl Ord for DropCounter { fn cmp(&self, other: &Self) -> Ordering { self.partial_cmp(other).unwrap() } } impl Drop for DropCounter { fn drop(&mut self) { DROP_COUNTS[self.id].fetch_add(1, Relaxed); VERSIONS.fetch_sub(self.version.get(), Relaxed); } } std::thread_local!(static SILENCE_PANIC: Cell = Cell::new(false)); #[test] #[cfg_attr(target_os = "emscripten", ignore)] // no threads #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn panic_safe() { panic::update_hook(move |prev, info| { if !SILENCE_PANIC.with(|s| s.get()) { prev(info); } }); let mut rng = test_rng(); // Miri is too slow (but still need to `chain` to make the types match) let lens = if cfg!(miri) { (1..10).chain(0..0) } else { (1..20).chain(70..MAX_LEN) }; let moduli: &[u32] = if cfg!(miri) { &[5] } else { &[5, 20, 50] }; for len in lens { for &modulus in moduli { for &has_runs in &[false, true] { let mut input = (0..len) .map(|id| DropCounter { x: rng.next_u32() % modulus, id: id, version: Cell::new(0), }) .collect::>(); if has_runs { for c in &mut input { c.x = c.id as u32; } for _ in 0..5 { let a = rng.gen::() % len; let b = rng.gen::() % len; if a < b { input[a..b].reverse(); } else { input.swap(a, b); } } } do_test!(input, sort_by); do_test!(input, sort_unstable_by); } } } // Set default panic hook again. drop(panic::take_hook()); } #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn test_sort() { let mut rng = test_rng(); for len in (2..25).chain(500..510) { for &modulus in &[5, 10, 100, 1000] { for _ in 0..10 { let orig: Vec<_> = (&mut rng) .sample_iter::(&Standard) .map(|x| x % modulus) .take(len) .collect(); // Sort in default order. let mut v = orig.clone(); v.sort(); assert!(v.windows(2).all(|w| w[0] <= w[1])); // Sort in ascending order. let mut v = orig.clone(); v.sort_by(|a, b| a.cmp(b)); assert!(v.windows(2).all(|w| w[0] <= w[1])); // Sort in descending order. let mut v = orig.clone(); v.sort_by(|a, b| b.cmp(a)); assert!(v.windows(2).all(|w| w[0] >= w[1])); // Sort in lexicographic order. let mut v1 = orig.clone(); let mut v2 = orig.clone(); v1.sort_by_key(|x| x.to_string()); v2.sort_by_cached_key(|x| x.to_string()); assert!(v1.windows(2).all(|w| w[0].to_string() <= w[1].to_string())); assert!(v1 == v2); // Sort with many pre-sorted runs. let mut v = orig.clone(); v.sort(); v.reverse(); for _ in 0..5 { let a = rng.gen::() % len; let b = rng.gen::() % len; if a < b { v[a..b].reverse(); } else { v.swap(a, b); } } v.sort(); assert!(v.windows(2).all(|w| w[0] <= w[1])); } } } // Sort using a completely random comparison function. // This will reorder the elements *somehow*, but won't panic. let mut v = [0; 500]; for i in 0..v.len() { v[i] = i as i32; } v.sort_by(|_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); v.sort(); for i in 0..v.len() { assert_eq!(v[i], i as i32); } // Should not panic. [0i32; 0].sort(); [(); 10].sort(); [(); 100].sort(); let mut v = [0xDEADBEEFu64]; v.sort(); assert!(v == [0xDEADBEEF]); } #[test] fn test_sort_stability() { // Miri is too slow let large_range = if cfg!(miri) { 0..0 } else { 500..510 }; let rounds = if cfg!(miri) { 1 } else { 10 }; let mut rng = test_rng(); for len in (2..25).chain(large_range) { for _ in 0..rounds { let mut counts = [0; 10]; // create a vector like [(6, 1), (5, 1), (6, 2), ...], // where the first item of each tuple is random, but // the second item represents which occurrence of that // number this element is, i.e., the second elements // will occur in sorted order. let orig: Vec<_> = (0..len) .map(|_| { let n = rng.gen::() % 10; counts[n] += 1; (n, counts[n]) }) .collect(); let mut v = orig.clone(); // Only sort on the first element, so an unstable sort // may mix up the counts. v.sort_by(|&(a, _), &(b, _)| a.cmp(&b)); // This comparison includes the count (the second item // of the tuple), so elements with equal first items // will need to be ordered with increasing // counts... i.e., exactly asserting that this sort is // stable. assert!(v.windows(2).all(|w| w[0] <= w[1])); let mut v = orig.clone(); v.sort_by_cached_key(|&(x, _)| x); assert!(v.windows(2).all(|w| w[0] <= w[1])); } } }