gen_map 0.2.2

This crate provides customizable generational maps.
Documentation
use super::{Bomb, DropItem, DropTracker};
use crate::GenMap;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::vec::Vec;

type Map = GenMap<i32>;

#[test]
fn drain_empty_map_yields_nothing() {
    let mut map = Map::new();
    let items: Vec<_> = map.drain().collect();
    assert!(items.is_empty());
    assert_eq!(map.len(), 0);
}

#[test]
fn drain_yields_every_value_with_its_key() {
    let mut map = Map::new();
    let k1 = map.insert(10);
    let k2 = map.insert(20);
    let k3 = map.insert(30);

    let items: Vec<_> = map.drain().collect();
    assert_eq!(items, [(k1, 10), (k2, 20), (k3, 30)]);
    assert_eq!(map.len(), 0);
}

#[test]
fn drain_is_exact_size() {
    let mut map = Map::new();
    for i in 0..5 {
        map.insert(i);
    }
    let mut drain = map.drain();
    assert_eq!(drain.len(), 5);
    drain.next();
    assert_eq!(drain.len(), 4);
}

#[test]
fn dropping_drain_unconsumed_empties_map() {
    let mut map = Map::new();
    let k1 = map.insert(1);
    let k2 = map.insert(2);

    drop(map.drain());

    assert_eq!(map.len(), 0);
    assert!(map.get(k1).is_none());
    assert!(map.get(k2).is_none());
}

#[test]
fn drain_invalidates_old_keys() {
    let mut map = Map::new();
    let k1 = map.insert(42);
    let k2 = map.insert(99);

    let _: Vec<_> = map.drain().collect();

    assert!(map.get(k1).is_none());
    assert!(map.get(k2).is_none());
}

#[test]
fn drain_frees_slots_for_reuse() {
    let mut map = Map::new();
    let k1 = map.insert(100);

    let _: Vec<_> = map.drain().collect();

    let k2 = map.insert(200);
    assert_eq!(k2.idx(), k1.idx());
    assert_ne!(k2.generation(), k1.generation());
    assert_eq!(map[k2], 200);
    assert_eq!(map.len(), 1);
    assert_eq!(map.slots_len(), 1);
}

#[test]
fn drain_skips_gaps_from_prior_removes() {
    let mut map = Map::new();
    let k1 = map.insert(1);
    let k2 = map.insert(2);
    let k3 = map.insert(3);

    map.remove(k1);

    let items: Vec<_> = map.drain().collect();
    assert_eq!(items, [(k2, 2), (k3, 3)]);
    assert_eq!(map.len(), 0);
}

#[test]
fn drain_then_insert_works() {
    let mut map = Map::new();
    map.insert(1);
    map.insert(2);

    let _: Vec<_> = map.drain().collect();

    let k = map.insert(42);
    assert_eq!(map.len(), 1);
    assert_eq!(map[k], 42);
}

#[test]
fn drain_dropped_unconsumed_drops_each_value_once() {
    let tracker = DropTracker::new();
    let mut map = GenMap::<DropItem>::new();
    for _ in 0..3 {
        map.insert(tracker.make_item());
    }
    tracker.assert_none_dropped();

    drop(map.drain());

    tracker.assert_all_dropped_exactly_once(3);
    assert_eq!(map.len(), 0);
}

#[test]
fn drain_partially_consumed_drops_each_value_once() {
    let tracker = DropTracker::new();
    let mut map = GenMap::<DropItem>::new();
    for _ in 0..5 {
        map.insert(tracker.make_item());
    }

    {
        let mut drain = map.drain();
        drop(drain.next().unwrap());
        drop(drain.next().unwrap());
        assert_eq!(tracker.total_dropped(), 2);
    }

    tracker.assert_all_dropped_exactly_once(5);
    assert_eq!(map.len(), 0);
}

#[test]
fn drain_fully_consumed_hands_ownership_to_the_caller() {
    let tracker = DropTracker::new();
    let mut map = GenMap::<DropItem>::new();
    for _ in 0..4 {
        map.insert(tracker.make_item());
    }

    let collected: Vec<_> = map.drain().collect();
    tracker.assert_none_dropped();

    drop(map);
    tracker.assert_none_dropped();

    drop(collected);
    tracker.assert_all_dropped_exactly_once(4);
}

#[test]
fn drain_with_gaps_drops_only_occupied_slots() {
    let tracker = DropTracker::new();
    let mut map = GenMap::<DropItem>::new();
    let k0 = map.insert(tracker.make_item());
    map.insert(tracker.make_item());
    map.insert(tracker.make_item());

    map.remove(k0);
    assert_eq!(tracker.total_dropped(), 1);

    drop(map.drain());

    tracker.assert_all_dropped_exactly_once(3);
    assert_eq!(map.len(), 0);
}

#[test]
fn dropping_a_drain_stays_consistent_when_a_drop_panics() {
    let tracker = DropTracker::new();
    let mut map = GenMap::new();
    let keys: Vec<_> = (0..5)
        .map(|i| map.insert(Bomb::new(&tracker, i == 2)))
        .collect();

    let mut drain = map.drain();
    drop(drain.next());
    assert!(catch_unwind(AssertUnwindSafe(move || drop(drain))).is_err());

    // The panic stopped the drain right after it took out the value at 2.
    assert_eq!(map.len(), 2);
    for key in &keys[..3] {
        assert!(map.get(*key).is_none());
    }
    for key in &keys[3..] {
        assert!(map.contains_key(*key));
    }

    drop(map);
    tracker.assert_all_dropped_exactly_once(5);
}

#[test]
fn a_panic_while_draining_still_empties_the_map() {
    let tracker = DropTracker::new();
    let mut map = GenMap::new();
    for _ in 0..5 {
        map.insert(tracker.make_item());
    }

    let result = catch_unwind(AssertUnwindSafe(|| {
        for (i, (_, item)) in map.drain().enumerate() {
            drop(item);
            if i == 2 {
                panic!("boom");
            }
        }
    }));
    assert!(result.is_err());

    // The drain is dropped while the panic unwinds, and dropping it removes
    // what it had not yielded yet.
    assert!(map.is_empty());
    tracker.assert_all_dropped_exactly_once(5);
}