Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BTree: share panicky test code & test panic during clear, clone #81300

Merged
merged 2 commits into from
Feb 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
285 changes: 154 additions & 131 deletions library/alloc/src/collections/btree/map/tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use super::super::{node, DeterministicRng};
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};
use super::*;
use crate::boxed::Box;
Expand All @@ -15,9 +17,6 @@ use std::ops::RangeBounds;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};

mod ord_chaos;
use ord_chaos::{Cyclic3, Governed, Governor};

// Capacity of a tree with a single level,
// i.e., a tree who's root is a leaf node at height 0.
const NODE_CAPACITY: usize = node::CAPACITY;
Expand Down Expand Up @@ -1136,103 +1135,78 @@ 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:
let result = catch_unwind(AssertUnwindSafe(|| it.next()));
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();
}
}
Expand Down Expand Up @@ -1439,6 +1413,43 @@ fn test_bad_zst() {
m.check();
}

#[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();
Expand Down Expand Up @@ -1484,6 +1495,35 @@ fn test_clone() {
map.check();
}

#[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();
Expand Down Expand Up @@ -1901,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]
Expand Down Expand Up @@ -2050,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);
}
}
}

Expand Down
Loading