|
1 | 1 | use core::assert_matches; |
2 | | -use std::iter; |
3 | 2 | use std::ops::Bound::{Excluded, Included, Unbounded}; |
4 | 3 | use std::panic::{AssertUnwindSafe, catch_unwind}; |
5 | 4 | use std::sync::atomic::AtomicUsize; |
6 | 5 | use std::sync::atomic::Ordering::SeqCst; |
| 6 | +use std::{cmp, iter}; |
7 | 7 |
|
8 | 8 | use super::*; |
9 | 9 | use crate::boxed::Box; |
@@ -2128,6 +2128,86 @@ create_append_test!(test_append_239, 239); |
2128 | 2128 | #[cfg(not(miri))] // Miri is too slow |
2129 | 2129 | create_append_test!(test_append_1700, 1700); |
2130 | 2130 |
|
| 2131 | +// a inserts (0, 0)..(8, 8) to its own tree |
| 2132 | +// b inserts (5, 5 * 2)..($len, 2 * $len) to its own tree |
| 2133 | +// note that between a and b, there are duplicate keys |
| 2134 | +// between 5..min($len, 8), so on merge we add the values |
| 2135 | +// of these keys together |
| 2136 | +// we check that: |
| 2137 | +// - the merged tree 'a' has a length of max(8, $len) |
| 2138 | +// - all keys in 'a' have the correct value associated |
| 2139 | +// - removing and inserting an element into the merged |
| 2140 | +// tree 'a' still keeps it in valid tree form |
| 2141 | +macro_rules! create_merge_test { |
| 2142 | + ($name:ident, $len:expr) => { |
| 2143 | + #[test] |
| 2144 | + fn $name() { |
| 2145 | + let mut a = BTreeMap::new(); |
| 2146 | + for i in 0..8 { |
| 2147 | + a.insert(i, i); |
| 2148 | + } |
| 2149 | + |
| 2150 | + let mut b = BTreeMap::new(); |
| 2151 | + for i in 5..$len { |
| 2152 | + b.insert(i, 2 * i); |
| 2153 | + } |
| 2154 | + |
| 2155 | + a.merge(b, |_, a_val, b_val| a_val + b_val); |
| 2156 | + |
| 2157 | + assert_eq!(a.len(), cmp::max($len, 8)); |
| 2158 | + |
| 2159 | + for i in 0..cmp::max($len, 8) { |
| 2160 | + if i < 5 { |
| 2161 | + assert_eq!(a[&i], i); |
| 2162 | + } else { |
| 2163 | + if i < cmp::min($len, 8) { |
| 2164 | + assert_eq!(a[&i], i + 2 * i); |
| 2165 | + } else if i >= $len { |
| 2166 | + assert_eq!(a[&i], i); |
| 2167 | + } else { |
| 2168 | + assert_eq!(a[&i], 2 * i); |
| 2169 | + } |
| 2170 | + } |
| 2171 | + } |
| 2172 | + |
| 2173 | + a.check(); |
| 2174 | + assert_eq!( |
| 2175 | + a.remove(&($len - 1)), |
| 2176 | + if $len >= 5 && $len < 8 { |
| 2177 | + Some(($len - 1) + 2 * ($len - 1)) |
| 2178 | + } else { |
| 2179 | + Some(2 * ($len - 1)) |
| 2180 | + } |
| 2181 | + ); |
| 2182 | + assert_eq!(a.insert($len - 1, 20), None); |
| 2183 | + a.check(); |
| 2184 | + } |
| 2185 | + }; |
| 2186 | +} |
| 2187 | + |
| 2188 | +// These are mostly for testing the algorithm that "fixes" the right edge after insertion. |
| 2189 | +// Single node, merge conflicting key values. |
| 2190 | +create_merge_test!(test_merge_7, 7); |
| 2191 | +// Single node. |
| 2192 | +create_merge_test!(test_merge_9, 9); |
| 2193 | +// Two leafs that don't need fixing. |
| 2194 | +create_merge_test!(test_merge_17, 17); |
| 2195 | +// Two leafs where the second one ends up underfull and needs stealing at the end. |
| 2196 | +create_merge_test!(test_merge_14, 14); |
| 2197 | +// Two leafs where the second one ends up empty because the insertion finished at the root. |
| 2198 | +create_merge_test!(test_merge_12, 12); |
| 2199 | +// Three levels; insertion finished at the root. |
| 2200 | +create_merge_test!(test_merge_144, 144); |
| 2201 | +// Three levels; insertion finished at leaf while there is an empty node on the second level. |
| 2202 | +create_merge_test!(test_merge_145, 145); |
| 2203 | +// Tests for several randomly chosen sizes. |
| 2204 | +create_merge_test!(test_merge_170, 170); |
| 2205 | +create_merge_test!(test_merge_181, 181); |
| 2206 | +#[cfg(not(miri))] // Miri is too slow |
| 2207 | +create_merge_test!(test_merge_239, 239); |
| 2208 | +#[cfg(not(miri))] // Miri is too slow |
| 2209 | +create_merge_test!(test_merge_1700, 1700); |
| 2210 | + |
2131 | 2211 | #[test] |
2132 | 2212 | #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] |
2133 | 2213 | fn test_append_drop_leak() { |
@@ -2169,6 +2249,84 @@ fn test_append_ord_chaos() { |
2169 | 2249 | map2.check(); |
2170 | 2250 | } |
2171 | 2251 |
|
| 2252 | +#[test] |
| 2253 | +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] |
| 2254 | +fn test_merge_drop_leak() { |
| 2255 | + let a = CrashTestDummy::new(0); |
| 2256 | + let b = CrashTestDummy::new(1); |
| 2257 | + let c = CrashTestDummy::new(2); |
| 2258 | + let mut left = BTreeMap::new(); |
| 2259 | + let mut right = BTreeMap::new(); |
| 2260 | + left.insert(a.spawn(Panic::Never), ()); |
| 2261 | + left.insert(b.spawn(Panic::Never), ()); |
| 2262 | + left.insert(c.spawn(Panic::Never), ()); |
| 2263 | + right.insert(b.spawn(Panic::InDrop), ()); // first duplicate key, dropped during merge |
| 2264 | + right.insert(c.spawn(Panic::Never), ()); |
| 2265 | + |
| 2266 | + catch_unwind(move || left.merge(right, |_, _, _| ())).unwrap_err(); |
| 2267 | + assert_eq!(a.dropped(), 1); // this should not be dropped |
| 2268 | + assert_eq!(b.dropped(), 2); // key is dropped on panic |
| 2269 | + assert_eq!(c.dropped(), 2); // key is dropped on panic |
| 2270 | +} |
| 2271 | + |
| 2272 | +#[test] |
| 2273 | +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] |
| 2274 | +fn test_merge_conflict_drop_leak() { |
| 2275 | + let a = CrashTestDummy::new(0); |
| 2276 | + let a_val_left = CrashTestDummy::new(0); |
| 2277 | + |
| 2278 | + let b = CrashTestDummy::new(1); |
| 2279 | + let b_val_left = CrashTestDummy::new(1); |
| 2280 | + let b_val_right = CrashTestDummy::new(1); |
| 2281 | + |
| 2282 | + let c = CrashTestDummy::new(2); |
| 2283 | + let c_val_left = CrashTestDummy::new(2); |
| 2284 | + let c_val_right = CrashTestDummy::new(2); |
| 2285 | + |
| 2286 | + let mut left = BTreeMap::new(); |
| 2287 | + let mut right = BTreeMap::new(); |
| 2288 | + |
| 2289 | + left.insert(a.spawn(Panic::Never), a_val_left.spawn(Panic::Never)); |
| 2290 | + left.insert(b.spawn(Panic::Never), b_val_left.spawn(Panic::Never)); |
| 2291 | + left.insert(c.spawn(Panic::Never), c_val_left.spawn(Panic::Never)); |
| 2292 | + right.insert(b.spawn(Panic::Never), b_val_right.spawn(Panic::Never)); |
| 2293 | + right.insert(c.spawn(Panic::Never), c_val_right.spawn(Panic::Never)); |
| 2294 | + |
| 2295 | + // First key that conflicts should |
| 2296 | + catch_unwind(move || { |
| 2297 | + left.merge(right, |_, _, _| panic!("Panic in conflict function")); |
| 2298 | + assert_eq!(left.len(), 1); // only 1 entry should be left |
| 2299 | + }) |
| 2300 | + .unwrap_err(); |
| 2301 | + assert_eq!(a.dropped(), 1); // should not panic |
| 2302 | + assert_eq!(a_val_left.dropped(), 1); // should not panic |
| 2303 | + assert_eq!(b.dropped(), 2); // should drop from panic (conflict) |
| 2304 | + assert_eq!(b_val_left.dropped(), 1); // should be 2 were it not for Rust issue #47949 |
| 2305 | + assert_eq!(b_val_right.dropped(), 1); // should be 2 were it not for Rust issue #47949 |
| 2306 | + assert_eq!(c.dropped(), 2); // should drop from panic (conflict) |
| 2307 | + assert_eq!(c_val_left.dropped(), 1); // should be 2 were it not for Rust issue #47949 |
| 2308 | + assert_eq!(c_val_right.dropped(), 1); // should be 2 were it not for Rust issue #47949 |
| 2309 | +} |
| 2310 | + |
| 2311 | +#[test] |
| 2312 | +fn test_merge_ord_chaos() { |
| 2313 | + let mut map1 = BTreeMap::new(); |
| 2314 | + map1.insert(Cyclic3::A, ()); |
| 2315 | + map1.insert(Cyclic3::B, ()); |
| 2316 | + let mut map2 = BTreeMap::new(); |
| 2317 | + map2.insert(Cyclic3::A, ()); |
| 2318 | + map2.insert(Cyclic3::B, ()); |
| 2319 | + map2.insert(Cyclic3::C, ()); // lands first, before A |
| 2320 | + map2.insert(Cyclic3::B, ()); // lands first, before C |
| 2321 | + map1.check(); |
| 2322 | + map2.check(); // keys are not unique but still strictly ascending |
| 2323 | + assert_eq!(map1.len(), 2); |
| 2324 | + assert_eq!(map2.len(), 4); |
| 2325 | + map1.merge(map2, |_, _, _| ()); |
| 2326 | + assert_eq!(map1.len(), 5); |
| 2327 | + map1.check(); |
| 2328 | +} |
| 2329 | + |
2172 | 2330 | fn rand_data(len: usize) -> Vec<(u32, u32)> { |
2173 | 2331 | let mut rng = DeterministicRng::new(); |
2174 | 2332 | Vec::from_iter((0..len).map(|_| (rng.next(), rng.next()))) |
@@ -2615,3 +2773,25 @@ fn test_id_based_append() { |
2615 | 2773 |
|
2616 | 2774 | assert_eq!(lhs.pop_first().unwrap().0.name, "lhs_k".to_string()); |
2617 | 2775 | } |
| 2776 | + |
| 2777 | +#[test] |
| 2778 | +fn test_id_based_merge() { |
| 2779 | + let mut lhs = BTreeMap::new(); |
| 2780 | + let mut rhs = BTreeMap::new(); |
| 2781 | + |
| 2782 | + lhs.insert(IdBased { id: 0, name: "lhs_k".to_string() }, "1".to_string()); |
| 2783 | + rhs.insert(IdBased { id: 0, name: "rhs_k".to_string() }, "2".to_string()); |
| 2784 | + |
| 2785 | + lhs.merge(rhs, |_, mut lhs_val, rhs_val| { |
| 2786 | + // confirming that lhs_val comes from lhs tree, |
| 2787 | + // rhs_val comes from rhs tree |
| 2788 | + assert_eq!(lhs_val, String::from("1")); |
| 2789 | + assert_eq!(rhs_val, String::from("2")); |
| 2790 | + lhs_val.push_str(&rhs_val); |
| 2791 | + lhs_val |
| 2792 | + }); |
| 2793 | + |
| 2794 | + let merged_kv_pair = lhs.pop_first().unwrap(); |
| 2795 | + assert_eq!(merged_kv_pair.0.id, 0); |
| 2796 | + assert_eq!(merged_kv_pair.0.name, "lhs_k".to_string()); |
| 2797 | +} |
0 commit comments