Skip to content

Commit dc98aa6

Browse files
authored
Rollup merge of #94145 - ssomers:binary_heap_tests, r=jyn514
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).
2 parents 6a4624d + a80e685 commit dc98aa6

File tree

10 files changed

+109
-60
lines changed

10 files changed

+109
-60
lines changed

library/alloc/src/collections/binary_heap/tests.rs

+74-24
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use super::*;
22
use crate::boxed::Box;
3+
use crate::testing::crash_test::{CrashTestDummy, Panic};
34
use std::iter::TrustedLen;
45
use std::panic::{catch_unwind, AssertUnwindSafe};
5-
use std::sync::atomic::{AtomicU32, Ordering};
66

77
#[test]
88
fn test_iterator() {
@@ -291,33 +291,83 @@ fn test_drain_sorted() {
291291

292292
#[test]
293293
fn test_drain_sorted_leak() {
294-
static DROPS: AtomicU32 = AtomicU32::new(0);
295-
296-
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
297-
struct D(u32, bool);
298-
299-
impl Drop for D {
300-
fn drop(&mut self) {
301-
DROPS.fetch_add(1, Ordering::SeqCst);
302-
303-
if self.1 {
304-
panic!("panic in `drop`");
305-
}
306-
}
307-
}
308-
294+
let d0 = CrashTestDummy::new(0);
295+
let d1 = CrashTestDummy::new(1);
296+
let d2 = CrashTestDummy::new(2);
297+
let d3 = CrashTestDummy::new(3);
298+
let d4 = CrashTestDummy::new(4);
299+
let d5 = CrashTestDummy::new(5);
309300
let mut q = BinaryHeap::from(vec![
310-
D(0, false),
311-
D(1, false),
312-
D(2, false),
313-
D(3, true),
314-
D(4, false),
315-
D(5, false),
301+
d0.spawn(Panic::Never),
302+
d1.spawn(Panic::Never),
303+
d2.spawn(Panic::Never),
304+
d3.spawn(Panic::InDrop),
305+
d4.spawn(Panic::Never),
306+
d5.spawn(Panic::Never),
316307
]);
317308

318-
catch_unwind(AssertUnwindSafe(|| drop(q.drain_sorted()))).ok();
309+
catch_unwind(AssertUnwindSafe(|| drop(q.drain_sorted()))).unwrap_err();
310+
311+
assert_eq!(d0.dropped(), 1);
312+
assert_eq!(d1.dropped(), 1);
313+
assert_eq!(d2.dropped(), 1);
314+
assert_eq!(d3.dropped(), 1);
315+
assert_eq!(d4.dropped(), 1);
316+
assert_eq!(d5.dropped(), 1);
317+
assert!(q.is_empty());
318+
}
319319

320-
assert_eq!(DROPS.load(Ordering::SeqCst), 6);
320+
#[test]
321+
fn test_drain_forget() {
322+
let a = CrashTestDummy::new(0);
323+
let b = CrashTestDummy::new(1);
324+
let c = CrashTestDummy::new(2);
325+
let mut q =
326+
BinaryHeap::from(vec![a.spawn(Panic::Never), b.spawn(Panic::Never), c.spawn(Panic::Never)]);
327+
328+
catch_unwind(AssertUnwindSafe(|| {
329+
let mut it = q.drain();
330+
it.next();
331+
mem::forget(it);
332+
}))
333+
.unwrap();
334+
// Behaviour after leaking is explicitly unspecified and order is arbitrary,
335+
// so it's fine if these start failing, but probably worth knowing.
336+
assert!(q.is_empty());
337+
assert_eq!(a.dropped() + b.dropped() + c.dropped(), 1);
338+
assert_eq!(a.dropped(), 0);
339+
assert_eq!(b.dropped(), 0);
340+
assert_eq!(c.dropped(), 1);
341+
drop(q);
342+
assert_eq!(a.dropped(), 0);
343+
assert_eq!(b.dropped(), 0);
344+
assert_eq!(c.dropped(), 1);
345+
}
346+
347+
#[test]
348+
fn test_drain_sorted_forget() {
349+
let a = CrashTestDummy::new(0);
350+
let b = CrashTestDummy::new(1);
351+
let c = CrashTestDummy::new(2);
352+
let mut q =
353+
BinaryHeap::from(vec![a.spawn(Panic::Never), b.spawn(Panic::Never), c.spawn(Panic::Never)]);
354+
355+
catch_unwind(AssertUnwindSafe(|| {
356+
let mut it = q.drain_sorted();
357+
it.next();
358+
mem::forget(it);
359+
}))
360+
.unwrap();
361+
// Behaviour after leaking is explicitly unspecified,
362+
// so it's fine if these start failing, but probably worth knowing.
363+
assert_eq!(q.len(), 2);
364+
assert_eq!(a.dropped(), 0);
365+
assert_eq!(b.dropped(), 0);
366+
assert_eq!(c.dropped(), 1);
367+
drop(q);
368+
assert_eq!(a.dropped(), 1);
369+
assert_eq!(b.dropped(), 1);
370+
assert_eq!(c.dropped(), 1);
321371
}
322372

323373
#[test]

library/alloc/src/collections/btree/map/tests.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use super::super::testing::crash_test::{CrashTestDummy, Panic};
2-
use super::super::testing::ord_chaos::{Cyclic3, Governed, Governor};
3-
use super::super::testing::rng::DeterministicRng;
41
use super::Entry::{Occupied, Vacant};
52
use super::*;
63
use crate::boxed::Box;
74
use crate::fmt::Debug;
85
use crate::rc::Rc;
96
use crate::string::{String, ToString};
7+
use crate::testing::crash_test::{CrashTestDummy, Panic};
8+
use crate::testing::ord_chaos::{Cyclic3, Governed, Governor};
9+
use crate::testing::rng::DeterministicRng;
1010
use crate::vec::Vec;
1111
use std::cmp::Ordering;
1212
use std::convert::TryFrom;

library/alloc/src/collections/btree/mod.rs

-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,3 @@ trait Recover<Q: ?Sized> {
2121
fn take(&mut self, key: &Q) -> Option<Self::Key>;
2222
fn replace(&mut self, key: Self::Key) -> Option<Self::Key>;
2323
}
24-
25-
#[cfg(test)]
26-
mod testing;

library/alloc/src/collections/btree/set/tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use super::super::testing::crash_test::{CrashTestDummy, Panic};
2-
use super::super::testing::rng::DeterministicRng;
31
use super::*;
2+
use crate::testing::crash_test::{CrashTestDummy, Panic};
3+
use crate::testing::rng::DeterministicRng;
44
use crate::vec::Vec;
55
use std::cmp::Ordering;
66
use std::hash::{Hash, Hasher};

library/alloc/src/collections/linked_list/tests.rs

+28-28
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::*;
2+
use crate::testing::crash_test::{CrashTestDummy, Panic};
23
use crate::vec::Vec;
34

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

985986
#[test]
986987
fn drain_filter_drop_panic_leak() {
987-
static mut DROPS: i32 = 0;
988-
989-
struct D(bool);
990-
991-
impl Drop for D {
992-
fn drop(&mut self) {
993-
unsafe {
994-
DROPS += 1;
995-
}
996-
997-
if self.0 {
998-
panic!("panic in `drop`");
999-
}
1000-
}
1001-
}
1002-
988+
let d0 = CrashTestDummy::new(0);
989+
let d1 = CrashTestDummy::new(1);
990+
let d2 = CrashTestDummy::new(2);
991+
let d3 = CrashTestDummy::new(3);
992+
let d4 = CrashTestDummy::new(4);
993+
let d5 = CrashTestDummy::new(5);
994+
let d6 = CrashTestDummy::new(6);
995+
let d7 = CrashTestDummy::new(7);
1003996
let mut q = LinkedList::new();
1004-
q.push_back(D(false));
1005-
q.push_back(D(false));
1006-
q.push_back(D(false));
1007-
q.push_back(D(false));
1008-
q.push_back(D(false));
1009-
q.push_front(D(false));
1010-
q.push_front(D(true));
1011-
q.push_front(D(false));
1012-
1013-
catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).ok();
1014-
1015-
assert_eq!(unsafe { DROPS }, 8);
997+
q.push_back(d3.spawn(Panic::Never));
998+
q.push_back(d4.spawn(Panic::Never));
999+
q.push_back(d5.spawn(Panic::Never));
1000+
q.push_back(d6.spawn(Panic::Never));
1001+
q.push_back(d7.spawn(Panic::Never));
1002+
q.push_front(d2.spawn(Panic::Never));
1003+
q.push_front(d1.spawn(Panic::InDrop));
1004+
q.push_front(d0.spawn(Panic::Never));
1005+
1006+
catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).unwrap_err();
1007+
1008+
assert_eq!(d0.dropped(), 1);
1009+
assert_eq!(d1.dropped(), 1);
1010+
assert_eq!(d2.dropped(), 1);
1011+
assert_eq!(d3.dropped(), 1);
1012+
assert_eq!(d4.dropped(), 1);
1013+
assert_eq!(d5.dropped(), 1);
1014+
assert_eq!(d6.dropped(), 1);
1015+
assert_eq!(d7.dropped(), 1);
10161016
assert!(q.is_empty());
10171017
}
10181018

library/alloc/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@
206206
extern crate std;
207207
#[cfg(test)]
208208
extern crate test;
209+
#[cfg(test)]
210+
mod testing;
209211

210212
// Module with internal macros used by other modules (needs to be included before other modules).
211213
#[macro_use]

0 commit comments

Comments
 (0)