use super::BranchRegistry;
use crate::branch::{fork_registered, fork_shards_registered};
use crate::tree::Hash;
fn hash(byte: u8) -> Hash {
Hash::from_bytes([byte; 32])
}
#[test]
fn new_registry_has_no_live_roots() {
let registry = BranchRegistry::new();
assert!(registry.live_roots().is_empty());
}
#[test]
fn registered_fork_root_is_live_until_handle_drops() {
let registry = BranchRegistry::new();
let root = hash(1);
{
let branch = fork_registered(root, ®istry);
assert_eq!(branch.fork_point(), root);
assert!(registry.live_roots().contains(&root));
}
assert!(!registry.live_roots().contains(&root));
}
#[test]
fn cloned_handle_keeps_registered_root_live() {
let registry = BranchRegistry::new();
let root = hash(2);
let branch = fork_registered(root, ®istry);
let clone = branch.clone();
drop(branch);
assert!(registry.live_roots().contains(&root));
drop(clone);
assert!(!registry.live_roots().contains(&root));
}
#[test]
fn duplicate_roots_are_reference_counted() {
let registry = BranchRegistry::new();
let root = hash(3);
let first = fork_registered(root, ®istry);
let second = fork_registered(root, ®istry);
drop(first);
assert!(registry.live_roots().contains(&root));
drop(second);
assert!(!registry.live_roots().contains(&root));
}
#[test]
fn registered_multi_shard_fork_tracks_every_shard_root() -> Result<(), crate::branch::BranchError> {
let registry = BranchRegistry::new();
let shard_three = hash(4);
let shard_five = hash(5);
{
let branch = fork_shards_registered([(3, shard_three), (5, shard_five)], ®istry)?;
assert_eq!(branch.shard_count(), 2);
let live = registry.live_roots();
assert!(live.contains(&shard_three));
assert!(live.contains(&shard_five));
}
assert!(registry.live_roots().is_empty());
Ok(())
}
#[test]
fn advance_swaps_a_singly_pinned_root() {
let registry = BranchRegistry::new();
let old = hash(6);
let new = hash(7);
registry.register(old);
registry.advance(old, new);
let live = registry.live_roots();
assert!(!live.contains(&old));
assert!(live.contains(&new));
}
#[test]
fn advance_preserves_a_refcounted_old_root() {
let registry = BranchRegistry::new();
let anchor = hash(8);
let head = hash(9);
registry.register(anchor);
registry.register(anchor);
registry.advance(anchor, head);
let live = registry.live_roots();
assert!(live.contains(&anchor));
assert!(live.contains(&head));
registry.deregister(anchor);
assert!(!registry.live_roots().contains(&anchor));
}
#[test]
fn live_roots_snapshot_never_contains_neither_root_during_advance() {
let registry = BranchRegistry::new();
let old = hash(10);
let new = hash(11);
registry.register(old);
let flipper = {
let registry = registry.clone();
std::thread::spawn(move || {
for _ in 0..2_000 {
registry.advance(old, new);
registry.advance(new, old);
}
})
};
for _ in 0..2_000 {
let live = registry.live_roots();
assert!(
live.contains(&old) || live.contains(&new),
"live_roots snapshot observed neither the superseded nor the advanced root"
);
}
assert!(flipper.join().is_ok());
let live = registry.live_roots();
assert!(live.contains(&old) || live.contains(&new));
}
#[test]
fn guard_replace_retargets_the_pin_released_on_drop() {
let registry = BranchRegistry::new();
let old = hash(12);
let new = hash(13);
let guard = registry.register_roots([old]);
registry.advance(old, new);
guard.replace(old, new);
assert!(registry.live_roots().contains(&new));
drop(guard);
assert!(registry.live_roots().is_empty());
}
#[test]
fn refcount_two_anchor_pattern_survives_one_advance_and_drop() {
let registry = BranchRegistry::new();
let anchor = hash(14);
let head = hash(15);
let guard = registry.register_roots([anchor, anchor]);
registry.advance(anchor, head);
guard.replace(anchor, head);
let live = registry.live_roots();
assert!(live.contains(&anchor), "anchor-role pin must survive");
assert!(live.contains(&head));
drop(guard);
assert!(registry.live_roots().is_empty());
}
#[test]
fn guard_replace_of_an_unpinned_root_still_tracks_the_new_pin() {
let registry = BranchRegistry::new();
let old = hash(16);
let new = hash(17);
let guard = registry.register_roots([hash(18)]);
registry.advance(old, new);
guard.replace(old, new);
assert!(registry.live_roots().contains(&new));
drop(guard);
assert!(registry.live_roots().is_empty());
}