Completed
Push — master ( d8cee1...b07d26 )
by Jaisen
02:05
created

elodie.tests.media.photo_test   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 421
Duplicated Lines 30.88 %

Importance

Changes 0
Metric Value
eloc 267
dl 130
loc 421
rs 8.96
c 0
b 0
f 0
wmc 43

32 Functions

Rating   Name   Duplication   Size   Complexity  
A test_empty_album() 0 3 1
A test_is_valid() 0 4 1
A test_get_coordinate_longitude() 0 5 1
A test_get_coordinate_default() 0 5 1
A test_get_coordinates_without_exif() 0 7 1
A test_get_metadata_of_invalid_photo() 0 5 1
A test_has_album() 0 5 1
A test_get_coordinate_latitude() 0 5 1
A test_get_coordinates_with_zero_coordinate() 0 7 1
A test_get_coordinate_longitude_plus() 0 5 1
A test_photo_extensions() 0 17 1
A test_is_not_valid() 0 4 1
A test_get_coordinate_latitude_minus() 0 5 1
A test_get_date_taken() 0 6 1
A test_set_location_minus() 25 25 1
A test_get_camera_make() 0 5 1
A test_is_valid_fallback_using_pillow() 0 4 1
A _test_photo_type_set() 0 29 4
A test_get_coordinates_with_null_coordinate() 0 7 1
A test_get_camera_make_not_set() 0 5 1
A test_set_title_non_ascii() 20 20 1
A test_various_types() 0 16 3
A test_get_camera_model_not_set() 0 5 1
A test_get_date_taken_without_exif() 0 8 1
A test_set_location() 25 25 1
A test_set_date_taken_with_missing_datetimeoriginal() 0 22 1
A test_get_camera_model() 0 5 1
A test_set_date_taken() 20 20 1
A _test_photo_type_get() 0 25 5
A test_set_album() 21 21 1
A test_pillow_not_loaded() 0 5 1
A test_set_title() 19 19 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like elodie.tests.media.photo_test often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8
2
# Project imports
3
from __future__ import unicode_literals
4
import os
5
import sys
6
7
from datetime import datetime
8
import shutil
9
import tempfile
10
import time
11
12
from nose.plugins.skip import SkipTest
13
14
sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))))
15
sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
16
17
import helper
18
from elodie.media.media import Media
19
from elodie.media.photo import Photo
20
21
os.environ['TZ'] = 'GMT'
22
23
setup_module = helper.setup_module
24
teardown_module = helper.teardown_module
25
26
def test_photo_extensions():
27
    photo = Photo()
28
    extensions = photo.extensions
29
30
    assert 'arw' in extensions
31
    assert 'cr2' in extensions
32
    assert 'dng' in extensions
33
    assert 'gif' in extensions
34
    assert 'heic' in extensions
35
    assert 'jpg' in extensions
36
    assert 'jpeg' in extensions
37
    assert 'nef' in extensions
38
    assert 'rw2' in extensions
39
40
    valid_extensions = Photo.get_valid_extensions()
41
42
    assert extensions == valid_extensions, valid_extensions
43
44
def test_empty_album():
45
    photo = Photo(helper.get_file('plain.jpg'))
46
    assert photo.get_album() is None
47
48
def test_has_album():
49
    photo = Photo(helper.get_file('with-album.jpg'))
50
    album = photo.get_album()
51
52
    assert album == 'Test Album', album
53
54
def test_is_valid():
55
    photo = Photo(helper.get_file('plain.jpg'))
56
57
    assert photo.is_valid()
58
59
def test_is_not_valid():
60
    photo = Photo(helper.get_file('text.txt'))
61
62
    assert not photo.is_valid()
63
64
def test_get_metadata_of_invalid_photo():
65
    photo = Photo(helper.get_file('invalid.jpg'))
66
    metadata = photo.get_metadata()
67
68
    assert metadata is None
69
70
def test_get_coordinate_default():
71
    photo = Photo(helper.get_file('with-location.jpg'))
72
    coordinate = photo.get_coordinate()
73
74
    assert helper.isclose(coordinate,37.3667027222), coordinate
75
76
def test_get_coordinate_latitude():
77
    photo = Photo(helper.get_file('with-location.jpg'))
78
    coordinate = photo.get_coordinate('latitude')
79
80
    assert helper.isclose(coordinate,37.3667027222), coordinate
81
82
def test_get_coordinate_latitude_minus():
83
    photo = Photo(helper.get_file('with-location-inv.jpg'))
84
    coordinate = photo.get_coordinate('latitude')
85
86
    assert helper.isclose(coordinate,-37.3667027222), coordinate
87
88
def test_get_coordinate_longitude():
89
    photo = Photo(helper.get_file('with-location.jpg'))
90
    coordinate = photo.get_coordinate('longitude')
91
92
    assert helper.isclose(coordinate,-122.033383611), coordinate
93
94
def test_get_coordinate_longitude_plus():
95
    photo = Photo(helper.get_file('with-location-inv.jpg'))
96
    coordinate = photo.get_coordinate('longitude')
97
98
    assert helper.isclose(coordinate,122.033383611), coordinate
99
100
def test_get_coordinates_without_exif():
101
    photo = Photo(helper.get_file('no-exif.jpg'))
102
    latitude = photo.get_coordinate('latitude')
103
    longitude = photo.get_coordinate('longitude')
104
105
    assert latitude is None, latitude
106
    assert longitude is None, longitude
107
108
def test_get_coordinates_with_zero_coordinate():
109
    photo = Photo(helper.get_file('with-location-zero-coordinate.jpg'))
110
    latitude = photo.get_coordinate('latitude')
111
    longitude = photo.get_coordinate('longitude')
112
113
    assert helper.isclose(latitude,51.55325), latitude
114
    assert helper.isclose(longitude,-0.00417777777778), longitude
115
116
def test_get_coordinates_with_null_coordinate():
117
    photo = Photo(helper.get_file('with-null-coordinates.jpg'))
118
    latitude = photo.get_coordinate('latitude')
119
    longitude = photo.get_coordinate('longitude')
120
121
    assert latitude is None, latitude
122
    assert longitude is None, longitude
123
124
def test_get_date_taken():
125
    photo = Photo(helper.get_file('plain.jpg'))
126
    date_taken = photo.get_date_taken()
127
128
    #assert date_taken == (2015, 12, 5, 0, 59, 26, 5, 339, 0), date_taken
129
    assert date_taken == helper.time_convert((2015, 12, 5, 0, 59, 26, 5, 339, 0)), date_taken
130
131
def test_get_date_taken_without_exif():
132
    source = helper.get_file('no-exif.jpg')
133
    photo = Photo(source)
134
    date_taken = photo.get_date_taken()
135
136
    date_taken_from_file = time.gmtime(min(os.path.getmtime(source), os.path.getctime(source)))
137
138
    assert date_taken == date_taken_from_file, date_taken
139
140
def test_get_camera_make():
141
    photo = Photo(helper.get_file('with-location.jpg'))
142
    make = photo.get_camera_make()
143
144
    assert make == 'Canon', make
145
146
def test_get_camera_make_not_set():
147
    photo = Photo(helper.get_file('no-exif.jpg'))
148
    make = photo.get_camera_make()
149
150
    assert make is None, make
151
152
def test_get_camera_model():
153
    photo = Photo(helper.get_file('with-location.jpg'))
154
    model = photo.get_camera_model()
155
156
    assert model == 'Canon EOS REBEL T2i', model
157
158
def test_get_camera_model_not_set():
159
    photo = Photo(helper.get_file('no-exif.jpg'))
160
    model = photo.get_camera_model()
161
162
    assert model is None, model
163
164
def test_is_valid():
165
    photo = Photo(helper.get_file('with-location.jpg'))
166
167
    assert photo.is_valid()
168
169
def test_is_not_valid():
170
    photo = Photo(helper.get_file('text.txt'))
171
172
    assert not photo.is_valid()
173
174
def test_is_valid_fallback_using_pillow():
175
    photo = Photo(helper.get_file('imghdr-error.jpg'))
176
177
    assert photo.is_valid()
178
179
def test_pillow_not_loaded():
180
    photo = Photo(helper.get_file('imghdr-error.jpg'))
181
    photo.pillow = None
182
183
    assert photo.is_valid() == False
184
185 View Code Duplication
def test_set_album():
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
186
    temporary_folder, folder = helper.create_working_folder()
187
188
    origin = '%s/photo.jpg' % folder
189
    shutil.copyfile(helper.get_file('plain.jpg'), origin)
190
191
    photo = Photo(origin)
192
    metadata = photo.get_metadata()
193
194
    assert metadata['album'] is None, metadata['album']
195
196
    status = photo.set_album('Test Album')
197
198
    assert status == True, status
199
200
    photo_new = Photo(origin)
201
    metadata_new = photo_new.get_metadata()
202
203
    shutil.rmtree(folder)
204
205
    assert metadata_new['album'] == 'Test Album', metadata_new['album']
206
207
def test_set_date_taken_with_missing_datetimeoriginal():
208
    # When datetimeoriginal (or other key) is missing we have to add it gh-74
209
    # https://github.com/jmathai/elodie/issues/74
210
    temporary_folder, folder = helper.create_working_folder()
211
212
    origin = '%s/photo.jpg' % folder
213
    shutil.copyfile(helper.get_file('no-exif.jpg'), origin)
214
215
    photo = Photo(origin)
216
    status = photo.set_date_taken(datetime(2013, 9, 30, 7, 6, 5))
217
218
    assert status == True, status
219
220
    photo_new = Photo(origin)
221
    metadata = photo_new.get_metadata()
222
223
    date_taken = metadata['date_taken']
224
225
    shutil.rmtree(folder)
226
227
    #assert date_taken == (2013, 9, 30, 7, 6, 5, 0, 273, 0), metadata['date_taken']
228
    assert date_taken == helper.time_convert((2013, 9, 30, 7, 6, 5, 0, 273, 0)), metadata['date_taken']
229
230 View Code Duplication
def test_set_date_taken():
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
231
    temporary_folder, folder = helper.create_working_folder()
232
233
    origin = '%s/photo.jpg' % folder
234
    shutil.copyfile(helper.get_file('plain.jpg'), origin)
235
236
    photo = Photo(origin)
237
    status = photo.set_date_taken(datetime(2013, 9, 30, 7, 6, 5))
238
239
    assert status == True, status
240
241
    photo_new = Photo(origin)
242
    metadata = photo_new.get_metadata()
243
244
    date_taken = metadata['date_taken']
245
246
    shutil.rmtree(folder)
247
248
    #assert date_taken == (2013, 9, 30, 7, 6, 5, 0, 273, 0), metadata['date_taken']
249
    assert date_taken == helper.time_convert((2013, 9, 30, 7, 6, 5, 0, 273, 0)), metadata['date_taken']
250
251 View Code Duplication
def test_set_location():
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
252
    temporary_folder, folder = helper.create_working_folder()
253
254
    origin = '%s/photo.jpg' % folder
255
    shutil.copyfile(helper.get_file('plain.jpg'), origin)
256
257
    photo = Photo(origin)
258
    origin_metadata = photo.get_metadata()
259
260
    # Verify that original photo has different location info that what we
261
    #   will be setting and checking
262
    assert not helper.isclose(origin_metadata['latitude'], 11.1111111111), origin_metadata['latitude']
263
    assert not helper.isclose(origin_metadata['longitude'], 99.9999999999), origin_metadata['longitude']
264
265
    status = photo.set_location(11.1111111111, 99.9999999999)
266
267
    assert status == True, status
268
269
    photo_new = Photo(origin)
270
    metadata = photo_new.get_metadata()
271
272
    shutil.rmtree(folder)
273
274
    assert helper.isclose(metadata['latitude'], 11.1111111111), metadata['latitude']
275
    assert helper.isclose(metadata['longitude'], 99.9999999999), metadata['longitude']
276
277 View Code Duplication
def test_set_location_minus():
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
278
    temporary_folder, folder = helper.create_working_folder()
279
280
    origin = '%s/photo.jpg' % folder
281
    shutil.copyfile(helper.get_file('plain.jpg'), origin)
282
283
    photo = Photo(origin)
284
    origin_metadata = photo.get_metadata()
285
286
    # Verify that original photo has different location info that what we
287
    #   will be setting and checking
288
    assert not helper.isclose(origin_metadata['latitude'], 11.1111111111), origin_metadata['latitude']
289
    assert not helper.isclose(origin_metadata['longitude'], 99.9999999999), origin_metadata['longitude']
290
291
    status = photo.set_location(-11.1111111111, -99.9999999999)
292
293
    assert status == True, status
294
295
    photo_new = Photo(origin)
296
    metadata = photo_new.get_metadata()
297
298
    shutil.rmtree(folder)
299
300
    assert helper.isclose(metadata['latitude'], -11.1111111111), metadata['latitude']
301
    assert helper.isclose(metadata['longitude'], -99.9999999999), metadata['longitude']
302
303 View Code Duplication
def test_set_title():
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
304
    temporary_folder, folder = helper.create_working_folder()
305
306
    origin = '%s/photo.jpg' % folder
307
    shutil.copyfile(helper.get_file('plain.jpg'), origin)
308
309
    photo = Photo(origin)
310
    origin_metadata = photo.get_metadata()
311
312
    status = photo.set_title('my photo title')
313
314
    assert status == True, status
315
316
    photo_new = Photo(origin)
317
    metadata = photo_new.get_metadata()
318
319
    shutil.rmtree(folder)
320
321
    assert metadata['title'] == 'my photo title', metadata['title']
322
323 View Code Duplication
def test_set_title_non_ascii():
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
324
    temporary_folder, folder = helper.create_working_folder()
325
326
    origin = '%s/photo.jpg' % folder
327
    shutil.copyfile(helper.get_file('plain.jpg'), origin)
328
329
    photo = Photo(origin)
330
    origin_metadata = photo.get_metadata()
331
332
    unicode_title = u'形声字 / 形聲字'
333
334
    status = photo.set_title(unicode_title)
335
    assert status == True, status
336
337
    photo_new = Photo(origin)
338
    metadata = photo_new.get_metadata()
339
340
    shutil.rmtree(folder)
341
342
    assert metadata['title'] == unicode_title, metadata['title']
343
344
# This is a test generator that will test reading and writing to
345
# various RAW formats. Each sample file has a different date which
346
# is the only information which needs to be added to run the tests
347
# for that file type.
348
# https://nose.readthedocs.io/en/latest/writing_tests.html#test-generators
349
def test_various_types():
350
    types = Photo.extensions
351
    #extensions = ('arw', 'cr2', 'dng', 'gif', 'jpeg', 'jpg', 'nef', 'rw2')
352
    dates = {
353
        'arw': (2007, 4, 8, 17, 41, 18, 6, 98, 0),
354
        'cr2': (2005, 10, 29, 16, 14, 44, 5, 302, 0),
355
        'dng': (2009, 10, 20, 9, 10, 46, 1, 293, 0),
356
        'heic': (2019, 5, 26, 10, 33, 20, 6, 146, 0),
357
        'nef': (2008, 10, 24, 9, 12, 56, 4, 298, 0),
358
        'rw2': (2014, 11, 19, 23, 7, 44, 2, 323, 0)
359
    }
360
361
    for type in types:
362
        if type in dates:
363
            yield (_test_photo_type_get, type, dates[type])
364
            yield (_test_photo_type_set, type, dates[type])
365
366
def _test_photo_type_get(type, date):
367
    temporary_folder, folder = helper.create_working_folder()
368
369
    photo_name = 'photo.{}'.format(type)
370
    photo_file = helper.get_file(photo_name)
371
    origin = '{}/{}'.format(folder, photo_name)
372
373
    if not photo_file:
374
        photo_file = helper.download_file(photo_name, folder)
375
        if not photo_file or not os.path.isfile(photo_file):
376
            raise SkipTest('{} file not downlaoded'.format(type))
377
378
        # downloading for each test is costly so we save it in the working directory
379
        file_path_save_as = helper.get_file_path(photo_name)
380
        if os.path.isfile(photo_file):
381
            shutil.copyfile(photo_file, file_path_save_as)
382
383
    shutil.copyfile(photo_file, origin)
384
385
    photo = Photo(origin)
386
    metadata = photo.get_metadata()
387
388
    shutil.rmtree(folder)
389
390
    assert metadata['date_taken'] == helper.time_convert(date), '{} date {}'.format(type, metadata['date_taken'])
391
392
def _test_photo_type_set(type, date):
393
    temporary_folder, folder = helper.create_working_folder()
394
395
    photo_name = 'photo.{}'.format(type)
396
    photo_file = helper.get_file(photo_name)
397
    origin = '{}/{}'.format(folder, photo_name)
398
399
    if not photo_file:
400
        photo_file = helper.download_file(photo_name, folder)
401
        if not photo_file or not os.path.isfile(photo_file):
402
            raise SkipTest('{} file not downlaoded'.format(type))
403
404
    shutil.copyfile(photo_file, origin)
405
406
    photo = Photo(origin)
407
    origin_metadata = photo.get_metadata()
408
409
    status = photo.set_location(11.1111111111, 99.9999999999)
410
411
    assert status == True, status
412
413
    photo_new = Photo(origin)
414
    metadata = photo_new.get_metadata()
415
416
    shutil.rmtree(folder)
417
418
    assert metadata['date_taken'] == helper.time_convert(date), '{} date {}'.format(type, metadata['date_taken'])
419
    assert helper.isclose(metadata['latitude'], 11.1111111111), '{} lat {}'.format(type, metadata['latitude'])
420
    assert helper.isclose(metadata['longitude'], 99.9999999999), '{} lon {}'.format(type, metadata['latitude'])
421