Skip to content

Commit 0fcc361

Browse files
committed
test: improve proptests for LruCache
1 parent 4831d47 commit 0fcc361

File tree

1 file changed

+62
-32
lines changed

1 file changed

+62
-32
lines changed

stacks-common/src/util/lru_cache.rs

Lines changed: 62 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,9 @@ mod tests {
305305
assert_eq!(flushed, [(3, 3), (2, 2)]);
306306
}
307307

308+
/// Simple LRU implementation for testing
308309
pub struct SimpleLRU {
309-
pub cache: Vec<u32>,
310+
pub cache: Vec<Node<u32, u32>>,
310311
capacity: usize,
311312
}
312313

@@ -318,24 +319,41 @@ mod tests {
318319
}
319320
}
320321

321-
pub fn insert(&mut self, key: u32) {
322-
if let Some(pos) = self.cache.iter().position(|&x| x == key) {
322+
pub fn insert(&mut self, key: u32, value: u32, dirty: bool) {
323+
if let Some(pos) = self.cache.iter().position(|x| x.key == key) {
323324
self.cache.remove(pos);
324325
} else if self.cache.len() == self.capacity {
325326
self.cache.remove(0);
326327
}
327-
self.cache.push(key);
328+
self.cache.push(Node {
329+
key,
330+
value,
331+
dirty,
332+
next: 0,
333+
prev: 0,
334+
});
328335
}
329336

330337
pub fn get(&mut self, key: u32) -> Option<u32> {
331-
if let Some(pos) = self.cache.iter().position(|&x| x == key) {
332-
self.cache.remove(pos);
333-
self.cache.push(key);
334-
Some(key)
338+
if let Some(pos) = self.cache.iter().position(|x| x.key == key) {
339+
let node = self.cache.remove(pos);
340+
let value = node.value;
341+
self.cache.push(node);
342+
Some(value)
335343
} else {
336344
None
337345
}
338346
}
347+
348+
pub fn flush<E>(&mut self, mut f: impl FnMut(&u32, u32) -> Result<(), E>) -> Result<(), E> {
349+
for node in self.cache.iter_mut().rev() {
350+
if node.dirty {
351+
f(&node.key, node.value)?;
352+
}
353+
node.dirty = false;
354+
}
355+
Ok(())
356+
}
339357
}
340358
}
341359

@@ -348,18 +366,18 @@ mod property_tests {
348366

349367
#[derive(Debug, Clone)]
350368
enum CacheOp {
351-
Insert(u32),
369+
Insert(u32, u32),
352370
Get(u32),
353-
InsertClean(u32),
371+
InsertClean(u32, u32),
354372
Flush,
355373
}
356374

357375
prop_compose! {
358-
fn arbitrary_op()(op_type in 0..4, value in 0..100u32) -> CacheOp {
376+
fn arbitrary_op()(op_type in 0..4, key in 0..100u32, value in 0..1000u32) -> CacheOp {
359377
match op_type {
360-
0 => CacheOp::Insert(value),
361-
1 => CacheOp::Get(value),
362-
2 => CacheOp::InsertClean(value),
378+
0 => CacheOp::Insert(key, value),
379+
1 => CacheOp::Get(key),
380+
2 => CacheOp::InsertClean(key, value),
363381
_ => CacheOp::Flush,
364382
}
365383
}
@@ -373,9 +391,9 @@ mod property_tests {
373391
let mut cache = LruCache::new(10);
374392
for op in ops {
375393
match op {
376-
CacheOp::Insert(v) => { cache.insert(v, v); }
377-
CacheOp::Get(v) => { cache.get(&v); }
378-
CacheOp::InsertClean(v) => { cache.insert_clean(v, v); }
394+
CacheOp::Insert(k, v) => { cache.insert(k, v); }
395+
CacheOp::Get(k) => { cache.get(&k); }
396+
CacheOp::InsertClean(k, v) => { cache.insert_clean(k, v); }
379397
CacheOp::Flush => { cache.flush(|_, _| Ok::<(), ()>(())).unwrap(); }
380398
}
381399
}
@@ -397,9 +415,9 @@ mod property_tests {
397415
let mut cache = LruCache::new(10);
398416
for op in ops {
399417
match op {
400-
CacheOp::Insert(v) => { cache.insert(v, v); }
401-
CacheOp::Get(v) => { cache.get(&v); }
402-
CacheOp::InsertClean(v) => { cache.insert_clean(v, v); }
418+
CacheOp::Insert(k, v) => { cache.insert(k, v); }
419+
CacheOp::Get(k) => { cache.get(&k); }
420+
CacheOp::InsertClean(k, v) => { cache.insert_clean(k, v); }
403421
CacheOp::Flush => { cache.flush(|_, _| Ok::<(), ()>(())).unwrap(); }
404422
}
405423
// Verify linked list integrity
@@ -426,20 +444,32 @@ mod property_tests {
426444
let mut simple = SimpleLRU::new(5);
427445
for op in ops {
428446
match op {
429-
CacheOp::Insert(v) => {
430-
cache.insert(v, v);
431-
simple.insert(v);
447+
CacheOp::Insert(k, v) => {
448+
cache.insert(k, v);
449+
simple.insert(k, v, true);
432450
}
433-
CacheOp::Get(v) => {
434-
let actual = cache.get(&v);
435-
let expected = simple.get(v);
451+
CacheOp::Get(k) => {
452+
let actual = cache.get(&k);
453+
let expected = simple.get(k);
436454
prop_assert_eq!(actual, expected);
437455
}
438-
CacheOp::InsertClean(v) => {
439-
cache.insert_clean(v, v);
440-
simple.insert(v);
456+
CacheOp::InsertClean(k, v) => {
457+
cache.insert_clean(k, v);
458+
simple.insert(k, v, false);
459+
}
460+
CacheOp::Flush => {
461+
let mut flushed = vec![];
462+
let mut simple_flushed = vec![];
463+
cache.flush(|k, v| {
464+
flushed.push((*k, v));
465+
Ok::<(), ()>(())
466+
}).unwrap();
467+
simple.flush(|k, v| {
468+
simple_flushed.push((*k, v));
469+
Ok::<(), ()>(())
470+
}).unwrap();
471+
prop_assert_eq!(flushed, simple_flushed);
441472
}
442-
CacheOp::Flush => cache.flush(|_, _| Ok::<(), ()>(())).unwrap(),
443473
};
444474

445475
// The cache should have the same order as the simple LRU
@@ -450,8 +480,8 @@ mod property_tests {
450480
prop_assert!(false, "Linked list cycle detected");
451481
}
452482
let idx = simple.cache.len() - count - 1;
453-
prop_assert_eq!(cache.order[curr].key, simple.cache[idx]);
454-
prop_assert_eq!(cache.order[curr].value, simple.cache[idx]);
483+
prop_assert_eq!(cache.order[curr].key, simple.cache[idx].key);
484+
prop_assert_eq!(cache.order[curr].value, simple.cache[idx].value);
455485
curr = cache.order[curr].next;
456486
count += 1;
457487
}

0 commit comments

Comments
 (0)