diff --git a/crates/iddqd-test-utils/src/naive_map.rs b/crates/iddqd-test-utils/src/naive_map.rs index 3f5f87e8..ae88e638 100644 --- a/crates/iddqd-test-utils/src/naive_map.rs +++ b/crates/iddqd-test-utils/src/naive_map.rs @@ -180,6 +180,14 @@ impl NaiveMap { pub fn clear(&mut self) { self.items.clear(); } + + pub fn extend>(&mut self, iter: I) { + // Mirrors `Extend` on the real maps: each item overwrites any + // pre-existing duplicates under the active uniqueness constraint. + for item in iter { + self.insert_overwrite(item); + } + } } /// Which keys to check uniqueness against. diff --git a/crates/iddqd/tests/integration/bi_hash_map.rs b/crates/iddqd/tests/integration/bi_hash_map.rs index 0ec1aa03..f8a7ca07 100644 --- a/crates/iddqd/tests/integration/bi_hash_map.rs +++ b/crates/iddqd/tests/integration/bi_hash_map.rs @@ -241,6 +241,11 @@ enum Operation { RetainValueContains(char, bool), #[weight(2)] RetainModulo(#[strategy(0..3_u8)] u8, #[strategy(1..4_u8)] u8, bool), + #[weight(2)] + Extend( + #[strategy(prop::collection::vec(any::(), 0..16))] + Vec, + ), Clear, // `additional` is kept modest so that reservations frequently // exceed the current `growth_left` and so trigger hashbrown's @@ -271,9 +276,8 @@ impl Operation { | Operation::Remove1(_) | Operation::Remove2(_) | Operation::RetainValueContains(_, _) - | Operation::RetainModulo(_, _, _) => { - CompactnessChange::NoLongerCompact - } + | Operation::RetainModulo(_, _, _) + | Operation::Extend(_) => CompactnessChange::NoLongerCompact, // Clear always makes the map compact (empty). Operation::Clear => CompactnessChange::BecomesCompact, } @@ -381,6 +385,11 @@ fn proptest_ops( }); map.validate(compactness).expect("map should be valid"); } + Operation::Extend(items) => { + map.extend(items.clone()); + naive_map.extend(items); + map.validate(compactness).expect("map should be valid"); + } Operation::Reserve(additional) => { map.reserve(additional); // `reserve` has no observable effect beyond capacity; the diff --git a/crates/iddqd/tests/integration/id_hash_map.rs b/crates/iddqd/tests/integration/id_hash_map.rs index 85ad1b54..e77b9443 100644 --- a/crates/iddqd/tests/integration/id_hash_map.rs +++ b/crates/iddqd/tests/integration/id_hash_map.rs @@ -194,6 +194,11 @@ enum Operation { RetainValueContains(char, bool), #[weight(2)] RetainModulo(#[strategy(0..3_u8)] u8, #[strategy(1..4_u8)] u8, bool), + #[weight(2)] + Extend( + #[strategy(prop::collection::vec(any::(), 0..16))] + Vec, + ), Clear, // `additional` is kept modest so that reservations frequently // exceed the current `growth_left` and so trigger hashbrown's @@ -222,9 +227,8 @@ impl Operation { Operation::InsertOverwrite(_) | Operation::Remove(_) | Operation::RetainValueContains(_, _) - | Operation::RetainModulo(_, _, _) => { - CompactnessChange::NoLongerCompact - } + | Operation::RetainModulo(_, _, _) + | Operation::Extend(_) => CompactnessChange::NoLongerCompact, // Clear always makes the map compact (empty). Operation::Clear => CompactnessChange::BecomesCompact, } @@ -310,6 +314,11 @@ fn proptest_ops( }); map.validate(compactness).expect("map should be valid"); } + Operation::Extend(items) => { + map.extend(items.clone()); + naive_map.extend(items); + map.validate(compactness).expect("map should be valid"); + } Operation::Clear => { map.clear(); naive_map.clear(); diff --git a/crates/iddqd/tests/integration/id_ord_map.rs b/crates/iddqd/tests/integration/id_ord_map.rs index 44a9f9ef..67754036 100644 --- a/crates/iddqd/tests/integration/id_ord_map.rs +++ b/crates/iddqd/tests/integration/id_ord_map.rs @@ -241,6 +241,11 @@ enum Operation { RetainValueContains(char, bool), #[weight(2)] RetainModulo(#[strategy(0..3_u8)] u8, #[strategy(1..4_u8)] u8, bool), + #[weight(2)] + Extend( + #[strategy(prop::collection::vec(any::(), 0..16))] + Vec, + ), // clear is set to a lower weight since it makes the map empty. Clear, } @@ -261,9 +266,8 @@ impl Operation { | Operation::PopFirst | Operation::PopLast | Operation::RetainValueContains(_, _) - | Operation::RetainModulo(_, _, _) => { - CompactnessChange::NoLongerCompact - } + | Operation::RetainModulo(_, _, _) + | Operation::Extend(_) => CompactnessChange::NoLongerCompact, // Clear always makes the map compact (empty). Operation::Clear => CompactnessChange::BecomesCompact, } @@ -428,6 +432,12 @@ fn proptest_ops( map.validate(compactness, ValidateChaos::No) .expect("map should be valid"); } + Operation::Extend(items) => { + map.extend(items.clone()); + naive_map.extend(items); + map.validate(compactness, ValidateChaos::No) + .expect("map should be valid"); + } Operation::Clear => { map.clear(); naive_map.clear(); diff --git a/crates/iddqd/tests/integration/tri_hash_map.rs b/crates/iddqd/tests/integration/tri_hash_map.rs index 40168091..d80fb8df 100644 --- a/crates/iddqd/tests/integration/tri_hash_map.rs +++ b/crates/iddqd/tests/integration/tri_hash_map.rs @@ -270,6 +270,11 @@ enum Operation { RetainValueContains(char, bool), #[weight(2)] RetainModulo(#[strategy(0..3_u8)] u8, #[strategy(1..4_u8)] u8, bool), + #[weight(2)] + Extend( + #[strategy(prop::collection::vec(any::(), 0..16))] + Vec, + ), Clear, // `additional` is kept modest so that reservations frequently // exceed the current `growth_left` and so trigger hashbrown's @@ -302,9 +307,8 @@ impl Operation { | Operation::Remove2(_) | Operation::Remove3(_) | Operation::RetainValueContains(_, _) - | Operation::RetainModulo(_, _, _) => { - CompactnessChange::NoLongerCompact - } + | Operation::RetainModulo(_, _, _) + | Operation::Extend(_) => CompactnessChange::NoLongerCompact, // Clear always makes the map compact (empty). Operation::Clear => CompactnessChange::BecomesCompact, } @@ -425,6 +429,11 @@ fn proptest_ops( }); map.validate(compactness).expect("map should be valid"); } + Operation::Extend(items) => { + map.extend(items.clone()); + naive_map.extend(items); + map.validate(compactness).expect("map should be valid"); + } Operation::Clear => { map.clear(); naive_map.clear();