1
use std::fmt::Debug;
2

            
3
use crate::{LruBTreeMap, LruHashMap, LruMap, Removed};
4

            
5
2
fn basic_tests<Map>()
6
2
where
7
2
    Map: LruMap<u32, u32> + Debug,
8
2
{
9
2
    let mut lru = Map::new(2);
10
2
    assert!(lru.is_empty());
11
2
    assert_eq!(lru.push(1, 1), None);
12
2
    assert_eq!(lru.len(), 1);
13
2
    assert_eq!(lru.push(2, 2), None);
14
2
    assert_eq!(lru.len(), 2);
15
    // Pushing a new value will expire the first push.
16
2
    assert_eq!(lru.push(3, 3), Some(Removed::Evicted(1, 1)));
17
2
    assert_eq!(lru.len(), 2);
18
    // Replacing 2 will return the existing value.
19
2
    assert_eq!(lru.push(2, 22), Some(Removed::PreviousValue(2)));
20
    // Replacing the value should have made 2 the most recent entry, meaning a
21
    // push will remove 3.
22
2
    assert_eq!(lru.push(4, 4), Some(Removed::Evicted(3, 3)));
23
    // Getting an entry should update its access
24
2
    assert_eq!(lru.get(&2), Some(&22));
25
    // But not using get_without_update
26
2
    assert_eq!(lru.get_without_update(&4), Some(&4));
27
    // Key 2 is still the front, and shouldn't be stale.
28
2
    assert_eq!(lru.entry(&2).unwrap().staleness(), 0);
29
    // Key 4 is the second, and there has been one modification since the entry
30
    // was last touched.
31
2
    assert_eq!(lru.entry(&4).unwrap().staleness(), 1);
32
2
    assert_eq!(lru.push(5, 5), Some(Removed::Evicted(4, 4)));
33
    // This will call move_node_to_front with the short-circuit evaluating true
34
    // at the start of the function.
35
2
    assert_eq!(lru.get(&5), Some(&5));
36
2
    assert_eq!(lru.head().unwrap().key(), &5);
37
2
    println!("Final State: {lru:?}");
38
2
}
39

            
40
1
#[test]
41
1
fn hash_basics() {
42
1
    basic_tests::<LruHashMap<_, _>>();
43
1
}
44

            
45
1
#[test]
46
1
fn btree_basics() {
47
1
    basic_tests::<LruBTreeMap<_, _>>();
48
1
}
49
2
fn larger_tests<Map>()
50
2
where
51
2
    Map: LruMap<u32, u32> + Debug,
52
2
{
53
2
    // The final re-ordering edge case only arises with at least 3 entries. With
54
2
    // only 2 entries, either entry is either the head or the tail.
55
2
    let mut lru = Map::new(5);
56
2
    lru.extend([(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]);
57
2
    // Test the second to last moving to the front => 2, 5, 4, 3, 1
58
2
    assert_eq!(lru.get(&2), Some(&2));
59
2
    assert_eq!(
60
10
        lru.iter().map(|(_key, value)| *value).collect::<Vec<_>>(),
61
2
        vec![2, 5, 4, 3, 1]
62
2
    );
63
    // Test moving the middle entry => 4, 2, 5, 3, 1
64
2
    assert_eq!(lru.get(&4), Some(&4));
65
2
    assert_eq!(
66
10
        lru.iter().map(|(_key, value)| *value).collect::<Vec<_>>(),
67
2
        vec![4, 2, 5, 3, 1]
68
2
    );
69
    // Test moving the second entry => 2, 4, 5, 3, 1
70
2
    assert_eq!(lru.get(&2), Some(&2));
71
    // Test the staleness (number of changes since last touch). 7 total
72
    // operations.
73
2
    assert_eq!(lru.entry(&2).unwrap().staleness(), 0); // touched on op 5 and 7
74
2
    assert_eq!(lru.entry(&4).unwrap().staleness(), 1); // touched on op 6
75
2
    assert_eq!(lru.entry(&5).unwrap().staleness(), 3); // touched on op 4
76
2
    assert_eq!(lru.entry(&3).unwrap().staleness(), 5); // Never touched
77
2
    assert_eq!(lru.entry(&1).unwrap().staleness(), 7); // Never touched
78

            
79
    // Verify the order, but use into_iter() this time.
80
2
    assert_eq!(
81
2
        lru.into_iter()
82
10
            .map(|(_key, value)| value)
83
2
            .collect::<Vec<_>>(),
84
2
        vec![2, 4, 5, 3, 1]
85
2
    );
86
2
}
87

            
88
1
#[test]
89
1
fn hash_larger() {
90
1
    larger_tests::<LruHashMap<_, _>>();
91
1
}
92

            
93
1
#[test]
94
1
fn btree_larger() {
95
1
    larger_tests::<LruBTreeMap<_, _>>();
96
1
}
97

            
98
#[allow(clippy::cognitive_complexity)]
99
2
fn enumeration_tests<Map>()
100
2
where
101
2
    Map: LruMap<u32, u32> + Debug,
102
2
{
103
2
    let mut lru = Map::new(3);
104
2
    assert!(lru.head().is_none());
105
2
    lru.push(1, 1);
106
2
    {
107
2
        let mut entry = lru.head().unwrap();
108
2
        assert_eq!(entry.key(), &1);
109
2
        assert!(!entry.move_next());
110
2
        assert_eq!(entry.key(), &1);
111
2
        assert!(!entry.move_previous());
112
2
        assert_eq!(entry.key(), &1);
113
    }
114
2
    lru.push(2, 2);
115
2
    {
116
2
        let mut entry = lru.head().unwrap();
117
2
        assert_eq!(entry.key(), &2);
118
2
        assert_eq!(entry.peek_value(), &2);
119
2
        assert!(entry.move_next());
120
2
        assert_eq!(entry.key(), &1);
121
2
        assert_eq!(entry.peek_value(), &1);
122
2
        assert!(!entry.move_next());
123
2
        assert!(entry.move_previous());
124
2
        assert_eq!(entry.key(), &2);
125
2
        assert_eq!(entry.peek_value(), &2);
126
2
        assert!(!entry.move_previous());
127
2
        assert_eq!(entry.key(), &2);
128
    }
129
2
    lru.push(3, 3);
130
2
    {
131
2
        // Test mutating and iterating.
132
2
        let mut entry = lru.tail().unwrap();
133
2
        assert_eq!(entry.key(), &1);
134
        // By accessing the value, this should now become the head.
135
2
        assert_eq!(entry.value(), &1);
136
2
        assert!(!entry.move_previous());
137
        // Iterate through the remaining entries.
138
2
        assert!(entry.move_next());
139
2
        assert_eq!(entry.key(), &3);
140
2
        assert_eq!(entry.peek_value(), &3);
141
2
        assert!(entry.move_next());
142
2
        assert_eq!(entry.key(), &2);
143
2
        assert_eq!(entry.peek_value(), &2);
144
2
        assert!(!entry.move_next());
145
    }
146
2
}
147

            
148
1
#[test]
149
1
fn hash_enumeration() {
150
1
    enumeration_tests::<LruHashMap<_, _>>();
151
1
}
152

            
153
1
#[test]
154
1
fn btree_enumeration() {
155
1
    enumeration_tests::<LruBTreeMap<_, _>>();
156
1
}
157

            
158
#[allow(clippy::cognitive_complexity)]
159
2
fn iteration_tests<Map>()
160
2
where
161
2
    Map: LruMap<u32, u32> + Debug,
162
2
{
163
2
    let mut lru = Map::new(5);
164
2
    lru.extend([(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]);
165
2
    assert_eq!(
166
2
        lru.iter().collect::<Vec<_>>(),
167
2
        &[(&5, &5), (&4, &4), (&3, &3), (&2, &2), (&1, &1)]
168
2
    );
169

            
170
    // Test double-ended iteration
171
2
    let mut iter = lru.iter();
172
2
    assert!(iter.next_back().is_none());
173
10
    for i in (1..=5).rev() {
174
10
        assert_eq!(iter.next().unwrap().0, &i);
175
    }
176
2
    assert!(iter.next().is_none());
177
    // We're now past the end of the tail, we should be able to recover and get
178
    // back to the head.
179
12
    for i in 1..=5 {
180
10
        assert_eq!(iter.next_back().unwrap().0, &i);
181
    }
182
2
    assert!(iter.next_back().is_none());
183

            
184
    // Test partial iteration
185
2
    assert_eq!(
186
2
        lru.entry(&3).unwrap().iter().collect::<Vec<_>>(),
187
2
        &[(&3, &3), (&2, &2), (&1, &1)]
188
2
    );
189
    // Moving back should return the previous entry from the starting point.
190
2
    assert_eq!(lru.entry(&3).unwrap().iter().next_back().unwrap().0, &4);
191
2
}
192

            
193
1
#[test]
194
1
fn hash_iteration() {
195
1
    iteration_tests::<LruHashMap<_, _>>();
196
1
}
197

            
198
1
#[test]
199
1
fn btree_iteration() {
200
1
    iteration_tests::<LruBTreeMap<_, _>>();
201
1
}
202

            
203
2
fn entry_removal_tests<Map>()
204
2
where
205
2
    Map: LruMap<u32, u32> + Debug,
206
2
{
207
2
    let mut lru = Map::new(3);
208
2
    lru.push(1, 1);
209
2
    lru.push(2, 2);
210
2
    lru.push(3, 3);
211
2
    let entry = lru.head().unwrap();
212
2
    // Remove 3, no previous, should return None.
213
2
    assert!(entry.remove_moving_previous().is_none());
214
2
    assert_eq!(lru.len(), 2);
215
2
    assert!(lru.get(&3).is_none());
216
2
    let entry = lru.tail().unwrap();
217
2
    // Remove 1, no next, should return None.
218
2
    assert!(entry.remove_moving_next().is_none());
219
2
    assert_eq!(lru.len(), 1);
220
2
    assert!(lru.get(&1).is_none());
221
2
    let (key, _value) = lru.head().unwrap().take();
222
2
    assert!(lru.is_empty());
223
2
    assert!(lru.get(&2).is_none());
224
2
    assert_eq!(key, 2);
225
2
    assert!(lru.head().is_none());
226
2
    assert!(lru.tail().is_none());
227

            
228
    // Start fresh and test deleting the other directions
229
2
    lru.push(1, 1);
230
2
    lru.push(2, 2);
231
2
    lru.push(3, 3);
232
2
    // Remove 3, moving next, should end up on 2
233
2
    let mut entry = lru.head().unwrap();
234
2
    entry = entry.remove_moving_next().unwrap();
235
2
    assert_eq!(entry.key(), &2);
236
    // Remove 2, moving previous, should end up on 2
237
2
    let mut entry = lru.tail().unwrap();
238
2
    entry = entry.remove_moving_previous().unwrap();
239
2
    let (key, _value) = entry.take();
240
2
    assert_eq!(key, 2);
241
2
    assert!(lru.head().is_none());
242
2
    assert!(lru.tail().is_none());
243
2
}
244

            
245
1
#[test]
246
1
fn hash_entry_removal() {
247
1
    entry_removal_tests::<LruHashMap<_, _>>();
248
1
}
249

            
250
1
#[test]
251
1
fn btree_entry_removal() {
252
1
    entry_removal_tests::<LruBTreeMap<_, _>>();
253
1
}