From b24735396148597fcd90c86b7e455992b8fd188a Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Wed, 28 Dec 2016 21:10:10 +0100 Subject: [PATCH 1/2] Update test suite - Add python 3.6 - Drop python 3.4 - Use tox on travis-ci --- .travis.yml | 30 +++++++++++++++++------------- setup.py | 3 --- tox.ini | 10 ++++++---- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index 85c7ba8..891a4af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,28 +1,32 @@ language: python sudo: false +dist: trusty cache: pip python: - "2.7" - - "3.4" - "3.5" + - "3.6" env: matrix: - - DJANGO="Django<1.9,>=1.8" - - DJANGO="Django<1.10,>=1.9" - - DJANGO="-e git+https://github.com/django/django.git@master#egg=Django" + - TOXENV=qa + - DJANGO=18 + - DJANGO=19 + - DJANGO=110 + - DJANGO=master matrix: fast_finish: true allow_failures: - - env: DJANGO="-e git+https://github.com/django/django.git@master#egg=Django" + - env: DJANGO=master install: - - pip install --upgrade pip - - pip install -r requirements-dev.txt - - pip install $DJANGO - - pip install --upgrade coveralls + - pip install --upgrade pip tox + - pip install -U coveralls +before_script: + - | + if [[ -z $TOXENV ]]; then + export TOXENV=py$(echo $TRAVIS_PYTHON_VERSION | sed -e 's/\.//g')-dj$DJANGO + fi + - echo $TOXENV script: - - isort --check-only --recursive --diff . - - flake8 --jobs=2 . - - pep257 --verbose --explain --source --count stdimage - - coverage run --source=stdimage -m 'pytest' + - tox -e $TOXENV after_success: - coveralls diff --git a/setup.py b/setup.py index 11c9317..506f85f 100755 --- a/setup.py +++ b/setup.py @@ -22,10 +22,7 @@ 'Programming Language :: Python', 'Topic :: Software Development', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', 'Framework :: Django', 'Framework :: Django :: 1.8', 'Framework :: Django :: 1.9', diff --git a/tox.ini b/tox.ini index 842a7eb..100b97c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,13 @@ [tox] -envlist = py{27,34,35}-django{18,19,master},qa +envlist = py{27,34,35}-dj{18,19,master},qa [testenv] deps= -rrequirements-dev.txt - django18: Django>=1.8,<1.9 - django19: Django>=1.9,<1.10 - djangomaster: -egit+https://github.com/django/django.git@master#egg=Django + -rrequirements-dev.txt + dj18: https://github.com/django/django/archive/stable/1.8.x.tar.gz#egg=django + dj19: https://github.com/django/django/archive/stable/1.9.x.tar.gz#egg=django + dj110: https://github.com/django/django/archive/stable/1.10.x.tar.gz#egg=django + djmaster: https://github.com/django/django/archive/master.tar.gz#egg=django commands= py.test \ --basetemp={envtmpdir} \ From df228d6fbc6c2e28e6a93558870deefe6ecaba7c Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Wed, 28 Dec 2016 23:19:25 +0100 Subject: [PATCH 2/2] Move tests to pytest --- requirements-dev.txt | 1 + setup.cfg | 4 +- tests/conftest.py | 20 ----- tests/test_models.py | 177 +++++++++++++++++++++---------------------- tox.ini | 2 +- 5 files changed, 90 insertions(+), 114 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 2e37128..cc76735 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -10,4 +10,5 @@ pep8-naming==0.4.1 pre-commit==0.9.4 py==1.4.32 pytest==3.0.5 +pytest-django==3.1.2 tox==2.5.0 diff --git a/setup.cfg b/setup.cfg index 90feb6d..63afc1e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ -[pytest] +[tool:pytest] norecursedirs=venv env DJANGO_SETTINGS_MODULE=tests.settings -addopts = --tb=short -rxs +addopts = --tb=short -rxs --nomigrations [flake8] max-line-length = 79 diff --git a/tests/conftest.py b/tests/conftest.py index 3a5b7ac..bfa9dc3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,30 +1,10 @@ import io -import os import pytest -from django import conf from django.core.files.uploadedfile import SimpleUploadedFile from PIL import Image -def pytest_configure(): - os.environ[conf.ENVIRONMENT_VARIABLE] = "tests.settings" - - try: - import django - django.setup() - except AttributeError: - pass - - from django.test.utils import setup_test_environment - - setup_test_environment() - - from django.db import connection - - connection.creation.create_test_db() - - @pytest.fixture def imagedata(): img = Image.new('RGB', (250, 250), (255, 55, 255)) diff --git a/tests/test_models.py b/tests/test_models.py index ef39e70..eec35b9 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,11 +1,16 @@ # coding: utf-8 from __future__ import absolute_import, unicode_literals -import filecmp +import io + import os -import shutil +import pytest import uuid +from PIL import Image +from django.core.files.storage import default_storage +from django.core.files.uploadedfile import SimpleUploadedFile + class UUID4Monkey(object): hex = '653d1c6863404b9689b75fa930c9d0a0' @@ -15,9 +20,6 @@ class UUID4Monkey(object): import django # NoQA from django.conf import settings # NoQA -from django.core.files import File # NoQA -from django.test import TestCase # NoQA -from django.contrib.auth.models import User # NoQA from .models import ( SimpleModel, ResizeModel, AdminDeleteModel, @@ -28,30 +30,27 @@ class UUID4Monkey(object): CustomRenderVariationsModel) # NoQA IMG_DIR = os.path.join(settings.MEDIA_ROOT, 'img') - -FIXTURE_DIR = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - 'fixtures' -) +FIXTURES = [ + ('100.gif', 'GIF', 100, 100), + ('600x400.gif', 'GIF', 600, 400), + ('600x400.jpg', 'JPEG', 600, 400), + ('600x400.jpg', 'PNG', 600, 400), +] -class TestStdImage(TestCase): - def setUp(self): - User.objects.create_superuser('admin', 'admin@email.com', 'admin') - self.client.login(username='admin', password='admin') +class TestStdImage(object): + fixtures = {} - self.fixtures = {} - fixture_paths = os.listdir(FIXTURE_DIR) - for fixture_filename in fixture_paths: - fixture_path = os.path.join(FIXTURE_DIR, fixture_filename) - if os.path.isfile(fixture_path): - f = open(fixture_path, 'rb') - self.fixtures[fixture_filename] = File(f) + @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 - def tearDown(self): - """Close all open fixtures and delete everything from media""" - for fixture in list(self.fixtures.values()): - fixture.close() + yield for root, dirs, files in os.walk(settings.MEDIA_ROOT, topdown=False): for name in files: @@ -63,63 +62,65 @@ def tearDown(self): class TestModel(TestStdImage): """Tests StdImage ModelField""" - def test_simple(self): + 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 = os.path.join(FIXTURE_DIR, '100.gif') + source_file = self.fixtures['100.gif'] - self.assertEqual(SimpleModel.objects.count(), 1) - self.assertEqual(SimpleModel.objects.get(pk=1), instance) + assert SimpleModel.objects.count() == 1 + assert SimpleModel.objects.get(pk=1) == instance - self.assertTrue(os.path.exists(target_file)) + assert os.path.exists(target_file) - self.assertTrue(filecmp.cmp(source_file, target_file)) + with open(target_file, 'rb') as f: + source_file.seek(0) + assert source_file.read() == f.read() - def test_variations(self): + 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 = os.path.join(FIXTURE_DIR, '600x400.jpg') + source_file = self.fixtures['600x400.jpg'] - self.assertTrue(os.path.exists(os.path.join(IMG_DIR, 'image.jpg'))) - self.assertEqual(instance.image.width, 600) - self.assertEqual(instance.image.height, 400) + assert os.path.exists(os.path.join(IMG_DIR, 'image.jpg')) + assert instance.image.width == 600 + assert instance.image.height == 400 path = os.path.join(IMG_DIR, 'image.jpg') - assert filecmp.cmp(source_file, path) + + with open(path, 'rb') as f: + source_file.seek(0) + assert source_file.read() == f.read() path = os.path.join(IMG_DIR, 'image.medium.jpg') assert os.path.exists(path) - self.assertEqual(instance.image.medium.width, 400) - self.assertLessEqual(instance.image.medium.height, 400) - self.assertFalse(filecmp.cmp( - source_file, - os.path.join(IMG_DIR, 'image.medium.jpg'))) - - self.assertTrue(os.path.exists( - os.path.join(IMG_DIR, 'image.thumbnail.jpg')) - ) - self.assertEqual(instance.image.thumbnail.width, 100) - self.assertLessEqual(instance.image.thumbnail.height, 75) - self.assertFalse(filecmp.cmp( - source_file, - os.path.join(IMG_DIR, 'image.thumbnail.jpg')) - ) + assert instance.image.medium.width == 400 + assert instance.image.medium.height <= 400 + with open(os.path.join(IMG_DIR, 'image.medium.jpg'), 'rb') as f: + source_file.seek(0) + assert source_file.read() != f.read() - def test_cropping(self): + assert os.path.exists(os.path.join(IMG_DIR, 'image.thumbnail.jpg')) + assert instance.image.thumbnail.width == 100 + assert instance.image.thumbnail.height <= 75 + with open(os.path.join(IMG_DIR, 'image.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'] ) - self.assertEqual(instance.image.thumbnail.width, 150) - self.assertEqual(instance.image.thumbnail.height, 150) + assert instance.image.thumbnail.width == 150 + assert instance.image.thumbnail.height == 150 - def test_variations_override(self): - source_file = os.path.join(FIXTURE_DIR, '600x400.jpg') + 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) - shutil.copyfile(source_file, target_file) + default_storage.save(target_file, source_file) ResizeModel.objects.create( image=self.fixtures['600x400.jpg'] ) @@ -128,7 +129,7 @@ def test_variations_override(self): thumbnail_path = os.path.join(IMG_DIR, 'image.thumbnail_1.jpg') assert not os.path.exists(thumbnail_path) - def test_delete_thumbnail(self): + def test_delete_thumbnail(self, db): """Delete an image with thumbnail""" obj = ThumbnailModel.objects.create( image=self.fixtures['100.gif'] @@ -140,14 +141,14 @@ def test_delete_thumbnail(self): path = os.path.join(IMG_DIR, 'image.thumbnail.gif') assert not os.path.exists(path) - def test_fore_min_size(self): - self.client.post('/admin/tests/forceminsizemodel/add/', { + 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): + def test_thumbnail_save_without_directory(self, db): obj = ThumbnailWithoutDirectoryModel.objects.create( image=self.fixtures['100.gif'] ) @@ -159,7 +160,7 @@ def test_thumbnail_save_without_directory(self): assert os.path.exists(original) assert os.path.exists(thumbnail) - def test_custom_render_variations(self): + def test_custom_render_variations(self, db): instance = CustomRenderVariationsModel.objects.create( image=self.fixtures['600x400.jpg'] ) @@ -171,48 +172,42 @@ def test_custom_render_variations(self): class TestUtils(TestStdImage): """Tests Utils""" - def test_deletion_singnal_receiver(self): + def test_deletion_singnal_receiver(self, db): obj = SimpleModel.objects.create( image=self.fixtures['100.gif'] ) obj.delete() - self.assertFalse( - os.path.exists(os.path.join(IMG_DIR, 'image.gif')) - ) + assert not os.path.exists(os.path.join(IMG_DIR, 'image.gif')) - def test_pre_save_delete_callback_clear(self): + def test_pre_save_delete_callback_clear(self, admin_client): AdminDeleteModel.objects.create( image=self.fixtures['100.gif'] ) if django.VERSION >= (1, 9): - self.client.post('/admin/tests/admindeletemodel/1/change/', { + admin_client.post('/admin/tests/admindeletemodel/1/change/', { 'image-clear': 'checked', }) else: - self.client.post('/admin/tests/admindeletemodel/1/', { + admin_client.post('/admin/tests/admindeletemodel/1/', { 'image-clear': 'checked', }) - self.assertFalse( - os.path.exists(os.path.join(IMG_DIR, 'image.gif')) - ) + assert not os.path.exists(os.path.join(IMG_DIR, 'image.gif')) - def test_pre_save_delete_callback_new(self): + def test_pre_save_delete_callback_new(self, admin_client): AdminDeleteModel.objects.create( image=self.fixtures['100.gif'] ) if django.VERSION >= (1, 9): - self.client.post('/admin/tests/admindeletemodel/1/change/', { + admin_client.post('/admin/tests/admindeletemodel/1/change/', { 'image': self.fixtures['600x400.jpg'], }) else: - self.client.post('/admin/tests/admindeletemodel/1/', { + admin_client.post('/admin/tests/admindeletemodel/1/', { 'image': self.fixtures['600x400.jpg'], }) - self.assertFalse( - os.path.exists(os.path.join(IMG_DIR, 'image.gif')) - ) + assert not os.path.exists(os.path.join(IMG_DIR, 'image.gif')) - def test_upload_to_auto_slug_class_name_dir(self): + def test_upload_to_auto_slug_class_name_dir(self, db): AutoSlugClassNameDirModel.objects.create( name='foo bar', image=self.fixtures['100.gif'] @@ -222,34 +217,34 @@ def test_upload_to_auto_slug_class_name_dir(self): 'autoslugclassnamedirmodel', 'foo-bar.gif' ) - self.assertTrue(os.path.exists(file_path)) + assert os.path.exists(file_path) - def test_upload_to_uuid(self): + def test_upload_to_uuid(self, db): UUIDModel.objects.create(image=self.fixtures['100.gif']) file_path = os.path.join( IMG_DIR, '653d1c6863404b9689b75fa930c9d0a0.gif' ) - self.assertTrue(os.path.exists(file_path)) + assert os.path.exists(file_path) - def test_render_variations_callback(self): + def test_render_variations_callback(self, db): UtilVariationsModel.objects.create(image=self.fixtures['100.gif']) file_path = os.path.join( IMG_DIR, 'image.thumbnail.gif' ) - self.assertTrue(os.path.exists(file_path)) + assert os.path.exists(file_path) class TestValidators(TestStdImage): - def test_max_size_validator(self): - self.client.post('/admin/tests/maxsizemodel/add/', { + def test_max_size_validator(self, admin_client): + admin_client.post('/admin/tests/maxsizemodel/add/', { 'image': self.fixtures['600x400.jpg'], }) - self.assertFalse(os.path.exists(os.path.join(IMG_DIR, 'image.jpg'))) + assert not os.path.exists(os.path.join(IMG_DIR, 'image.jpg')) - def test_min_size_validator(self): - self.client.post('/admin/tests/minsizemodel/add/', { + def test_min_size_validator(self, admin_client): + admin_client.post('/admin/tests/minsizemodel/add/', { 'image': self.fixtures['100.gif'], }) - self.assertFalse(os.path.exists(os.path.join(IMG_DIR, 'image.gif'))) + assert not os.path.exists(os.path.join(IMG_DIR, 'image.gif')) diff --git a/tox.ini b/tox.ini index 100b97c..b231263 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{27,34,35}-dj{18,19,master},qa +envlist = py{27,35,36}-dj{18,19,master},qa [testenv] deps= -rrequirements-dev.txt