#![cfg(not(miri))] extern crate evmap; use evmap::handles::{ReadHandle, WriteHandle}; extern crate quickcheck; #[macro_use(quickcheck)] extern crate quickcheck_macros; use quickcheck::Arbitrary; use quickcheck::Gen; use rand::Rng; use std::cmp::{min, Ord}; use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::hash::{BuildHasher, Hash}; use std::ops::Deref; fn set<'a, T: 'a, I>(iter: I) -> HashSet where I: IntoIterator, T: Copy + Hash + Eq, { iter.into_iter().cloned().collect() } #[quickcheck] fn contains(insert: Vec) -> bool { let (mut w, r) = evmap::new(); for &key in &insert { w.insert(key, ()); } w.publish(); insert.iter().all(|&key| r.get(&key).is_some()) } #[quickcheck] fn contains_not(insert: Vec, not: Vec) -> bool { let (mut w, r) = evmap::new(); for &key in &insert { w.insert(key, ()); } w.publish(); let nots = &set(¬) - &set(&insert); nots.iter().all(|&key| r.get(&key).is_none()) } #[quickcheck] fn insert_empty(insert: Vec, remove: Vec) -> bool { let (mut w, r) = evmap::new(); for &key in &insert { w.insert(key, ()); } w.publish(); for &key in &remove { w.remove_entry(key); } w.publish(); let elements = &set(&insert) - &set(&remove); r.len() == elements.len() && r.enter().iter().map(|r| r.keys()).flatten().count() == elements.len() && elements.iter().all(|k| r.get(k).is_some()) } use Op::*; #[derive(Copy, Clone, Debug)] enum Op { Add(K, V), Remove(K), RemoveValue(K, V), Refresh, } impl Arbitrary for Op where K: Arbitrary, V: Arbitrary, { fn arbitrary(g: &mut G) -> Self { match g.gen::() % 4 { 0 => Add(K::arbitrary(g), V::arbitrary(g)), 1 => Remove(K::arbitrary(g)), 2 => RemoveValue(K::arbitrary(g), V::arbitrary(g)), _ => Refresh, } } } fn do_ops( ops: &[Op], evmap: &mut WriteHandle, write_ref: &mut HashMap>, read_ref: &mut HashMap>, ) where K: Hash + Eq + Clone, V: Clone + Eq + Hash, S: BuildHasher + Clone, { for op in ops { match *op { Add(ref k, ref v) => { evmap.insert(k.clone(), v.clone()); write_ref .entry(k.clone()) .or_insert_with(Vec::new) .push(v.clone()); } Remove(ref k) => { evmap.remove_entry(k.clone()); write_ref.remove(k); } RemoveValue(ref k, ref v) => { evmap.remove_value(k.clone(), v.clone()); write_ref.get_mut(k).and_then(|values| { values .iter_mut() .position(|value| value == v) .and_then(|pos| Some(values.swap_remove(pos))) }); } Refresh => { evmap.publish(); *read_ref = write_ref.clone(); } } } } fn assert_maps_equivalent(a: &ReadHandle, b: &HashMap>) -> bool where K: Clone + Hash + Eq + Debug, V: Hash + Eq + Debug + Ord + Copy, S: BuildHasher, { assert_eq!(a.len(), b.len()); for key in a.enter().iter().map(|r| r.keys()).flatten() { assert!(b.contains_key(key), "b does not contain {:?}", key); } for key in b.keys() { assert!(a.get(key).is_some(), "a does not contain {:?}", key); } let guard = if let Some(guard) = a.enter() { guard } else { // Reference was empty, ReadHandle was destroyed, so all is well. Maybe. return true; }; for key in guard.keys() { let mut ev_map_values: Vec = guard.get(key).unwrap().iter().copied().collect(); ev_map_values.sort(); let mut map_values = b[key].clone(); map_values.sort(); assert_eq!(ev_map_values, map_values); } true } /// quickcheck Arbitrary adaptor -- make a larger vec #[derive(Clone, Debug)] struct Large(T); impl Deref for Large { type Target = T; fn deref(&self) -> &T { &self.0 } } impl Arbitrary for Large> where T: Arbitrary, { fn arbitrary(g: &mut G) -> Self { let len = g.next_u32() % (g.size() * 10) as u32; Large((0..len).map(|_| T::arbitrary(g)).collect()) } fn shrink(&self) -> Box> { Box::new((**self).shrink().map(Large)) } } #[derive(Clone, Debug, Hash, PartialEq, Eq)] struct Alphabet(String); impl Deref for Alphabet { type Target = String; fn deref(&self) -> &String { &self.0 } } const ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyz"; impl Arbitrary for Alphabet { fn arbitrary(g: &mut G) -> Self { let len = g.next_u32() % g.size() as u32; let len = min(len, 16); Alphabet( (0..len) .map(|_| ALPHABET[g.next_u32() as usize % ALPHABET.len()] as char) .collect(), ) } fn shrink(&self) -> Box> { Box::new((**self).shrink().map(Alphabet)) } } #[quickcheck] fn operations_i8(ops: Large>>) -> bool { let (mut w, r) = evmap::new(); let mut write_ref = HashMap::new(); let mut read_ref = HashMap::new(); do_ops(&ops, &mut w, &mut write_ref, &mut read_ref); assert_maps_equivalent(&r, &read_ref); w.publish(); assert_maps_equivalent(&r, &write_ref) } #[quickcheck] fn operations_string(ops: Vec>) -> bool { // safety: Alphabet is just String, and has stable Hash + Eq. let (mut w, r) = unsafe { evmap::new_assert_stable() }; let mut write_ref = HashMap::new(); let mut read_ref = HashMap::new(); do_ops(&ops, &mut w, &mut write_ref, &mut read_ref); assert_maps_equivalent(&r, &read_ref); w.publish(); assert_maps_equivalent(&r, &write_ref) } #[quickcheck] fn keys_values(ops: Large>>) -> bool { let (mut w, r) = evmap::new(); let mut write_ref = HashMap::new(); let mut read_ref = HashMap::new(); do_ops(&ops, &mut w, &mut write_ref, &mut read_ref); if let Some(read_guard) = r.enter() { let (mut w_visit, r_visit) = evmap::new(); for (k, v_set) in read_guard.keys().zip(read_guard.values()) { assert!(read_guard[k].iter().all(|v| v_set.contains(v))); assert!(v_set.iter().all(|v| read_guard[k].contains(v))); assert!(!r_visit.contains_key(k)); // If the value set is empty, we still need to add the key. // But we need to add the key with an empty value bag. // Just add something and then remove the value, but leave the bag. if v_set.is_empty() { w_visit.insert(*k, 0); w_visit.remove_value(*k, 0); } else { for value in v_set { w_visit.insert(*k, *value); } } w_visit.publish(); } assert_eq!(r_visit.len(), read_ref.len()); } true }