diff options
Diffstat (limited to 'tests')
23 files changed, 329 insertions, 32 deletions
diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py index 5cc6f6251a..5ec4fbb544 100644 --- a/tests/admin_views/models.py +++ b/tests/admin_views/models.py @@ -137,7 +137,7 @@ class Thing(models.Model): class Actor(models.Model): name = models.CharField(max_length=50) age = models.IntegerField() - title = models.CharField(max_length=50, null=True) + title = models.CharField(max_length=50, null=True, blank=True) def __str__(self): return self.name diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 528c728069..235fe0cb54 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -57,7 +57,7 @@ for a staff account. Note that both fields may be case-sensitive." @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) -class AdminViewBasicTest(TestCase): +class AdminViewBasicTestCase(TestCase): fixtures = ['admin-views-users.xml', 'admin-views-colors.xml', 'admin-views-fabrics.xml', 'admin-views-books.xml'] @@ -92,6 +92,7 @@ class AdminViewBasicTest(TestCase): failing_msg ) +class AdminViewBasicTest(AdminViewBasicTestCase): def testTrailingSlashRequired(self): """ If you leave off the trailing slash, app should redirect and add it. @@ -583,6 +584,14 @@ class AdminViewBasicTest(TestCase): response = self.client.get("/test_admin/admin/admin_views/inquisition/?leader__name=Palin&leader__age=27") self.assertEqual(response.status_code, 200) + def test_popup_dismiss_related(self): + """ + Regression test for ticket 20664 - ensure the pk is properly quoted. + """ + actor = Actor.objects.create(name="Palin", age=27) + response = self.client.get("/test_admin/admin/admin_views/actor/?%s" % IS_POPUP_VAR) + self.assertContains(response, "opener.dismissRelatedLookupPopup(window, '%s')" % actor.pk) + def test_hide_change_password(self): """ Tests if the "change password" link in the admin is hidden if the User @@ -753,7 +762,7 @@ class SaveAsTests(TestCase): self.assertEqual(response.context['form_url'], '/test_admin/admin/admin_views/person/add/') -class CustomModelAdminTest(AdminViewBasicTest): +class CustomModelAdminTest(AdminViewBasicTestCase): urls = "admin_views.urls" urlbit = "admin2" @@ -2554,6 +2563,17 @@ action)</option> '/test_admin/admin/admin_views/subscriber/?%s' % IS_POPUP_VAR) self.assertEqual(response.context["action_form"], None) + def test_popup_template_response(self): + """ + Success on popups shall be rendered from template in order to allow + easy customization. + """ + response = self.client.post( + '/test_admin/admin/admin_views/actor/add/?%s=1' % IS_POPUP_VAR, + {'name': 'Troy McClure', 'age': '55', IS_POPUP_VAR: '1'}) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.template_name, 'admin/popup_response.html') + @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class TestCustomChangeList(TestCase): @@ -4275,7 +4295,7 @@ class AdminKeepChangeListFiltersTests(TestCase): # Get the `add_view`. response = self.client.get(self.get_add_url()) self.assertEqual(response.status_code, 200) - + # Check the form action. form_action = """<form enctype="multipart/form-data" action="?%s" method="post" id="user_form">""" % self.get_preserved_filters_querystring() self.assertContains(response, form_action, count=1) diff --git a/tests/backends/tests.py b/tests/backends/tests.py index b6e3e9e36f..c1a26df7fc 100644 --- a/tests/backends/tests.py +++ b/tests/backends/tests.py @@ -11,9 +11,12 @@ from django.core.management.color import no_style from django.db import (connection, connections, DEFAULT_DB_ALIAS, DatabaseError, IntegrityError, transaction) from django.db.backends.signals import connection_created +from django.db.backends.sqlite3.base import DatabaseOperations from django.db.backends.postgresql_psycopg2 import version as pg_version from django.db.backends.util import format_number from django.db.models import Sum, Avg, Variance, StdDev +from django.db.models.fields import (AutoField, DateField, DateTimeField, + DecimalField, IntegerField, TimeField) from django.db.utils import ConnectionHandler from django.test import (TestCase, skipUnlessDBFeature, skipIfDBFeature, TransactionTestCase) @@ -402,7 +405,7 @@ class EscapingChecksDebug(EscapingChecks): pass -class SqlliteAggregationTests(TestCase): +class SqliteAggregationTests(TestCase): """ #19360: Raise NotImplementedError when aggregating on date/time fields. """ @@ -418,16 +421,59 @@ class SqlliteAggregationTests(TestCase): models.Item.objects.all().aggregate, aggregate('last_modified')) +class SqliteChecks(TestCase): + + @unittest.skipUnless(connection.vendor == 'sqlite', + "No need to do SQLite checks") + def test_convert_values_to_handle_null_value(self): + database_operations = DatabaseOperations(connection) + self.assertEqual( + None, + database_operations.convert_values(None, AutoField(primary_key=True)) + ) + self.assertEqual( + None, + database_operations.convert_values(None, DateField()) + ) + self.assertEqual( + None, + database_operations.convert_values(None, DateTimeField()) + ) + self.assertEqual( + None, + database_operations.convert_values(None, DecimalField()) + ) + self.assertEqual( + None, + database_operations.convert_values(None, IntegerField()) + ) + self.assertEqual( + None, + database_operations.convert_values(None, TimeField()) + ) + + class BackendTestCase(TestCase): def create_squares_with_executemany(self, args): + self.create_squares(args, 'format', True) + + def create_squares(self, args, paramstyle, multiple): cursor = connection.cursor() opts = models.Square._meta tbl = connection.introspection.table_name_converter(opts.db_table) f1 = connection.ops.quote_name(opts.get_field('root').column) f2 = connection.ops.quote_name(opts.get_field('square').column) - query = 'INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' % (tbl, f1, f2) - cursor.executemany(query, args) + if paramstyle=='format': + query = 'INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' % (tbl, f1, f2) + elif paramstyle=='pyformat': + query = 'INSERT INTO %s (%s, %s) VALUES (%%(root)s, %%(square)s)' % (tbl, f1, f2) + else: + raise ValueError("unsupported paramstyle in test") + if multiple: + cursor.executemany(query, args) + else: + cursor.execute(query, args) def test_cursor_executemany(self): #4896: Test cursor.executemany @@ -456,6 +502,35 @@ class BackendTestCase(TestCase): self.create_squares_with_executemany(args) self.assertEqual(models.Square.objects.count(), 9) + @skipUnlessDBFeature('supports_paramstyle_pyformat') + def test_cursor_execute_with_pyformat(self): + #10070: Support pyformat style passing of paramters + args = {'root': 3, 'square': 9} + self.create_squares(args, 'pyformat', multiple=False) + self.assertEqual(models.Square.objects.count(), 1) + + @skipUnlessDBFeature('supports_paramstyle_pyformat') + def test_cursor_executemany_with_pyformat(self): + #10070: Support pyformat style passing of paramters + args = [{'root': i, 'square': i**2} for i in range(-5, 6)] + self.create_squares(args, 'pyformat', multiple=True) + self.assertEqual(models.Square.objects.count(), 11) + for i in range(-5, 6): + square = models.Square.objects.get(root=i) + self.assertEqual(square.square, i**2) + + @skipUnlessDBFeature('supports_paramstyle_pyformat') + def test_cursor_executemany_with_pyformat_iterator(self): + args = iter({'root': i, 'square': i**2} for i in range(-3, 2)) + self.create_squares(args, 'pyformat', multiple=True) + self.assertEqual(models.Square.objects.count(), 5) + + args = iter({'root': i, 'square': i**2} for i in range(3, 7)) + with override_settings(DEBUG=True): + # same test for DebugCursorWrapper + self.create_squares(args, 'pyformat', multiple=True) + self.assertEqual(models.Square.objects.count(), 9) + def test_unicode_fetches(self): #6254: fetchone, fetchmany, fetchall return strings as unicode objects qn = connection.ops.quote_name diff --git a/tests/compat_checks/__init__.py b/tests/check/__init__.py index e69de29bb2..e69de29bb2 100644 --- a/tests/compat_checks/__init__.py +++ b/tests/check/__init__.py diff --git a/tests/compat_checks/models.py b/tests/check/models.py index 78a10abba6..78a10abba6 100644 --- a/tests/compat_checks/models.py +++ b/tests/check/models.py diff --git a/tests/compat_checks/tests.py b/tests/check/tests.py index 879988c905..98495e38ae 100644 --- a/tests/compat_checks/tests.py +++ b/tests/check/tests.py @@ -1,6 +1,6 @@ -from django.core.compat_checks import base -from django.core.compat_checks import django_1_6_0 -from django.core.management.commands import checksetup +from django.core.checks.compatibility import base +from django.core.checks.compatibility import django_1_6_0 +from django.core.management.commands import check from django.core.management import call_command from django.test import TestCase @@ -86,22 +86,22 @@ class CompatChecksTestCase(TestCase): def test_management_command(self): # Again, we unfortunately have to patch out ``warnings``. Different - old_warnings = checksetup.warnings - checksetup.warnings = FakeWarnings() + old_warnings = check.warnings + check.warnings = FakeWarnings() - self.assertEqual(len(checksetup.warnings._warnings), 0) + self.assertEqual(len(check.warnings._warnings), 0) # Should not produce any warnings. with self.settings(TEST_RUNNER='myapp.test.CustomRunnner'): - call_command('checksetup') + call_command('check') - self.assertEqual(len(checksetup.warnings._warnings), 0) + self.assertEqual(len(check.warnings._warnings), 0) with self.settings(TEST_RUNNER='django.test.runner.DiscoverRunner'): - call_command('checksetup') + call_command('check') - self.assertEqual(len(checksetup.warnings._warnings), 1) - self.assertTrue("You have not explicitly set 'TEST_RUNNER'" in checksetup.warnings._warnings[0]) + self.assertEqual(len(check.warnings._warnings), 1) + self.assertTrue("You have not explicitly set 'TEST_RUNNER'" in check.warnings._warnings[0]) # Restore the ``warnings``. base.warnings = old_warnings diff --git a/tests/field_deconstruction/tests.py b/tests/field_deconstruction/tests.py index 683854940b..4ccf4d048c 100644 --- a/tests/field_deconstruction/tests.py +++ b/tests/field_deconstruction/tests.py @@ -108,7 +108,7 @@ class FieldDeconstructionTests(TestCase): name, path, args, kwargs = field.deconstruct() self.assertEqual(path, "django.db.models.EmailField") self.assertEqual(args, []) - self.assertEqual(kwargs, {}) + self.assertEqual(kwargs, {"max_length": 75}) field = models.EmailField(max_length=255) name, path, args, kwargs = field.deconstruct() self.assertEqual(path, "django.db.models.EmailField") diff --git a/tests/files/tests.py b/tests/files/tests.py index cd2d15acdb..f1e3d5b14b 100644 --- a/tests/files/tests.py +++ b/tests/files/tests.py @@ -1,11 +1,13 @@ from __future__ import absolute_import +import os import gzip import shutil import tempfile from django.core.cache import cache from django.core.files import File +from django.core.files.move import file_move_safe from django.core.files.base import ContentFile from django.core.files.uploadedfile import SimpleUploadedFile from django.test import TestCase @@ -146,3 +148,15 @@ class FileTests(unittest.TestCase): file = SimpleUploadedFile("mode_test.txt", b"content") self.assertFalse(hasattr(file, 'mode')) g = gzip.GzipFile(fileobj=file) + + +class FileMoveSafeTests(unittest.TestCase): + def test_file_move_overwrite(self): + handle_a, self.file_a = tempfile.mkstemp(dir=os.environ['DJANGO_TEST_TEMP_DIR']) + handle_b, self.file_b = tempfile.mkstemp(dir=os.environ['DJANGO_TEST_TEMP_DIR']) + + # file_move_safe should raise an IOError exception if destination file exists and allow_overwrite is False + self.assertRaises(IOError, lambda: file_move_safe(self.file_a, self.file_b, allow_overwrite=False)) + + # should allow it and continue on if allow_overwrite is True + self.assertIsNone(file_move_safe(self.file_a, self.file_b, allow_overwrite=True)) diff --git a/tests/foreign_object/models.py b/tests/foreign_object/models.py index 2d02b5624c..eee8091a15 100644 --- a/tests/foreign_object/models.py +++ b/tests/foreign_object/models.py @@ -150,3 +150,11 @@ class ArticleTranslation(models.Model): class Meta: unique_together = ('article', 'lang') ordering = ('active_translation__title',) + +class ArticleTag(models.Model): + article = models.ForeignKey(Article, related_name="tags", related_query_name="tag") + name = models.CharField(max_length=255) + +class ArticleIdea(models.Model): + articles = models.ManyToManyField(Article, related_name="ideas", related_query_name="idea_things") + name = models.CharField(max_length=255) diff --git a/tests/foreign_object/tests.py b/tests/foreign_object/tests.py index 69636ee49b..670fc94dc5 100644 --- a/tests/foreign_object/tests.py +++ b/tests/foreign_object/tests.py @@ -1,9 +1,10 @@ import datetime from operator import attrgetter -from .models import Country, Person, Group, Membership, Friendship, Article, ArticleTranslation +from .models import Country, Person, Group, Membership, Friendship, Article, ArticleTranslation, ArticleTag, ArticleIdea from django.test import TestCase from django.utils.translation import activate +from django.core.exceptions import FieldError from django import forms class MultiColumnFKTests(TestCase): @@ -321,6 +322,24 @@ class MultiColumnFKTests(TestCase): with self.assertRaisesMessage(Article.DoesNotExist, 'ArticleTranslation has no article'): referrer.article + def test_foreign_key_related_query_name(self): + a1 = Article.objects.create(pub_date=datetime.date.today()) + ArticleTag.objects.create(article=a1, name="foo") + self.assertEqual(Article.objects.filter(tag__name="foo").count(), 1) + self.assertEqual(Article.objects.filter(tag__name="bar").count(), 0) + with self.assertRaises(FieldError): + Article.objects.filter(tags__name="foo") + + def test_many_to_many_related_query_name(self): + a1 = Article.objects.create(pub_date=datetime.date.today()) + i1 = ArticleIdea.objects.create(name="idea1") + a1.ideas.add(i1) + self.assertEqual(Article.objects.filter(idea_things__name="idea1").count(), 1) + self.assertEqual(Article.objects.filter(idea_things__name="idea2").count(), 0) + with self.assertRaises(FieldError): + Article.objects.filter(ideas__name="idea1") + + class FormsTests(TestCase): # ForeignObjects should not have any form fields, currently the user needs # to manually deal with the foreignobject relation. diff --git a/tests/forms_tests/tests/test_fields.py b/tests/forms_tests/tests/test_fields.py index 47c637befa..f7d83503f4 100644 --- a/tests/forms_tests/tests/test_fields.py +++ b/tests/forms_tests/tests/test_fields.py @@ -296,7 +296,7 @@ class FieldsTests(SimpleTestCase): def test_decimalfield_1(self): f = DecimalField(max_digits=4, decimal_places=2) - self.assertWidgetRendersTo(f, '<input id="id_f" step="0.01" type="number" name="f" maxlength="6" />') + self.assertWidgetRendersTo(f, '<input id="id_f" step="0.01" type="number" name="f" />') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) self.assertEqual(f.clean('1'), Decimal("1")) @@ -342,7 +342,7 @@ class FieldsTests(SimpleTestCase): def test_decimalfield_3(self): f = DecimalField(max_digits=4, decimal_places=2, max_value=Decimal('1.5'), min_value=Decimal('0.5')) - self.assertWidgetRendersTo(f, '<input step="0.01" name="f" min="0.5" max="1.5" maxlength="6" type="number" id="id_f" />') + self.assertWidgetRendersTo(f, '<input step="0.01" name="f" min="0.5" max="1.5" type="number" id="id_f" />') self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'", f.clean, '1.6') self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'", f.clean, '0.4') self.assertEqual(f.clean('1.5'), Decimal("1.5")) diff --git a/tests/forms_tests/tests/test_formsets.py b/tests/forms_tests/tests/test_formsets.py index b26017bc78..41577e6049 100644 --- a/tests/forms_tests/tests/test_formsets.py +++ b/tests/forms_tests/tests/test_formsets.py @@ -1072,7 +1072,8 @@ ArticleFormSet = formset_factory(ArticleForm) class TestIsBoundBehavior(TestCase): def test_no_data_raises_validation_error(self): - self.assertRaises(ValidationError, ArticleFormSet, {}) + with self.assertRaises(ValidationError): + ArticleFormSet({}).is_valid() def test_with_management_data_attrs_work_fine(self): data = { diff --git a/tests/generic_views/test_edit.py b/tests/generic_views/test_edit.py index 435e48ba99..84d18ebcb2 100644 --- a/tests/generic_views/test_edit.py +++ b/tests/generic_views/test_edit.py @@ -7,8 +7,9 @@ from django.core.urlresolvers import reverse from django import forms from django.test import TestCase from django.utils.unittest import expectedFailure +from django.test.client import RequestFactory from django.views.generic.base import View -from django.views.generic.edit import FormMixin, CreateView, UpdateView +from django.views.generic.edit import FormMixin, CreateView from . import views from .models import Artist, Author @@ -22,6 +23,24 @@ class FormMixinTests(TestCase): initial_2 = FormMixin().get_initial() self.assertNotEqual(initial_1, initial_2) + def test_get_prefix(self): + """ Test prefix can be set (see #18872) """ + test_string = 'test' + + rf = RequestFactory() + get_request = rf.get('/') + + class TestFormMixin(FormMixin): + request = get_request + + default_kwargs = TestFormMixin().get_form_kwargs() + self.assertEqual(None, default_kwargs.get('prefix')) + + set_mixin = TestFormMixin() + set_mixin.prefix = test_string + set_kwargs = set_mixin.get_form_kwargs() + self.assertEqual(test_string, set_kwargs.get('prefix')) + class BasicFormTests(TestCase): urls = 'generic_views.urls' diff --git a/tests/i18n/commands/__init__.py b/tests/i18n/commands/__init__.py index ffd439140e..76249ca34d 100644 --- a/tests/i18n/commands/__init__.py +++ b/tests/i18n/commands/__init__.py @@ -1,4 +1,4 @@ -from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext as _, ungettext # Translators: This comment should be extracted dummy1 = _("This is a translatable string.") @@ -6,3 +6,7 @@ dummy1 = _("This is a translatable string.") # This comment should not be extracted dummy2 = _("This is another translatable string.") +# This file has a literal with plural forms. When processed first, makemessages +# shouldn't create a .po file with duplicate `Plural-Forms` headers +number = 3 +dummuy3 = ungettext("%(number)s Foo", "%(number)s Foos", number) % {'number': number} diff --git a/tests/i18n/commands/extraction.py b/tests/i18n/commands/extraction.py index 8696ae453b..dd1dce2ed0 100644 --- a/tests/i18n/commands/extraction.py +++ b/tests/i18n/commands/extraction.py @@ -329,6 +329,15 @@ class SymlinkExtractorTests(ExtractorTests): class CopyPluralFormsExtractorTests(ExtractorTests): + PO_FILE_ES = 'locale/es/LC_MESSAGES/django.po' + + def tearDown(self): + os.chdir(self.test_dir) + try: + self._rmrf('locale/es') + except OSError: + pass + os.chdir(self._cwd) def test_copy_plural_forms(self): os.chdir(self.test_dir) @@ -338,6 +347,16 @@ class CopyPluralFormsExtractorTests(ExtractorTests): po_contents = force_text(fp.read()) self.assertTrue('Plural-Forms: nplurals=2; plural=(n != 1)' in po_contents) + def test_override_plural_forms(self): + """Ticket #20311.""" + os.chdir(self.test_dir) + management.call_command('makemessages', locale='es', extensions=['djtpl'], verbosity=0) + self.assertTrue(os.path.exists(self.PO_FILE_ES)) + with open(self.PO_FILE_ES, 'r') as fp: + po_contents = force_text(fp.read()) + found = re.findall(r'^(?P<value>"Plural-Forms.+?\\n")\s*$', po_contents, re.MULTILINE | re.DOTALL) + self.assertEqual(1, len(found)) + class NoWrapExtractorTests(ExtractorTests): diff --git a/tests/i18n/commands/templates/plural.djtpl b/tests/i18n/commands/templates/plural.djtpl new file mode 100644 index 0000000000..b2f2a18750 --- /dev/null +++ b/tests/i18n/commands/templates/plural.djtpl @@ -0,0 +1,6 @@ +{% load i18n %} +{% comment %} +This file has a literal with plural forms. When processed first, makemessages +shouldn't create a .po file with duplicate `Plural-Forms` headers +{% endcomment %} +{% blocktrans count number=3 %}{{ number }} Bar{% plural %}{{ number }} Bars{% endblocktrans %} diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py index de7105f92d..ee9c5afe1d 100644 --- a/tests/lookup/tests.py +++ b/tests/lookup/tests.py @@ -610,6 +610,28 @@ class LookupTests(TestCase): self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'b(.).*b\1'), ['<Article: barfoobaz>', '<Article: bazbaRFOO>', '<Article: foobarbaz>']) + def test_regex_null(self): + """ + Ensure that a regex lookup does not fail on null/None values + """ + Season.objects.create(year=2012, gt=None) + self.assertQuerysetEqual(Season.objects.filter(gt__regex=r'^$'), []) + + def test_regex_non_string(self): + """ + Ensure that a regex lookup does not fail on non-string fields + """ + Season.objects.create(year=2013, gt=444) + self.assertQuerysetEqual(Season.objects.filter(gt__regex=r'^444$'), + ['<Season: 2013>']) + + def test_regex_non_ascii(self): + """ + Ensure that a regex lookup does not trip on non-ascii characters. + """ + Player.objects.create(name='\u2660') + Player.objects.get(name__regex='\u2660') + def test_nonfield_lookups(self): """ Ensure that a lookup query containing non-fields raises the proper diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py index 03cd3b0159..43509c471f 100644 --- a/tests/model_formsets/tests.py +++ b/tests/model_formsets/tests.py @@ -8,7 +8,7 @@ from decimal import Decimal from django import forms from django.db import models from django.forms.models import (_get_foreign_key, inlineformset_factory, - modelformset_factory) + modelformset_factory, BaseModelFormSet) from django.test import TestCase, skipUnlessDBFeature from django.utils import six @@ -386,6 +386,23 @@ class ModelFormsetTest(TestCase): formset = PostFormSet() self.assertFalse("subtitle" in formset.forms[0].fields) + def test_custom_queryset_init(self): + """ + Test that a queryset can be overriden in the __init__ method. + https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#changing-the-queryset + """ + author1 = Author.objects.create(name='Charles Baudelaire') + author2 = Author.objects.create(name='Paul Verlaine') + + class BaseAuthorFormSet(BaseModelFormSet): + def __init__(self, *args, **kwargs): + super(BaseAuthorFormSet, self).__init__(*args, **kwargs) + self.queryset = Author.objects.filter(name__startswith='Charles') + + AuthorFormSet = modelformset_factory(Author, formset=BaseAuthorFormSet) + formset = AuthorFormSet() + self.assertEqual(len(formset.get_queryset()), 1) + def test_model_inheritance(self): BetterAuthorFormSet = modelformset_factory(BetterAuthor, fields="__all__") formset = BetterAuthorFormSet() @@ -559,7 +576,7 @@ class ModelFormsetTest(TestCase): formset = AuthorBooksFormSet2(instance=author) self.assertEqual(len(formset.forms), 1) self.assertHTMLEqual(formset.forms[0].as_p(), - '<p><label for="id_bookwithcustompk_set-0-my_pk">My pk:</label> <input id="id_bookwithcustompk_set-0-my_pk" type="number" name="bookwithcustompk_set-0-my_pk" maxlength="6" /></p>\n' + '<p><label for="id_bookwithcustompk_set-0-my_pk">My pk:</label> <input id="id_bookwithcustompk_set-0-my_pk" type="number" name="bookwithcustompk_set-0-my_pk" /></p>\n' '<p><label for="id_bookwithcustompk_set-0-title">Title:</label> <input id="id_bookwithcustompk_set-0-title" type="text" name="bookwithcustompk_set-0-title" maxlength="100" /><input type="hidden" name="bookwithcustompk_set-0-author" value="1" id="id_bookwithcustompk_set-0-author" /></p>') data = { diff --git a/tests/raw_query/tests.py b/tests/raw_query/tests.py index e404c8b065..7242b8309b 100644 --- a/tests/raw_query/tests.py +++ b/tests/raw_query/tests.py @@ -3,7 +3,7 @@ from __future__ import absolute_import from datetime import date from django.db.models.query_utils import InvalidQuery -from django.test import TestCase +from django.test import TestCase, skipUnlessDBFeature from .models import Author, Book, Coffee, Reviewer, FriendlyAuthor @@ -123,10 +123,27 @@ class RawQueryTests(TestCase): query = "SELECT * FROM raw_query_author WHERE first_name = %s" author = Author.objects.all()[2] params = [author.first_name] - results = list(Author.objects.raw(query, params=params)) + qset = Author.objects.raw(query, params=params) + results = list(qset) self.assertProcessed(Author, results, [author]) self.assertNoAnnotations(results) self.assertEqual(len(results), 1) + self.assertIsInstance(repr(qset), str) + + @skipUnlessDBFeature('supports_paramstyle_pyformat') + def testPyformatParams(self): + """ + Test passing optional query parameters + """ + query = "SELECT * FROM raw_query_author WHERE first_name = %(first)s" + author = Author.objects.all()[2] + params = {'first': author.first_name} + qset = Author.objects.raw(query, params=params) + results = list(qset) + self.assertProcessed(Author, results, [author]) + self.assertNoAnnotations(results) + self.assertEqual(len(results), 1) + self.assertIsInstance(repr(qset), str) def testManyToMany(self): """ diff --git a/tests/servers/tests.py b/tests/servers/tests.py index 3377793d68..be74afe820 100644 --- a/tests/servers/tests.py +++ b/tests/servers/tests.py @@ -96,6 +96,11 @@ class LiveServerAddress(LiveServerBase): del os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] @classmethod + def tearDownClass(cls): + # skip it, as setUpClass doesn't call its parent either + pass + + @classmethod def raises_exception(cls, address, exception): os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = address try: diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py index f8d66bb2b7..625c4f31df 100644 --- a/tests/settings_tests/tests.py +++ b/tests/settings_tests/tests.py @@ -9,17 +9,19 @@ from django.test.utils import override_settings from django.utils import unittest, six -@override_settings(TEST='override') +@override_settings(TEST='override', TEST_OUTER='outer') class FullyDecoratedTranTestCase(TransactionTestCase): available_apps = [] def test_override(self): self.assertEqual(settings.TEST, 'override') + self.assertEqual(settings.TEST_OUTER, 'outer') @override_settings(TEST='override2') def test_method_override(self): self.assertEqual(settings.TEST, 'override2') + self.assertEqual(settings.TEST_OUTER, 'outer') def test_decorated_testcase_name(self): self.assertEqual(FullyDecoratedTranTestCase.__name__, 'FullyDecoratedTranTestCase') @@ -168,6 +170,29 @@ class SettingsTests(TestCase): self.assertRaises(AttributeError, getattr, settings, 'USE_I18N') self.assertEqual(settings.USE_I18N, previous_i18n) + def test_override_settings_nested(self): + """ + Test that override_settings uses the actual _wrapped attribute at + runtime, not when it was instantiated. + """ + + self.assertRaises(AttributeError, getattr, settings, 'TEST') + self.assertRaises(AttributeError, getattr, settings, 'TEST2') + + inner = override_settings(TEST2='override') + with override_settings(TEST='override'): + self.assertEqual('override', settings.TEST) + with inner: + self.assertEqual('override', settings.TEST) + self.assertEqual('override', settings.TEST2) + # inner's __exit__ should have restored the settings of the outer + # context manager, not those when the class was instantiated + self.assertEqual('override', settings.TEST) + self.assertRaises(AttributeError, getattr, settings, 'TEST2') + + self.assertRaises(AttributeError, getattr, settings, 'TEST') + self.assertRaises(AttributeError, getattr, settings, 'TEST2') + def test_allowed_include_roots_string(self): """ ALLOWED_INCLUDE_ROOTS is not allowed to be incorrectly set to a string diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py index 206c648398..76712a09a6 100644 --- a/tests/template_tests/tests.py +++ b/tests/template_tests/tests.py @@ -854,6 +854,10 @@ class TemplateTests(TransRealMixin, TestCase): 'filter02': ('{% filter upper %}django{% endfilter %}', {}, 'DJANGO'), 'filter03': ('{% filter upper|lower %}django{% endfilter %}', {}, 'django'), 'filter04': ('{% filter cut:remove %}djangospam{% endfilter %}', {'remove': 'spam'}, 'django'), + 'filter05': ('{% filter safe %}fail{% endfilter %}', {}, template.TemplateSyntaxError), + 'filter05bis': ('{% filter upper|safe %}fail{% endfilter %}', {}, template.TemplateSyntaxError), + 'filter06': ('{% filter escape %}fail{% endfilter %}', {}, template.TemplateSyntaxError), + 'filter06bis': ('{% filter upper|escape %}fail{% endfilter %}', {}, template.TemplateSyntaxError), ### FIRSTOF TAG ########################################################### 'firstof01': ('{% firstof a b c %}', {'a':0,'b':0,'c':0}, ''), diff --git a/tests/transactions/tests.py b/tests/transactions/tests.py index 24b7615d6f..756fa40abd 100644 --- a/tests/transactions/tests.py +++ b/tests/transactions/tests.py @@ -1,9 +1,8 @@ from __future__ import absolute_import import sys -import warnings -from django.db import connection, transaction, IntegrityError +from django.db import connection, transaction, DatabaseError, IntegrityError from django.test import TransactionTestCase, skipUnlessDBFeature from django.test.utils import IgnorePendingDeprecationWarningsMixin from django.utils import six @@ -188,6 +187,29 @@ class AtomicTests(TransactionTestCase): raise Exception("Oops, that's his first name") self.assertQuerysetEqual(Reporter.objects.all(), []) + def test_force_rollback(self): + with transaction.atomic(): + Reporter.objects.create(first_name="Tintin") + # atomic block shouldn't rollback, but force it. + self.assertFalse(transaction.get_rollback()) + transaction.set_rollback(True) + self.assertQuerysetEqual(Reporter.objects.all(), []) + + def test_prevent_rollback(self): + with transaction.atomic(): + Reporter.objects.create(first_name="Tintin") + sid = transaction.savepoint() + # trigger a database error inside an inner atomic without savepoint + with self.assertRaises(DatabaseError): + with transaction.atomic(savepoint=False): + connection.cursor().execute( + "SELECT no_such_col FROM transactions_reporter") + transaction.savepoint_rollback(sid) + # atomic block should rollback, but prevent it, as we just did it. + self.assertTrue(transaction.get_rollback()) + transaction.set_rollback(False) + self.assertQuerysetEqual(Reporter.objects.all(), ['<Reporter: Tintin>']) + class AtomicInsideTransactionTests(AtomicTests): """All basic tests for atomic should also pass within an existing transaction.""" |
