From 2f7c929da640db223da01ee807344f257926c819 Mon Sep 17 00:00:00 2001 From: Rain Date: Sun, 19 Jul 2026 14:56:24 -0700 Subject: [PATCH 1/3] [spr] initial version Created using spr 1.3.6-beta.1 --- crates/iddqd/tests/integration/id_ord_map.rs | 22 +++ .../iddqd/tests/integration/pathological.rs | 126 ++++++++++++++++++ 2 files changed, 148 insertions(+) diff --git a/crates/iddqd/tests/integration/id_ord_map.rs b/crates/iddqd/tests/integration/id_ord_map.rs index 975af5d7..ae75adb1 100644 --- a/crates/iddqd/tests/integration/id_ord_map.rs +++ b/crates/iddqd/tests/integration/id_ord_map.rs @@ -193,6 +193,28 @@ fn test_insert_unique() { assert_eq!(*e2, v1); } +#[test] +fn from_iter_unique_duplicate_key_reports_error() { + let existing = TestItem::new(1, 'a', "x", "first"); + let new_item = TestItem::new(1, 'c', "z", "dup"); + let items = [ + existing.clone(), + TestItem::new(2, 'b', "y", "second"), + new_item.clone(), + ]; + + let error = IdOrdMap::::from_iter_unique(items).unwrap_err(); + assert_eq!(error.new_item(), &new_item); + assert_eq!(error.duplicates(), &[existing]); +} + +#[test] +fn from_iter_unique_empty_is_ok() { + let map = IdOrdMap::::from_iter_unique(Vec::new()) + .expect("empty iterator yields an empty map"); + assert!(map.is_empty()); +} + // Test that the unsafe block within RefMut doesn't trip up miri. #[test] fn test_ref_mut_aliasing() { diff --git a/crates/iddqd/tests/integration/pathological.rs b/crates/iddqd/tests/integration/pathological.rs index fc3a1325..e847dd31 100644 --- a/crates/iddqd/tests/integration/pathological.rs +++ b/crates/iddqd/tests/integration/pathological.rs @@ -1015,6 +1015,132 @@ fn tri_hash_silent_tertiary_key_change_insert_overwrite() { .expect("map remains valid after silent-mutation insert_overwrite"); } +fn assert_panic_message(f: impl FnOnce(), expected: &str) { + let payload = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) + .expect_err("the armed flip should trigger a fail-fast panic"); + let message = payload + .downcast_ref::<&str>() + .expect("fail-fast panics carry a static string payload"); + assert_eq!(*message, expected); +} + +#[derive(Debug)] +struct FlipItem { + id: u32, + flip_id: u32, + flip_after: Cell>, +} + +impl FlipItem { + fn plain(id: u32) -> Self { + FlipItem { id, flip_id: id, flip_after: Cell::new(None) } + } + + fn flips_after_first_key_call(id: u32, flip_id: u32) -> Self { + FlipItem { id, flip_id, flip_after: Cell::new(Some(1)) } + } + + fn current_key(&self) -> u32 { + match self.flip_after.get() { + None => self.id, + Some(0) => self.flip_id, + Some(n) => { + self.flip_after.set(Some(n - 1)); + self.id + } + } + } +} + +impl IdHashItem for FlipItem { + type Key<'a> = u32; + fn key(&self) -> Self::Key<'_> { + self.current_key() + } + id_upcast!(); +} + +impl IdOrdItem for FlipItem { + type Key<'a> = u32; + fn key(&self) -> Self::Key<'_> { + self.current_key() + } + id_upcast!(); +} + +#[test] +fn id_hash_flip_key_insert_overwrite_panics() { + let mut map: IdHashMap = IdHashMap::with_capacity_and_hasher( + 8, + foldhash::fast::FixedState::with_seed(0), + ); + for id in 0..8u32 { + map.insert_unique(FlipItem::plain(id)).unwrap(); + } + + assert_panic_message( + || { + map.insert_overwrite(FlipItem::flips_after_first_key_call(99, 42)); + }, + "key hashes do not match", + ); + + assert_eq!(map.len(), 8); + assert!(map.get(&99u32).is_none()); + assert!(map.get(&42u32).is_none()); + map.validate(ValidateCompact::NonCompact) + .expect("map remains valid after a flip-key insert_overwrite panic"); +} + +#[test] +fn id_ord_flip_key_insert_overwrite_panics() { + let mut map = IdOrdMap::::new(); + for id in 0..8u32 { + map.insert_unique(FlipItem::plain(id)).unwrap(); + } + + assert_panic_message( + || { + map.insert_overwrite(FlipItem::flips_after_first_key_call(99, 3)); + }, + "key already present in map", + ); + + assert_eq!(map.len(), 8); + assert!(map.get(&99u32).is_none()); + assert_eq!(map.get(&3u32).expect("original id 3 remains").id, 3); + map.validate(ValidateCompact::NonCompact, ValidateChaos::No) + .expect("map remains valid after a flip-key insert_overwrite panic"); +} + +#[test] +#[should_panic = "key already present in map"] +fn id_ord_flip_key_from_iter_unique_existing_key_panics() { + let _ = IdOrdMap::::from_iter_unique([ + FlipItem::plain(0), + FlipItem::plain(1), + FlipItem::plain(2), + FlipItem::flips_after_first_key_call(99, 1), + ]); +} + +#[test] +fn id_ord_flip_key_from_iter_unique_inserts_under_flipped_key() { + let map = IdOrdMap::::from_iter_unique([ + FlipItem::plain(0), + FlipItem::plain(1), + FlipItem::plain(2), + FlipItem::flips_after_first_key_call(99, 100), + ]) + .expect("from_iter_unique inserts the flipped key without a duplicate"); + + assert_eq!(map.len(), 4); + assert!(map.get(&99u32).is_none()); + assert_eq!(map.get(&100u32).expect("item stored under flipped key").id, 99); + map.validate(ValidateCompact::NonCompact, ValidateChaos::No) + .expect("map remains valid after a flip-key from_iter_unique"); +} + #[test] fn lying_ord_remove_must_not_remove_wrong_btree_entry() { // Build a 64-element map under an honest `Ord`. The B-tree is sorted by From 83ea5d14d4da3eeb304ad3a04760d0dd832d1bd5 Mon Sep 17 00:00:00 2001 From: Rain Date: Sun, 19 Jul 2026 15:20:24 -0700 Subject: [PATCH 2/3] review feedback Created using spr 1.3.6-beta.1 --- crates/iddqd/tests/integration/pathological.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/iddqd/tests/integration/pathological.rs b/crates/iddqd/tests/integration/pathological.rs index e847dd31..7e0e33eb 100644 --- a/crates/iddqd/tests/integration/pathological.rs +++ b/crates/iddqd/tests/integration/pathological.rs @@ -1018,10 +1018,14 @@ fn tri_hash_silent_tertiary_key_change_insert_overwrite() { fn assert_panic_message(f: impl FnOnce(), expected: &str) { let payload = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) .expect_err("the armed flip should trigger a fail-fast panic"); - let message = payload - .downcast_ref::<&str>() - .expect("fail-fast panics carry a static string payload"); - assert_eq!(*message, expected); + let message: &str = if let Some(s) = payload.downcast_ref::<&str>() { + *s + } else if let Some(s) = payload.downcast_ref::() { + s.as_str() + } else { + panic!("fail-fast panics carry a string payload"); + }; + assert_eq!(message, expected); } #[derive(Debug)] From 09b128ca920a4bed31f7ffd6e504079b17de1912 Mon Sep 17 00:00:00 2001 From: Rain Date: Sun, 19 Jul 2026 15:22:46 -0700 Subject: [PATCH 3/3] clippy Created using spr 1.3.6-beta.1 --- crates/iddqd/tests/integration/pathological.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/iddqd/tests/integration/pathological.rs b/crates/iddqd/tests/integration/pathological.rs index 7e0e33eb..6e57acb3 100644 --- a/crates/iddqd/tests/integration/pathological.rs +++ b/crates/iddqd/tests/integration/pathological.rs @@ -1019,7 +1019,7 @@ fn assert_panic_message(f: impl FnOnce(), expected: &str) { let payload = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) .expect_err("the armed flip should trigger a fail-fast panic"); let message: &str = if let Some(s) = payload.downcast_ref::<&str>() { - *s + s } else if let Some(s) = payload.downcast_ref::() { s.as_str() } else {