edq-utils 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of edq-utils might be problematic. Click here for more details.

edq/util/dirent_test.py CHANGED
@@ -1,7 +1,6 @@
1
1
  import os
2
- import sys
3
- import unittest
4
2
 
3
+ import edq.testing.unittest
5
4
  import edq.util.dirent
6
5
 
7
6
  THIS_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
@@ -16,9 +15,10 @@ This test data directory is laid out as:
16
15
  │   └── c.txt
17
16
  ├── dir_empty
18
17
  ├── file_empty
19
- ├── symlinklink_a.txt -> a.txt
20
- ├── symlinklink_dir_1 -> dir_1
21
- └── symlinklink_dir_empty -> dir_empty
18
+ ├── symlink_a.txt -> a.txt
19
+ ├── symlink_dir_1 -> dir_1
20
+ ├── symlink_dir_empty -> dir_empty
21
+ └── symlink_file_empty -> file_empty
22
22
 
23
23
  Where non-empty files are filled with their filename (without the extension).
24
24
  dir_empty will not exist in the repository (since it is an empty directory),
@@ -27,12 +27,12 @@ but it will be created by _prep_temp_dir().
27
27
 
28
28
  DIRENT_TYPE_DIR = 'dir'
29
29
  DIRENT_TYPE_FILE = 'file'
30
+ DIRENT_TYPE_BROKEN_SYMLINK = 'broken_symlink'
30
31
 
31
- class TestDirentOperations(unittest.TestCase):
32
+ class TestDirent(edq.testing.unittest.BaseTest):
32
33
  """ Test basic operations on dirents. """
33
34
 
34
- @unittest.skipIf((sys.platform.startswith("win") and (sys.version_info > (3, 11))), "windows symlink behavior")
35
- def test_dirent_base(self):
35
+ def test_setup(self):
36
36
  """ Test that the base temp directory is properly setup. """
37
37
 
38
38
  temp_dir = self._prep_temp_dir()
@@ -45,14 +45,675 @@ class TestDirentOperations(unittest.TestCase):
45
45
  (os.path.join('dir_1', 'dir_2', 'c.txt'), DIRENT_TYPE_FILE),
46
46
  ('dir_empty', DIRENT_TYPE_DIR),
47
47
  ('file_empty', DIRENT_TYPE_FILE),
48
- ('symlinklink_a.txt', DIRENT_TYPE_FILE, True),
49
- ('symlinklink_dir_1', DIRENT_TYPE_DIR, True),
50
- ('symlinklink_dir_empty', DIRENT_TYPE_DIR, True),
48
+ ('symlink_a.txt', DIRENT_TYPE_FILE, True),
49
+ ('symlink_dir_1', DIRENT_TYPE_DIR, True),
50
+ ('symlink_dir_empty', DIRENT_TYPE_DIR, True),
51
+ ('symlink_file_empty', DIRENT_TYPE_FILE, True),
51
52
  ]
52
53
 
53
54
  self._check_existing_paths(temp_dir, expected_paths)
54
55
 
55
- def test_dirent_operations_remove(self):
56
+ def test_contains_path_base(self):
57
+ """ Test checking path containment. """
58
+
59
+ temp_dir = self._prep_temp_dir()
60
+
61
+ # [(parent, child, contains?), ...]
62
+ test_cases = [
63
+ # Containment
64
+ ('a', os.path.join('a', 'b', 'c'), True),
65
+ (os.path.join('a', 'b'), os.path.join('a', 'b', 'c'), True),
66
+ ('.', os.path.join('a', 'b', 'c'), True),
67
+ ('..', '.', True),
68
+
69
+ # Self No Containment
70
+ ('a', 'a', False),
71
+ (os.path.join('a', 'b', 'c'), os.path.join('a', 'b', 'c'), False),
72
+ ('.', '.', False),
73
+
74
+ # Trivial No Containment
75
+ ('a', 'b', False),
76
+ ('z', os.path.join('a', 'b', 'c'), False),
77
+ ('aa', os.path.join('a', 'b', 'c'), False),
78
+ ('a', os.path.join('aa', 'b', 'c'), False),
79
+
80
+ # Child Contains Parent
81
+ (os.path.join('a', 'b', 'c'), 'a', False),
82
+ (os.path.join('a', 'b', 'c'), os.path.join('a', 'b'), False),
83
+ ]
84
+
85
+ for (i, test_case) in enumerate(test_cases):
86
+ (parent, child, expected) = test_case
87
+
88
+ with self.subTest(msg = f"Case {i} ('{parent}' ⊂ '{child}'):"):
89
+ parent = os.path.join(temp_dir, parent)
90
+ child = os.path.join(temp_dir, child)
91
+
92
+ actual = edq.util.dirent.contains_path(parent, child)
93
+ self.assertEqual(expected, actual)
94
+
95
+ def test_read_write_file_base(self):
96
+ """ Test reading and writing a file. """
97
+
98
+ # [(path, write kwargs, read kwargs, write contents, expected contents, error substring), ...]
99
+ test_cases = [
100
+ # Base
101
+ (
102
+ "test.txt",
103
+ {},
104
+ {},
105
+ "test",
106
+ "test",
107
+ None,
108
+ ),
109
+
110
+ # Defaults
111
+ (
112
+ "test.txt",
113
+ {},
114
+ {},
115
+ " test ",
116
+ "test",
117
+ None,
118
+ ),
119
+
120
+ # No Modifications
121
+ (
122
+ "test.txt",
123
+ {'strip': False, 'newline': False},
124
+ {'strip': False},
125
+ " test ",
126
+ " test ",
127
+ None,
128
+ ),
129
+
130
+ # No Strip
131
+ (
132
+ "test.txt",
133
+ {'strip': False, 'newline': True},
134
+ {'strip': False},
135
+ " test ",
136
+ " test \n",
137
+ None,
138
+ ),
139
+
140
+ # No Read Strip
141
+ (
142
+ "test.txt",
143
+ {},
144
+ {'strip': False},
145
+ "test",
146
+ "test\n",
147
+ None,
148
+ ),
149
+
150
+ # Empty Write
151
+ (
152
+ "test.txt",
153
+ {'newline': False},
154
+ {},
155
+ "",
156
+ "",
157
+ None,
158
+ ),
159
+
160
+ # None
161
+ (
162
+ "test.txt",
163
+ {'newline': False},
164
+ {},
165
+ None,
166
+ "",
167
+ None,
168
+ ),
169
+
170
+ # Clobber
171
+ (
172
+ "a.txt",
173
+ {},
174
+ {},
175
+ "test",
176
+ "test",
177
+ None,
178
+ ),
179
+ (
180
+ "dir_1",
181
+ {},
182
+ {},
183
+ "test",
184
+ "test",
185
+ None,
186
+ ),
187
+ (
188
+ "symlink_a.txt",
189
+ {},
190
+ {},
191
+ "test",
192
+ "test",
193
+ None,
194
+ ),
195
+
196
+ # No Clobber
197
+ (
198
+ "a.txt",
199
+ {'no_clobber': True},
200
+ {},
201
+ "test",
202
+ "test",
203
+ 'Destination of write already exists',
204
+ ),
205
+ (
206
+ "dir_1",
207
+ {'no_clobber': True},
208
+ {},
209
+ "test",
210
+ "test",
211
+ 'Destination of write already exists',
212
+ ),
213
+ (
214
+ "symlink_a.txt",
215
+ {'no_clobber': True},
216
+ {},
217
+ "test",
218
+ "test",
219
+ 'Destination of write already exists',
220
+ ),
221
+ ]
222
+
223
+ for (i, test_case) in enumerate(test_cases):
224
+ (path, write_options, read_options, write_contents, expected_contents, error_substring) = test_case
225
+
226
+ with self.subTest(msg = f"Case {i} ('{path}'):"):
227
+ temp_dir = self._prep_temp_dir()
228
+
229
+ path = os.path.join(temp_dir, path)
230
+
231
+ try:
232
+ edq.util.dirent.write_file(path, write_contents, **write_options)
233
+ actual_contents = edq.util.dirent.read_file(path, **read_options)
234
+ except Exception as ex:
235
+ error_string = self.format_error_string(ex)
236
+ if (error_substring is None):
237
+ self.fail(f"Unexpected error: '{error_string}'.")
238
+
239
+ self.assertIn(error_substring, error_string, 'Error is not as expected.')
240
+
241
+ continue
242
+
243
+ if (error_substring is not None):
244
+ self.fail(f"Did not get expected error: '{error_substring}'.")
245
+
246
+ self.assertEqual(expected_contents, actual_contents)
247
+
248
+ def test_copy_contents_base(self):
249
+ """
250
+ Test copying the contents of a dirent.
251
+ Note that the base functionality of copy_contents() is tested by test_setup().
252
+ """
253
+
254
+ # [(source, dest, no clobber?, error substring), ...]
255
+ test_cases = [
256
+ ('a.txt', 'dir_1', False, None),
257
+ ('a.txt', 'ZZZ', False, None),
258
+ ('dir_empty', 'dir_1', False, None),
259
+
260
+ ('dir_1', 'dir_1', False, 'Source and destination of contents copy cannot be the same'),
261
+ ('dir_empty', 'symlink_dir_empty', False, 'Source and destination of contents copy cannot be the same'),
262
+
263
+ ('a.txt', 'file_empty', False, 'Destination of contents copy exists and is not a dir'),
264
+ ]
265
+
266
+ for (i, test_case) in enumerate(test_cases):
267
+ (source, dest, no_clobber, error_substring) = test_case
268
+
269
+ with self.subTest(msg = f"Case {i} ('{source}' -> '{dest}'):"):
270
+ temp_dir = self._prep_temp_dir()
271
+
272
+ source = os.path.join(temp_dir, source)
273
+ dest = os.path.join(temp_dir, dest)
274
+
275
+ try:
276
+ edq.util.dirent.copy_contents(source, dest, no_clobber = no_clobber)
277
+ except Exception as ex:
278
+ error_string = self.format_error_string(ex)
279
+ if (error_substring is None):
280
+ self.fail(f"Unexpected error: '{error_string}'.")
281
+
282
+ self.assertIn(error_substring, error_string, 'Error is not as expected.')
283
+
284
+ continue
285
+
286
+ if (error_substring is not None):
287
+ self.fail(f"Did not get expected error: '{error_substring}'.")
288
+
289
+ def test_copy_base(self):
290
+ """ Test copying dirents. """
291
+
292
+ # [(source, dest, no_clobber?, error substring), ...]
293
+ test_cases = [
294
+ # File
295
+ ('a.txt', 'test.txt', False, None),
296
+ ('a.txt', 'test.txt', True, None),
297
+ ('a.txt', os.path.join('dir_1', 'test.txt'), False, None),
298
+ ('a.txt', os.path.join('dir_1', 'test.txt'), True, None),
299
+
300
+ # File - Clobber
301
+ ('a.txt', 'file_empty', False, None),
302
+ ('a.txt', os.path.join('dir_1', 'b.txt'), False, None),
303
+ ('a.txt', 'dir_1', False, None),
304
+ ('a.txt', os.path.join('dir_1', 'dir_2'), False, None),
305
+
306
+ # File - No Clobber
307
+ ('a.txt', 'file_empty', True, 'Destination of copy already exists'),
308
+ ('a.txt', os.path.join('dir_1', 'b.txt'), True, 'Destination of copy already exists'),
309
+ ('a.txt', 'dir_1', True, 'Destination of copy already exists'),
310
+ ('a.txt', os.path.join('dir_1', 'dir_2'), True, 'Destination of copy already exists'),
311
+
312
+ # Dir
313
+ ('dir_empty', 'test', False, None),
314
+ ('dir_empty', 'test', True, None),
315
+ ('dir_empty', os.path.join('dir_1', 'test'), False, None),
316
+ ('dir_empty', os.path.join('dir_1', 'test'), True, None),
317
+
318
+ # Dir - Clobber
319
+ ('dir_empty', 'file_empty', False, None),
320
+ ('dir_empty', os.path.join('dir_1', 'b.txt'), False, None),
321
+ ('dir_empty', 'dir_1', False, None),
322
+ ('dir_empty', os.path.join('dir_1', 'dir_2'), False, None),
323
+
324
+ # Dir - No Clobber
325
+ ('dir_empty', 'file_empty', True, 'Destination of copy already exists'),
326
+ ('dir_empty', os.path.join('dir_1', 'b.txt'), True, 'Destination of copy already exists'),
327
+ ('dir_empty', 'dir_1', True, 'Destination of copy already exists'),
328
+ ('dir_empty', os.path.join('dir_1', 'dir_2'), True, 'Destination of copy already exists'),
329
+
330
+ # Link
331
+ ('symlink_a.txt', 'test.txt', False, None),
332
+ ('symlink_dir_1', 'test', False, None),
333
+ ('symlink_dir_empty', 'test', False, None),
334
+ ('symlink_file_empty', 'test.txt', False, None),
335
+
336
+ # Link - Clobber
337
+ ('symlink_a.txt', 'file_empty', False, None),
338
+ ('symlink_a.txt', 'symlink_dir_1', False, None),
339
+
340
+ # Link - No Clobber
341
+ ('symlink_a.txt', 'file_empty', True, 'Destination of copy already exists'),
342
+ ('symlink_a.txt', 'symlink_dir_1', True, 'Destination of copy already exists'),
343
+
344
+ # Clobber Parent
345
+ (os.path.join('dir_1', 'b.txt'), 'dir_1', False, 'Destination of copy cannot contain the source.'),
346
+
347
+ # Same
348
+ ('a.txt', 'a.txt', False, None),
349
+ ('symlink_a.txt', 'a.txt', False, None),
350
+ ('a.txt', 'a.txt', True, None),
351
+ ('symlink_a.txt', 'a.txt', True, None),
352
+
353
+ # Missing Source
354
+ ('ZZZ', 'test.txt', False, 'Source of copy does not exist'),
355
+ ('ZZZ', 'test.txt', True, 'Source of copy does not exist'),
356
+ ]
357
+
358
+ for (i, test_case) in enumerate(test_cases):
359
+ (source, dest, no_clobber, error_substring) = test_case
360
+
361
+ with self.subTest(msg = f"Case {i} ('{source}' -> '{dest}'):"):
362
+ temp_dir = self._prep_temp_dir()
363
+
364
+ source = os.path.join(temp_dir, source)
365
+ dest = os.path.join(temp_dir, dest)
366
+
367
+ try:
368
+ edq.util.dirent.copy(source, dest, no_clobber = no_clobber)
369
+ except Exception as ex:
370
+ error_string = self.format_error_string(ex)
371
+ if (error_substring is None):
372
+ self.fail(f"Unexpected error: '{error_string}'.")
373
+
374
+ self.assertIn(error_substring, error_string, 'Error is not as expected.')
375
+
376
+ continue
377
+
378
+ if (error_substring is not None):
379
+ self.fail(f"Did not get expected error: '{error_substring}'.")
380
+
381
+ dirent_type, is_link = self._get_dirent_type(source)
382
+
383
+ checks = [
384
+ (source, dirent_type, is_link),
385
+ ]
386
+
387
+ if (not edq.util.dirent.same(source, dest)):
388
+ checks += [
389
+ (dest, dirent_type, is_link),
390
+ ]
391
+
392
+ self._check_existing_paths(temp_dir, checks)
393
+
394
+ def test_same_base(self):
395
+ """ Test checking for two paths pointing to the same dirent. """
396
+
397
+ temp_dir = self._prep_temp_dir()
398
+
399
+ # [(path, path, same?), ...]
400
+ test_cases = [
401
+ # Same
402
+ ('a.txt', 'a.txt', True),
403
+ ('dir_1', 'dir_1', True),
404
+ (os.path.join('dir_1', 'b.txt'), os.path.join('dir_1', 'b.txt'), True),
405
+ (os.path.join('dir_1', 'b.txt'), os.path.join('dir_1', '..', 'dir_1', 'b.txt'), True),
406
+
407
+ # Not Same
408
+ ('a.txt', 'dir_1', False),
409
+ ('a.txt', os.path.join('dir_1', 'b.txt'), False),
410
+ ('a.txt', 'file_empty', False),
411
+ ('a.txt', 'dir_empty', False),
412
+
413
+ # Not Exists
414
+ ('a.txt', 'ZZZ', False),
415
+ (os.path.join('dir_1', 'b.txt'), os.path.join('dir_1', 'ZZZ'), False),
416
+ (os.path.join('dir_1', 'b.txt'), os.path.join('ZZZ', 'b.txt'), False),
417
+
418
+ # Links
419
+ ('a.txt', 'symlink_a.txt', True),
420
+ ('a.txt', 'symlink_file_empty', False),
421
+ ('dir_1', 'symlink_dir_1', True),
422
+ ('dir_1', 'symlink_dir_empty', False),
423
+ ]
424
+
425
+ for (i, test_case) in enumerate(test_cases):
426
+ (a, b, expected) = test_case
427
+
428
+ with self.subTest(msg = f"Case {i} ('{a}' vs '{b}'):"):
429
+ a = os.path.join(temp_dir, a)
430
+ b = os.path.join(temp_dir, b)
431
+
432
+ actual = edq.util.dirent.same(a, b)
433
+ self.assertEqual(expected, actual)
434
+
435
+ def test_mkdir_base(self):
436
+ """ Test creating directories. """
437
+
438
+ temp_dir = self._prep_temp_dir()
439
+
440
+ # [(path, error substring), ...]
441
+ test_cases = [
442
+ # Base
443
+ ('new_dir_1', None),
444
+ (os.path.join('dir_1', 'new_dir_2'), None),
445
+
446
+ # Missing Parents
447
+ (os.path.join('ZZZ', 'new_dir_ZZZ'), None),
448
+ (os.path.join('ZZZ', 'YYY', 'XXX', 'new_dir_XXX'), None),
449
+
450
+ # Existing Dir
451
+ ('dir_1', None),
452
+ ('dir_empty', None),
453
+ ('symlink_dir_1', None),
454
+
455
+ # Existing Non-Dir
456
+ ('a.txt', 'Target of mkdir already exists'),
457
+ ('symlink_a.txt', 'Target of mkdir already exists'),
458
+
459
+ # Existing Non-Dir Parent
460
+ (os.path.join('dir_1', 'b.txt', 'BBB'), 'Target of mkdir contains parent'),
461
+ ]
462
+
463
+ for (i, test_case) in enumerate(test_cases):
464
+ (path, error_substring) = test_case
465
+
466
+ with self.subTest(msg = f"Case {i} ('{path}'):"):
467
+ path = os.path.join(temp_dir, path)
468
+
469
+ try:
470
+ edq.util.dirent.mkdir(path)
471
+ except Exception as ex:
472
+ error_string = self.format_error_string(ex)
473
+ if (error_substring is None):
474
+ self.fail(f"Unexpected error: '{error_string}'.")
475
+
476
+ self.assertIn(error_substring, error_string, 'Error is not as expected.')
477
+
478
+ continue
479
+
480
+ if (error_substring is not None):
481
+ self.fail(f"Did not get expected error: '{error_substring}'.")
482
+
483
+ self.assertTrue(edq.util.dirent.exists(path), 'Dir does not exist post mkdir.')
484
+
485
+ def test_get_temp_path_base(self):
486
+ """ Ensure that temp paths are not the same. """
487
+
488
+ a = edq.util.dirent.get_temp_path()
489
+ b = edq.util.dirent.get_temp_path()
490
+
491
+ self.assertNotEqual(a, b)
492
+
493
+ def test_get_temp_dir_base(self):
494
+ """ Ensure that the temp dir exists. """
495
+
496
+ path = edq.util.dirent.get_temp_dir()
497
+ self.assertTrue(edq.util.dirent.exists(path))
498
+
499
+ def test_exists_base(self):
500
+ """
501
+ Test checking for existence.
502
+
503
+ ./dir_empty and ./file_empty will be removed to check for broken links.
504
+ """
505
+
506
+ temp_dir = self._prep_temp_dir()
507
+
508
+ # Remove some dirents to break links.
509
+ edq.util.dirent.remove(os.path.join(temp_dir, 'dir_empty'))
510
+ edq.util.dirent.remove(os.path.join(temp_dir, 'file_empty'))
511
+
512
+ # [(path, exists?), ...]
513
+ test_cases = [
514
+ # File
515
+ ('a.txt', True),
516
+ (os.path.join('dir_1', 'b.txt'), True),
517
+
518
+ # Dir
519
+ ('dir_1', True),
520
+ (os.path.join('dir_1', 'dir_2'), True),
521
+
522
+ # Links
523
+ ('symlink_a.txt', True),
524
+ ('symlink_dir_1', True),
525
+ ('symlink_dir_empty', True), # Broken Link
526
+ ('symlink_file_empty', True), # Broken Link
527
+
528
+ # Not Exists
529
+ ('dir_empty', False),
530
+ ('file_empty', False),
531
+ (os.path.join('dir_1', 'ZZZ'), False),
532
+ ]
533
+
534
+ for (i, test_case) in enumerate(test_cases):
535
+ (path, expected) = test_case
536
+
537
+ with self.subTest(msg = f"Case {i} ('{path}'):"):
538
+ path = os.path.join(temp_dir, path)
539
+ actual = edq.util.dirent.exists(path)
540
+ self.assertEqual(expected, actual)
541
+
542
+ def test_move_base(self):
543
+ """
544
+ Test moving dirents.
545
+
546
+ This test will create some additional dirents:
547
+ ├── dir_1
548
+ │   └── dir_2
549
+ │   ├── a.txt
550
+ │   └── dir_empty
551
+ """
552
+
553
+ # [(source, dest, no_clobber?, error substring), ...]
554
+ # The dest can be a single string, or a tuple of (operation input, expected output).
555
+ test_cases = [
556
+ # File
557
+ ('a.txt', 'test.txt', False, None),
558
+
559
+ # Move into Dir - Explicit
560
+ ('a.txt', os.path.join('dir_1', 'a.txt'), False, None),
561
+
562
+ # Move into Dir - Implicit
563
+ ('a.txt', ('dir_1', os.path.join('dir_1', 'a.txt')), False, None),
564
+
565
+ # Move out of Dir
566
+ (os.path.join('dir_1', 'b.txt'), 'b.txt', False, None),
567
+
568
+ # Missing Parents
569
+ ('a.txt', os.path.join('dir_1', 'a', 'b', 'a.txt'), False, None),
570
+
571
+ # Same File
572
+ ('a.txt', 'a.txt', False, None),
573
+
574
+ # Clobber File with File
575
+ ('a.txt', os.path.join('dir_1', 'b.txt'), False, None),
576
+
577
+ # No Clobber File with File
578
+ ('a.txt', os.path.join('dir_1', 'b.txt'), True, 'Destination of move already exists'),
579
+
580
+ # Clobber File with File - Implicit
581
+ ('a.txt', (os.path.join('dir_1', 'dir_2'), os.path.join('dir_1', 'dir_2', 'a.txt')), False, None),
582
+
583
+ # No Clobber File with File - Implicit
584
+ ('a.txt', os.path.join('dir_1', 'dir_2'), True, 'Destination of move already exists'),
585
+
586
+ # Clobber Dir with Dir
587
+ ('dir_empty', 'dir_1', False, None),
588
+
589
+ # Clobber Dir with Dir - Implicit
590
+ ('dir_empty', (os.path.join('dir_1', 'dir_2'), os.path.join('dir_1', 'dir_2', 'dir_empty')), False, None),
591
+
592
+ # No Clobber Dir with Dir - Implicit
593
+ ('dir_empty', os.path.join('dir_1', 'dir_2'), True, 'Destination of move already exists'),
594
+ ]
595
+
596
+ for (i, test_case) in enumerate(test_cases):
597
+ (source, raw_dest, no_clobber, error_substring) = test_case
598
+
599
+ with self.subTest(msg = f"Case {i} ('{source}' -> '{raw_dest}'):"):
600
+ temp_dir = self._prep_temp_dir()
601
+
602
+ # Create the additional dirents for this test.
603
+ edq.util.dirent.copy(os.path.join(temp_dir, 'a.txt'), os.path.join(temp_dir, 'dir_1', 'dir_2', 'a.txt'))
604
+ edq.util.dirent.copy(os.path.join(temp_dir, 'dir_empty'), os.path.join(temp_dir, 'dir_1', 'dir_2', 'dir_empty'))
605
+
606
+ if (isinstance(raw_dest, tuple)):
607
+ (input_dest, expected_dest) = raw_dest
608
+ else:
609
+ input_dest = raw_dest
610
+ expected_dest = raw_dest
611
+
612
+ source = os.path.join(temp_dir, source)
613
+ input_dest = os.path.join(temp_dir, input_dest)
614
+ expected_dest = os.path.join(temp_dir, expected_dest)
615
+
616
+ try:
617
+ edq.util.dirent.move(source, input_dest, no_clobber = no_clobber)
618
+ except Exception as ex:
619
+ error_string = self.format_error_string(ex)
620
+ if (error_substring is None):
621
+ self.fail(f"Unexpected error: '{error_string}'.")
622
+
623
+ self.assertIn(error_substring, error_string, 'Error is not as expected.')
624
+
625
+ continue
626
+
627
+ if (error_substring is not None):
628
+ self.fail(f"Did not get expected error: '{error_substring}'.")
629
+
630
+ self._check_existing_paths(temp_dir, [expected_dest])
631
+
632
+ if (not edq.util.dirent.same(os.path.join(temp_dir, source), os.path.join(temp_dir, expected_dest))):
633
+ self._check_nonexisting_paths(temp_dir, [source])
634
+
635
+ def test_move_rename(self):
636
+ """ Test renaming dirents (via move()). """
637
+
638
+ temp_dir = self._prep_temp_dir()
639
+
640
+ # [(source, dest, expected error), ...]
641
+ rename_relpaths = [
642
+ # Symlink - File
643
+ ('symlink_a.txt', 'rename_symlink_a.txt', None),
644
+
645
+ # Symlink - Dir
646
+ ('symlink_dir_1', 'rename_symlink_dir_1', None),
647
+
648
+ # File in Directory
649
+ (os.path.join('dir_1', 'dir_2', 'c.txt'), os.path.join('dir_1', 'dir_2', 'rename_c.txt'), None),
650
+
651
+ # File
652
+ ('a.txt', 'rename_a.txt', None),
653
+
654
+ # Empty File
655
+ ('file_empty', 'rename_file_empty', None),
656
+
657
+ # Directory
658
+ ('dir_1', 'rename_dir_1', None),
659
+
660
+ # Empty Directory
661
+ ('dir_empty', 'rename_dir_empty', None),
662
+
663
+ # Non-Existent
664
+ ('ZZZ', 'rename_ZZZ', 'Source of move does not exist'),
665
+ ]
666
+
667
+ expected_paths = [
668
+ ('rename_a.txt', DIRENT_TYPE_FILE),
669
+ ('rename_dir_1', DIRENT_TYPE_DIR),
670
+ (os.path.join('rename_dir_1', 'b.txt'), DIRENT_TYPE_FILE),
671
+ (os.path.join('rename_dir_1', 'dir_2'), DIRENT_TYPE_DIR),
672
+ (os.path.join('rename_dir_1', 'dir_2', 'rename_c.txt'), DIRENT_TYPE_FILE),
673
+ ('rename_dir_empty', DIRENT_TYPE_DIR),
674
+ ('rename_file_empty', DIRENT_TYPE_FILE),
675
+ ('rename_symlink_a.txt', DIRENT_TYPE_BROKEN_SYMLINK, True),
676
+ ('rename_symlink_dir_1', DIRENT_TYPE_BROKEN_SYMLINK, True),
677
+ ('symlink_dir_empty', DIRENT_TYPE_BROKEN_SYMLINK, True),
678
+ ('symlink_file_empty', DIRENT_TYPE_BROKEN_SYMLINK, True),
679
+ ]
680
+
681
+ unexpected_paths = [
682
+ 'symlink_a.txt',
683
+ 'symlink_dir_1',
684
+ os.path.join('dir_1', 'dir_2', 'c.txt'),
685
+ 'a.txt',
686
+ 'file_empty',
687
+ 'dir_1',
688
+ 'dir_empty',
689
+ 'ZZZ',
690
+ 'rename_ZZZ',
691
+ ]
692
+
693
+ for (i, test_case) in enumerate(rename_relpaths):
694
+ (source, dest, error_substring) = test_case
695
+
696
+ source = os.path.join(temp_dir, source)
697
+ dest = os.path.join(temp_dir, dest)
698
+
699
+ try:
700
+ edq.util.dirent.move(source, dest)
701
+ except Exception as ex:
702
+ error_string = self.format_error_string(ex)
703
+ if (error_substring is None):
704
+ self.fail(f"Case {i}: Unexpected error: '{error_string}'.")
705
+
706
+ self.assertIn(error_substring, error_string, 'Error is not as expected.')
707
+
708
+ continue
709
+
710
+ if (error_substring is not None):
711
+ self.fail(f"Case {i}: Did not get expected error: '{error_substring}'.")
712
+
713
+ self._check_nonexisting_paths(temp_dir, unexpected_paths)
714
+ self._check_existing_paths(temp_dir, expected_paths)
715
+
716
+ def test_remove_base(self):
56
717
  """ Test removing dirents. """
57
718
 
58
719
  temp_dir = self._prep_temp_dir()
@@ -60,10 +721,10 @@ class TestDirentOperations(unittest.TestCase):
60
721
  # Remove these paths in this order.
61
722
  remove_relpaths = [
62
723
  # Symlink - File
63
- 'symlinklink_a.txt',
724
+ 'symlink_a.txt',
64
725
 
65
726
  # Symlink - Dir
66
- 'symlinklink_dir_1',
727
+ 'symlink_dir_1',
67
728
 
68
729
  # File in Directory
69
730
  os.path.join('dir_1', 'dir_2', 'c.txt'),
@@ -78,17 +739,17 @@ class TestDirentOperations(unittest.TestCase):
78
739
  'dir_1',
79
740
 
80
741
  # Empty Directory
81
- 'dir_empty'
742
+ 'dir_empty',
82
743
 
83
744
  # Non-Existent
84
- 'ZZZ'
745
+ 'ZZZ',
85
746
  ]
86
747
 
87
748
  expected_paths = [
88
749
  (os.path.join('dir_1', 'dir_2'), DIRENT_TYPE_DIR),
89
750
  ('file_empty', DIRENT_TYPE_FILE),
90
751
  # Windows has some symlink issues, so we will not check for this file.
91
- # ('symlinklink_dir_empty', DIRENT_TYPE_DIR, True),
752
+ # ('symlink_dir_empty', DIRENT_TYPE_DIR, True),
92
753
  ]
93
754
 
94
755
  for relpath in remove_relpaths:
@@ -100,8 +761,10 @@ class TestDirentOperations(unittest.TestCase):
100
761
 
101
762
  def _prep_temp_dir(self):
102
763
  temp_dir = edq.util.dirent.get_temp_dir(prefix = 'edq_test_dirent_')
764
+
103
765
  edq.util.dirent.mkdir(os.path.join(temp_dir, 'dir_empty'))
104
766
  edq.util.dirent.copy_contents(TEST_BASE_DIR, temp_dir)
767
+
105
768
  return temp_dir
106
769
 
107
770
  def _check_existing_paths(self, base_dir, raw_paths):
@@ -118,7 +781,7 @@ class TestDirentOperations(unittest.TestCase):
118
781
  for raw_path in raw_paths:
119
782
  relpath = ''
120
783
  dirent_type = None
121
- is_link = None
784
+ is_link = False
122
785
 
123
786
  if (isinstance(raw_path, str)):
124
787
  relpath = raw_path
@@ -137,9 +800,14 @@ class TestDirentOperations(unittest.TestCase):
137
800
  path = os.path.join(base_dir, relpath)
138
801
 
139
802
  # Check the path exists.
140
- if (not os.path.exists(path)):
803
+ if (not edq.util.dirent.exists(path)):
141
804
  self.fail(f"Expected path does not exist: '{relpath}'.")
142
805
 
806
+ # Check the link status.
807
+ if (is_link is not None):
808
+ if (is_link != os.path.islink(path)):
809
+ self.fail(f"Expected path does not have a matching link status. Expected {is_link}, but is {not is_link}: '{relpath}'.")
810
+
143
811
  # Check the type of the dirent.
144
812
  if (dirent_type is not None):
145
813
  if (dirent_type == DIRENT_TYPE_DIR):
@@ -148,14 +816,12 @@ class TestDirentOperations(unittest.TestCase):
148
816
  elif (dirent_type == DIRENT_TYPE_FILE):
149
817
  if (not os.path.isfile(path)):
150
818
  self.fail(f"Expected path to be a file, but it is not: '{relpath}'.")
819
+ elif (dirent_type == DIRENT_TYPE_BROKEN_SYMLINK):
820
+ if ((not os.path.islink(path)) or os.path.isfile(path) or os.path.isdir(path)):
821
+ self.fail(f"Expected path to be a broken link, but it is not: '{relpath}'.")
151
822
  else:
152
823
  raise ValueError(f"Unknown dirent type '{dirent_type}' for path: '{relpath}'.")
153
824
 
154
- # Check the link status.
155
- if (is_link is not None):
156
- if (is_link != os.path.islink(path)):
157
- self.fail(f"Expected path does not have a matching link status. Expected {is_link}, but is {not is_link}: '{relpath}'.")
158
-
159
825
  def _check_nonexisting_paths(self, base_dir, raw_paths):
160
826
  """
161
827
  Ensure that specific paths do not exists, and fail the test if they do exist.
@@ -166,5 +832,20 @@ class TestDirentOperations(unittest.TestCase):
166
832
  for relpath in raw_paths:
167
833
  path = os.path.join(base_dir, relpath)
168
834
 
169
- if (os.path.exists(path)):
835
+ if (edq.util.dirent.exists(path)):
170
836
  self.fail(f"Path exists when it should not: '{relpath}'.")
837
+
838
+ def _get_dirent_type(self, path):
839
+ is_link = os.path.islink(path)
840
+ dirent_type = None
841
+
842
+ if (os.path.isdir(path)):
843
+ dirent_type = DIRENT_TYPE_DIR
844
+ elif (os.path.isfile(path)):
845
+ dirent_type = DIRENT_TYPE_FILE
846
+ elif (os.path.islink(path)):
847
+ dirent_type = DIRENT_TYPE_BROKEN_SYMLINK
848
+ else:
849
+ raise ValueError(f"Unknown dirent type: '{path}'.")
850
+
851
+ return dirent_type, is_link