Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions crates/iddqd/tests/integration/id_ord_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<TestItem>::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::<TestItem>::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() {
Expand Down
130 changes: 130 additions & 0 deletions crates/iddqd/tests/integration/pathological.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,136 @@ 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: &str = if let Some(s) = payload.downcast_ref::<&str>() {
s
} else if let Some(s) = payload.downcast_ref::<String>() {
s.as_str()
} else {
panic!("fail-fast panics carry a string payload");
};
assert_eq!(message, expected);
}
Comment thread
Copilot marked this conversation as resolved.

#[derive(Debug)]
struct FlipItem {
id: u32,
flip_id: u32,
flip_after: Cell<Option<u32>>,
}

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<FlipItem, _> = 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::<FlipItem>::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::<FlipItem>::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::<FlipItem>::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
Expand Down
Loading