@@ -109,3 +109,65 @@ fn multiple_hardlinks_to_a_single_file_without_deduplication() {
109109
110110 assert_eq ! ( actual_size, expected_size) ;
111111}
112+
113+ #[ test]
114+ fn complex_tree_with_shared_and_unique_files_with_deduplication ( ) {
115+ let files_per_branch = 255 ;
116+ let workspace =
117+ SampleWorkspace :: complex_tree_with_shared_and_unique_files ( files_per_branch, 100_000 ) ;
118+
119+ let tree = Command :: new ( PDU )
120+ . with_current_dir ( & workspace)
121+ . with_arg ( "--min-ratio=0" )
122+ . with_arg ( "--quantity=apparent-size" )
123+ . with_arg ( "--deduplicate-hardlinks" )
124+ . with_arg ( "--json-output" )
125+ . pipe ( stdio)
126+ . output ( )
127+ . expect ( "spawn command" )
128+ . pipe ( stdout_text)
129+ . pipe_as_ref ( serde_json:: from_str :: < JsonData > )
130+ . expect ( "parse stdout as JsonData" )
131+ . body
132+ . pipe ( JsonTree :: < Bytes > :: try_from)
133+ . expect ( "get tree of bytes" ) ;
134+
135+ let actual_size = tree. size ;
136+
137+ let file_size = workspace
138+ . join ( "no-hardlinks/file-0.txt" )
139+ . pipe_as_ref ( read_apparent_size)
140+ . pipe ( Bytes :: new) ;
141+
142+ let inode_size = |path : & str | {
143+ workspace
144+ . join ( path)
145+ . pipe_as_ref ( read_apparent_size)
146+ . pipe ( Bytes :: new)
147+ } ;
148+
149+ // The following formula treat the first file as "real" and
150+ // the non-first file with the same inode as "fake" for ease
151+ // of reasoning.
152+ // It should still produce the same result as the proper
153+ // deduplication formula however.
154+ #[ expect( clippy:: erasing_op) ]
155+ let expected_size: Bytes = [
156+ inode_size ( "." ) ,
157+ inode_size ( "no-hardlinks" ) ,
158+ inode_size ( "some-hardlinks" ) ,
159+ inode_size ( "only-hardlinks" ) ,
160+ inode_size ( "only-hardlinks/exclusive" ) ,
161+ inode_size ( "only-hardlinks/mixed" ) ,
162+ inode_size ( "only-hardlinks/external" ) ,
163+ file_size * files_per_branch, // no-hardlinks/*
164+ file_size * files_per_branch, // some-hardlinks/*
165+ file_size * files_per_branch, // only-hardlinks/exclusive/*
166+ file_size * files_per_branch, // only-hardlinks/mixed/*
167+ file_size * 0usize , // only-hardlinks/external/*
168+ ]
169+ . into_iter ( )
170+ . sum ( ) ;
171+
172+ assert_eq ! ( actual_size, expected_size) ;
173+ }
0 commit comments