#![allow(clippy::unwrap_used, clippy::expect_used)]
use proptest::prelude::*;
use txn_db::{Db, Snapshot};
const KEY_SPACE: u8 = 6;
#[derive(Debug, Clone)]
enum Step {
Put(u8, u8),
Delete(u8),
Snapshot,
Gc,
}
fn step_strategy() -> impl Strategy<Value = Step> {
prop_oneof![
4 => (0..KEY_SPACE, any::<u8>()).prop_map(|(k, v)| Step::Put(k, v)),
1 => (0..KEY_SPACE).prop_map(Step::Delete),
2 => Just(Step::Snapshot),
2 => Just(Step::Gc),
]
}
struct Pinned {
snap: Snapshot,
observed: Vec<Option<u8>>,
}
fn observe(snap: &Snapshot) -> Vec<Option<u8>> {
(0..KEY_SPACE)
.map(|k| snap.get(&[k]).unwrap().map(|v| v[0]))
.collect()
}
proptest! {
#[test]
fn gc_never_reclaims_a_live_snapshots_versions(steps in prop::collection::vec(step_strategy(), 1..80)) {
let db = Db::new();
let mut pinned: Vec<Pinned> = Vec::new();
for step in steps {
match step {
Step::Put(k, v) => {
let mut tx = db.begin();
tx.put(vec![k], vec![v]);
prop_assert!(tx.commit().is_ok());
}
Step::Delete(k) => {
let mut tx = db.begin();
tx.delete(vec![k]);
prop_assert!(tx.commit().is_ok());
}
Step::Snapshot => {
let snap = db.snapshot();
let observed = observe(&snap);
pinned.push(Pinned { snap, observed });
}
Step::Gc => {
let _ = db.collect_garbage();
for p in &pinned {
prop_assert_eq!(observe(&p.snap), p.observed.clone());
}
}
}
}
for p in &pinned {
prop_assert_eq!(observe(&p.snap), p.observed.clone());
}
}
#[test]
fn gc_with_no_reader_keeps_only_the_newest_value(values in prop::collection::vec(any::<u8>(), 1..30)) {
let db = Db::new();
for v in &values {
let mut tx = db.begin();
tx.put(b"k".to_vec(), vec![*v]);
prop_assert!(tx.commit().is_ok());
}
let _ = db.collect_garbage();
let last = [*values.last().unwrap()];
let snap = db.snapshot();
let got = snap.get(b"k").unwrap();
prop_assert_eq!(got.as_deref(), Some(&last[..]));
}
}