diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..5c99f6582 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +# http://editorconfig.org +# Source: pydanny cookiecutter-django repo + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{py,rst,ini}] +indent_style = space +indent_size = 4 + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.travis.yml b/.travis.yml index 5f766278e..4ee3b8ffa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,46 +5,46 @@ sudo: false env: - TOX_ENV=py27-flake8 - TOX_ENV=py34-flake8 - - TOX_ENV=py26-django14 - - TOX_ENV=py27-django14 - - TOX_ENV=py26-django15 - - TOX_ENV=py26-django16 - - TOX_ENV=py27-django15 - - TOX_ENV=py27-django16 - - TOX_ENV=py32-django15 - - TOX_ENV=py32-django16 - - TOX_ENV=py33-django15 - - TOX_ENV=py33-django16 - - TOX_ENV=py34-django15 - - TOX_ENV=py34-django16 - - TOX_ENV=py27-django17 - - TOX_ENV=py27-django18alpha - - TOX_ENV=py27-djangomaster - - TOX_ENV=py32-django17 - - TOX_ENV=py32-django18alpha - - TOX_ENV=py32-djangomaster - - TOX_ENV=py33-django17 - - TOX_ENV=py33-django18alpha - - TOX_ENV=py33-djangomaster - - TOX_ENV=py34-django17 - - TOX_ENV=py34-django18alpha - - TOX_ENV=py34-djangomaster - - TOX_ENV=pypy-django17 - - TOX_ENV=pypy-django18alpha - - TOX_ENV=pypy-djangomaster - - TOX_ENV=pypy3-django17 - - TOX_ENV=pypy3-django18alpha - - TOX_ENV=pypy3-djangomaster + - TOX_ENV=py26-dj14 + - TOX_ENV=py27-dj14 + - TOX_ENV=py26-dj15 + - TOX_ENV=py26-dj16 + - TOX_ENV=py27-dj15 + - TOX_ENV=py27-dj16 + - TOX_ENV=py32-dj15 + - TOX_ENV=py32-dj16 + - TOX_ENV=py33-dj15 + - TOX_ENV=py33-dj16 + - TOX_ENV=py34-dj15 + - TOX_ENV=py34-dj16 + - TOX_ENV=py27-dj17 + - TOX_ENV=py27-dj18a + - TOX_ENV=py27-djmaster + - TOX_ENV=py32-dj17 + - TOX_ENV=py32-dj18a + - TOX_ENV=py32-djmaster + - TOX_ENV=py33-dj17 + - TOX_ENV=py33-dj18a + - TOX_ENV=py33-djmaster + - TOX_ENV=py34-dj17 + - TOX_ENV=py34-dj18a + - TOX_ENV=py34-djmaster + - TOX_ENV=pypy-dj17 + - TOX_ENV=pypy-dj18a + - TOX_ENV=pypy-djmaster + - TOX_ENV=pypy3-dj17 + - TOX_ENV=pypy3-dj18a + - TOX_ENV=pypy3-djmaster matrix: fast_finish: true allow_failures: - - env: TOX_ENV=py34-djangomaster - - env: TOX_ENV=py33-djangomaster - - env: TOX_ENV=py32-djangomaster - - env: TOX_ENV=py27-djangomaster - - env: TOX_ENV=pypy-djangomaster - - env: TOX_ENV=pypy3-djangomaster + - env: TOX_ENV=py34-djmaster + - env: TOX_ENV=py33-djmaster + - env: TOX_ENV=py32-djmaster + - env: TOX_ENV=py27-djmaster + - env: TOX_ENV=pypy-djmaster + - env: TOX_ENV=pypy3-djmaster install: - pip install tox diff --git a/conftest.py b/conftest.py new file mode 100644 index 000000000..8105b1671 --- /dev/null +++ b/conftest.py @@ -0,0 +1,73 @@ +from django.conf import settings + + +def pytest_configure(): + import sys + import tempfile + + try: + import django # NOQA + except ImportError: + print("Error: missing test dependency:") + print(" django library is needed to run test suite") + print(" you can install it with 'pip install django'") + print(" or use tox to automatically handle test dependencies") + sys.exit(1) + + try: + import shortuuid # NOQA + except ImportError: + print("Error: missing test dependency:") + print(" shortuuid library is needed to run test suite") + print(" you can install it with 'pip install shortuuid'") + print(" or use tox to automatically handle test dependencies") + sys.exit(1) + + try: + import dateutil # NOQA + except ImportError: + print("Error: missing test dependency:") + print(" dateutil library is needed to run test suite") + print(" you can install it with 'pip install python-dateutil'") + print(" or use tox to automatically handle test dependencies") + sys.exit(1) + + try: + import six # NOQA + except ImportError: + print("Error: missing test dependency:") + print(" six library is needed to run test suite") + print(" you can install it with 'pip install six'") + print(" or use tox to automatically handle test dependencies") + sys.exit(1) + + # Dynamically configure the Django settings with the minimum necessary to + # get Django running tests. + settings.configure( + INSTALLED_APPS=[ + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.admin', + 'django.contrib.sessions', + 'tests.testapp', + 'django_extensions', + ], + MIDDLEWARE_CLASSES=( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + ), + # Django replaces this, but it still wants it. *shrugs* + DATABASE_ENGINE='django.db.backends.sqlite3', + DATABASES={ + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': ':memory:', + } + }, + MEDIA_ROOT='/tmp/django_extensions_test_media/', + MEDIA_PATH='/media/', + ROOT_URLCONF='tests.urls', + DEBUG=True, + TEMPLATE_DEBUG=True, + ) diff --git a/django_extensions/tests/__init__.py b/django_extensions/tests/__init__.py deleted file mode 100644 index c1bdd479a..000000000 --- a/django_extensions/tests/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -from django.db import models # NOQA -from django_extensions.tests.test_dumpscript import DumpScriptTests -from django_extensions.tests.utils import TruncateLetterTests -from django_extensions.tests.json_field import JsonFieldTest -from django_extensions.tests.uuid_field import (UUIDFieldTest, - PostgreSQLUUIDFieldTest) -from django_extensions.tests.shortuuid_field import ShortUUIDFieldTest -from django_extensions.tests.fields import AutoSlugFieldTest -from django_extensions.tests.management_command import CommandTest, \ - ShowTemplateTagsTests, UpdatePermissionsTests, CommandSignalTests -from django_extensions.tests.test_templatetags import TemplateTagsTests -from django_extensions.tests.test_clean_pyc import CleanPycTests -from django_extensions.tests.test_compile_pyc import CompilePycTests - -__test_classes__ = [ - DumpScriptTests, JsonFieldTest, UUIDFieldTest, AutoSlugFieldTest, - CommandTest, ShowTemplateTagsTests, TruncateLetterTests, TemplateTagsTests, - ShortUUIDFieldTest, PostgreSQLUUIDFieldTest, CleanPycTests, CompilePycTests, - UpdatePermissionsTests, CommandSignalTests -] - -try: - from django_extensions.tests.encrypted_fields import EncryptedFieldsTestCase - __test_classes__.append(EncryptedFieldsTestCase) -except ImportError: - pass diff --git a/django_extensions/tests/shortuuid_field.py b/django_extensions/tests/shortuuid_field.py deleted file mode 100644 index d3ec136fd..000000000 --- a/django_extensions/tests/shortuuid_field.py +++ /dev/null @@ -1,39 +0,0 @@ -import six -from django.conf import settings -from django.core.management import call_command -from django.db.models import loading -from django.utils import unittest - -from django_extensions.tests.testapp.models import ShortUUIDTestModel_field, ShortUUIDTestModel_pk, ShortUUIDTestAgregateModel, ShortUUIDTestManyToManyModel - - -class ShortUUIDFieldTest(unittest.TestCase): - def setUp(self): - self.old_installed_apps = settings.INSTALLED_APPS - settings.INSTALLED_APPS = list(settings.INSTALLED_APPS) - settings.INSTALLED_APPS.append('django_extensions.tests') - loading.cache.loaded = False - call_command('syncdb', verbosity=0) - - def tearDown(self): - settings.INSTALLED_APPS = self.old_installed_apps - - def testUUIDFieldCreate(self): - j = ShortUUIDTestModel_field.objects.create(a=6, uuid_field=six.u('vytxeTZskVKR7C7WgdSP3d')) - self.assertEqual(j.uuid_field, six.u('vytxeTZskVKR7C7WgdSP3d')) - - def testUUIDField_pkCreate(self): - j = ShortUUIDTestModel_pk.objects.create(uuid_field=six.u('vytxeTZskVKR7C7WgdSP3d')) - self.assertEqual(j.uuid_field, six.u('vytxeTZskVKR7C7WgdSP3d')) - self.assertEqual(j.pk, six.u('vytxeTZskVKR7C7WgdSP3d')) - - def testUUIDField_pkAgregateCreate(self): - j = ShortUUIDTestAgregateModel.objects.create(a=6) - self.assertEqual(j.a, 6) - self.assertIsInstance(j.pk, six.string_types) - self.assertTrue(len(j.pk) < 23) - - def testUUIDFieldManyToManyCreate(self): - j = ShortUUIDTestManyToManyModel.objects.create(uuid_field=six.u('vytxeTZskVKR7C7WgdSP3e')) - self.assertEqual(j.uuid_field, six.u('vytxeTZskVKR7C7WgdSP3e')) - self.assertEqual(j.pk, six.u('vytxeTZskVKR7C7WgdSP3e')) diff --git a/django_extensions/tests/utils.py b/django_extensions/tests/utils.py deleted file mode 100644 index 23935b66e..000000000 --- a/django_extensions/tests/utils.py +++ /dev/null @@ -1,76 +0,0 @@ -# -*- coding: utf-8 -*- -import sys -import six - -from django.test import TestCase -from django.utils.unittest import skipIf - -from django_extensions.utils.text import truncate_letters -try: - import uuid - assert uuid -except ImportError: - from django_extensions.utils import uuid - - -class TruncateLetterTests(TestCase): - def test_truncate_more_than_text_length(self): - self.assertEqual(six.u("hello tests"), truncate_letters("hello tests", 100)) - - def test_truncate_text(self): - self.assertEqual(six.u("hello..."), truncate_letters("hello tests", 5)) - - def test_truncate_with_range(self): - for i in range(10, -1, -1): - self.assertEqual( - six.u('hello tests'[:i]) + '...', - truncate_letters("hello tests", i) - ) - - def test_with_non_ascii_characters(self): - self.assertEqual( - six.u('\u5ce0 (\u3068\u3046\u3052 t\u014dg...'), - truncate_letters("峠 (とうげ tōge - mountain pass)", 10) - ) - - -class UUIDTests(TestCase): - @skipIf(sys.version_info >= (2, 5, 0), 'uuid already in stdlib') - def test_uuid3(self): - # make a UUID using an MD5 hash of a namespace UUID and a name - self.assertEqual( - uuid.UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e'), - uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org') - ) - - @skipIf(sys.version_info >= (2, 5, 0), 'uuid already in stdlib') - def test_uuid5(self): - # make a UUID using a SHA-1 hash of a namespace UUID and a name - self.assertEqual( - uuid.UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d'), - uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org') - ) - - @skipIf(sys.version_info >= (2, 5, 0), 'uuid already in stdlib') - def test_uuid_str(self): - # make a UUID from a string of hex digits (braces and hyphens ignored) - x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}') - # convert a UUID to a string of hex digits in standard form - self.assertEqual('00010203-0405-0607-0809-0a0b0c0d0e0f', str(x)) - - @skipIf(sys.version_info >= (2, 5, 0), 'uuid already in stdlib') - def test_uuid_bytes(self): - # make a UUID from a string of hex digits (braces and hyphens ignored) - x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}') - # get the raw 16 bytes of the UUID - self.assertEqual( - '\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f', - x.bytes - ) - - @skipIf(sys.version_info >= (2, 5, 0), 'uuid already in stdlib') - def test_make_uuid_from_byte_string(self): - self.assertEqual( - uuid.UUID(bytes='\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f'), - uuid.UUID('00010203-0405-0607-0809-0a0b0c0d0e0f') - ) diff --git a/run_tests.py b/run_tests.py deleted file mode 100755 index 2679feb4b..000000000 --- a/run_tests.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python - -import sys -import shutil -import tempfile - -try: - import django -except ImportError: - print("Error: missing test dependency:") - print(" django library is needed to run test suite") - print(" you can install it with 'pip install django'") - print(" or use tox to automatically handle test dependencies") - sys.exit(1) - -try: - import shortuuid -except ImportError: - print("Error: missing test dependency:") - print(" shortuuid library is needed to run test suite") - print(" you can install it with 'pip install shortuuid'") - print(" or use tox to automatically handle test dependencies") - sys.exit(1) - -try: - import dateutil -except ImportError: - print("Error: missing test dependency:") - print(" dateutil library is needed to run test suite") - print(" you can install it with 'pip install python-dateutil'") - print(" or use tox to automatically handle test dependencies") - sys.exit(1) - -try: - import six -except ImportError: - print("Error: missing test dependency:") - print(" six library is needed to run test suite") - print(" you can install it with 'pip install six'") - print(" or use tox to automatically handle test dependencies") - sys.exit(1) - -__test_libs__ = [ - django, - shortuuid, - dateutil, - six -] - -from django.conf import settings - - -def main(): - # Dynamically configure the Django settings with the minimum necessary to - # get Django running tests. - KEY_LOCS = {} - try: - try: - # If KeyCzar is available, set up the environment. - from keyczar import keyczart, keyinfo - - # Create an RSA private key. - keys_dir = tempfile.mkdtemp("django_extensions_tests_keyzcar_rsa_dir") - keyczart.Create(keys_dir, "test", keyinfo.DECRYPT_AND_ENCRYPT, asymmetric=True) - keyczart.AddKey(keys_dir, "PRIMARY", size=4096) - KEY_LOCS['DECRYPT_AND_ENCRYPT'] = keys_dir - - # Create an RSA public key. - pub_dir = tempfile.mkdtemp("django_extensions_tests_keyzcar_pub_dir") - keyczart.PubKey(keys_dir, pub_dir) - KEY_LOCS['ENCRYPT'] = pub_dir - except ImportError: - pass - - settings.configure( - INSTALLED_APPS=[ - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.admin', - 'django.contrib.sessions', - 'django_extensions.tests.testapp', - 'django_extensions', - ], - MIDDLEWARE_CLASSES=( - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - ), - # Django replaces this, but it still wants it. *shrugs* - DATABASE_ENGINE='django.db.backends.sqlite3', - DATABASES={ - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': ':memory:', - } - }, - MEDIA_ROOT='/tmp/django_extensions_test_media/', - MEDIA_PATH='/media/', - ROOT_URLCONF='django_extensions.tests.urls', - DEBUG=True, - TEMPLATE_DEBUG=True, - ENCRYPTED_FIELD_KEYS_DIR=KEY_LOCS, - ) - - if django.VERSION >= (1, 7): - django.setup() - - apps = ['django_extensions'] - if django.VERSION >= (1, 6): - apps.append('django_extensions.tests.testapp') - apps.append('django_extensions.tests') - - from django.core.management import call_command - from django.test.utils import get_runner - - try: - from django.contrib.auth import get_user_model - except ImportError: - USERNAME_FIELD = "username" - else: - USERNAME_FIELD = get_user_model().USERNAME_FIELD - - DjangoTestRunner = get_runner(settings) - - class TestRunner(DjangoTestRunner): - def setup_databases(self, *args, **kwargs): - result = super(TestRunner, self).setup_databases(*args, **kwargs) - kwargs = { - "interactive": False, - "email": "admin@doesnotexit.com", - USERNAME_FIELD: "admin", - } - call_command("createsuperuser", **kwargs) - return result - - failures = TestRunner(verbosity=2, interactive=True).run_tests(apps) - sys.exit(failures) - - finally: - for name, path in KEY_LOCS.items(): - # cleanup crypto key temp dirs - shutil.rmtree(path) - - -if __name__ == '__main__': - main() diff --git a/django_extensions/tests/management/__init__.py b/tests/__init__.py similarity index 100% rename from django_extensions/tests/management/__init__.py rename to tests/__init__.py diff --git a/django_extensions/tests/management/commands/__init__.py b/tests/management/__init__.py similarity index 100% rename from django_extensions/tests/management/commands/__init__.py rename to tests/management/__init__.py diff --git a/django_extensions/tests/testapp/__init__.py b/tests/management/commands/__init__.py similarity index 100% rename from django_extensions/tests/testapp/__init__.py rename to tests/management/commands/__init__.py diff --git a/django_extensions/tests/management/commands/error_raising_command.py b/tests/management/commands/error_raising_command.py similarity index 100% rename from django_extensions/tests/management/commands/error_raising_command.py rename to tests/management/commands/error_raising_command.py diff --git a/django_extensions/tests/fields.py b/tests/test_autoslug_fields.py similarity index 74% rename from django_extensions/tests/fields.py rename to tests/test_autoslug_fields.py index 16e683f2b..8ca90a09c 100644 --- a/django_extensions/tests/fields.py +++ b/tests/test_autoslug_fields.py @@ -1,58 +1,33 @@ +import pytest + import django -from django.conf import settings -from django.core.management import call_command -from django.db.models import loading from django.db import models -from django.utils import unittest - +from django.test import TestCase from django_extensions.db.fields import AutoSlugField -from django_extensions.tests.testapp.models import SluggedTestModel, ChildSluggedTestModel -if django.VERSION[:2] >= (1, 7): +from .testapp.models import ChildSluggedTestModel, SluggedTestModel + + +if django.VERSION >= (1, 7): from django.db import migrations # NOQA from django.db.migrations.writer import MigrationWriter # NOQA from django.utils import six # NOQA import django_extensions # NOQA -class FieldTestCase(unittest.TestCase): - def setUp(self): - self.old_installed_apps = settings.INSTALLED_APPS - #settings.INSTALLED_APPS = list(settings.INSTALLED_APPS) - #settings.INSTALLED_APPS.append('django_extensions.tests.testapp') - loading.cache.loaded = False - - # Don't migrate if south is installed - migrate = 'south' not in settings.INSTALLED_APPS - call_command('syncdb', verbosity=0, migrate=migrate) - - def tearDown(self): - settings.INSTALLED_APPS = self.old_installed_apps - - def safe_exec(self, string, value=None): - l = {} - try: - exec(string, globals(), l) - except Exception as e: - if value: - self.fail("Could not exec %r (from value %r): %s" % (string.strip(), value, e)) - else: - self.fail("Could not exec %r: %s" % (string.strip(), e)) - return l - - -class AutoSlugFieldTest(FieldTestCase): +@pytest.mark.usefixtures("admin_user") +class AutoSlugFieldTest(TestCase): def tearDown(self): super(AutoSlugFieldTest, self).tearDown() SluggedTestModel.objects.all().delete() - def testAutoCreateSlug(self): + def test_auto_create_slug(self): m = SluggedTestModel(title='foo') m.save() self.assertEqual(m.slug, 'foo') - def testAutoCreateNextSlug(self): + def test_auto_create_next_slug(self): m = SluggedTestModel(title='foo') m.save() @@ -60,18 +35,18 @@ def testAutoCreateNextSlug(self): m.save() self.assertEqual(m.slug, 'foo-2') - def testAutoCreateSlugWithNumber(self): + def test_auto_create_slug_with_number(self): m = SluggedTestModel(title='foo 2012') m.save() self.assertEqual(m.slug, 'foo-2012') - def testAutoUpdateSlugWithNumber(self): + def test_auto_update_slug_with_number(self): m = SluggedTestModel(title='foo 2012') m.save() m.save() self.assertEqual(m.slug, 'foo-2012') - def testUpdateSlug(self): + def test_update_slug(self): m = SluggedTestModel(title='foo') m.save() self.assertEqual(m.slug, 'foo') @@ -92,7 +67,7 @@ def testUpdateSlug(self): self.assertEqual(m.title, 'bar') self.assertEqual(m.slug, 'foo-2012') - def testSimpleSlugSource(self): + def test_simple_slug_source(self): m = SluggedTestModel(title='-foo') m.save() self.assertEqual(m.slug, 'foo') @@ -104,7 +79,7 @@ def testSimpleSlugSource(self): n.save() self.assertEqual(n.slug, 'foo-2') - def testEmptySlugSource(self): + def test_empty_slug_source(self): # regression test m = SluggedTestModel(title='') @@ -118,7 +93,7 @@ def testEmptySlugSource(self): n.save() self.assertEqual(n.slug, '-3') - def testInheritanceCreatesNextSlug(self): + def test_inheritance_creates_next_slug(self): m = SluggedTestModel(title='foo') m.save() @@ -130,8 +105,21 @@ def testInheritanceCreatesNextSlug(self): o.save() self.assertEqual(o.slug, 'foo-3') - @unittest.skipIf(django.VERSION[0] <= 1 and django.VERSION[1] <= 6, - "Migrations are handled by south in Django <1.7") + +@pytest.mark.skipif(django.VERSION < (1, 7), + reason="Migrations are handled by south in Django <1.7") +class MigrationTest(TestCase): + def safe_exec(self, string, value=None): + l = {} + try: + exec(string, globals(), l) + except Exception as e: + if value: + self.fail("Could not exec %r (from value %r): %s" % (string.strip(), value, e)) + else: + self.fail("Could not exec %r: %s" % (string.strip(), e)) + return l + def test_17_migration(self): """ Tests making migrations with Django 1.7+'s migration framework diff --git a/django_extensions/tests/test_clean_pyc.py b/tests/test_clean_pyc.py similarity index 93% rename from django_extensions/tests/test_clean_pyc.py rename to tests/test_clean_pyc.py index 6a19cc9cb..09885923b 100644 --- a/django_extensions/tests/test_clean_pyc.py +++ b/tests/test_clean_pyc.py @@ -1,9 +1,10 @@ +import fnmatch import os -import six import shutil -import fnmatch -from django.test import TestCase +import six + from django.core.management import call_command +from django.test import TestCase from django_extensions.management.utils import get_project_root @@ -35,7 +36,7 @@ def test_removes_pyc_files(self): def test_takes_path(self): out = six.StringIO() - project_root = os.path.join(get_project_root(), 'tests', 'testapp') + project_root = os.path.join('tests', 'testapp') call_command('compile_pyc', path=project_root) pyc_glob = self._find_pyc(project_root) self.assertTrue(len(pyc_glob) > 0) @@ -45,7 +46,7 @@ def test_takes_path(self): def test_removes_pyo_files(self): out = six.StringIO() - project_root = os.path.join(get_project_root(), 'tests', 'testapp') + project_root = os.path.join('tests', 'testapp') call_command('compile_pyc', path=project_root) pyc_glob = self._find_pyc(project_root) self.assertTrue(len(pyc_glob) > 0) diff --git a/django_extensions/tests/test_compile_pyc.py b/tests/test_compile_pyc.py similarity index 99% rename from django_extensions/tests/test_compile_pyc.py rename to tests/test_compile_pyc.py index 6ccd9487f..2e729ee4a 100644 --- a/django_extensions/tests/test_compile_pyc.py +++ b/tests/test_compile_pyc.py @@ -1,8 +1,9 @@ +import fnmatch import os import six -import fnmatch -from django.test import TestCase + from django.core.management import call_command +from django.test import TestCase from django_extensions.management.utils import get_project_root diff --git a/django_extensions/tests/test_dumpscript.py b/tests/test_dumpscript.py similarity index 72% rename from django_extensions/tests/test_dumpscript.py rename to tests/test_dumpscript.py index 4da5291e5..6446df5d0 100644 --- a/django_extensions/tests/test_dumpscript.py +++ b/tests/test_dumpscript.py @@ -1,32 +1,18 @@ -import sys +import ast import six +import sys from django.core.management import call_command +from django.test import TestCase -from django_extensions.tests.testapp.models import Name, Note, Person -from django_extensions.tests.fields import FieldTestCase - -if sys.version_info[:2] >= (2, 6): - import ast as compiler # NOQA -else: - import compiler # NOQA +from .testapp.models import Name, Note, Person -class DumpScriptTests(FieldTestCase): +class DumpScriptTests(TestCase): def setUp(self): - super(DumpScriptTests, self).setUp() - - self.real_stdout = sys.stdout - self.real_stderr = sys.stderr sys.stdout = six.StringIO() sys.stderr = six.StringIO() - def tearDown(self): - super(DumpScriptTests, self).tearDown() - - sys.stdout = self.real_stdout - sys.stderr = self.real_stderr - def test_runs(self): # lame test...does it run? n = Name(name='Gabriel') @@ -34,7 +20,6 @@ def test_runs(self): call_command('dumpscript', 'django_extensions') self.assertTrue('Gabriel' in sys.stdout.getvalue()) - #---------------------------------------------------------------------- def test_replaced_stdout(self): # check if stdout can be replaced sys.stdout = six.StringIO() @@ -46,7 +31,6 @@ def test_replaced_stdout(self): self.assertEqual(0, len(sys.stdout.getvalue())) # there should not be any output to sys.stdout tmp_out.close() - #---------------------------------------------------------------------- def test_replaced_stderr(self): # check if stderr can be replaced, without changing stdout n = Name(name='Fred') @@ -59,7 +43,6 @@ def test_replaced_stderr(self): self.assertEqual(0, len(sys.stderr.getvalue())) # there should not be any output to sys.stderr tmp_err.close() - #---------------------------------------------------------------------- def test_valid_syntax(self): n1 = Name(name='John') n1.save() @@ -77,7 +60,7 @@ def test_valid_syntax(self): p2.notes.add(note1, note2) tmp_out = six.StringIO() call_command('dumpscript', 'django_extensions', stdout=tmp_out) - ast_syntax_tree = compiler.parse(tmp_out.getvalue()) + ast_syntax_tree = ast.parse(tmp_out.getvalue()) if hasattr(ast_syntax_tree, 'body'): self.assertTrue(len(ast_syntax_tree.body) > 1) else: diff --git a/django_extensions/tests/encrypted_fields.py b/tests/test_encrypted_fields.py similarity index 84% rename from django_extensions/tests/encrypted_fields.py rename to tests/test_encrypted_fields.py index d9c187ffe..a9e378033 100644 --- a/django_extensions/tests/encrypted_fields.py +++ b/tests/test_encrypted_fields.py @@ -1,12 +1,15 @@ +import tempfile from contextlib import contextmanager -import functools +import pytest + +import django from django.conf import settings from django.db import connection, models -from django.db.models import loading +from django.test import TestCase + +from .testapp.models import Secret -from django_extensions.tests.models import Secret -from django_extensions.tests.fields import FieldTestCase # Only perform encrypted fields tests if keyczar is present. Resolves # http://github.com/django-extensions/django-extensions/issues/#issue/17 @@ -17,20 +20,31 @@ except ImportError: keyczar_active = False +# Locations of both private and public keys. +KEY_LOCS = {} -def run_if_active(func): - "Method decorator that only runs a test if KeyCzar is available." - @functools.wraps(func) - def inner(self): - if not keyczar_active: - return - return func(self) - return inner +@pytest.fixture(scope="class") +def keyczar_keys(request): + # If KeyCzar is available, set up the environment. + if keyczar_active: + # Create an RSA private key. + keys_dir = tempfile.mkdtemp("django_extensions_tests_keyzcar_rsa_dir") + keyczart.Create(keys_dir, "test", keyinfo.DECRYPT_AND_ENCRYPT, asymmetric=True) + keyczart.AddKey(keys_dir, "PRIMARY", size=4096) + KEY_LOCS['DECRYPT_AND_ENCRYPT'] = keys_dir + # Create an RSA public key. + pub_dir = tempfile.mkdtemp("django_extensions_tests_keyzcar_pub_dir") + keyczart.PubKey(keys_dir, pub_dir) + KEY_LOCS['ENCRYPT'] = pub_dir -# Locations of both private and public keys. -KEY_LOCS = getattr(settings, 'ENCRYPTED_FIELD_KEYS_DIR', {}) + # cleanup crypto key temp dirs + def cleanup(): + import shutil + for name, path in KEY_LOCS.items(): + shutil.rmtree(path) + request.addfinalizer(cleanup) @contextmanager @@ -94,21 +108,12 @@ def secret_model(): #differences-between-proxy-inheritance-and-unmanaged-models """ - # Store Django's cached model, if present, so we can restore when the - # manager exits. - orig_model = None - try: - orig_model = loading.cache.app_models['tests']['secret'] - del loading.cache.app_models['tests']['secret'] - except KeyError: - pass - try: # Create a new class that shadows tests.models.Secret. attrs = { 'name': EncryptedCharField("Name", max_length=Secret._meta.get_field('name').max_length), 'text': EncryptedTextField("Text"), - '__module__': 'django_extensions.tests.models', + '__module__': 'tests.testapp.models', 'Meta': type('Meta', (object, ), { 'managed': False, 'db_table': Secret._meta.db_table @@ -119,17 +124,12 @@ def secret_model(): except: raise # Reraise any exceptions. - finally: - # Restore Django's model cache. - try: - loading.cache.app_models['tests']['secret'] = orig_model - except KeyError: - pass - -class EncryptedFieldsTestCase(FieldTestCase): - @run_if_active - def testCharFieldCreate(self): +@pytest.mark.skipif(keyczar_active is False or django.VERSION < (1, 7), + reason="Encrypted fields needs that keyczar is installed") +@pytest.mark.usefixtures("admin_user", "keyczar_keys") +class EncryptedFieldsTestCase(TestCase): + def test_char_field_create(self): """ Uses a private key to encrypt data on model creation. Verifies the data is encrypted in the database and can be decrypted. @@ -146,8 +146,7 @@ def testCharFieldCreate(self): decrypted_val = crypt.Decrypt(db_val[len(EncryptedCharField.prefix):]) self.assertEqual(test_val, decrypted_val) - @run_if_active - def testCharFieldRead(self): + def test_char_field_read(self): """ Uses a private key to encrypt data on model creation. Verifies the data is decrypted when reading the value back from the @@ -160,8 +159,7 @@ def testCharFieldRead(self): retrieved_secret = model.objects.get(id=secret.id) self.assertEqual(test_val, retrieved_secret.name) - @run_if_active - def testTextFieldCreate(self): + def test_text_field_create(self): """ Uses a private key to encrypt data on model creation. Verifies the data is encrypted in the database and can be decrypted. @@ -177,8 +175,7 @@ def testTextFieldCreate(self): decrypted_val = crypt.Decrypt(db_val[len(EncryptedCharField.prefix):]) self.assertEqual(test_val, decrypted_val) - @run_if_active - def testTextFieldRead(self): + def test_text_field_read(self): """ Uses a private key to encrypt data on model creation. Verifies the data is decrypted when reading the value back from the @@ -191,8 +188,7 @@ def testTextFieldRead(self): retrieved_secret = model.objects.get(id=secret.id) self.assertEqual(test_val, retrieved_secret.text) - @run_if_active - def testCannotDecrypt(self): + def test_cannot_decrypt(self): """ Uses a public key to encrypt data on model creation. Verifies that the data cannot be decrypted using the same key. @@ -205,8 +201,7 @@ def testCannotDecrypt(self): self.assertNotEqual(test_val, retrieved_secret.name) self.assertTrue(retrieved_secret.name.startswith(EncryptedCharField.prefix)) - @run_if_active - def testUnacceptablePurpose(self): + def test_unacceptable_purpose(self): """ Tries to create an encrypted field with a mode mismatch. A purpose of "DECRYPT_AND_ENCRYPT" cannot be used with a public key, @@ -220,8 +215,7 @@ def testUnacceptablePurpose(self): # definition time, so any code in here would never get run. pass - @run_if_active - def testDecryptionForbidden(self): + def test_decryption_forbidden(self): """ Uses a private key to encrypt data, but decryption is not allowed. ENCRYPTED_FIELD_MODE is explicitly set to ENCRYPT, meaning data should @@ -235,8 +229,7 @@ def testDecryptionForbidden(self): self.assertNotEqual(test_val, retrieved_secret.name) self.assertTrue(retrieved_secret.name.startswith(EncryptedCharField.prefix)) - @run_if_active - def testEncryptPublicDecryptPrivate(self): + def test_encrypt_public_decrypt_private(self): """ Uses a public key to encrypt, and a private key to decrypt data. """ diff --git a/django_extensions/tests/json_field.py b/tests/test_json_field.py similarity index 58% rename from django_extensions/tests/json_field.py rename to tests/test_json_field.py index ace83bdba..ae93ce46e 100644 --- a/django_extensions/tests/json_field.py +++ b/tests/test_json_field.py @@ -1,17 +1,18 @@ -from django_extensions.tests.fields import FieldTestCase -from django_extensions.tests.testapp.models import JSONFieldTestModel +from django.test import TestCase +from .testapp.models import JSONFieldTestModel -class JsonFieldTest(FieldTestCase): - def testCharFieldCreate(self): + +class JsonFieldTest(TestCase): + def test_char_field_create(self): j = JSONFieldTestModel.objects.create(a=6, j_field=dict(foo='bar')) self.assertEqual(j.a, 6) - def testDefault(self): + def test_default(self): j = JSONFieldTestModel.objects.create(a=1) self.assertEqual(j.j_field, {}) - def testEmptyList(self): + def test_empty_list(self): j = JSONFieldTestModel.objects.create(a=6, j_field=[]) self.assertTrue(isinstance(j.j_field, list)) self.assertEqual(j.j_field, []) diff --git a/django_extensions/tests/management_command.py b/tests/test_management_command.py similarity index 97% rename from django_extensions/tests/management_command.py rename to tests/test_management_command.py index 04c82165d..457a7eafa 100644 --- a/django_extensions/tests/management_command.py +++ b/tests/test_management_command.py @@ -1,6 +1,10 @@ # -*- coding: utf-8 -*- -import sys import logging +import sys + +from django.core.management import call_command +from django.test import TestCase + try: from cStringIO import StringIO # NOQA @@ -12,8 +16,6 @@ except ImportError: from django.utils import importlib # NOQA -from django.core.management import call_command -from django.test import TestCase class MockLoggingHandler(logging.Handler): @@ -41,7 +43,7 @@ def test_error_logging(self): # Ensure command errors are properly logged and reraised from django_extensions.management.base import logger logger.addHandler(MockLoggingHandler()) - module_path = "django_extensions.tests.management.commands.error_raising_command" + module_path = "tests.management.commands.error_raising_command" module = importlib.import_module(module_path) error_raising_command = module.Command() self.assertRaises(Exception, error_raising_command.execute) diff --git a/django_extensions/tests/test_models.py b/tests/test_models.py similarity index 96% rename from django_extensions/tests/test_models.py rename to tests/test_models.py index 3d4f68d48..859ffa6a5 100644 --- a/django_extensions/tests/test_models.py +++ b/tests/test_models.py @@ -1,7 +1,7 @@ from django.test import TestCase - from django_extensions.db.models import ActivatorModel -from django_extensions.tests.testapp.models import Post + +from .testapp.models import Post class ActivatorModelTestCase(TestCase): diff --git a/tests/test_shortuuid_field.py b/tests/test_shortuuid_field.py new file mode 100644 index 000000000..7b47ad3dc --- /dev/null +++ b/tests/test_shortuuid_field.py @@ -0,0 +1,32 @@ +import six + +from django.test import TestCase + +from .testapp.models import ( + ShortUUIDTestAgregateModel, + ShortUUIDTestManyToManyModel, + ShortUUIDTestModel_field, + ShortUUIDTestModel_pk, +) + + +class ShortUUIDFieldTest(TestCase): + def test_UUID_field_create(self): + j = ShortUUIDTestModel_field.objects.create(a=6, uuid_field=six.u('vytxeTZskVKR7C7WgdSP3d')) + self.assertEqual(j.uuid_field, six.u('vytxeTZskVKR7C7WgdSP3d')) + + def test_UUID_field_pk_create(self): + j = ShortUUIDTestModel_pk.objects.create(uuid_field=six.u('vytxeTZskVKR7C7WgdSP3d')) + self.assertEqual(j.uuid_field, six.u('vytxeTZskVKR7C7WgdSP3d')) + self.assertEqual(j.pk, six.u('vytxeTZskVKR7C7WgdSP3d')) + + def test_UUID_field_pk_agregate_create(self): + j = ShortUUIDTestAgregateModel.objects.create(a=6) + self.assertEqual(j.a, 6) + self.assertIsInstance(j.pk, six.string_types) + self.assertTrue(len(j.pk) < 23) + + def test_UUID_field_manytomany_create(self): + j = ShortUUIDTestManyToManyModel.objects.create(uuid_field=six.u('vytxeTZskVKR7C7WgdSP3e')) + self.assertEqual(j.uuid_field, six.u('vytxeTZskVKR7C7WgdSP3e')) + self.assertEqual(j.pk, six.u('vytxeTZskVKR7C7WgdSP3e')) diff --git a/django_extensions/tests/test_templatetags.py b/tests/test_templatetags.py similarity index 99% rename from django_extensions/tests/test_templatetags.py rename to tests/test_templatetags.py index 70e23d70a..ff729529c 100644 --- a/django_extensions/tests/test_templatetags.py +++ b/tests/test_templatetags.py @@ -1,7 +1,6 @@ import six from django.test import TestCase - from django_extensions.templatetags.widont import widont, widont_html diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 000000000..bbf59233f --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +import six + +from django.test import TestCase +from django_extensions.utils.text import truncate_letters + + +class TruncateLetterTests(TestCase): + def test_truncate_more_than_text_length(self): + self.assertEqual(six.u("hello tests"), truncate_letters("hello tests", 100)) + + def test_truncate_text(self): + self.assertEqual(six.u("hello..."), truncate_letters("hello tests", 5)) + + def test_truncate_with_range(self): + for i in range(10, -1, -1): + self.assertEqual( + six.u('hello tests'[:i]) + '...', + truncate_letters("hello tests", i) + ) + + def test_with_non_ascii_characters(self): + self.assertEqual( + six.u('\u5ce0 (\u3068\u3046\u3052 t\u014dg...'), + truncate_letters("峠 (とうげ tōge - mountain pass)", 10) + ) diff --git a/django_extensions/tests/uuid_field.py b/tests/test_uuid_field.py similarity index 81% rename from django_extensions/tests/uuid_field.py rename to tests/test_uuid_field.py index 761c4069c..2f158d73e 100644 --- a/django_extensions/tests/uuid_field.py +++ b/tests/test_uuid_field.py @@ -1,36 +1,41 @@ import re -import uuid - import six +import uuid +from django.test import TestCase from django_extensions.db.fields import PostgreSQLUUIDField -from django_extensions.tests.fields import FieldTestCase -from django_extensions.tests.testapp.models import UUIDTestModel_field, UUIDTestModel_pk, UUIDTestAgregateModel, UUIDTestManyToManyModel + +from .testapp.models import ( + UUIDTestAgregateModel, + UUIDTestManyToManyModel, + UUIDTestModel_field, + UUIDTestModel_pk, +) -class UUIDFieldTest(FieldTestCase): - def testUUIDFieldCreate(self): +class UUIDFieldTest(TestCase): + def test_UUID_field_create(self): j = UUIDTestModel_field.objects.create(a=6, uuid_field=six.u('550e8400-e29b-41d4-a716-446655440000')) self.assertEqual(j.uuid_field, six.u('550e8400-e29b-41d4-a716-446655440000')) - def testUUIDField_pkCreate(self): + def test_UUID_field_pk_create(self): j = UUIDTestModel_pk.objects.create(uuid_field=six.u('550e8400-e29b-41d4-a716-446655440000')) self.assertEqual(j.uuid_field, six.u('550e8400-e29b-41d4-a716-446655440000')) self.assertEqual(j.pk, six.u('550e8400-e29b-41d4-a716-446655440000')) - def testUUIDField_pkAgregateCreate(self): + def test_UUID_field_pk_agregate_create(self): j = UUIDTestAgregateModel.objects.create(a=6, uuid_field=six.u('550e8400-e29b-41d4-a716-446655440001')) self.assertEqual(j.a, 6) self.assertIsInstance(j.pk, six.string_types) self.assertEqual(len(j.pk), 36) - def testUUIDFieldManyToManyCreate(self): + def test_UUID_field_manytomany_create(self): j = UUIDTestManyToManyModel.objects.create(uuid_field=six.u('550e8400-e29b-41d4-a716-446655440010')) self.assertEqual(j.uuid_field, six.u('550e8400-e29b-41d4-a716-446655440010')) self.assertEqual(j.pk, six.u('550e8400-e29b-41d4-a716-446655440010')) -class PostgreSQLUUIDFieldTest(FieldTestCase): +class PostgreSQLUUIDFieldTest(TestCase): def test_uuid_casting(self): # As explain by postgres documentation # http://www.postgresql.org/docs/9.1/static/datatype-uuid.html diff --git a/django_extensions/tests/testapp/urls.py b/tests/testapp/__init__.py similarity index 100% rename from django_extensions/tests/testapp/urls.py rename to tests/testapp/__init__.py diff --git a/django_extensions/tests/testapp/models.py b/tests/testapp/models.py similarity index 100% rename from django_extensions/tests/testapp/models.py rename to tests/testapp/models.py diff --git a/tests/testapp/urls.py b/tests/testapp/urls.py new file mode 100644 index 000000000..e69de29bb diff --git a/tox.ini b/tox.ini index 219242e89..4fd7f5f1a 100644 --- a/tox.ini +++ b/tox.ini @@ -6,22 +6,25 @@ [tox] envlist = {py27,py34}-flake8, - {py26,py27}-django14, - {py26,py27,py32,py33,py34}-django{15,16}, - {py27,py32,py33,py34,pypy,pypy3}-django{17,18alpha,master} + {py26,py27}-dj14, + {py26,py27,py32,py33,py34}-dj{15,16}, + {py27,py32,py33,py34,pypy,pypy3}-dj{17,18a,master} [testenv] -commands = {envpython} setup.py test +commands = py.test {posargs} -deps = - django14: Django>=1.4,<1.5 - django15: Django>=1.5,<1.6 - django16: Django>=1.6,<1.7 - django17: Django>=1.7,<1.8 - django18alpha: https://www.djangoproject.com/download/1.8a1/tarball/ - djangomaster: https://github.com/django/django/zipball/master - shortuuid==0.4 - python-dateutil +deps = + dj14: Django>=1.4,<1.5 + dj15: Django>=1.5,<1.6 + dj16: Django>=1.6,<1.7 + dj17: Django>=1.7,<1.8 + dj18a: https://www.djangoproject.com/download/1.8a1/tarball/ + djmaster: https://github.com/django/django/zipball/master + shortuuid==0.4 + python-dateutil + pytest-django==2.8.0 + pytest-xdist + py27: python-keyczar [testenv:py27-flake8] deps = @@ -32,4 +35,3 @@ commands = python setup.py flake8 deps = flake8 commands = python setup.py flake8 -