forked from eigenholser/jpeg_rename
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_jpeg_rename_unit.py
449 lines (392 loc) · 17.9 KB
/
test_jpeg_rename_unit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
import os
import sys
import pytest
from mock import Mock, patch
app_path = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, app_path + '/../')
from jpeg_rename import *
# Setup valid EXIF data with expected new filename
EXIF_DATA_VALID = {
'exif_data': {
'DateTimeOriginal': '2014:08:26 06:20:20'
},
}
EXIF_DATA_NOT_VALID = {'DateTimeOriginal': '2014:08:26 06:20'}
expected_new_fn = re.sub(r':', r'',
EXIF_DATA_VALID['exif_data']['DateTimeOriginal'])
expected_new_fn = re.sub(r' ', r'_', expected_new_fn)
expected_new_fn = '{0}.jpg'.format(expected_new_fn)
EXIF_DATA_VALID['expected_new_fn'] = expected_new_fn
OLD_FN_JPG_LOWER = 'filename.jpg'
OLD_FN_JPG_UPPER = 'filename.JPG'
OLD_FN_JPEG = 'filename.jpeg'
SKIP_TEST = False
class TestGetNewFn():
"""Tests for method get_new_fn() are in this class."""
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@pytest.mark.parametrize("old_fn,expected_new_fn,exif_data", [
(OLD_FN_JPG_LOWER, EXIF_DATA_VALID['expected_new_fn'],
EXIF_DATA_VALID['exif_data'],),
(OLD_FN_JPG_LOWER, OLD_FN_JPG_LOWER, EXIF_DATA_NOT_VALID),
(OLD_FN_JPG_LOWER, OLD_FN_JPG_LOWER, {},),
(OLD_FN_JPEG, OLD_FN_JPG_LOWER, {},),
])
def test_get_new_fn_parametrized_exif_data(self, old_fn, expected_new_fn,
exif_data):
"""Test get_new_fn() with various EXIF data."""
filemap = FileMap(old_fn, None, exif_data)
new_fn = filemap.new_fn
assert new_fn == expected_new_fn
class TestGetExifData():
"""Tests for method get_exif_data() are in this class."""
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.TAGS')
@patch.object(Image, 'open')
def test_get_exif_data(self, mock_img, mock_tags):
"""Tests read_exif_data() with valid EXIF data. Tests for normal
operation. Verify expected EXIF data in instantiated object."""
class TestImage():
def _getexif(self):
return EXIF_DATA_VALID['exif_data']
def get(arg1, arg2):
return 'DateTimeOriginal'
old_fn = OLD_FN_JPG_LOWER
mock_img.return_value = TestImage()
mock_tags.get = get
filemap = FileMap(old_fn)
assert filemap.exif_data == EXIF_DATA_VALID['exif_data']
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.TAGS')
@patch.object(Image, 'open')
def test_get_exif_data_info_none(self, mock_img, mock_tags):
"""Tests read_exif_data() with no EXIF data available. Tests for
raised Exception. Verify expected exception message."""
class TestImage():
def _getexif(self):
return None
def get(arg1, arg2):
return 'DateTimeOriginal'
old_fn = OLD_FN_JPG_LOWER
mock_img.return_value = TestImage()
mock_tags.get = get
with pytest.raises(Exception) as excinfo:
filemap = FileMap(old_fn)
assert excinfo.value[0] == "{0} has no EXIF data.".format(old_fn)
class TestMove():
"""Tess for method move() are in this class."""
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.FileMap.make_new_fn_unique')
@patch('jpeg_rename.os.rename')
def test_move_orthodox(self, mock_os, mock_fn_unique):
"""Rename file with mocked os.rename. Verify called with args."""
mock_fn_unique.return_value = None
old_fn = OLD_FN_JPG_LOWER
exif_data = EXIF_DATA_NOT_VALID
filemap = FileMap(old_fn, avoid_collisions=None, exif_data=exif_data)
new_fn = filemap.new_fn
filemap.move()
mock_os.assert_called_with(old_fn, new_fn)
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.FileMap.make_new_fn_unique')
@patch('jpeg_rename.os.rename')
def test_move_orthodox_rename_raises_exeption(self, mock_os,
mock_fn_unique):
"""Rename file with mocked os.rename. Verify called with args."""
mock_fn_unique.return_value = None
mock_os.side_effect = OSError((1, "Just testing.",))
old_fn = OLD_FN_JPG_LOWER
exif_data = EXIF_DATA_NOT_VALID
filemap = FileMap(old_fn, avoid_collisions=None, exif_data=exif_data)
new_fn = filemap.new_fn
filemap.move()
mock_os.assert_called_with(old_fn, new_fn)
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.FileMap.make_new_fn_unique')
@patch('jpeg_rename.os.rename')
def test_move_orthodox_fn_unique_raises_exception(self, mock_os,
mock_fn_unique):
"""Rename file with mocked os.rename. Verify called with args."""
mock_fn_unique.side_effect = OSError((1, "Just testing.",))
old_fn = OLD_FN_JPG_LOWER
exif_data = EXIF_DATA_NOT_VALID
filemap = FileMap(old_fn, avoid_collisions=None, exif_data=exif_data)
new_fn = filemap.new_fn
with pytest.raises(OSError):
filemap.move()
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.FileMap.make_new_fn_unique')
@patch('jpeg_rename.os.path.exists')
def test_move_collision_detected(self, mock_exists, mock_fn_unique):
"""Move file with collision_detected simulating avoid_collisions=False.
"""
mock_fn_unique.return_value = None
mock_exists.return_value = True
old_fn = OLD_FN_JPG_LOWER
exif_data = {}
filemap = FileMap(old_fn, avoid_collisions=None, exif_data=exif_data)
filemap.collision_detected = True
new_fn = filemap.new_fn
filemap.move()
assert new_fn == old_fn
class TestRename():
"""Tests for method get_new_fn() are in this class."""
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.os.path.exists')
def test_rename_empty_exif_data(self, mock_exists):
"""Make unique filename with empty EXIF data."""
mock_exists.return_value = True
old_fn = OLD_FN_JPG_LOWER
exif_data = EXIF_DATA_NOT_VALID
filemap = FileMap(old_fn, avoid_collisions=True, exif_data=exif_data)
new_fn = filemap.new_fn
filemap.make_new_fn_unique()
assert filemap.new_fn == old_fn
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.os.path.exists')
def test_rename_with_valid_exif_data_and_avoid_collisions(self,
mock_exists):
"""Make unique new filename from valid EXIF data. Avoid collisions."""
mock_exists.return_value = True
old_fn = OLD_FN_JPG_LOWER
exif_data = EXIF_DATA_VALID['exif_data']
filemap = FileMap(old_fn, avoid_collisions=True, exif_data=exif_data)
new_fn = filemap.new_fn
filemap.MAX_RENAME_ATTEMPTS = 2
with pytest.raises(Exception):
filemap.make_new_fn_unique()
assert filemap.new_fn == EXIF_DATA_VALID['expected_new_fn']
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.os.path.exists')
def test_rename_with_valid_exif_data_and_no_avoid_collisions(self,
mock_exists):
"""Make unique new filename from valid EXIF data. Do not avoid
collisions."""
mock_exists.return_value = True
old_fn = OLD_FN_JPG_LOWER
exif_data = EXIF_DATA_VALID['exif_data']
filemap = FileMap(old_fn, avoid_collisions=False, exif_data=exif_data)
new_fn = filemap.new_fn
filemap.MAX_RENAME_ATTEMPTS = 2
filemap.make_new_fn_unique()
assert filemap.new_fn == EXIF_DATA_VALID['expected_new_fn']
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.os.path.exists')
def test_rename_no_collision(self, mock_exists):
"""Make unique new filename from valid EXIF data. Do not avoid
collisions."""
mock_exists.return_value = False
old_fn = OLD_FN_JPG_LOWER
exif_data = {}
filemap = FileMap(old_fn, avoid_collisions=False, exif_data=exif_data)
new_fn = filemap.new_fn
filemap.make_new_fn_unique()
assert filemap.new_fn == old_fn
class TestInitFileMap():
"""Tests for function init_file_map() are in this class."""
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.FileMap')
@patch('jpeg_rename.glob')
def test_init_file_map_orthodox(self, mock_glob, mock_filemap):
"""Tests init_file_map() list building. Verifies expected return value.
"""
test_file_map = TestFileMap()
mock_filemap.return_value = test_file_map
mock_glob.glob.return_value = ['/foo/bar']
file_map = init_file_map('.')
assert file_map.file_map == [test_file_map,
test_file_map,
test_file_map]
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.FileMap')
@patch('jpeg_rename.glob')
def test_init_file_map_raises_exception(self, mock_glob, mock_filemap):
"""Tests init_file_map() with exception handling. Test exception raised
when append to file_map list. Verify expected file_map returned."""
mock_filemap.side_effect = Exception("Just testing.")
mock_glob.glob.return_value = ['/foo/bar']
file_map = init_file_map('.')
assert file_map.file_map == []
class TestProcessFileMap():
"""Tests for function process_file_map() are in this class."""
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
def test_process_file_map(self):
"""Test process_file_map()."""
def move_func(old_fn, new_fn):
pass
file_map_list = FileMapList()
file_map_list.add(TestFileMap())
assert process_file_map(file_map_list, True, move_func) == None
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
def test_process_file_map_raise_exception(self):
"""Test exception handling in process_file_map()."""
def move_func(old_fn, new_fn):
raise Exception("Faux failure")
file_map_list = FileMapList()
file_map_list.add(TestFileMap())
assert process_file_map(file_map_list, True, move_func) == None
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.FileMap')
def test_process_file_map_simon_sez_false_fn_eq(self, mock_fm):
"""Test process_file_map() with simon_sez=False. Tests else branch with
old_fn == new_fn. Verify same_files attribute value is True."""
mock_fm.same_files = True
mock_fm.old_fn = OLD_FN_JPG_LOWER
mock_fm.new_fn = OLD_FN_JPG_LOWER
file_map_list = FileMapList()
file_map_list.add(mock_fm)
process_file_map(file_map_list)
assert mock_fm.same_files == True
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.FileMap')
def test_process_file_map_simon_sez_false_fn_ne(self, mock_fm):
"""Test process_file_map() with simon_sez=False. Tests else branch with
old_fn != new_fn. Verify same_files attribute value is False."""
mock_fm.same_files = False
mock_fm.old_fn = OLD_FN_JPG_LOWER
mock_fm.new_fn = ''
file_map_list = FileMapList()
file_map_list.add(mock_fm)
process_file_map(file_map_list)
assert mock_fm.same_files == False
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.FileMap.move')
@patch('jpeg_rename.FileMap')
def test_process_file_map_simon_sez(self, mock_fm, mock_fm_move):
"""Call process_file_map with simon_sez=True, move_func=None. Tests
call to move() method of FileMap instance when no test stub present and
simon_sez True. Verify call to move() made as expected."""
file_map_list = FileMapList()
file_map_list.add(mock_fm)
process_file_map(file_map_list, simon_sez=True)
mock_fm.move.assert_called_with()
class TestProcessAllFiles():
"""Tests for the function process_all_files() are in this class."""
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.init_file_map')
@patch('jpeg_rename.os.access')
@patch('jpeg_rename.os.path.dirname')
@patch('jpeg_rename.os.path.exists')
@patch('jpeg_rename.os.path.abspath')
def test_process_all_files_workdir_none(self, mock_abspath, mock_exists,
mock_dirname, mock_os_access, mock_init_file_map):
"""Test process_all_files() with workdir=None, avoid_collisions=None.
Verify that init_file_map() is called with (DIRNAME, None)."""
DIRNAME = '/foo/bar'
mock_abspath.return_value = DIRNAME
mock_exists.return_value = True
mock_dirname.return_value = DIRNAME
process_all_files()
mock_init_file_map.assert_called_with(DIRNAME, None)
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.process_file_map')
@patch('jpeg_rename.init_file_map')
@patch('jpeg_rename.os.access')
def test_process_all_files_workdir_not_none(self, mock_os_access,
mock_init_file_map, mock_process_file_map):
"""Test process_all_files() with workdir set. Tests negative of branch
testing workdir. Verify process_file_map() called with expected
arguments."""
file_map = [TestFileMap()]
mock_init_file_map.return_value = file_map
mock_os_access.return_value = True
process_all_files('.')
mock_process_file_map.assert_called_with(file_map, None)
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.process_file_map')
@patch('jpeg_rename.init_file_map')
@patch('jpeg_rename.os.path.exists')
def test_process_all_files_exists_true(self, mock_os_path,
mock_init_file_map,
mock_process_file_map):
"""Test process_all_files() with workdir path exists True. Verify that
process_file_map() called with correct arguments."""
file_map = [TestFileMap()]
mock_init_file_map.return_value = file_map
mock_os_path.return_value = True
process_all_files('.')
mock_process_file_map.assert_called_with(file_map, None)
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.os.path.exists')
@patch('jpeg_rename.sys.exit')
def test_process_all_files_exists_false(self, mock_sys_exit, mock_os_path):
"""Test process_all_files() with workdir path exists False. Tests
positive branch of workdir not exists test. Verify that sys.exit() is
called with expected argument."""
mock_os_path.return_value = False
process_all_files('.')
mock_sys_exit.assert_called_with(1)
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.process_file_map')
@patch('jpeg_rename.init_file_map')
@patch('jpeg_rename.os.access')
def test_process_all_files_access_true(self, mock_os_access,
mock_init_file_map, mock_process_file_map):
"""Test process_all_files() with workdir access True. Tests for
negative branch of W_OK access test. Verify process_file_map() called
with expected arguments."""
file_map = [TestFileMap()]
mock_init_file_map.return_value = file_map
mock_os_access.return_value = True
process_all_files('.')
mock_process_file_map.assert_called_with(file_map, None)
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.os.access')
@patch('jpeg_rename.sys.exit')
def test_process_all_files_access_false(self, mock_sys_exit,
mock_os_access):
"""Test process_all_files() with workdir access False. Tests for
positive branch of W_OK access test. Verify sys.exit() called with
expected arguments."""
mock_os_access.return_value = False
process_all_files('.')
mock_sys_exit.assert_called_with(1)
class TestFileMapList():
"""Tests for FileMapList."""
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
def test_file_map_list_init(self):
"""Instantiate FileMapList() and test __init__() method. Verify
file_map is initialized to empty list."""
file_map_list = FileMapList()
assert file_map_list.file_map == []
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
def test_file_map_list_add(self):
"""Tests FileMapList add() method. Adds multiple FileMap instances
and verifies expected ordering of instances based on new_fn attribute.
"""
test_file_map_1 = TestFileMap()
test_file_map_2 = TestFileMap()
test_file_map_2.new_fn = 'aaa.jpg'
file_map_list = FileMapList()
file_map_list.add(test_file_map_1)
file_map_list.add(test_file_map_2)
assert file_map_list.file_map == [test_file_map_2, test_file_map_1]
class TestMainFunction():
"""Tests for main() function."""
class TestArgumentParser():
"""Stub class."""
def add_argument(self, *args, **kwargs):
"""Stub method."""
pass
def parse_args(self):
"""Stub method."""
class Args:
"""Stub class."""
directory = '.'
simon_sez = False
avoid_collisions = False
return Args()
@pytest.mark.skipif(SKIP_TEST, reason="Work in progress")
@patch('jpeg_rename.argparse.ArgumentParser', TestArgumentParser)
@patch('jpeg_rename.process_all_files')
def test_main_function(self, mock_process_all_files):
"""Test main() function. Mock argparse and replace with stubs. Verify
process_all_files called with expected arguments."""
mock_process_all_files.return_value = 1234
retval = main()
mock_process_all_files.assert_called_with(workdir='.', simon_sez=False,
avoid_collisions=False)
class TestFileMap():
"""Stub to be used in place of FileMap()."""
def __init__(self):
self.old_fn = 'foo.jpg'
self.new_fn = 'bar.jpg'