#![forbid(unsafe_code)]
use tempfile::TempDir;
use crate::core::extent::ChunkId;
use crate::core::materialize::materialize_to_vec;
use crate::core::representation::{Representation, Residual};
use crate::optimizer::background::{PassCursor, current_persisted_bytes, optimize_pass};
use crate::optimizer::policy::OptimizeOptions;
use crate::store::inode::InodeData;
use crate::store::transaction::CrashHooks;
use crate::store::{ExtentUpdate, NewEntry, Store, StoreConfig};
fn create_store(dir: &TempDir) -> Store {
let cfg = StoreConfig {
segment_size: 1024 * 1024,
..Default::default()
};
Store::create(dir.path(), &cfg, [0x77; 16]).unwrap()
}
fn ino(store: &mut Store) -> u64 {
store
.create_entry(
1,
b"f",
NewEntry::file(0o644, 1000, 1000),
&CrashHooks::none(),
)
.unwrap()
}
fn chunk(seed: u32, edit: u8) -> Vec<u8> {
let mut b: Vec<u8> = (0..65536u64)
.map(|i| (((i.wrapping_mul(seed as u64 * 2654435761)) >> 8) % 251) as u8)
.collect();
if edit > 0 {
b[10] ^= edit;
b[32000] ^= edit.wrapping_add(1);
b[65530] ^= edit.wrapping_add(2);
}
b
}
fn extents_of(store: &Store, ino: u64) -> Vec<(u64, Representation)> {
let limits = *store.limits();
let inode = store.get_inode(ino).unwrap().unwrap();
let root = match inode.data {
InodeData::File { extent_root } => extent_root,
_ => return Vec::new(),
};
crate::store::extent_tree::scan_all(root, 64, limits.max_fanout, store)
.unwrap()
.into_iter()
.map(|(start, bytes)| {
let d = crate::format::descriptor::decode(
&bytes,
limits.max_descriptor_bytes,
limits.max_inline_bytes,
limits.max_palette,
limits.max_period,
limits.max_chunk_size,
)
.unwrap();
(start, d)
})
.collect()
}
#[test]
fn background_pass_preserves_exact_bytes() {
let dir = TempDir::new().unwrap();
let mut store = create_store(&dir);
let f = ino(&mut store);
let mut content = Vec::new();
for i in 0..8u32 {
content.extend_from_slice(&chunk(i + 1, (i % 3) as u8));
}
store.write_region(f, 0, &content).unwrap();
let before = store.read_file(f, 0, content.len() as u64).unwrap();
assert_eq!(before, content);
let stats = optimize_pass(&mut store, OptimizeOptions::default(), None, None).unwrap();
let after = store.read_file(f, 0, content.len() as u64).unwrap();
assert_eq!(after, content);
let _ = stats;
}
#[test]
fn drift_workload_stays_shallow_and_exact() {
let dir = TempDir::new().unwrap();
let mut store = create_store(&dir);
let f = ino(&mut store);
let base: Vec<u8> = (0..65536u64).map(|i| ((i * 7) % 251) as u8).collect();
store.write_region(f, 0, &base).unwrap();
let mut current = base.clone();
for v in 1..12u64 {
for j in 0..v {
let pos = ((j * 997) % 65536) as usize;
let val = (v + j) as u8;
store.write_region(f, pos as u64, &[val]).unwrap();
current[pos] = val;
}
}
let got = store.read_file(f, 0, 65536).unwrap();
assert_eq!(got, current, "drift writes corrupted bytes");
let exts = extents_of(&store, f);
assert_eq!(exts.len(), 1);
let (_, desc) = &exts[0];
assert!(
matches!(desc, Representation::BaseResidual { .. }),
"expected BASE_RESIDUAL, got {:?}",
desc.family()
);
let depth = crate::optimizer::rebase::chain_depth(&store, desc);
assert!(depth <= 2, "chain must stay shallow, got {depth}");
let limits = *store.limits();
assert_eq!(materialize_to_vec(desc, &store, &limits).unwrap(), current);
}
#[test]
fn background_pass_densifies_sequential_edits() {
let dir = TempDir::new().unwrap();
let mut store = create_store(&dir);
let f = ino(&mut store);
let base = chunk(7, 0);
store.write_region(f, 0, &base).unwrap();
let mut offsets = Vec::new();
for i in 1..4u32 {
let c = chunk(7, i as u8);
store.write_region(f, (i as u64) * 65536, &c).unwrap();
offsets.push((i as u64) * 65536);
}
let before = store.read_file(f, 0, 4 * 65536).unwrap();
let physical_before = store.physical_used();
let stats = optimize_pass(&mut store, OptimizeOptions::default(), None, None).unwrap();
assert!(stats.rewritten > 0, "expected rewrites, got {stats:?}");
let after = store.read_file(f, 0, 4 * 65536).unwrap();
assert_eq!(after, before, "background pass changed logical bytes");
crate::store::gc::collect(&mut store, &CrashHooks::none()).unwrap();
let physical_after = store.physical_used();
assert!(
physical_after <= physical_before,
"densification must not grow the store: before {physical_before}, after {physical_after}"
);
let exts = extents_of(&store, f);
assert!(exts.len() >= 2);
let base_residuals = exts
.iter()
.filter(|(_, d)| matches!(d, Representation::BaseResidual { .. }))
.count();
assert!(
base_residuals >= 1,
"expected at least one BASE_RESIDUAL, got families: {:?}",
exts.iter().map(|(_, d)| d.family()).collect::<Vec<_>>()
);
}
#[test]
fn background_pass_is_idempotent_and_byte_exact_after_remount() {
let dir = TempDir::new().unwrap();
let mut store = create_store(&dir);
let f = ino(&mut store);
let mut content = Vec::new();
for i in 0..6u32 {
content.extend_from_slice(&chunk(i + 1, i as u8));
}
store.write_region(f, 0, &content).unwrap();
optimize_pass(&mut store, OptimizeOptions::default(), None, None).unwrap();
let after_first = store.read_file(f, 0, content.len() as u64).unwrap();
assert_eq!(after_first, content);
optimize_pass(&mut store, OptimizeOptions::default(), None, None).unwrap();
let after_second = store.read_file(f, 0, content.len() as u64).unwrap();
assert_eq!(after_second, content);
drop(store);
let store2 = Store::open(dir.path(), &StoreConfig::default()).unwrap();
let after_remount = store2.read_file(f, 0, content.len() as u64).unwrap();
assert_eq!(after_remount, content);
}
#[test]
fn resumable_cursor_advances() {
let dir = TempDir::new().unwrap();
let mut store = create_store(&dir);
let f = ino(&mut store);
let mut content = Vec::new();
for i in 0..16u32 {
content.extend_from_slice(&chunk(i + 1, i as u8));
}
store.write_region(f, 0, &content).unwrap();
let mut cursor = PassCursor::default();
let s1 = optimize_pass(
&mut store,
OptimizeOptions::default(),
Some(4),
Some(&mut cursor),
)
.unwrap();
assert_eq!(s1.scanned, 4);
assert!(cursor.ino_index >= 1 || cursor.offset > 0);
let s2 = optimize_pass(
&mut store,
OptimizeOptions::default(),
Some(4),
Some(&mut cursor),
)
.unwrap();
assert_eq!(s2.scanned, 4);
let s3 = optimize_pass(
&mut store,
OptimizeOptions::default(),
None,
Some(&mut cursor),
)
.unwrap();
assert_eq!(cursor, PassCursor::default());
let _ = s3;
}
#[test]
fn chain_depth_resolves_through_the_chunk_index() {
let dir = TempDir::new().unwrap();
let mut store = create_store(&dir);
let f = ino(&mut store);
let base_bytes = chunk(3, 0);
store.write_region(f, 0, &base_bytes).unwrap();
let base_cid = ChunkId::of(&base_bytes);
let b1 = Representation::BaseResidual {
base: base_cid,
base_len: base_bytes.len() as u64,
residual: Residual::XorSparse {
len: base_bytes.len() as u64,
edits: Vec::new(),
},
len: base_bytes.len() as u64,
};
let b1_bytes = crate::format::descriptor::encode(&b1).unwrap();
let b1_cid = ChunkId::of(&b1_bytes);
store
.commit_file_extents(
f,
vec![ExtentUpdate {
offset: 65536,
descriptor: b1.clone(),
content_id: b1_cid,
objects: Vec::new(),
}],
None,
&CrashHooks::none(),
)
.unwrap();
assert_eq!(crate::optimizer::rebase::chain_depth(&store, &b1), 1);
let b2 = Representation::BaseResidual {
base: b1_cid,
base_len: base_bytes.len() as u64,
residual: Residual::XorSparse {
len: base_bytes.len() as u64,
edits: Vec::new(),
},
len: base_bytes.len() as u64,
};
let b2_bytes = crate::format::descriptor::encode(&b2).unwrap();
let b2_cid = ChunkId::of(&b2_bytes);
store
.commit_file_extents(
f,
vec![ExtentUpdate {
offset: 131072,
descriptor: b2.clone(),
content_id: b2_cid,
objects: Vec::new(),
}],
None,
&CrashHooks::none(),
)
.unwrap();
assert_eq!(crate::optimizer::rebase::chain_depth(&store, &b2), 2);
let limits = *store.limits();
let bytes = materialize_to_vec(&b2, &store, &limits).unwrap();
let flat = crate::optimizer::rebase::flatten_if_deep(&store, 131072, &b2, &bytes, &b2_cid)
.unwrap()
.expect("flatten should fire");
assert_eq!(crate::optimizer::rebase::depth_of(&flat.descriptor), 0);
let back = materialize_to_vec(&flat.descriptor, &store, &limits).unwrap();
assert_eq!(back, bytes);
assert_eq!(ChunkId::of(&back), ChunkId::of(&base_bytes));
}
#[test]
fn current_persisted_bytes_counts_objects() {
let dir = TempDir::new().unwrap();
let mut store = create_store(&dir);
let f = ino(&mut store);
let data = chunk(4, 0);
store.write_region(f, 0, &data).unwrap();
let exts = extents_of(&store, f);
let (_, desc) = &exts[0];
let bytes = current_persisted_bytes(&store, desc);
assert!(bytes >= desc.encoded_size());
let zeros = vec![0u8; 65536];
store.write_region(f, 65536, &zeros).unwrap();
let exts = extents_of(&store, f);
let (_, zdesc) = exts.iter().find(|(o, _)| *o == 65536).unwrap();
assert!(matches!(zdesc, Representation::Zero { .. }));
assert!(current_persisted_bytes(&store, zdesc) < 32);
}
#[test]
fn ablation_modes_preserve_bytes_and_differ() {
let dir = TempDir::new().unwrap();
let mut store = create_store(&dir);
let f = ino(&mut store);
let data = chunk(9, 0);
store
.write_region_with(f, 0, &data, OptimizeOptions::raw_only())
.unwrap();
let read = store.read_file(f, 0, data.len() as u64).unwrap();
assert_eq!(read, data);
let exts = extents_of(&store, f);
assert!(matches!(exts[0].1, Representation::Raw { .. }));
let dir2 = TempDir::new().unwrap();
let mut store2 = create_store(&dir2);
let f2 = ino(&mut store2);
store2.write_region(f2, 0, &data).unwrap();
let exts2 = extents_of(&store2, f2);
let raw_bytes = current_persisted_bytes(&store, &exts[0].1);
let full_bytes = current_persisted_bytes(&store2, &exts2[0].1);
assert!(full_bytes <= raw_bytes);
}