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/__init__.py +1 -1
- edq/testing/run.py +91 -0
- edq/testing/unittest.py +47 -0
- edq/util/dirent.py +194 -44
- edq/util/dirent_test.py +706 -25
- edq/util/json.py +80 -0
- edq/util/json_test.py +121 -0
- edq/util/pyimport.py +73 -0
- edq/util/pyimport_test.py +83 -0
- edq/util/reflection.py +32 -0
- edq/util/testdata/dirent-operations/symlink_file_empty +0 -0
- edq/util/time.py +75 -0
- edq/util/time_test.py +107 -0
- {edq_utils-0.0.1.dist-info → edq_utils-0.0.2.dist-info}/METADATA +2 -1
- edq_utils-0.0.2.dist-info/RECORD +26 -0
- edq_utils-0.0.1.dist-info/RECORD +0 -16
- /edq/util/testdata/dirent-operations/{symlinklink_a.txt → symlink_a.txt} +0 -0
- /edq/util/testdata/dirent-operations/{symlinklink_dir_1 → symlink_dir_1}/b.txt +0 -0
- /edq/util/testdata/dirent-operations/{symlinklink_dir_1 → symlink_dir_1}/dir_2/c.txt +0 -0
- {edq_utils-0.0.1.dist-info → edq_utils-0.0.2.dist-info}/WHEEL +0 -0
- {edq_utils-0.0.1.dist-info → edq_utils-0.0.2.dist-info}/licenses/LICENSE +0 -0
- {edq_utils-0.0.1.dist-info → edq_utils-0.0.2.dist-info}/top_level.txt +0 -0
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
|
-
├──
|
|
20
|
-
├──
|
|
21
|
-
|
|
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
|
|
32
|
+
class TestDirent(edq.testing.unittest.BaseTest):
|
|
32
33
|
""" Test basic operations on dirents. """
|
|
33
34
|
|
|
34
|
-
|
|
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
|
-
('
|
|
49
|
-
('
|
|
50
|
-
('
|
|
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
|
|
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
|
-
'
|
|
724
|
+
'symlink_a.txt',
|
|
64
725
|
|
|
65
726
|
# Symlink - Dir
|
|
66
|
-
'
|
|
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
|
-
# ('
|
|
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 =
|
|
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
|
|
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 (
|
|
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
|