Nothing to see here, move along meow
1use lancer_core::fs::BLOCK_SIZE_MIN;
2use lancer_lancerfs::test_helpers::setup_fs;
3use lancer_lancerfs::{dedup, integrity};
4
5#[test]
6fn identical_blocks_deduped() {
7 let mut fs = setup_fs(4096);
8 let root = fs.root_block();
9
10 let (_inode_a, block_a) = fs.create_file(root, b"file_a");
11 let (_inode_b, block_b) = fs.create_file(root, b"file_b");
12
13 let data = vec![0xABu8; BLOCK_SIZE_MIN as usize];
14
15 fs.write_file(block_a, 0, &data);
16 fs.write_file(block_b, 0, &data);
17
18 let read_a = fs.read_file(block_a, 0, BLOCK_SIZE_MIN as usize);
19 let read_b = fs.read_file(block_b, 0, BLOCK_SIZE_MIN as usize);
20
21 assert_eq!(read_a, data);
22 assert_eq!(read_b, data);
23
24 let inode_a = fs.read_inode(block_a);
25 let inode_b = fs.read_inode(block_b);
26 assert_eq!(
27 inode_a.direct[0].physical_block_addr(),
28 inode_b.direct[0].physical_block_addr()
29 );
30
31 let content_hash = integrity::xxhash128(&data);
32 let shard = dedup::dedup_shard(content_hash);
33 let dedup_result = dedup::dedup_check(
34 &mut fs.pool,
35 &mut fs.cache,
36 &mut fs.bio,
37 &fs.state.dedup_roots[shard],
38 content_hash,
39 )
40 .unwrap();
41 assert!(
42 matches!(dedup_result, dedup::DedupResult::Reused(_)),
43 "dedup index must contain an entry for the shared content hash"
44 );
45}
46
47#[test]
48fn different_blocks_not_deduped() {
49 let mut fs = setup_fs(4096);
50 let root = fs.root_block();
51
52 let (_inode_a, block_a) = fs.create_file(root, b"diff_a");
53 let (_inode_b, block_b) = fs.create_file(root, b"diff_b");
54
55 let data_a = vec![0x11u8; BLOCK_SIZE_MIN as usize];
56 let data_b = vec![0x22u8; BLOCK_SIZE_MIN as usize];
57
58 fs.write_file(block_a, 0, &data_a);
59 fs.write_file(block_b, 0, &data_b);
60
61 let inode_a = fs.read_inode(block_a);
62 let inode_b = fs.read_inode(block_b);
63 assert_ne!(
64 inode_a.direct[0].physical_block_addr(),
65 inode_b.direct[0].physical_block_addr()
66 );
67}
68
69#[test]
70fn dedup_data_survives_one_file_delete() {
71 let mut fs = setup_fs(4096);
72 let root = fs.root_block();
73
74 let (_inode_a, block_a) = fs.create_file(root, b"surv_a");
75 let (_inode_b, block_b) = fs.create_file(root, b"surv_b");
76
77 let data = vec![0xCDu8; BLOCK_SIZE_MIN as usize];
78 fs.write_file(block_a, 0, &data);
79 fs.write_file(block_b, 0, &data);
80 fs.commit();
81
82 fs.delete_file(root, b"surv_a").unwrap();
83 fs.commit();
84
85 let read_b = fs.read_file(block_b, 0, BLOCK_SIZE_MIN as usize);
86 assert_eq!(read_b, data);
87}
88
89#[test]
90fn dedup_across_multiple_writes() {
91 let mut fs = setup_fs(4096);
92 let root = fs.root_block();
93
94 let (_inode, block) = fs.create_file(root, b"multi_dedup");
95
96 let data = vec![0xEEu8; BLOCK_SIZE_MIN as usize];
97 fs.write_file(block, 0, &data);
98 fs.write_file(block, BLOCK_SIZE_MIN as u64, &data);
99
100 let inode = fs.read_inode(block);
101 assert_eq!(
102 inode.direct[0].physical_block_addr(),
103 inode.direct[1].physical_block_addr()
104 );
105
106 let read_first = fs.read_file(block, 0, BLOCK_SIZE_MIN as usize);
107 let read_second = fs.read_file(block, BLOCK_SIZE_MIN as u64, BLOCK_SIZE_MIN as usize);
108 assert_eq!(read_first, data);
109 assert_eq!(read_second, data);
110}
111
112#[test]
113fn dedup_both_deleted_bulkfree_reclaims() {
114 let mut fs = setup_fs(4096);
115 let root = fs.root_block();
116
117 let (_inode_a, block_a) = fs.create_file(root, b"dup_a");
118 let (_inode_b, block_b) = fs.create_file(root, b"dup_b");
119
120 let data = vec![0xFFu8; BLOCK_SIZE_MIN as usize];
121 fs.write_file(block_a, 0, &data);
122 fs.write_file(block_b, 0, &data);
123 fs.commit();
124
125 let inode_a = fs.read_inode(block_a);
126 let inode_b = fs.read_inode(block_b);
127 assert_eq!(
128 inode_a.direct[0].physical_block_addr(),
129 inode_b.direct[0].physical_block_addr(),
130 "both files must share the same physical block"
131 );
132
133 let alloc_before = fs.count_allocated_blocks();
134
135 fs.delete_file(root, b"dup_a").unwrap();
136 fs.delete_file(root, b"dup_b").unwrap();
137 fs.commit();
138 fs.run_bulkfree();
139
140 let alloc_after = fs.count_allocated_blocks();
141 assert!(
142 alloc_after < alloc_before,
143 "bulkfree must reclaim blocks after deleting both dedup references: before={alloc_before}, after={alloc_after}"
144 );
145}