Skip to content

Commit b47dbba

Browse files
committed
Add additional test from alloc
This test is taken from `library/alloc/src/collections/binary_heap/tests.rs` in Rust 1.62.0. Note that the setup in the test is adapted to this crate by implementing `Ord` instead of `PartialOrd` for `PanicOrd<T>` as is done upstream, since the heap operations here rely on the `Ord` trait whereas upstream they rely on the `PartialOrd` trait.
1 parent 6e6e244 commit b47dbba

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

src/lib.rs

+87
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,93 @@ mod from_liballoc {
430430
d
431431
}
432432
}
433+
434+
// old binaryheap failed this test
435+
//
436+
// Integrity means that all elements are present after a comparison panics,
437+
// even if the order might not be correct.
438+
//
439+
// Destructors must be called exactly once per element.
440+
// FIXME: re-enable emscripten once it can unwind again
441+
#[test]
442+
#[cfg(not(target_os = "emscripten"))]
443+
fn panic_safe() {
444+
use std::cmp;
445+
use std::panic::{self, AssertUnwindSafe};
446+
use std::sync::atomic::{AtomicUsize, Ordering};
447+
448+
use rand::{seq::SliceRandom, thread_rng};
449+
450+
static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
451+
452+
#[derive(Eq, PartialEq, PartialOrd, Clone, Debug)]
453+
struct PanicOrd<T>(T, bool);
454+
455+
impl<T> Drop for PanicOrd<T> {
456+
fn drop(&mut self) {
457+
// update global drop count
458+
DROP_COUNTER.fetch_add(1, Ordering::SeqCst);
459+
}
460+
}
461+
462+
impl<T: Ord> Ord for PanicOrd<T> {
463+
fn cmp(&self, other: &Self) -> cmp::Ordering {
464+
if self.1 || other.1 {
465+
panic!("Panicking comparison");
466+
}
467+
self.0.cmp(&other.0)
468+
}
469+
}
470+
let mut rng = thread_rng();
471+
const DATASZ: usize = 32;
472+
// Miri is too slow
473+
let ntest = if cfg!(miri) { 1 } else { 10 };
474+
475+
// don't use 0 in the data -- we want to catch the zeroed-out case.
476+
let data = (1..=DATASZ).collect::<Vec<_>>();
477+
478+
// since it's a fuzzy test, run several tries.
479+
for _ in 0..ntest {
480+
for i in 1..=DATASZ {
481+
DROP_COUNTER.store(0, Ordering::SeqCst);
482+
483+
let mut panic_ords: Vec<_> = data
484+
.iter()
485+
.filter(|&&x| x != i)
486+
.map(|&x| PanicOrd(x, false))
487+
.collect();
488+
let panic_item = PanicOrd(i, true);
489+
490+
// heapify the sane items
491+
panic_ords.shuffle(&mut rng);
492+
let mut heap = BinaryHeap::from(panic_ords);
493+
let inner_data;
494+
495+
{
496+
// push the panicking item to the heap and catch the panic
497+
let thread_result = {
498+
let mut heap_ref = AssertUnwindSafe(&mut heap);
499+
panic::catch_unwind(move || {
500+
heap_ref.push(panic_item);
501+
})
502+
};
503+
assert!(thread_result.is_err());
504+
505+
// Assert no elements were dropped
506+
let drops = DROP_COUNTER.load(Ordering::SeqCst);
507+
assert!(drops == 0, "Must not drop items. drops={}", drops);
508+
inner_data = heap.clone().into_vec();
509+
drop(heap);
510+
}
511+
let drops = DROP_COUNTER.load(Ordering::SeqCst);
512+
assert_eq!(drops, DATASZ);
513+
514+
let mut data_sorted = inner_data.into_iter().map(|p| p.0).collect::<Vec<_>>();
515+
data_sorted.sort();
516+
assert_eq!(data_sorted, data);
517+
}
518+
}
519+
}
433520
}
434521

435522
#[cfg(feature = "serde")]

0 commit comments

Comments
 (0)