@@ -116,6 +116,130 @@ impl SampleWorkspace {
116116
117117 SampleWorkspace ( temp)
118118 }
119+
120+ /// Set up a temporary directory for tests.
121+ ///
122+ /// The tree in this tests have a diverse types of files, both shared (hardlinks)
123+ /// and unique (non-hardlinks).
124+ pub fn complex_tree_with_shared_and_unique_files (
125+ files_per_branch : usize ,
126+ bytes_per_file : usize ,
127+ ) -> Self {
128+ use std:: fs:: { create_dir_all, hard_link, write as write_file} ;
129+
130+ let whole = files_per_branch;
131+ let half = files_per_branch / 2 ;
132+ let quarter = files_per_branch / 4 ;
133+ let half_quarter = files_per_branch / 8 ;
134+ let temp = Temp :: new_dir ( ) . expect ( "create working directory for sample workspace" ) ;
135+
136+ temp. join ( "no-hardlinks" )
137+ . pipe ( create_dir_all)
138+ . expect ( "create no-hardlinks" ) ;
139+ temp. join ( "some-hardlinks" )
140+ . pipe ( create_dir_all)
141+ . expect ( "create some-hardlinks" ) ;
142+ temp. join ( "only-hardlinks/exclusive" )
143+ . pipe ( create_dir_all)
144+ . expect ( "create only-hardlinks/exclusive" ) ;
145+ temp. join ( "only-hardlinks/mixed" )
146+ . pipe ( create_dir_all)
147+ . expect ( "create only-hardlinks/mixed" ) ;
148+ temp. join ( "only-hardlinks/external" )
149+ . pipe ( create_dir_all)
150+ . expect ( "create only-hardlinks/external" ) ;
151+
152+ // Create files in no-hardlinks.
153+ // There will be no files with nlink > 1.
154+ ( 0 ..files_per_branch) . par_bridge ( ) . for_each ( |index| {
155+ let file_name = format ! ( "file-{index}.txt" ) ;
156+ let file_path = temp. join ( "no-hardlinks" ) . join ( file_name) ;
157+ if let Err ( error) = write_file ( & file_path, "a" . repeat ( bytes_per_file) ) {
158+ panic ! ( "Failed to write {bytes_per_file} bytes into {file_path:?}: {error}" ) ;
159+ }
160+ } ) ;
161+
162+ // Create files in some-hardlinks.
163+ // Let's divide the files into 8 equal groups.
164+ // Each file in the first group will have 2 exclusive links.
165+ // Each file in the second group will have 1 exclusive link.
166+ // Each file in the third and fourth groups will have no links.
167+ // Each file in the remaining groups is PLANNED to have 1 external link from only-hardlinks/mixed.
168+ ( 0 ..whole) . par_bridge ( ) . for_each ( |file_index| {
169+ let file_name = format ! ( "file-{file_index}.txt" ) ;
170+ let file_path = temp. join ( "some-hardlinks" ) . join ( file_name) ;
171+ if let Err ( error) = write_file ( & file_path, "a" . repeat ( bytes_per_file) ) {
172+ panic ! ( "Failed to write {bytes_per_file} bytes into {file_path:?}: {error}" ) ;
173+ }
174+
175+ let link_count =
176+ ( ( file_index < quarter) as usize ) + ( ( file_index < half_quarter) as usize ) ;
177+
178+ for link_index in 0 ..link_count {
179+ let link_name = format ! ( "link{link_index}-file{file_index}.txt" ) ;
180+ let link_path = temp. join ( "some-hardlinks" ) . join ( link_name) ;
181+ if let Err ( error) = hard_link ( & file_path, & link_path) {
182+ panic ! ( "Failed to link {file_path:?} to {link_path:?}: {error}" ) ;
183+ }
184+ }
185+ } ) ;
186+
187+ // Create files in only-hardlinks/exclusive.
188+ // Each file in this directory will have 1 exclusive link.
189+ ( 0 ..whole) . par_bridge ( ) . for_each ( |index| {
190+ let file_name = format ! ( "file-{index}.txt" ) ;
191+ let file_path = temp. join ( "only-hardlinks/exclusive" ) . join ( file_name) ;
192+ if let Err ( error) = write_file ( & file_path, "a" . repeat ( bytes_per_file) ) {
193+ panic ! ( "Failed to write {bytes_per_file} bytes into {file_path:?}: {error}" ) ;
194+ }
195+ let link_name = format ! ( "link-{index}.txt" ) ;
196+ let link_path = temp. join ( "only-hardlinks/exclusive" ) . join ( link_name) ;
197+ if let Err ( error) = hard_link ( & file_path, & link_path) {
198+ panic ! ( "Failed to link {file_path:?} to {link_path:?}: {error}" ) ;
199+ }
200+ } ) ;
201+
202+ // Create links in only-hardlinks/mixed.
203+ // Let's divide the PLANNED links into 2 equal groups.
204+ // Each link in the first group is PLANNED to shared with only-hardlinks/external.
205+ // Each link in the second group is exclusive.
206+ ( half..whole) . par_bridge ( ) . for_each ( |index| {
207+ let file_name = format ! ( "link0-{index}.txt" ) ;
208+ let file_path = temp. join ( "only-hardlinks/mixed" ) . join ( file_name) ;
209+ if let Err ( error) = write_file ( & file_path, "a" . repeat ( bytes_per_file) ) {
210+ panic ! ( "Failed to write {bytes_per_file} bytes to {file_path:?}: {error}" ) ;
211+ }
212+
213+ let link_name = format ! ( "link1-{index}.txt" ) ;
214+ let link_path = temp. join ( "only-hardlinks/mixed" ) . join ( link_name) ;
215+ if let Err ( error) = hard_link ( & file_path, & link_path) {
216+ panic ! ( "Failed to link {file_path:?} to {link_path:?}: {error}" ) ;
217+ }
218+ } ) ;
219+
220+ // Create links in only-hardlinks/external
221+ // Let's divide the links into 2 equal groups.
222+ // The first group will share with only-hardlinks/mixed.
223+ // The second group will share with some-hardlinks.
224+ ( 0 ..whole) . par_bridge ( ) . for_each ( |index| {
225+ let link_name = format ! ( "linkX-{index}.txt" ) ;
226+ let link_path = temp. join ( "only-hardlinks/external" ) . join ( link_name) ;
227+
228+ let file_path = if index <= half {
229+ let file_name = format ! ( "link0-{index}.txt" ) ; // file name from only-hardlinks/mixed
230+ temp. join ( "only-hardlinks/mixed" ) . join ( file_name)
231+ } else {
232+ let file_name = format ! ( "file-{index}.txt" ) ; // file name from some-hardlinks
233+ temp. join ( "some-hardlinks" ) . join ( file_name)
234+ } ;
235+
236+ if let Err ( error) = hard_link ( & file_path, & link_path) {
237+ panic ! ( "Failed to link {file_path:?} to {link_path:?}: {error}" ) ;
238+ }
239+ } ) ;
240+
241+ SampleWorkspace ( temp)
242+ }
119243}
120244
121245/// Make the snapshot of a [`TreeReflection`] testable.
0 commit comments