#![forbid(unsafe_code)]
use crate::optimizer::policy::OptimizeOptions;
use crate::store::transaction::CrashHooks;
use crate::store::{NewEntry, Store, StoreConfig};
use tempfile::TempDir;
fn create_store(dir: &TempDir) -> Store {
let cfg = StoreConfig {
segment_size: 128 * 1024 * 1024,
..Default::default()
};
Store::create(dir.path(), &cfg, [0x5c; 16]).unwrap()
}
fn create_file(store: &Store, name: &str) -> u64 {
store
.create_entry(
store.current_root().root_dir_ino,
name.as_bytes(),
NewEntry::file(0o644, 1000, 1000),
&CrashHooks::none(),
)
.unwrap()
}
fn noise(n: usize, seed: u64) -> Vec<u8> {
let mut state = seed;
let mut out = Vec::with_capacity(n);
while out.len() < n {
state = state.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = state;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^= z >> 31;
let b = z.to_le_bytes();
let take = (n - out.len()).min(8);
out.extend_from_slice(&b[..take]);
}
out
}
fn drift_text(n_chunks: usize) -> Vec<u8> {
let chunk = 65536usize;
let mut out = Vec::with_capacity(n_chunks * chunk);
for c in 0..n_chunks {
for i in 0..chunk {
let mut b = b'a' + ((i / 7) % 23) as u8;
if i % 97 == 0 {
b = b"fn main() { return 0; }"[i % 23];
}
if i == c * 1009 % chunk {
b = b'X'; }
out.push(b);
}
}
out
}
fn extent_descriptor(store: &Store, ino: u64, offset: u64) -> Vec<u8> {
let limits = store.limits();
let inode = store.get_inode(ino).unwrap().unwrap();
let root = match inode.data {
crate::store::inode::InodeData::File { extent_root } => extent_root,
_ => panic!("not a file"),
};
let (_, bytes) = crate::store::extent_tree::covering(
root,
offset,
crate::store::BTREE_ORDER,
limits.max_fanout,
store,
)
.unwrap()
.expect("extent covers offset");
bytes
}
fn extent_family(store: &Store, ino: u64, offset: u64) -> String {
let limits = store.limits();
let bytes = extent_descriptor(store, ino, offset);
let desc = crate::format::descriptor::decode(
&bytes,
limits.max_descriptor_bytes,
limits.max_inline_bytes,
limits.max_palette,
limits.max_period,
limits.max_chunk_size,
)
.unwrap();
desc.family().to_string()
}
#[test]
fn parallel_multi_chunk_writes_are_byte_exact() {
let dir = TempDir::new().unwrap();
let store = create_store(&dir);
let ino = create_file(&store, "a");
let corpora: Vec<(String, Vec<u8>)> = vec![
("noise".into(), noise(16 * 65536, 0x1a2b_3c4d)),
("drift-text".into(), drift_text(16)),
("zeros".into(), vec![0u8; 16 * 65536]),
("periodic".into(), {
let mut v = Vec::with_capacity(16 * 65536);
for i in 0..(16 * 65536) {
v.push((i % 256) as u8);
}
v
}),
];
for (label, data) in &corpora {
let f = create_file(&store, label);
store.write_region(f, 0, data).unwrap();
let back = store.read_file(f, 0, data.len() as u64).unwrap();
assert_eq!(&back, data, "{label} single-call byte-exactness");
}
let data = drift_text(8);
let mut writes: Vec<(u64, Vec<u8>)> = Vec::new();
let mut off = 0u64;
while off < data.len() as u64 {
let len = ((off * 7) % 30000 + 1) as usize;
let len = len.min(data.len() - off as usize);
writes.push((off, data[off as usize..off as usize + len].to_vec()));
off += len as u64;
}
store
.write_region_batch(ino, &writes, OptimizeOptions::default())
.unwrap();
let back = store.read_file(ino, 0, data.len() as u64).unwrap();
assert_eq!(&back[..], &data[..], "group-commit byte-exactness");
let report = crate::fsck::fsck(dir.path(), &crate::fsck::FsckOptions::default()).unwrap();
assert!(report.is_clean(), "fsck: {}", report.render());
}
#[test]
fn identical_content_encodes_deterministically_across_stores() {
let dir1 = TempDir::new().unwrap();
let dir2 = TempDir::new().unwrap();
let store1 = create_store(&dir1);
let store2 = create_store(&dir2);
let data = drift_text(4);
let a1 = create_file(&store1, "a");
store1.write_region(a1, 0, &data).unwrap();
let a2 = create_file(&store2, "a");
store2.write_region(a2, 0, &data).unwrap();
for i in 0..4 {
let off = (i * 65536) as u64;
let d1 = extent_descriptor(&store1, a1, off);
let d2 = extent_descriptor(&store2, a2, off);
assert_eq!(
d1, d2,
"extent {i} descriptor must be deterministic across stores"
);
}
assert_eq!(store1.read_file(a1, 0, data.len() as u64).unwrap(), data);
assert_eq!(store2.read_file(a2, 0, data.len() as u64).unwrap(), data);
}
#[test]
fn committed_content_reused_in_second_file_is_byte_exact() {
let dir = TempDir::new().unwrap();
let store = create_store(&dir);
let data = drift_text(4);
let a = create_file(&store, "a");
store.write_region(a, 0, &data).unwrap();
let b = create_file(&store, "b");
store.write_region(b, 0, &data).unwrap();
assert_eq!(store.read_file(a, 0, data.len() as u64).unwrap(), data);
assert_eq!(store.read_file(b, 0, data.len() as u64).unwrap(), data);
store.write_region(a, 0, &data).unwrap();
assert_eq!(store.read_file(a, 0, data.len() as u64).unwrap(), data);
let report = crate::fsck::fsck(dir.path(), &crate::fsck::FsckOptions::default()).unwrap();
assert!(report.is_clean(), "fsck: {}", report.render());
}
#[test]
fn consecutive_identical_chunks_in_one_batch() {
let dir = TempDir::new().unwrap();
let store = create_store(&dir);
let ino = create_file(&store, "a");
let chunk = noise(65536, 0xfeed_face);
let mut data = Vec::with_capacity(8 * 65536);
for _ in 0..8 {
data.extend_from_slice(&chunk);
}
store.write_region(ino, 0, &data).unwrap();
let back = store.read_file(ino, 0, data.len() as u64).unwrap();
assert_eq!(back, data, "identical-chunk batch byte-exactness");
let report = crate::fsck::fsck(dir.path(), &crate::fsck::FsckOptions::default()).unwrap();
assert!(report.is_clean(), "fsck: {}", report.render());
}
#[test]
fn duplicate_chunks_after_aliased_first_occurrence() {
let dir = TempDir::new().unwrap();
let store = create_store(&dir);
let x = drift_text(2);
let a = create_file(&store, "a");
store.write_region(a, 0, &x).unwrap();
assert_ne!(extent_family(&store, a, 0), "RAW", "X must be compressed");
let b = create_file(&store, "b");
store.write_region(b, 0, &x).unwrap();
let back = store.read_file(b, 0, x.len() as u64).unwrap();
assert_eq!(back, x, "duplicate-of-aliased content byte-exactness");
let report = crate::fsck::fsck(dir.path(), &crate::fsck::FsckOptions::default()).unwrap();
assert!(report.is_clean(), "fsck: {}", report.render());
}
#[test]
fn in_batch_dict_chain_never_exceeds_decode_cap() {
let dir = TempDir::new().unwrap();
let store = create_store(&dir);
let ino = create_file(&store, "a");
let data = drift_text(6);
store.write_region(ino, 0, &data).unwrap();
let max_depth = store.limits().max_reference_depth;
let back = store.read_file(ino, 0, data.len() as u64).unwrap();
assert_eq!(back, data);
for i in 0..6 {
let (_, bytes) = {
let limits = store.limits();
let inode = store.get_inode(ino).unwrap().unwrap();
let root = match inode.data {
crate::store::inode::InodeData::File { extent_root } => extent_root,
_ => panic!("not a file"),
};
crate::store::extent_tree::covering(
root,
(i * 65536) as u64,
crate::store::BTREE_ORDER,
limits.max_fanout,
&store,
)
.unwrap()
.expect("extent covers offset")
};
let desc = crate::format::descriptor::decode(
&bytes,
store.limits().max_descriptor_bytes,
store.limits().max_inline_bytes,
store.limits().max_palette,
store.limits().max_period,
store.limits().max_chunk_size,
)
.unwrap();
let depth = crate::optimizer::rebase::chain_depth(&store, &desc);
assert!(
depth <= max_depth,
"chunk {i}: chain depth {depth} exceeds the cap {max_depth}"
);
}
let family = extent_family(&store, ino, 5 * 65536);
assert_ne!(
family, "SEQUENCE_DICT",
"chunk 5 must re-anchor at the depth cap (got {family})"
);
let report = crate::fsck::fsck(dir.path(), &crate::fsck::FsckOptions::default()).unwrap();
assert!(report.is_clean(), "fsck: {}", report.render());
}