Skip to content

Commit bac213b

Browse files
authoredNov 16, 2020
Rollup merge of #78903 - ssomers:btree_order_chaos_testing, r=Mark-Simulacrum
BTreeMap: test chaotic ordering & other bits & bobs r? `@Mark-Simulacrum`
2 parents c0a9bf9 + 8972bcb commit bac213b

File tree

3 files changed

+201
-26
lines changed

3 files changed

+201
-26
lines changed
 

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

+121-25
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ use std::ops::RangeBounds;
1414
use std::panic::{catch_unwind, AssertUnwindSafe};
1515
use std::sync::atomic::{AtomicUsize, Ordering};
1616

17+
mod ord_chaos;
18+
use ord_chaos::{Cyclic3, Governed, Governor};
19+
1720
// Capacity of a tree with a single level,
1821
// i.e., a tree who's root is a leaf node at height 0.
1922
const NODE_CAPACITY: usize = node::CAPACITY;
@@ -28,7 +31,7 @@ const MIN_INSERTS_HEIGHT_1: usize = NODE_CAPACITY + 1;
2831
// It's not the minimum size: removing an element from such a tree does not always reduce height.
2932
const MIN_INSERTS_HEIGHT_2: usize = 89;
3033

31-
// Gather all references from a mutable iterator and make sure Miri notices if
34+
// Gathers all references from a mutable iterator and makes sure Miri notices if
3235
// using them is dangerous.
3336
fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator<Item = &'a mut T>) {
3437
// Gather all those references.
@@ -43,28 +46,43 @@ fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator<Item = &'a mut T>
4346
}
4447

4548
impl<K, V> BTreeMap<K, V> {
46-
/// Panics if the map (or the code navigating it) is corrupted.
47-
fn check(&self)
48-
where
49-
K: Copy + Debug + Ord,
50-
{
49+
// Panics if the map (or the code navigating it) is corrupted.
50+
fn check_invariants(&self) {
5151
if let Some(root) = &self.root {
5252
let root_node = root.node_as_ref();
5353

54+
// Check the back pointers top-down, before we attempt to rely on
55+
// more serious navigation code.
5456
assert!(root_node.ascend().is_err());
5557
root_node.assert_back_pointers();
5658

59+
// Check consistenty of `length` and some of the navigation.
5760
assert_eq!(self.length, root_node.calc_length());
61+
assert_eq!(self.length, self.keys().count());
5862

63+
// Lastly, check the invariant causing the least harm.
5964
root_node.assert_min_len(if root_node.height() > 0 { 1 } else { 0 });
6065
} else {
66+
// Check consistenty of `length` and some of the navigation.
6167
assert_eq!(self.length, 0);
68+
assert_eq!(self.length, self.keys().count());
6269
}
70+
}
6371

64-
self.assert_ascending();
72+
// Panics if the map is corrupted or if the keys are not in strictly
73+
// ascending order, in the current opinion of the `Ord` implementation.
74+
// If the `Ord` implementation does not honor transitivity, this method
75+
// does not guarantee that all the keys are unique, just that adjacent
76+
// keys are unique.
77+
fn check(&self)
78+
where
79+
K: Debug + Ord,
80+
{
81+
self.check_invariants();
82+
self.assert_strictly_ascending();
6583
}
6684

67-
/// Returns the height of the root, if any.
85+
// Returns the height of the root, if any.
6886
fn height(&self) -> Option<usize> {
6987
self.root.as_ref().map(node::Root::height)
7088
}
@@ -80,22 +98,18 @@ impl<K, V> BTreeMap<K, V> {
8098
}
8199
}
82100

83-
/// Asserts that the keys are in strictly ascending order.
84-
fn assert_ascending(&self)
101+
// Panics if the keys are not in strictly ascending order.
102+
fn assert_strictly_ascending(&self)
85103
where
86-
K: Copy + Debug + Ord,
104+
K: Debug + Ord,
87105
{
88-
let mut num_seen = 0;
89106
let mut keys = self.keys();
90107
if let Some(mut previous) = keys.next() {
91-
num_seen = 1;
92108
for next in keys {
93109
assert!(previous < next, "{:?} >= {:?}", previous, next);
94110
previous = next;
95-
num_seen += 1;
96111
}
97112
}
98-
assert_eq!(num_seen, self.len());
99113
}
100114
}
101115

@@ -111,7 +125,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal>
111125
}
112126
}
113127

114-
// Test our value of MIN_INSERTS_HEIGHT_2. It may change according to the
128+
// Tests our value of MIN_INSERTS_HEIGHT_2. It may change according to the
115129
// implementation of insertion, but it's best to be aware of when it does.
116130
#[test]
117131
fn test_levels() {
@@ -149,6 +163,25 @@ fn test_levels() {
149163
assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2, "{}", map.dump_keys());
150164
}
151165

166+
// Ensures the testing infrastructure usually notices order violations.
167+
#[test]
168+
#[should_panic]
169+
fn test_check_ord_chaos() {
170+
let gov = Governor::new();
171+
let map: BTreeMap<_, _> = (0..2).map(|i| (Governed(i, &gov), ())).collect();
172+
gov.flip();
173+
map.check();
174+
}
175+
176+
// Ensures the testing infrastructure doesn't always mind order violations.
177+
#[test]
178+
fn test_check_invariants_ord_chaos() {
179+
let gov = Governor::new();
180+
let map: BTreeMap<_, _> = (0..2).map(|i| (Governed(i, &gov), ())).collect();
181+
gov.flip();
182+
map.check_invariants();
183+
}
184+
152185
#[test]
153186
fn test_basic_large() {
154187
let mut map = BTreeMap::new();
@@ -334,7 +367,7 @@ fn test_iter_rev() {
334367
test(size, map.into_iter().rev());
335368
}
336369

337-
/// Specifically tests iter_mut's ability to mutate the value of pairs in-line
370+
// Specifically tests iter_mut's ability to mutate the value of pairs in-line.
338371
fn do_test_iter_mut_mutation<T>(size: usize)
339372
where
340373
T: Copy + Debug + Ord + TryFrom<usize>,
@@ -439,6 +472,8 @@ fn test_iter_entering_root_twice() {
439472
*back.1 = 42;
440473
assert_eq!(front, (&0, &mut 24));
441474
assert_eq!(back, (&1, &mut 42));
475+
assert_eq!(it.next(), None);
476+
assert_eq!(it.next_back(), None);
442477
map.check();
443478
}
444479

@@ -591,11 +626,12 @@ fn test_range_small() {
591626

592627
#[test]
593628
fn test_range_height_1() {
594-
// Tests tree with a root and 2 leaves. Depending on details we don't want or need
595-
// to rely upon, the single key at the root will be 6 or 7.
629+
// Tests tree with a root and 2 leaves. The single key in the root node is
630+
// close to the middle among the keys.
596631

597-
let map: BTreeMap<_, _> = (1..=MIN_INSERTS_HEIGHT_1 as i32).map(|i| (i, i)).collect();
598-
for &root in &[6, 7] {
632+
let map: BTreeMap<_, _> = (0..MIN_INSERTS_HEIGHT_1 as i32).map(|i| (i, i)).collect();
633+
let middle = MIN_INSERTS_HEIGHT_1 as i32 / 2;
634+
for root in middle - 2..=middle + 2 {
599635
assert_eq!(range_keys(&map, (Excluded(root), Excluded(root + 1))), vec![]);
600636
assert_eq!(range_keys(&map, (Excluded(root), Included(root + 1))), vec![root + 1]);
601637
assert_eq!(range_keys(&map, (Included(root), Excluded(root + 1))), vec![root]);
@@ -727,6 +763,19 @@ fn test_range_backwards_4() {
727763
map.range((Excluded(3), Excluded(2)));
728764
}
729765

766+
#[test]
767+
#[should_panic]
768+
fn test_range_backwards_5() {
769+
let mut map = BTreeMap::new();
770+
map.insert(Cyclic3::B, ());
771+
// Lacking static_assert, call `range` conditionally, to emphasise that
772+
// we cause a different panic than `test_range_backwards_1` does.
773+
// A more refined `should_panic` would be welcome.
774+
if Cyclic3::C < Cyclic3::A {
775+
map.range(Cyclic3::C..=Cyclic3::A);
776+
}
777+
}
778+
730779
#[test]
731780
fn test_range_1000() {
732781
// Miri is too slow
@@ -831,18 +880,28 @@ mod test_drain_filter {
831880
}
832881

833882
#[test]
834-
fn consuming_nothing() {
883+
fn consumed_keeping_all() {
835884
let pairs = (0..3).map(|i| (i, i));
836885
let mut map: BTreeMap<_, _> = pairs.collect();
837886
assert!(map.drain_filter(|_, _| false).eq(iter::empty()));
838887
map.check();
839888
}
840889

841890
#[test]
842-
fn consuming_all() {
891+
fn consumed_removing_all() {
843892
let pairs = (0..3).map(|i| (i, i));
844893
let mut map: BTreeMap<_, _> = pairs.clone().collect();
845894
assert!(map.drain_filter(|_, _| true).eq(pairs));
895+
assert!(map.is_empty());
896+
map.check();
897+
}
898+
899+
#[test]
900+
fn dropped_removing_all() {
901+
let pairs = (0..3).map(|i| (i, i));
902+
let mut map: BTreeMap<_, _> = pairs.collect();
903+
map.drain_filter(|_, _| true);
904+
assert!(map.is_empty());
846905
map.check();
847906
}
848907

@@ -1723,6 +1782,27 @@ fn test_append_drop_leak() {
17231782
assert_eq!(DROPS.load(Ordering::SeqCst), 4); // Rust issue #47949 ate one little piggy
17241783
}
17251784

1785+
#[test]
1786+
fn test_append_ord_chaos() {
1787+
let mut map1 = BTreeMap::new();
1788+
map1.insert(Cyclic3::A, ());
1789+
map1.insert(Cyclic3::B, ());
1790+
let mut map2 = BTreeMap::new();
1791+
map2.insert(Cyclic3::A, ());
1792+
map2.insert(Cyclic3::B, ());
1793+
map2.insert(Cyclic3::C, ()); // lands first, before A
1794+
map2.insert(Cyclic3::B, ()); // lands first, before C
1795+
map1.check();
1796+
map2.check(); // keys are not unique but still strictly ascending
1797+
assert_eq!(map1.len(), 2);
1798+
assert_eq!(map2.len(), 4);
1799+
map1.append(&mut map2);
1800+
assert_eq!(map1.len(), 5);
1801+
assert_eq!(map2.len(), 0);
1802+
map1.check();
1803+
map2.check();
1804+
}
1805+
17261806
fn rand_data(len: usize) -> Vec<(u32, u32)> {
17271807
assert!(len * 2 <= 70029); // from that point on numbers repeat
17281808
let mut rng = DeterministicRng::new();
@@ -1885,11 +1965,27 @@ fn test_insert_remove_intertwined() {
18851965
let loops = if cfg!(miri) { 100 } else { 1_000_000 };
18861966
let mut map = BTreeMap::new();
18871967
let mut i = 1;
1968+
let offset = 165; // somewhat arbitrarily chosen to cover some code paths
18881969
for _ in 0..loops {
1889-
i = (i + 421) & 0xFF;
1970+
i = (i + offset) & 0xFF;
18901971
map.insert(i, i);
18911972
map.remove(&(0xFF - i));
18921973
}
1893-
18941974
map.check();
18951975
}
1976+
1977+
#[test]
1978+
fn test_insert_remove_intertwined_ord_chaos() {
1979+
let loops = if cfg!(miri) { 100 } else { 1_000_000 };
1980+
let gov = Governor::new();
1981+
let mut map = BTreeMap::new();
1982+
let mut i = 1;
1983+
let offset = 165; // more arbitrarily copied from above
1984+
for _ in 0..loops {
1985+
i = (i + offset) & 0xFF;
1986+
map.insert(Governed(i, &gov), ());
1987+
map.remove(&Governed(0xFF - i, &gov));
1988+
gov.flip();
1989+
}
1990+
map.check_invariants();
1991+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use std::cell::Cell;
2+
use std::cmp::Ordering::{self, *};
3+
use std::ptr;
4+
5+
#[derive(Debug)]
6+
pub enum Cyclic3 {
7+
A,
8+
B,
9+
C,
10+
}
11+
use Cyclic3::*;
12+
13+
impl PartialOrd for Cyclic3 {
14+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
15+
Some(self.cmp(other))
16+
}
17+
}
18+
19+
impl Ord for Cyclic3 {
20+
fn cmp(&self, other: &Self) -> Ordering {
21+
match (self, other) {
22+
(A, A) | (B, B) | (C, C) => Equal,
23+
(A, B) | (B, C) | (C, A) => Less,
24+
(A, C) | (B, A) | (C, B) => Greater,
25+
}
26+
}
27+
}
28+
29+
impl PartialEq for Cyclic3 {
30+
fn eq(&self, other: &Self) -> bool {
31+
self.cmp(&other) == Equal
32+
}
33+
}
34+
35+
impl Eq for Cyclic3 {}
36+
37+
#[derive(Debug)]
38+
pub struct Governor {
39+
flipped: Cell<bool>,
40+
}
41+
42+
impl Governor {
43+
pub fn new() -> Self {
44+
Governor { flipped: Cell::new(false) }
45+
}
46+
47+
pub fn flip(&self) {
48+
self.flipped.set(!self.flipped.get());
49+
}
50+
}
51+
52+
#[derive(Debug)]
53+
pub struct Governed<'a, T>(pub T, pub &'a Governor);
54+
55+
impl<T: Ord> PartialOrd for Governed<'_, T> {
56+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
57+
Some(self.cmp(other))
58+
}
59+
}
60+
61+
impl<T: Ord> Ord for Governed<'_, T> {
62+
fn cmp(&self, other: &Self) -> Ordering {
63+
assert!(ptr::eq(self.1, other.1));
64+
let ord = self.0.cmp(&other.0);
65+
if self.1.flipped.get() { ord.reverse() } else { ord }
66+
}
67+
}
68+
69+
impl<T: PartialEq> PartialEq for Governed<'_, T> {
70+
fn eq(&self, other: &Self) -> bool {
71+
assert!(ptr::eq(self.1, other.1));
72+
self.0.eq(&other.0)
73+
}
74+
}
75+
76+
impl<T: Eq> Eq for Governed<'_, T> {}

‎library/alloc/src/collections/btree/node/tests.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::string::String;
55
use core::cmp::Ordering::*;
66

77
impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> {
8-
/// Asserts that the back pointer in each reachable node points to its parent.
8+
// Asserts that the back pointer in each reachable node points to its parent.
99
pub fn assert_back_pointers(self) {
1010
if let ForceResult::Internal(node) = self.force() {
1111
for idx in 0..=node.len() {
@@ -17,6 +17,9 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal>
1717
}
1818
}
1919

20+
// Renders a multi-line display of the keys in order and in tree hierarchy,
21+
// picturing the tree growing sideways from its root on the left to its
22+
// leaves on the right.
2023
pub fn dump_keys(self) -> String
2124
where
2225
K: Debug,

0 commit comments

Comments
 (0)
Please sign in to comment.