1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::rc::{Rc, Weak};
use quickcheck::{Arbitrary, Gen, quickcheck};
use weak_table::WeakKeyHashMap;
use self::Cmd::*;
fn test_script<K, V>(script: &Script<K, V>) -> bool
where K: Clone + Debug + Eq + Hash,
V: Clone + Debug + Eq
{
let mut tester = Tester::with_capacity(4);
tester.execute_script(script);
tester.check()
}
quickcheck! {
fn prop_u8_u8(script: Script<u8, u8>) -> bool {
test_script(&script)
}
fn prop_string_usize(script: Script<String, usize>) -> bool {
test_script(&script)
}
}
#[derive(Clone, Debug)]
pub enum Cmd<K, V>
{
Insert(K, V),
Reinsert(usize, V),
RemoveInserted(usize),
RemoveOther(K),
ForgetInserted(usize),
}
#[derive(Clone, Debug)]
pub struct Script<K, V>(Vec<Cmd<K, V>>);
#[derive(Clone, Debug)]
pub struct Tester<K: Hash + Eq, V> {
weak: WeakKeyHashMap<Weak<K>, V>,
strong: HashMap<Rc<K>, V>,
log: Vec<K>,
}
impl<K, V> Tester<K, V>
where K: Hash + Eq + Clone + Debug,
V: Eq + Clone + Debug
{
pub fn new() -> Self {
Tester::with_capacity(8)
}
pub fn with_capacity(capacity: usize) -> Self {
Tester {
weak: WeakKeyHashMap::with_capacity(capacity),
strong: HashMap::new(),
log: Vec::new(),
}
}
pub fn check(&self) -> bool {
let copy = self.weak.iter().map(|(k, v)| (k, v.clone())).collect();
if self.strong == copy {
// eprintln!("Tester::check: succeeded: {:?}", self.weak);
true
} else {
eprintln!("Tester::check: failed: {:?} ≠ {:?}", self.strong, copy);
false
}
}
pub fn execute_script(&mut self, script: &Script<K, V>) {
// eprintln!("\n*** Starting script ***");
for cmd in &script.0 {
self.execute_command(cmd);
}
}
pub fn execute_command(&mut self, cmd: &Cmd<K, V>) {
// eprintln!("Executing command: {:?}", cmd);
match *cmd {
Insert(ref k, ref v) => self.insert(k, v, true),
Reinsert(index, ref v) => self.reinsert(index, v),
RemoveInserted(index) => self.remove_inserted(index),
RemoveOther(ref k) => self.remove_other(k),
ForgetInserted(index) => self.forget_inserted(index),
}
// eprintln!("Table state: {:?}", self.weak);
}
pub fn insert(&mut self, key: &K, value: &V, log: bool) {
let key_ptr = Rc::new(key.clone());
self.weak.insert(key_ptr.clone(), value.clone());
self.strong.remove(key);
self.strong.insert(key_ptr, value.clone());
if log { self.log.push(key.clone()); }
}
pub fn reinsert(&mut self, index: usize, value: &V) {
if let Some(key) = self.nth_key_mod_len(index) {
self.insert(&key, value, false);
}
}
pub fn remove_inserted(&mut self, index: usize) {
if let Some(key) = self.nth_key_mod_len(index) {
self.strong.remove(&key);
self.weak.remove(&key);
}
}
pub fn remove_other(&mut self, key: &K) {
self.strong.remove(key);
self.weak.remove(key);
}
pub fn forget_inserted(&mut self, index: usize) {
if let Some(key) = self.nth_key_mod_len(index) {
self.strong.remove(&key);
}
}
fn nth_key_mod_len(&self, n: usize) -> Option<K>
{
if self.log.is_empty() {
None
} else {
Some(self.log[n % self.log.len()].clone())
}
}
}
impl<K: Arbitrary, V: Arbitrary> Arbitrary for Cmd<K, V> {
fn arbitrary(g: &mut Gen) -> Self {
let choice = u8::arbitrary(g);
match choice % 10 {
0..=3 => Insert(K::arbitrary(g), V::arbitrary(g)),
4 => Reinsert(usize::arbitrary(g), V::arbitrary(g)),
5..=6 => RemoveInserted(usize::arbitrary(g)),
7 => RemoveOther(K::arbitrary(g)),
8..=9 => ForgetInserted(usize::arbitrary(g)),
_ => unreachable!(),
}
}
}
impl<K: Arbitrary, V: Arbitrary> Arbitrary for Script<K, V> {
fn arbitrary(g: &mut Gen) -> Self {
Script(Vec::<Cmd<K, V>>::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item=Self>> {
Box::new(self.0.shrink().map(|v| Script(v)))
}
}
|