Skip to content

Commit

Permalink
Rollup merge of rust-lang#94145 - ssomers:binary_heap_tests, r=jyn514
Browse files Browse the repository at this point in the history
Test leaking of BinaryHeap Drain iterators

Add test cases about forgetting the `BinaryHeap::Drain` iterator, and slightly fortifies some other test cases.

Consists of separate commits that I don't think are relevant on their own (but I'll happily turn these into more PRs if desired).
  • Loading branch information
matthiaskrgr authored Dec 28, 2022
2 parents 739d68a + a80e685 commit 05f567a
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 60 deletions.
98 changes: 74 additions & 24 deletions library/alloc/src/collections/binary_heap/tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::*;
use crate::boxed::Box;
use crate::testing::crash_test::{CrashTestDummy, Panic};
use std::iter::TrustedLen;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::sync::atomic::{AtomicU32, Ordering};

#[test]
fn test_iterator() {
Expand Down Expand Up @@ -291,33 +291,83 @@ fn test_drain_sorted() {

#[test]
fn test_drain_sorted_leak() {
static DROPS: AtomicU32 = AtomicU32::new(0);

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
struct D(u32, bool);

impl Drop for D {
fn drop(&mut self) {
DROPS.fetch_add(1, Ordering::SeqCst);

if self.1 {
panic!("panic in `drop`");
}
}
}

let d0 = CrashTestDummy::new(0);
let d1 = CrashTestDummy::new(1);
let d2 = CrashTestDummy::new(2);
let d3 = CrashTestDummy::new(3);
let d4 = CrashTestDummy::new(4);
let d5 = CrashTestDummy::new(5);
let mut q = BinaryHeap::from(vec![
D(0, false),
D(1, false),
D(2, false),
D(3, true),
D(4, false),
D(5, false),
d0.spawn(Panic::Never),
d1.spawn(Panic::Never),
d2.spawn(Panic::Never),
d3.spawn(Panic::InDrop),
d4.spawn(Panic::Never),
d5.spawn(Panic::Never),
]);

catch_unwind(AssertUnwindSafe(|| drop(q.drain_sorted()))).ok();
catch_unwind(AssertUnwindSafe(|| drop(q.drain_sorted()))).unwrap_err();

assert_eq!(d0.dropped(), 1);
assert_eq!(d1.dropped(), 1);
assert_eq!(d2.dropped(), 1);
assert_eq!(d3.dropped(), 1);
assert_eq!(d4.dropped(), 1);
assert_eq!(d5.dropped(), 1);
assert!(q.is_empty());
}

assert_eq!(DROPS.load(Ordering::SeqCst), 6);
#[test]
fn test_drain_forget() {
let a = CrashTestDummy::new(0);
let b = CrashTestDummy::new(1);
let c = CrashTestDummy::new(2);
let mut q =
BinaryHeap::from(vec![a.spawn(Panic::Never), b.spawn(Panic::Never), c.spawn(Panic::Never)]);

catch_unwind(AssertUnwindSafe(|| {
let mut it = q.drain();
it.next();
mem::forget(it);
}))
.unwrap();
// Behaviour after leaking is explicitly unspecified and order is arbitrary,
// so it's fine if these start failing, but probably worth knowing.
assert!(q.is_empty());
assert_eq!(a.dropped() + b.dropped() + c.dropped(), 1);
assert_eq!(a.dropped(), 0);
assert_eq!(b.dropped(), 0);
assert_eq!(c.dropped(), 1);
drop(q);
assert_eq!(a.dropped(), 0);
assert_eq!(b.dropped(), 0);
assert_eq!(c.dropped(), 1);
}

#[test]
fn test_drain_sorted_forget() {
let a = CrashTestDummy::new(0);
let b = CrashTestDummy::new(1);
let c = CrashTestDummy::new(2);
let mut q =
BinaryHeap::from(vec![a.spawn(Panic::Never), b.spawn(Panic::Never), c.spawn(Panic::Never)]);

catch_unwind(AssertUnwindSafe(|| {
let mut it = q.drain_sorted();
it.next();
mem::forget(it);
}))
.unwrap();
// Behaviour after leaking is explicitly unspecified,
// so it's fine if these start failing, but probably worth knowing.
assert_eq!(q.len(), 2);
assert_eq!(a.dropped(), 0);
assert_eq!(b.dropped(), 0);
assert_eq!(c.dropped(), 1);
drop(q);
assert_eq!(a.dropped(), 1);
assert_eq!(b.dropped(), 1);
assert_eq!(c.dropped(), 1);
}

#[test]
Expand Down
6 changes: 3 additions & 3 deletions library/alloc/src/collections/btree/map/tests.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
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;
use crate::fmt::Debug;
use crate::rc::Rc;
use crate::string::{String, ToString};
use crate::testing::crash_test::{CrashTestDummy, Panic};
use crate::testing::ord_chaos::{Cyclic3, Governed, Governor};
use crate::testing::rng::DeterministicRng;
use crate::vec::Vec;
use std::cmp::Ordering;
use std::convert::TryFrom;
Expand Down
3 changes: 0 additions & 3 deletions library/alloc/src/collections/btree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,3 @@ trait Recover<Q: ?Sized> {
fn take(&mut self, key: &Q) -> Option<Self::Key>;
fn replace(&mut self, key: Self::Key) -> Option<Self::Key>;
}

#[cfg(test)]
mod testing;
4 changes: 2 additions & 2 deletions library/alloc/src/collections/btree/set/tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::super::testing::crash_test::{CrashTestDummy, Panic};
use super::super::testing::rng::DeterministicRng;
use super::*;
use crate::testing::crash_test::{CrashTestDummy, Panic};
use crate::testing::rng::DeterministicRng;
use crate::vec::Vec;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
Expand Down
56 changes: 28 additions & 28 deletions library/alloc/src/collections/linked_list/tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::*;
use crate::testing::crash_test::{CrashTestDummy, Panic};
use crate::vec::Vec;

use std::panic::{catch_unwind, AssertUnwindSafe};
Expand Down Expand Up @@ -984,35 +985,34 @@ fn drain_filter_complex() {

#[test]
fn drain_filter_drop_panic_leak() {
static mut DROPS: i32 = 0;

struct D(bool);

impl Drop for D {
fn drop(&mut self) {
unsafe {
DROPS += 1;
}

if self.0 {
panic!("panic in `drop`");
}
}
}

let d0 = CrashTestDummy::new(0);
let d1 = CrashTestDummy::new(1);
let d2 = CrashTestDummy::new(2);
let d3 = CrashTestDummy::new(3);
let d4 = CrashTestDummy::new(4);
let d5 = CrashTestDummy::new(5);
let d6 = CrashTestDummy::new(6);
let d7 = CrashTestDummy::new(7);
let mut q = LinkedList::new();
q.push_back(D(false));
q.push_back(D(false));
q.push_back(D(false));
q.push_back(D(false));
q.push_back(D(false));
q.push_front(D(false));
q.push_front(D(true));
q.push_front(D(false));

catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).ok();

assert_eq!(unsafe { DROPS }, 8);
q.push_back(d3.spawn(Panic::Never));
q.push_back(d4.spawn(Panic::Never));
q.push_back(d5.spawn(Panic::Never));
q.push_back(d6.spawn(Panic::Never));
q.push_back(d7.spawn(Panic::Never));
q.push_front(d2.spawn(Panic::Never));
q.push_front(d1.spawn(Panic::InDrop));
q.push_front(d0.spawn(Panic::Never));

catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).unwrap_err();

assert_eq!(d0.dropped(), 1);
assert_eq!(d1.dropped(), 1);
assert_eq!(d2.dropped(), 1);
assert_eq!(d3.dropped(), 1);
assert_eq!(d4.dropped(), 1);
assert_eq!(d5.dropped(), 1);
assert_eq!(d6.dropped(), 1);
assert_eq!(d7.dropped(), 1);
assert!(q.is_empty());
}

Expand Down
2 changes: 2 additions & 0 deletions library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@
extern crate std;
#[cfg(test)]
extern crate test;
#[cfg(test)]
mod testing;

// Module with internal macros used by other modules (needs to be included before other modules).
#[macro_use]
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit 05f567a

Please sign in to comment.