diff options
| author | Stein Somers <git@steinsomers.be> | 2021-01-23 15:25:56 +0100 |
|---|---|---|
| committer | Stein Somers <git@steinsomers.be> | 2021-02-09 12:33:18 +0100 |
| commit | 3e1d602a6b3ed491e2188176addf41a059c1ff02 (patch) | |
| tree | a5261a5b5f065ffe8041cc9dbf07353ab99dc389 /library/alloc/src | |
| parent | 6d2247eac2256b3f6e8d10e3bc9c3b6068d74967 (diff) | |
| download | rust-3e1d602a6b3ed491e2188176addf41a059c1ff02.tar.gz rust-3e1d602a6b3ed491e2188176addf41a059c1ff02.zip | |
BTreeMap: share panicky test code & test panic during clear, clone
Diffstat (limited to 'library/alloc/src')
| -rw-r--r-- | library/alloc/src/collections/btree/map/tests.rs | 279 | ||||
| -rw-r--r-- | library/alloc/src/collections/btree/set/tests.rs | 87 | ||||
| -rw-r--r-- | library/alloc/src/collections/btree/testing/crash_test.rs | 119 | ||||
| -rw-r--r-- | library/alloc/src/collections/btree/testing/mod.rs | 1 |
4 files changed, 303 insertions, 183 deletions
diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 73607a2a638..56d6ae57e04 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -1,3 +1,4 @@ +use super::super::testing::crash_test::{CrashTestDummy, Panic}; use super::super::testing::ord_chaos::{Cyclic3, Governed, Governor}; use super::super::testing::rng::DeterministicRng; use super::Entry::{Occupied, Vacant}; @@ -1134,91 +1135,62 @@ mod test_drain_filter { #[test] fn drop_panic_leak() { - static PREDS: AtomicUsize = AtomicUsize::new(0); - static DROPS: AtomicUsize = AtomicUsize::new(0); - - struct D; - impl Drop for D { - fn drop(&mut self) { - if DROPS.fetch_add(1, SeqCst) == 1 { - panic!("panic in `drop`"); - } - } - } - - // Keys are multiples of 4, so that each key is counted by a hexadecimal digit. - let mut map = (0..3).map(|i| (i * 4, D)).collect::<BTreeMap<_, _>>(); + let a = CrashTestDummy::new(0); + let b = CrashTestDummy::new(1); + let c = CrashTestDummy::new(2); + let mut map = BTreeMap::new(); + map.insert(a.spawn(Panic::Never), ()); + map.insert(b.spawn(Panic::InDrop), ()); + map.insert(c.spawn(Panic::Never), ()); - catch_unwind(move || { - drop(map.drain_filter(|i, _| { - PREDS.fetch_add(1usize << i, SeqCst); - true - })) - }) - .unwrap_err(); + catch_unwind(move || drop(map.drain_filter(|dummy, _| dummy.query(true)))).unwrap_err(); - assert_eq!(PREDS.load(SeqCst), 0x011); - assert_eq!(DROPS.load(SeqCst), 3); + assert_eq!(a.queried(), 1); + assert_eq!(b.queried(), 1); + assert_eq!(c.queried(), 0); + assert_eq!(a.dropped(), 1); + assert_eq!(b.dropped(), 1); + assert_eq!(c.dropped(), 1); } #[test] fn pred_panic_leak() { - static PREDS: AtomicUsize = AtomicUsize::new(0); - static DROPS: AtomicUsize = AtomicUsize::new(0); - - struct D; - impl Drop for D { - fn drop(&mut self) { - DROPS.fetch_add(1, SeqCst); - } - } - - // Keys are multiples of 4, so that each key is counted by a hexadecimal digit. - let mut map = (0..3).map(|i| (i * 4, D)).collect::<BTreeMap<_, _>>(); - - catch_unwind(AssertUnwindSafe(|| { - drop(map.drain_filter(|i, _| { - PREDS.fetch_add(1usize << i, SeqCst); - match i { - 0 => true, - _ => panic!(), - } - })) - })) - .unwrap_err(); - - assert_eq!(PREDS.load(SeqCst), 0x011); - assert_eq!(DROPS.load(SeqCst), 1); + let a = CrashTestDummy::new(0); + let b = CrashTestDummy::new(1); + let c = CrashTestDummy::new(2); + let mut map = BTreeMap::new(); + map.insert(a.spawn(Panic::Never), ()); + map.insert(b.spawn(Panic::InQuery), ()); + map.insert(c.spawn(Panic::InQuery), ()); + + catch_unwind(AssertUnwindSafe(|| drop(map.drain_filter(|dummy, _| dummy.query(true))))) + .unwrap_err(); + + assert_eq!(a.queried(), 1); + assert_eq!(b.queried(), 1); + assert_eq!(c.queried(), 0); + assert_eq!(a.dropped(), 1); + assert_eq!(b.dropped(), 0); + assert_eq!(c.dropped(), 0); assert_eq!(map.len(), 2); - assert_eq!(map.first_entry().unwrap().key(), &4); - assert_eq!(map.last_entry().unwrap().key(), &8); + assert_eq!(map.first_entry().unwrap().key().id(), 1); + assert_eq!(map.last_entry().unwrap().key().id(), 2); map.check(); } // Same as above, but attempt to use the iterator again after the panic in the predicate #[test] fn pred_panic_reuse() { - static PREDS: AtomicUsize = AtomicUsize::new(0); - static DROPS: AtomicUsize = AtomicUsize::new(0); - - struct D; - impl Drop for D { - fn drop(&mut self) { - DROPS.fetch_add(1, SeqCst); - } - } - - // Keys are multiples of 4, so that each key is counted by a hexadecimal digit. - let mut map = (0..3).map(|i| (i * 4, D)).collect::<BTreeMap<_, _>>(); + let a = CrashTestDummy::new(0); + let b = CrashTestDummy::new(1); + let c = CrashTestDummy::new(2); + let mut map = BTreeMap::new(); + map.insert(a.spawn(Panic::Never), ()); + map.insert(b.spawn(Panic::InQuery), ()); + map.insert(c.spawn(Panic::InQuery), ()); { - let mut it = map.drain_filter(|i, _| { - PREDS.fetch_add(1usize << i, SeqCst); - match i { - 0 => true, - _ => panic!(), - } - }); + let mut it = map.drain_filter(|dummy, _| dummy.query(true)); catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err(); // Iterator behaviour after a panic is explicitly unspecified, // so this is just the current implementation: @@ -1226,11 +1198,15 @@ mod test_drain_filter { assert!(matches!(result, Ok(None))); } - assert_eq!(PREDS.load(SeqCst), 0x011); - assert_eq!(DROPS.load(SeqCst), 1); + assert_eq!(a.queried(), 1); + assert_eq!(b.queried(), 1); + assert_eq!(c.queried(), 0); + assert_eq!(a.dropped(), 1); + assert_eq!(b.dropped(), 0); + assert_eq!(c.dropped(), 0); assert_eq!(map.len(), 2); - assert_eq!(map.first_entry().unwrap().key(), &4); - assert_eq!(map.last_entry().unwrap().key(), &8); + assert_eq!(map.first_entry().unwrap().key().id(), 1); + assert_eq!(map.last_entry().unwrap().key().id(), 2); map.check(); } } @@ -1438,6 +1414,43 @@ fn test_bad_zst() { } #[test] +fn test_clear() { + let mut map = BTreeMap::new(); + for &len in &[MIN_INSERTS_HEIGHT_1, MIN_INSERTS_HEIGHT_2, 0, NODE_CAPACITY] { + for i in 0..len { + map.insert(i, ()); + } + assert_eq!(map.len(), len); + map.clear(); + map.check(); + assert!(map.is_empty()); + } +} + +#[test] +fn test_clear_drop_panic_leak() { + let a = CrashTestDummy::new(0); + let b = CrashTestDummy::new(1); + let c = CrashTestDummy::new(2); + + let mut map = BTreeMap::new(); + map.insert(a.spawn(Panic::Never), ()); + map.insert(b.spawn(Panic::InDrop), ()); + map.insert(c.spawn(Panic::Never), ()); + + catch_unwind(AssertUnwindSafe(|| map.clear())).unwrap_err(); + assert_eq!(a.dropped(), 1); + assert_eq!(b.dropped(), 1); + assert_eq!(c.dropped(), 1); + assert_eq!(map.len(), 0); + + drop(map); + assert_eq!(a.dropped(), 1); + assert_eq!(b.dropped(), 1); + assert_eq!(c.dropped(), 1); +} + +#[test] fn test_clone() { let mut map = BTreeMap::new(); let size = MIN_INSERTS_HEIGHT_1; @@ -1483,6 +1496,35 @@ fn test_clone() { } #[test] +fn test_clone_panic_leak() { + let a = CrashTestDummy::new(0); + let b = CrashTestDummy::new(1); + let c = CrashTestDummy::new(2); + + let mut map = BTreeMap::new(); + map.insert(a.spawn(Panic::Never), ()); + map.insert(b.spawn(Panic::InClone), ()); + map.insert(c.spawn(Panic::Never), ()); + + catch_unwind(|| map.clone()).unwrap_err(); + assert_eq!(a.cloned(), 1); + assert_eq!(b.cloned(), 1); + assert_eq!(c.cloned(), 0); + assert_eq!(a.dropped(), 1); + assert_eq!(b.dropped(), 0); + assert_eq!(c.dropped(), 0); + assert_eq!(map.len(), 3); + + drop(map); + assert_eq!(a.cloned(), 1); + assert_eq!(b.cloned(), 1); + assert_eq!(c.cloned(), 0); + assert_eq!(a.dropped(), 2); + assert_eq!(b.dropped(), 1); + assert_eq!(c.dropped(), 1); +} + +#[test] fn test_clone_from() { let mut map1 = BTreeMap::new(); let max_size = MIN_INSERTS_HEIGHT_1; @@ -1899,29 +1941,21 @@ create_append_test!(test_append_1700, 1700); #[test] fn test_append_drop_leak() { - static DROPS: AtomicUsize = AtomicUsize::new(0); - - struct D; - - impl Drop for D { - fn drop(&mut self) { - if DROPS.fetch_add(1, SeqCst) == 0 { - panic!("panic in `drop`"); - } - } - } - + let a = CrashTestDummy::new(0); + let b = CrashTestDummy::new(1); + let c = CrashTestDummy::new(2); let mut left = BTreeMap::new(); let mut right = BTreeMap::new(); - left.insert(0, D); - left.insert(1, D); // first to be dropped during append - left.insert(2, D); - right.insert(1, D); - right.insert(2, D); + left.insert(a.spawn(Panic::Never), ()); + left.insert(b.spawn(Panic::InDrop), ()); // first duplicate key, dropped during append + left.insert(c.spawn(Panic::Never), ()); + right.insert(b.spawn(Panic::Never), ()); + right.insert(c.spawn(Panic::Never), ()); catch_unwind(move || left.append(&mut right)).unwrap_err(); - - assert_eq!(DROPS.load(SeqCst), 4); // Rust issue #47949 ate one little piggy + assert_eq!(a.dropped(), 1); + assert_eq!(b.dropped(), 1); // should be 2 were it not for Rust issue #47949 + assert_eq!(c.dropped(), 2); } #[test] @@ -2048,51 +2082,42 @@ fn test_split_off_large_random_sorted() { #[test] fn test_into_iter_drop_leak_height_0() { - static DROPS: AtomicUsize = AtomicUsize::new(0); - - struct D; - - impl Drop for D { - fn drop(&mut self) { - if DROPS.fetch_add(1, SeqCst) == 3 { - panic!("panic in `drop`"); - } - } - } - + let a = CrashTestDummy::new(0); + let b = CrashTestDummy::new(1); + let c = CrashTestDummy::new(2); + let d = CrashTestDummy::new(3); + let e = CrashTestDummy::new(4); let mut map = BTreeMap::new(); - map.insert("a", D); - map.insert("b", D); - map.insert("c", D); - map.insert("d", D); - map.insert("e", D); + map.insert("a", a.spawn(Panic::Never)); + map.insert("b", b.spawn(Panic::Never)); + map.insert("c", c.spawn(Panic::Never)); + map.insert("d", d.spawn(Panic::InDrop)); + map.insert("e", e.spawn(Panic::Never)); catch_unwind(move || drop(map.into_iter())).unwrap_err(); - assert_eq!(DROPS.load(SeqCst), 5); + assert_eq!(a.dropped(), 1); + assert_eq!(b.dropped(), 1); + assert_eq!(c.dropped(), 1); + assert_eq!(d.dropped(), 1); + assert_eq!(e.dropped(), 1); } #[test] fn test_into_iter_drop_leak_height_1() { let size = MIN_INSERTS_HEIGHT_1; - static DROPS: AtomicUsize = AtomicUsize::new(0); - static PANIC_POINT: AtomicUsize = AtomicUsize::new(0); - - struct D; - impl Drop for D { - fn drop(&mut self) { - if DROPS.fetch_add(1, SeqCst) == PANIC_POINT.load(SeqCst) { - panic!("panic in `drop`"); - } - } - } - for panic_point in vec![0, 1, size - 2, size - 1] { - DROPS.store(0, SeqCst); - PANIC_POINT.store(panic_point, SeqCst); - let map: BTreeMap<_, _> = (0..size).map(|i| (i, D)).collect(); + let dummies: Vec<_> = (0..size).map(|i| CrashTestDummy::new(i)).collect(); + let map: BTreeMap<_, _> = (0..size) + .map(|i| { + let panic = if i == panic_point { Panic::InDrop } else { Panic::Never }; + (dummies[i].spawn(Panic::Never), dummies[i].spawn(panic)) + }) + .collect(); catch_unwind(move || drop(map.into_iter())).unwrap_err(); - assert_eq!(DROPS.load(SeqCst), size); + for i in 0..size { + assert_eq!(dummies[i].dropped(), 2); + } } } diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index 9c6fb44af43..4cb6e3d6619 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -1,10 +1,10 @@ +use super::super::testing::crash_test::{CrashTestDummy, Panic}; use super::super::testing::rng::DeterministicRng; use super::*; use crate::vec::Vec; use std::cmp::Ordering; use std::iter::FromIterator; use std::panic::{catch_unwind, AssertUnwindSafe}; -use std::sync::atomic::{AtomicU32, Ordering::SeqCst}; #[test] fn test_clone_eq() { @@ -349,70 +349,45 @@ fn test_drain_filter() { #[test] fn test_drain_filter_drop_panic_leak() { - static PREDS: AtomicU32 = AtomicU32::new(0); - static DROPS: AtomicU32 = AtomicU32::new(0); - - #[derive(PartialEq, Eq, PartialOrd, Ord)] - struct D(i32); - impl Drop for D { - fn drop(&mut self) { - if DROPS.fetch_add(1, SeqCst) == 1 { - panic!("panic in `drop`"); - } - } - } - + let a = CrashTestDummy::new(0); + let b = CrashTestDummy::new(1); + let c = CrashTestDummy::new(2); let mut set = BTreeSet::new(); - set.insert(D(0)); - set.insert(D(4)); - set.insert(D(8)); + set.insert(a.spawn(Panic::Never)); + set.insert(b.spawn(Panic::InDrop)); + set.insert(c.spawn(Panic::Never)); - catch_unwind(move || { - drop(set.drain_filter(|d| { - PREDS.fetch_add(1u32 << d.0, SeqCst); - true - })) - }) - .ok(); + catch_unwind(move || drop(set.drain_filter(|dummy| dummy.query(true)))).ok(); - assert_eq!(PREDS.load(SeqCst), 0x011); - assert_eq!(DROPS.load(SeqCst), 3); + assert_eq!(a.queried(), 1); + assert_eq!(b.queried(), 1); + assert_eq!(c.queried(), 0); + assert_eq!(a.dropped(), 1); + assert_eq!(b.dropped(), 1); + assert_eq!(c.dropped(), 1); } #[test] fn test_drain_filter_pred_panic_leak() { - static PREDS: AtomicU32 = AtomicU32::new(0); - static DROPS: AtomicU32 = AtomicU32::new(0); - - #[derive(PartialEq, Eq, PartialOrd, Ord)] - struct D(i32); - impl Drop for D { - fn drop(&mut self) { - DROPS.fetch_add(1, SeqCst); - } - } - + let a = CrashTestDummy::new(0); + let b = CrashTestDummy::new(1); + let c = CrashTestDummy::new(2); let mut set = BTreeSet::new(); - set.insert(D(0)); - set.insert(D(4)); - set.insert(D(8)); - - catch_unwind(AssertUnwindSafe(|| { - drop(set.drain_filter(|d| { - PREDS.fetch_add(1u32 << d.0, SeqCst); - match d.0 { - 0 => true, - _ => panic!(), - } - })) - })) - .ok(); - - assert_eq!(PREDS.load(SeqCst), 0x011); - assert_eq!(DROPS.load(SeqCst), 1); + set.insert(a.spawn(Panic::Never)); + set.insert(b.spawn(Panic::InQuery)); + set.insert(c.spawn(Panic::InQuery)); + + catch_unwind(AssertUnwindSafe(|| drop(set.drain_filter(|dummy| dummy.query(true))))).ok(); + + assert_eq!(a.queried(), 1); + assert_eq!(b.queried(), 1); + assert_eq!(c.queried(), 0); + assert_eq!(a.dropped(), 1); + assert_eq!(b.dropped(), 0); + assert_eq!(c.dropped(), 0); assert_eq!(set.len(), 2); - assert_eq!(set.first().unwrap().0, 4); - assert_eq!(set.last().unwrap().0, 8); + assert_eq!(set.first().unwrap().id(), 1); + assert_eq!(set.last().unwrap().id(), 2); } #[test] diff --git a/library/alloc/src/collections/btree/testing/crash_test.rs b/library/alloc/src/collections/btree/testing/crash_test.rs new file mode 100644 index 00000000000..b2527b95f5b --- /dev/null +++ b/library/alloc/src/collections/btree/testing/crash_test.rs @@ -0,0 +1,119 @@ +use crate::fmt::Debug; +use std::cmp::Ordering; +use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; + +/// A blueprint for crash test dummy instances that monitor particular events. +/// Some instances may be configured to panic at some point. +/// Events are `clone`, `drop` or some anonymous `query`. +/// +/// Crash test dummies are identified and ordered by an id, so they can be used +/// as keys in a BTreeMap. The implementation intentionally uses does not rely +/// on anything defined in the crate, apart from the `Debug` trait. +#[derive(Debug)] +pub struct CrashTestDummy { + id: usize, + cloned: AtomicUsize, + dropped: AtomicUsize, + queried: AtomicUsize, +} + +impl CrashTestDummy { + /// Creates a crash test dummy design. The `id` determines order and equality of instances. + pub fn new(id: usize) -> CrashTestDummy { + CrashTestDummy { + id, + cloned: AtomicUsize::new(0), + dropped: AtomicUsize::new(0), + queried: AtomicUsize::new(0), + } + } + + /// Creates an instance of a crash test dummy that records what events it experiences + /// and optionally panics. + pub fn spawn(&self, panic: Panic) -> Instance<'_> { + Instance { origin: self, panic } + } + + /// Returns how many times instances of the dummy have been cloned. + pub fn cloned(&self) -> usize { + self.cloned.load(SeqCst) + } + + /// Returns how many times instances of the dummy have been dropped. + pub fn dropped(&self) -> usize { + self.dropped.load(SeqCst) + } + + /// Returns how many times instances of the dummy have had their `query` member invoked. + pub fn queried(&self) -> usize { + self.queried.load(SeqCst) + } +} + +#[derive(Debug)] +pub struct Instance<'a> { + origin: &'a CrashTestDummy, + panic: Panic, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Panic { + Never, + InClone, + InDrop, + InQuery, +} + +impl Instance<'_> { + pub fn id(&self) -> usize { + self.origin.id + } + + /// Some anonymous query, the result of which is already given. + pub fn query<R>(&self, result: R) -> R { + self.origin.queried.fetch_add(1, SeqCst); + if self.panic == Panic::InQuery { + panic!("panic in `query`"); + } + result + } +} + +impl Clone for Instance<'_> { + fn clone(&self) -> Self { + self.origin.cloned.fetch_add(1, SeqCst); + if self.panic == Panic::InClone { + panic!("panic in `clone`"); + } + Self { origin: self.origin, panic: Panic::Never } + } +} + +impl Drop for Instance<'_> { + fn drop(&mut self) { + self.origin.dropped.fetch_add(1, SeqCst); + if self.panic == Panic::InDrop { + panic!("panic in `drop`"); + } + } +} + +impl PartialOrd for Instance<'_> { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + self.id().partial_cmp(&other.id()) + } +} + +impl Ord for Instance<'_> { + fn cmp(&self, other: &Self) -> Ordering { + self.id().cmp(&other.id()) + } +} + +impl PartialEq for Instance<'_> { + fn eq(&self, other: &Self) -> bool { + self.id().eq(&other.id()) + } +} + +impl Eq for Instance<'_> {} diff --git a/library/alloc/src/collections/btree/testing/mod.rs b/library/alloc/src/collections/btree/testing/mod.rs index 03880e7014e..7a094f8a595 100644 --- a/library/alloc/src/collections/btree/testing/mod.rs +++ b/library/alloc/src/collections/btree/testing/mod.rs @@ -1,2 +1,3 @@ +pub mod crash_test; pub mod ord_chaos; pub mod rng; |
