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