forked from codingjoe/django-stdimage
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtest_models.py
341 lines (283 loc) · 12.5 KB
/
test_models.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
import io
import os
import time
from copy import deepcopy
import pytest
from django.conf import settings
from django.core.files.storage import default_storage
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db.models.fields.files import ImageFieldFile
from PIL import Image
from stdimage.models import StdImageFieldFile
from . import models
from .models import (
AdminDeleteModel,
AdminUpdateModel,
CustomRenderVariationsModel,
ResizeCropModel,
ResizeModel,
SimpleModel,
ThumbnailModel,
ThumbnailWithoutDirectoryModel,
UtilVariationsModel,
)
IMG_DIR = os.path.join(settings.MEDIA_ROOT, "img")
FIXTURES = [
("100.gif", "GIF", 100, 100),
("600x400.gif", "GIF", 600, 400),
("600x400.jpg", "JPEG", 600, 400),
("600x400.jpg", "PNG", 600, 400),
]
class TestStdImage:
fixtures = {}
@pytest.fixture(autouse=True)
def setup(self):
for fixture_filename, img_format, width, height in FIXTURES:
with io.BytesIO() as f:
img = Image.new("RGB", (width, height), (255, 55, 255))
img.save(f, format=img_format)
suf = SimpleUploadedFile(fixture_filename, f.getvalue())
self.fixtures[fixture_filename] = suf
yield
for root, dirs, files in os.walk(settings.MEDIA_ROOT, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
os.rmdir(os.path.join(root, name))
class TestModel(TestStdImage):
"""Tests StdImage ModelField"""
def test_simple(self, db):
"""Tests if Field behaves just like Django's ImageField."""
instance = SimpleModel.objects.create(image=self.fixtures["100.gif"])
target_file = os.path.join(IMG_DIR, "100.gif")
source_file = self.fixtures["100.gif"]
assert SimpleModel.objects.count() == 1
assert SimpleModel.objects.get(pk=1) == instance
assert os.path.exists(target_file)
with open(target_file, "rb") as f:
source_file.seek(0)
assert source_file.read() == f.read()
def test_variations(self, db):
"""Adds image and checks filesystem as well as width and height."""
instance = ResizeModel.objects.create(image=self.fixtures["600x400.jpg"])
source_file = self.fixtures["600x400.jpg"]
assert os.path.exists(os.path.join(IMG_DIR, "600x400.jpg"))
assert instance.image.width == 600
assert instance.image.height == 400
path = os.path.join(IMG_DIR, "600x400.jpg")
with open(path, "rb") as f:
source_file.seek(0)
assert source_file.read() == f.read()
path = os.path.join(IMG_DIR, "600x400.medium.jpg")
assert os.path.exists(path)
assert instance.image.medium.width == 400
assert instance.image.medium.height <= 400
with open(os.path.join(IMG_DIR, "600x400.medium.jpg"), "rb") as f:
source_file.seek(0)
assert source_file.read() != f.read()
assert os.path.exists(os.path.join(IMG_DIR, "600x400.thumbnail.jpg"))
assert instance.image.thumbnail.width == 100
assert instance.image.thumbnail.height <= 75
with open(os.path.join(IMG_DIR, "600x400.thumbnail.jpg"), "rb") as f:
source_file.seek(0)
assert source_file.read() != f.read()
def test_cropping(self, db):
instance = ResizeCropModel.objects.create(image=self.fixtures["600x400.jpg"])
assert instance.image.thumbnail.width == 150
assert instance.image.thumbnail.height == 150
def test_variations_override(self, db):
source_file = self.fixtures["600x400.jpg"]
target_file = os.path.join(IMG_DIR, "image.thumbnail.jpg")
os.mkdir(IMG_DIR)
default_storage.save(target_file, source_file)
ResizeModel.objects.create(image=self.fixtures["600x400.jpg"])
thumbnail_path = os.path.join(IMG_DIR, "image.thumbnail.jpg")
assert os.path.exists(thumbnail_path)
thumbnail_path = os.path.join(IMG_DIR, "image.thumbnail_1.jpg")
assert not os.path.exists(thumbnail_path)
def test_delete_thumbnail(self, db):
"""Delete an image with thumbnail"""
obj = ThumbnailModel.objects.create(image=self.fixtures["100.gif"])
obj.image.delete()
path = os.path.join(IMG_DIR, "image.gif")
assert not os.path.exists(path)
path = os.path.join(IMG_DIR, "image.thumbnail.gif")
assert not os.path.exists(path)
def test_fore_min_size(self, admin_client):
admin_client.post(
"/admin/tests/forceminsizemodel/add/",
{
"image": self.fixtures["100.gif"],
},
)
path = os.path.join(IMG_DIR, "image.gif")
assert not os.path.exists(path)
def test_thumbnail_save_without_directory(self, db):
obj = ThumbnailWithoutDirectoryModel.objects.create(
image=self.fixtures["100.gif"]
)
obj.save()
# Our model saves the images directly into the MEDIA_ROOT directory
# not IMG_DIR, under a custom name
original = os.path.join(settings.MEDIA_ROOT, "custom.gif")
thumbnail = os.path.join(settings.MEDIA_ROOT, "custom.thumbnail.gif")
assert os.path.exists(original)
assert os.path.exists(thumbnail)
def test_custom_render_variations(self, db):
instance = CustomRenderVariationsModel.objects.create(
image=self.fixtures["600x400.jpg"]
)
# Image size must be 100x100 despite variations settings
assert instance.image.thumbnail.width == 100
assert instance.image.thumbnail.height == 100
def test_defer(self, db, django_assert_num_queries):
"""
`set_variations` does not access a deferred field.
Accessing a deferred field would cause Django to do
a second implicit database query.
"""
instance = ResizeModel.objects.create(image=self.fixtures["100.gif"])
with django_assert_num_queries(1):
deferred = ResizeModel.objects.only("pk").get(pk=instance.pk)
with django_assert_num_queries(1):
deferred.image
assert instance.image.thumbnail == deferred.image.thumbnail
@pytest.mark.django_db
def test_variations_deepcopy_unsaved(self):
instance_original = ResizeModel(image=self.fixtures["600x400.jpg"])
instance = deepcopy(instance_original)
assert isinstance(instance.image, StdImageFieldFile)
assert instance.image == instance_original.image
@pytest.mark.django_db
def test_variations_deepcopy_without_image(self):
instance_original = ThumbnailModel.objects.create(image=None)
instance = deepcopy(instance_original)
assert isinstance(instance.image, StdImageFieldFile)
assert instance.image == instance_original.image
@pytest.mark.django_db
def test_variations_deepcopy(self):
"""Tests test_variations() with a deep copied object"""
instance_original = ResizeModel.objects.create(
image=self.fixtures["600x400.jpg"]
)
instance = deepcopy(instance_original)
assert isinstance(instance.image, StdImageFieldFile)
assert hasattr(instance.image, "thumbnail")
assert hasattr(instance.image, "medium")
assert isinstance(instance.image.thumbnail, ImageFieldFile)
assert isinstance(instance.image.medium, ImageFieldFile)
source_file = self.fixtures["600x400.jpg"]
assert os.path.exists(os.path.join(IMG_DIR, "600x400.jpg"))
assert instance.image.width == 600
assert instance.image.height == 400
path = os.path.join(IMG_DIR, "600x400.jpg")
with open(path, "rb") as f:
source_file.seek(0)
assert source_file.read() == f.read()
path = os.path.join(IMG_DIR, "600x400.medium.jpg")
assert os.path.exists(path)
assert instance.image.medium.width == 400
assert instance.image.medium.height <= 400
with open(os.path.join(IMG_DIR, "600x400.medium.jpg"), "rb") as f:
source_file.seek(0)
assert source_file.read() != f.read()
assert os.path.exists(os.path.join(IMG_DIR, "600x400.thumbnail.jpg"))
assert instance.image.thumbnail.width == 100
assert instance.image.thumbnail.height <= 75
with open(os.path.join(IMG_DIR, "600x400.thumbnail.jpg"), "rb") as f:
source_file.seek(0)
assert source_file.read() != f.read()
class TestUtils(TestStdImage):
"""Tests Utils"""
def test_deletion_singnal_receiver(self, db):
obj = AdminDeleteModel.objects.create(image=self.fixtures["100.gif"])
path = obj.image.path
obj.delete()
assert not os.path.exists(path)
def test_deletion_singnal_receiver_many(self, db):
obj = AdminDeleteModel.objects.create(image=self.fixtures["100.gif"])
path = obj.image.path
AdminDeleteModel.objects.all().delete()
assert not os.path.exists(path)
def test_pre_save_delete_callback_clear(self, admin_client):
obj = AdminDeleteModel.objects.create(image=self.fixtures["100.gif"])
path = obj.image.path
admin_client.post(
"/admin/tests/admindeletemodel/1/change/",
{
"image-clear": "checked",
},
)
assert not os.path.exists(path)
def test_pre_save_delete_callback_new(self, admin_client):
obj = AdminDeleteModel.objects.create(image=self.fixtures["100.gif"])
path = obj.image.path
assert os.path.exists(path)
admin_client.post(
"/admin/tests/admindeletemodel/1/change/",
{
"image": self.fixtures["600x400.jpg"],
},
)
assert not os.path.exists(path)
assert os.path.exists(os.path.join(IMG_DIR, "600x400.jpg"))
def test_pre_save_delete_callback_update(self, admin_client):
obj = AdminUpdateModel.objects.create(image=self.fixtures["100.gif"])
path = obj.image.path
assert os.path.exists(path)
admin_client.post(
"/admin/tests/adminupdatemodel/1/change/",
{
"image": self.fixtures["600x400.jpg"],
},
)
assert not os.path.exists(path)
assert os.path.exists(os.path.join(IMG_DIR, "600x400.jpg"))
def test_render_variations_callback(self, db):
obj = UtilVariationsModel.objects.create(image=self.fixtures["100.gif"])
file_path = obj.image.thumbnail.path
assert os.path.exists(file_path)
def test_render_variations_overwrite(self, db, image_upload_file):
obj = ThumbnailModel.objects.create(image=image_upload_file)
file_path = obj.image.thumbnail.path
before = os.path.getmtime(file_path)
time.sleep(0.1)
os.remove(obj.image.path)
assert os.path.exists(file_path)
obj.image = image_upload_file
obj.save()
assert file_path == obj.image.thumbnail.path
after = os.path.getmtime(file_path)
assert before != after, obj.image.path
class TestValidators(TestStdImage):
def test_max_size_validator(self, admin_client):
response = admin_client.post(
"/admin/tests/maxsizemodel/add/",
{
"image": self.fixtures["600x400.jpg"],
},
)
assert "too large" in response.context["adminform"].form.errors["image"][0]
assert not os.path.exists(os.path.join(IMG_DIR, "800x600.jpg"))
def test_min_size_validator(self, admin_client):
response = admin_client.post(
"/admin/tests/minsizemodel/add/",
{
"image": self.fixtures["100.gif"],
},
)
assert "too small" in response.context["adminform"].form.errors["image"][0]
assert not os.path.exists(os.path.join(IMG_DIR, "100.gif"))
class TestJPEGField(TestStdImage):
def test_convert(self, db):
obj = models.JPEGModel.objects.create(image=self.fixtures["100.gif"])
assert obj.image.thumbnail.path.endswith("img/100.thumbnail.jpeg")
assert obj.image.full.width == 100
assert obj.image.full.height == 100
def test_convert_multiple(self, db):
large = models.JPEGModel.objects.create(image=self.fixtures["600x400.gif"])
small = models.JPEGModel.objects.create(image=self.fixtures["100.gif"])
assert large.image.field._variations["full"] == (None, None)
assert small.image.field._variations["full"] == (None, None)
assert large.image.full.width == 600
assert small.image.full.width == 100