Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
FileUtilBaseTest.java
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2021 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24package jau.test.fs;
25
26import org.jau.fs.CopyOptions;
27import org.jau.fs.FileStats;
28import org.jau.fs.FileUtil;
29import org.jau.fs.TraverseEvent;
30import org.jau.fs.TraverseOptions;
31import org.jau.io.PrintUtil;
32import org.junit.Assert;
33
34import jau.test.junit.util.JunitTracer;
35
36public class FileUtilBaseTest extends JunitTracer {
37 public static final String image_file = "test_data.sqfs";
38 public static final String root = "test_data";
39 // normal location with jaulib as sole project (a)
40 public static final String project_root1a = "../../../test_data";
41 // normal location with jaulib as sole project (b)
42 public static final String project_root1b = "../../../../test_data";
43 // submodule location with jaulib directly hosted below main project (a)
44 public static final String project_root2a = "../../../../jaulib/test_data";
45 // submodule location with jaulib directly hosted below main project (b)
46 public static final String project_root2b = "../../../../../jaulib/test_data";
47 // external filesystem to test ...
48 public static final String project_root_ext = "/mnt/ssd0/data/test_data";
49 // external vfat filesystem destination to test ...
50 public static final String dest_fs_vfat = "/mnt/vfat";
51
52 public static final FileStats getTestDataDirStats() {
53 FileStats path_stats = new FileStats(project_root1a);
54 if( path_stats.exists() ) {
55 return path_stats;
56 }
57 path_stats = new FileStats(project_root1b);
58 if( path_stats.exists() ) {
59 return path_stats;
60 }
61 path_stats = new FileStats(project_root2a);
62 if( path_stats.exists() ) {
63 return path_stats;
64 }
65 path_stats = new FileStats(project_root2b);
66 if( path_stats.exists() ) {
67 return path_stats;
68 }
69 return new FileStats();
70 }
71 public static String getTestDataRelDir() {
72 FileStats path_stats = new FileStats(project_root1a);
73 if( path_stats.exists() ) {
74 return project_root1a;
75 }
76 path_stats = new FileStats(project_root1b);
77 if( path_stats.exists() ) {
78 return project_root1b;
79 }
80 path_stats = new FileStats(project_root2a);
81 if( path_stats.exists() ) {
82 return project_root2a;
83 }
84 path_stats = new FileStats(project_root2b);
85 if( path_stats.exists() ) {
86 return project_root2b;
87 }
88 return "";
89 }
90 public static final FileStats getTestDataImageFile() {
91 FileStats path_stats = new FileStats("../"+image_file);
92 if( path_stats.exists() ) {
93 return path_stats;
94 }
95 path_stats = new FileStats("../../"+image_file);
96 if( path_stats.exists() ) {
97 return path_stats;
98 }
99 return new FileStats();
100 }
101
102 public static class VisitorStats {
103 public TraverseOptions topts;
104 public int total_real;
107 public int total_no_access;
109 public long total_file_bytes;
110 public int files_real;
111 public int files_sym_link;
112 public int dirs_real;
113 public int dirs_sym_link;
114
115 public VisitorStats(final TraverseOptions topts_) {
116 topts = topts_;
117 total_real = 0;
120 total_no_access = 0;
123 files_real = 0;
124 files_sym_link = 0;
125 dirs_real = 0;
126 dirs_sym_link = 0;
127 }
128
129 public void add(final FileStats element_stats) {
130 if( element_stats.is_link() ) {
131 if( element_stats.exists() ) {
133 } else {
135 }
136 } else {
137 total_real++;
138 }
139 if( !element_stats.has_access() ) {
141 }
142 if( !element_stats.exists() ) {
144 }
145 if( element_stats.is_file() ) {
146 if( element_stats.is_link() ) {
148 if( topts.isSet(TraverseOptions.Bit.follow_symlinks) ) {
149 total_file_bytes += element_stats.size();
150 }
151 } else {
152 files_real++;
153 total_file_bytes += element_stats.size();
154 }
155 } else if( element_stats.is_dir() ) {
156 if( element_stats.is_link() ) {
158 } else {
159 dirs_real++;
160 }
161 }
162 }
163
164 @Override
165 public boolean equals(final Object other) {
166 if( this == other ) {
167 return true;
168 }
169 if( !( other instanceof VisitorStats ) ) {
170 return false;
171 }
172 final VisitorStats o = (VisitorStats)other;
173 return total_file_bytes == o.total_file_bytes &&
174 total_real == o.total_real &&
175 total_sym_links_existing == o.total_sym_links_existing &&
176 total_sym_links_not_existing == o.total_sym_links_not_existing &&
177 total_no_access == o.total_no_access &&
178 total_not_existing == o.total_not_existing &&
179 files_real == o.files_real &&
180 files_sym_link == o.files_sym_link &&
181 dirs_real == o.dirs_real &&
183 }
184
185 @Override
186 public String toString() {
187 final StringBuilder res = new StringBuilder();
188 res.append( "- traverse_options ").append(topts).append("\n");
189 res.append( "- total_real ").append(total_real).append("\n");
190 res.append( "- total_sym_links_existing ").append(total_sym_links_existing).append("\n");
191 res.append( "- total_sym_links_not_existing ").append(total_sym_links_not_existing).append("\n");
192 res.append( "- total_no_access ").append(total_no_access).append("\n");
193 res.append( "- total_not_existing ").append(total_not_existing).append("\n");
194 res.append( "- total_file_bytes ").append(String.format("%,d", total_file_bytes)).append("\n");
195 res.append( "- files_real ").append(files_real).append("\n");
196 res.append( "- files_sym_link ").append(files_sym_link).append("\n");
197 res.append( "- dirs_real ").append(dirs_real).append("\n");
198 res.append( "- dirs_sym_link ").append(dirs_sym_link).append("\n");
199 return res.toString();
200 }
201 }
202
203 public static class PathStatsVisitor implements FileUtil.PathVisitor {
204 private final VisitorStats stats;
205
206 public PathStatsVisitor(final VisitorStats stats_) {
207 stats = stats_;
208 }
209
210 @Override
211 public boolean visit(final TraverseEvent tevt, final FileStats item_stats, final long depth) {
212 // PrintUtil.fprintf_td(System.err, "add: item_stats "+item_stats+", tevt "+tevt+"\n");
213 stats.add(item_stats);
214 return true;
215 }
216 }
217
218 static class source_visitor_params {
219 public String title;
220 public String source_folder_path;
221 public FileStats dest;
222 public boolean dest_is_vfat;
223 public boolean opt_drop_dest_links;
224
225 public source_visitor_params(final String t, final String sfp, final FileStats d, final boolean dest_is_vfat_, final boolean opt_drop_dest_links_) {
226 title = t;
227 source_folder_path = sfp;
228 dest = d;
229 dest_is_vfat = dest_is_vfat_;
230 opt_drop_dest_links = opt_drop_dest_links_;
231 }
232 };
233
234 static class dest_visitor_params {
235 public String title;
236 public String source_folder_path;
237 public String dest_folder_path;
238 public String source_basename;
239 public FileStats stats;
240 public boolean dest_is_vfat;
241 public boolean match;
242 public dest_visitor_params(final String t, final String sfp, final String dfp, final String sb, final FileStats s, final boolean dest_is_vfat_) {
243 title = t;
244 source_folder_path = sfp;
245 dest_folder_path = dfp;
246 source_basename = sb;
247 stats = s;
248 dest_is_vfat = dest_is_vfat_;
249 match = false;
250 }
251 };
252
253 public void testxx_copy_r_p(final String title, final FileStats source, final int source_added_dead_links,
254 final String dest,
255 final CopyOptions copts,
256 final boolean dest_is_vfat) {
257 Assert.assertTrue( source.exists() );
258 Assert.assertTrue( source.is_dir() );
259
260 final boolean dest_is_parent;
261 final String dest_root;
262 {
263 final FileStats dest_stats = new FileStats(dest);
264 if( dest_stats.exists() ) {
265 // If dest_path exists as a directory, source_path dir will be copied below the dest_path directory
266 // _if_ copy_options::into_existing_dir is not set. Otherwise its content is copied into the existing dest_path.
267 Assert.assertTrue( dest_stats.is_dir() );
268 if( copts.isSet(CopyOptions.Bit.into_existing_dir) ) {
269 dest_is_parent = false;
270 dest_root = dest;
271 } else {
272 dest_is_parent = true;
273 dest_root = dest + "/" + source.item().basename();
274 }
275 } else {
276 // If dest_path doesn't exist, source_path dir content is copied into the newly created dest_path.
277 dest_is_parent = false;
278 dest_root = dest;
279 }
280 }
281 PrintUtil.fprintf_td(System.err, "%s: source %s, dest[arg %s, is_parent %b, dest_root %s], copts %s, dest_is_vfat %b\n",
282 title, source, dest, dest_is_parent, dest_root, copts, dest_is_vfat);
283
284 final boolean opt_follow_links = copts.isSet(CopyOptions.Bit.follow_symlinks);
285 final boolean opt_drop_dest_links = !opt_follow_links && copts.isSet(CopyOptions.Bit.ignore_symlink_errors);
286
287 Assert.assertTrue( true == FileUtil.copy(source.path(), dest, copts) );
288
289 final FileStats dest_stats = new FileStats(dest_root);
290 Assert.assertTrue( true == dest_stats.exists() );
291 Assert.assertTrue( true == dest_stats.ok() );
292 Assert.assertTrue( true == dest_stats.is_dir() );
293
294 {
295 final TraverseOptions topts = new TraverseOptions();
296 topts.set(TraverseOptions.Bit.recursive);
297 topts.set(TraverseOptions.Bit.dir_entry);
298
299 final VisitorStats stats = new VisitorStats(topts);
300 final VisitorStats stats_copy = new VisitorStats(topts);
301
302 final PathStatsVisitor pv_orig = new PathStatsVisitor(stats);
303 final PathStatsVisitor pv_copy = new PathStatsVisitor(stats_copy);
304
305 Assert.assertTrue( true == FileUtil.visit(source, topts, pv_orig) );
306 Assert.assertTrue( true == FileUtil.visit(dest_stats, topts, pv_copy) );
307
308 PrintUtil.fprintf_td(System.err, "%s: copy %s, traverse %s\n", title, copts, topts);
309 PrintUtil.fprintf_td(System.err, "%s: source visitor stats\n%s\n", title, stats);
310 PrintUtil.fprintf_td(System.err, "%s: destination visitor stats\n%s\n", title, stats_copy);
311
312 Assert.assertTrue( 7 == stats.total_real );
313 Assert.assertTrue( 10 - source_added_dead_links == stats.total_sym_links_existing );
314 Assert.assertTrue( 4 + source_added_dead_links == stats.total_sym_links_not_existing );
315 Assert.assertTrue( 0 == stats.total_no_access );
316 Assert.assertTrue( 4 + source_added_dead_links == stats.total_not_existing );
317 Assert.assertTrue( 60 == stats.total_file_bytes );
318 Assert.assertTrue( 4 == stats.files_real );
319 Assert.assertTrue( 9 - source_added_dead_links == stats.files_sym_link );
320 Assert.assertTrue( 3 == stats.dirs_real );
321 Assert.assertTrue( 1 == stats.dirs_sym_link );
322
323 if( ( !opt_follow_links && !opt_drop_dest_links ) ||
324 ( opt_drop_dest_links && 0 < stats_copy.total_sym_links_existing )
325 )
326 {
327 // 1:1 exact copy
328 Assert.assertTrue( 7 == stats_copy.total_real );
329 Assert.assertTrue( 9 == stats_copy.total_sym_links_existing );
330 Assert.assertTrue( 5 == stats_copy.total_sym_links_not_existing ); // symlink ../README.txt + 4 dead_link*
331 Assert.assertTrue( 0 == stats_copy.total_no_access );
332 Assert.assertTrue( 5 == stats_copy.total_not_existing ); // symlink ../README.txt + 4 dead_link*
333 Assert.assertTrue( 60 == stats_copy.total_file_bytes );
334 Assert.assertTrue( 4 == stats_copy.files_real );
335 Assert.assertTrue( 8 == stats_copy.files_sym_link );
336 Assert.assertTrue( 3 == stats_copy.dirs_real );
337 Assert.assertTrue( 1 == stats_copy.dirs_sym_link );
338 } else if( opt_drop_dest_links ) {
339 // destination filesystem has no symlink support, i.e. vfat
340 Assert.assertTrue( 7 == stats_copy.total_real );
341 Assert.assertTrue( 0 == stats_copy.total_sym_links_existing );
342 Assert.assertTrue( 0 == stats_copy.total_sym_links_not_existing ); // symlink ../README.txt + 4 dead_link*
343 Assert.assertTrue( 0 == stats_copy.total_no_access );
344 Assert.assertTrue( 0 == stats_copy.total_not_existing ); // symlink ../README.txt + 4 dead_link*
345 Assert.assertTrue( 60 == stats_copy.total_file_bytes );
346 Assert.assertTrue( 4 == stats_copy.files_real );
347 Assert.assertTrue( 0 == stats_copy.files_sym_link );
348 Assert.assertTrue( 3 == stats_copy.dirs_real );
349 Assert.assertTrue( 0 == stats_copy.dirs_sym_link );
350 } else if( opt_follow_links ) {
351 // followed symlinks
352 Assert.assertTrue( 20 == stats_copy.total_real );
353 Assert.assertTrue( 0 == stats_copy.total_sym_links_existing );
354 Assert.assertTrue( 0 == stats_copy.total_sym_links_not_existing ); // symlink ../README.txt + 4 dead_link*
355 Assert.assertTrue( 0 == stats_copy.total_no_access );
356 Assert.assertTrue( 0 == stats_copy.total_not_existing ); // symlink ../README.txt + 4 dead_link*
357 Assert.assertTrue( 60 < stats_copy.total_file_bytes );
358 Assert.assertTrue( 16 == stats_copy.files_real );
359 Assert.assertTrue( 0 == stats_copy.files_sym_link );
360 Assert.assertTrue( 4 == stats_copy.dirs_real );
361 Assert.assertTrue( 0 == stats_copy.dirs_sym_link );
362 }
363 }
364 {
365 // compare each file in detail O(n*n)
366 final TraverseOptions topts = new TraverseOptions();
367 topts.set(TraverseOptions.Bit.recursive);
368 topts.set(TraverseOptions.Bit.dir_entry);
369
370 final source_visitor_params svp = new source_visitor_params(title, source.path(), dest_stats, dest_is_vfat, opt_drop_dest_links);
371 final FileUtil.PathVisitor pv1 = new FileUtil.PathVisitor() {
372 @Override
373 public boolean visit(final TraverseEvent tevt1, final FileStats element_stats1, final long depth) {
374 final dest_visitor_params dvp = new dest_visitor_params(svp.title, svp.source_folder_path, svp.dest.path(), FileUtil.basename(element_stats1.path() ), element_stats1, svp.dest_is_vfat);
375 final FileUtil.PathVisitor pv2 = new FileUtil.PathVisitor() {
376 @Override
377 public boolean visit(final TraverseEvent tevt2, final FileStats element_stats2, final long depth2) {
378 final String path2 = element_stats2.path();
379 final String basename2 = FileUtil.basename( path2 );
380 final String source_folder_basename = FileUtil.basename( dvp.source_folder_path );
381 if( basename2.equals( dvp.source_basename ) ||
382 ( source_folder_basename.equals( dvp.source_basename ) && dvp.dest_folder_path.equals( path2 ) )
383 )
384 {
385 boolean attr_equal, bit_equal;
386 if( "README_slink08_relext.txt".equals(basename2) || 0 == basename2.indexOf("dead_link") ) {
387 // symlink to ../README.txt not existent on target
388 // dead_link* files intentionally not existant
389 attr_equal = element_stats2.is_link() &&
390 !element_stats2.exists();
391
392 bit_equal = true; // pretend
393 } else {
394 if( !dvp.dest_is_vfat ) {
395 // full attribute check
396 attr_equal =
397 element_stats2.mode().equals( dvp.stats.mode() ) &&
398 // element_stats2.atime().equals( dvp.stats.atime() ) && // destination access-time may differ due to processing post copy
399 element_stats2.mtime().equals( dvp.stats.mtime() ) &&
400 element_stats2.uid() == dvp.stats.uid() &&
401 element_stats2.gid() == dvp.stats.gid() &&
402 element_stats2.size() == dvp.stats.size();
403 } else {
404 // minimal vfat attribute check
405 // const jau::fraction_timespec td(5_s);
406 final long td_ms = 5000;
407
408 attr_equal =
409 // ( element_stats2.mode() & jau::fs::fmode_t::rwx_usr ) == ( dvp.stats.mode() & jau::fs::fmode_t::rwx_usr ) &&
410 // element_stats2.atime().equals( dvp.stats.atime() ) && // destination access-time may differ due to processing post copy
411 Math.abs( element_stats2.mtime().toEpochMilli() - dvp.stats.mtime().toEpochMilli() ) <= td_ms &&
412 element_stats2.uid() == dvp.stats.uid() &&
413 // element_stats2.gid() == dvp.stats.gid() &&
414 element_stats2.size() == dvp.stats.size();
415 }
416 if( dvp.stats.is_file() ) {
417 bit_equal = FileUtil.compare(dvp.stats.path(), element_stats2.path(), true);
418 } else {
419 bit_equal = true; // pretend
420 }
421 }
422 dvp.match = attr_equal && bit_equal;
423 PrintUtil.fprintf_td(System.err, "%s.check: '%s', match [attr %b, bit %b -> %b]\n\t source %s\n\t dest__ %s\n\n",
424 dvp.title, basename2, attr_equal, bit_equal, dvp.match,
425 dvp.stats,
426 element_stats2);
427 return false; // done
428 } else {
429 return true; // continue search
430 }
431 } };
432 if( FileUtil.visit(svp.dest, topts, pv2) ) {
433 // not found
434 final boolean ignore = element_stats1.is_link() && svp.opt_drop_dest_links;
435 PrintUtil.fprintf_td(System.err, "%s.check: %s: '%s', not found!\n\t source %s\n\n",
436 svp.title, ignore ? "Ignored" : "Error", dvp.source_basename, element_stats1);
437 return ignore;
438 } else {
439 // found
440 if( dvp.match ) {
441 return true; // found and matching, continue
442 } else {
443 return false; // found not matching, abort
444 }
445 }
446 } };
447 Assert.assertTrue( true == FileUtil.visit(source, topts, pv1) );
448 }
449 }
450
451}
boolean visit(final TraverseEvent tevt, final FileStats item_stats, final long depth)
void add(final FileStats element_stats)
VisitorStats(final TraverseOptions topts_)
static final FileStats getTestDataImageFile()
static final String project_root_ext
static final String project_root1a
static final String project_root2b
static final String project_root1b
static final String project_root2a
static final FileStats getTestDataDirStats()
void testxx_copy_r_p(final String title, final FileStats source, final int source_added_dead_links, final String dest, final CopyOptions copts, final boolean dest_is_vfat)
static final String image_file
static final String dest_fs_vfat