use super::ShardCommitState;
use crate::tree::Hash;
fn hash(byte: u8) -> Hash {
Hash::of(&[byte])
}
#[test]
fn initial_snapshot_is_markerless_and_clean_by_gens() {
let cell = ShardCommitState::new();
let snapshot = cell.snapshot();
assert_eq!(snapshot.root, None);
assert_eq!(snapshot.dirty_gen, snapshot.committed_gen);
assert_eq!(snapshot.incarnation, 0);
assert!(!snapshot.recovering);
assert!(!snapshot.unreconciled);
assert_eq!(snapshot.advance_gen, 0);
}
#[test]
fn begin_recovery_raises_recovering_and_bumps_incarnation() {
let cell = ShardCommitState::new();
cell.begin_recovery();
let first = cell.snapshot();
assert!(first.recovering);
assert_eq!(first.incarnation, 1);
cell.publish_recovered(Some(hash(1)), true);
cell.begin_recovery();
assert_eq!(cell.snapshot().incarnation, 2, "each (re)spawn bumps once");
}
#[test]
fn publish_recovered_sets_dirty_iff_buffer_nonempty_and_preserves_advance_gen() {
let cell = ShardCommitState::new();
assert_eq!(cell.publish_committed(Some(hash(9)), true), Some(1));
cell.begin_recovery();
cell.publish_recovered(Some(hash(1)), true);
let clean = cell.snapshot();
assert!(!clean.recovering);
assert_eq!(clean.root, Some(hash(1)));
assert_eq!(
clean.committed_gen, clean.dirty_gen,
"empty buffer => clean"
);
assert_eq!(clean.advance_gen, 1, "advance_gen survives the incarnation");
cell.begin_recovery();
cell.publish_recovered(Some(hash(2)), false);
let dirty = cell.snapshot();
assert_ne!(
dirty.committed_gen, dirty.dirty_gen,
"non-empty recovered buffer => dirty"
);
assert_eq!(dirty.advance_gen, 1, "advance_gen still preserved");
}
#[test]
fn sync_from_buffer_tracks_emptiness() {
let cell = ShardCommitState::new();
cell.publish_recovered(Some(hash(0)), true); cell.sync_from_buffer(false);
assert_ne!(
cell.snapshot().committed_gen,
cell.snapshot().dirty_gen,
"non-empty buffer => dirty"
);
let after_first = cell.snapshot().dirty_gen;
cell.sync_from_buffer(false);
assert_eq!(
cell.snapshot().dirty_gen,
after_first,
"already dirty is idempotent"
);
cell.sync_from_buffer(true);
assert_eq!(
cell.snapshot().committed_gen,
cell.snapshot().dirty_gen,
"emptied buffer => clean"
);
}
#[test]
fn publish_committed_bumps_advance_gen_iff_advanced() {
let cell = ShardCommitState::new();
cell.sync_from_buffer(false); assert_eq!(cell.publish_committed(Some(hash(1)), true), Some(1));
let after = cell.snapshot();
assert_eq!(after.root, Some(hash(1)));
assert_eq!(after.committed_gen, after.dirty_gen, "commit => clean");
assert_eq!(after.advance_gen, 1);
assert_eq!(cell.publish_committed(Some(hash(1)), false), None);
assert_eq!(cell.snapshot().advance_gen, 1);
cell.sync_from_buffer(false);
assert_eq!(cell.publish_committed(Some(hash(2)), true), Some(2));
}
#[test]
fn mark_unreconciled_sets_the_flag() {
let cell = ShardCommitState::new();
cell.publish_recovered(Some(hash(0)), true);
assert!(!cell.snapshot().unreconciled);
cell.mark_unreconciled();
assert!(cell.snapshot().unreconciled);
cell.publish_committed(Some(hash(1)), true);
assert!(!cell.snapshot().unreconciled);
}
#[test]
fn classify_is_dirty_throughout_recovery() {
let cell = ShardCommitState::new();
cell.publish_committed(Some(hash(7)), true); assert!(!cell.classify().dirty, "clean before recovery");
cell.begin_recovery();
assert!(
cell.classify().dirty,
"a reader mid-recovery must classify DIRTY (recovering set)"
);
assert!(cell.classify().dirty, "still dirty later in the window");
cell.publish_recovered(Some(hash(7)), true); assert!(
!cell.classify().dirty,
"only the completing publish returns the shard to clean"
);
}
#[test]
fn incarnation_bumps_and_advance_gen_survives_across_incarnations() {
let cell = ShardCommitState::new();
cell.begin_recovery();
cell.publish_recovered(None, true);
assert_eq!(cell.snapshot().incarnation, 1);
cell.sync_from_buffer(false);
assert_eq!(cell.publish_committed(Some(hash(1)), true), Some(1));
cell.sync_from_buffer(false);
assert_eq!(cell.publish_committed(Some(hash(2)), true), Some(2));
assert_eq!(cell.snapshot().advance_gen, 2);
cell.begin_recovery();
assert_eq!(
cell.snapshot().incarnation,
2,
"each respawn bumps incarnation"
);
cell.publish_recovered(Some(hash(2)), true);
assert_eq!(
cell.snapshot().advance_gen,
2,
"advance_gen survives the incarnation (never reset)"
);
cell.sync_from_buffer(false);
assert_eq!(
cell.publish_committed(Some(hash(3)), true),
Some(3),
"the advance sequence continues across incarnations without a gap"
);
}
#[test]
fn poison_is_adopted_then_cleared_by_the_next_write() {
let cell = ShardCommitState::new();
cell.publish_committed(Some(hash(1)), true);
cell.force_poison_for_test();
assert!(cell.is_poisoned(), "the mutex is poisoned");
assert_eq!(cell.snapshot().root, Some(hash(1)));
cell.sync_from_buffer(true);
assert!(!cell.is_poisoned(), "the next write clears the poison");
}