summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorHonza Král <honza.kral@gmail.com>2009-11-07 17:09:09 +0000
committerHonza Král <honza.kral@gmail.com>2009-11-07 17:09:09 +0000
commit30ea350dabad28b0e524feabce434d446e013d6f (patch)
tree880b6b01d6cbf9423943025f77d8b4ad0b460e83 /tests
parentdfe495fbe8e360ee3b3cd8b29e55ee19d86fc9d2 (diff)
[soc2009/model-validation] Merged to trunk at r11724
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@11725 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests')
-rw-r--r--tests/modeltests/invalid_models/models.py1
-rw-r--r--tests/modeltests/lookup/models.py8
-rw-r--r--tests/modeltests/m2m_through/models.py10
-rw-r--r--tests/modeltests/model_package/__init__.py1
-rw-r--r--tests/modeltests/model_package/models/__init__.py3
-rw-r--r--tests/modeltests/model_package/models/article.py10
-rw-r--r--tests/modeltests/model_package/models/publication.py7
-rw-r--r--tests/modeltests/model_package/tests.py34
-rw-r--r--tests/regressiontests/admin_validation/models.py39
-rw-r--r--tests/regressiontests/admin_views/tests.py15
-rw-r--r--tests/regressiontests/cache/models.py11
-rw-r--r--tests/regressiontests/cache/tests.py42
-rw-r--r--tests/regressiontests/comment_tests/tests/moderation_view_tests.py47
-rw-r--r--tests/regressiontests/comment_tests/urls_admin.py13
-rw-r--r--tests/regressiontests/context_processors/fixtures/context-processors-users.xml17
-rw-r--r--tests/regressiontests/context_processors/templates/context_processors/auth_attrs_access.html1
-rw-r--r--tests/regressiontests/context_processors/templates/context_processors/auth_attrs_messages.html1
-rw-r--r--tests/regressiontests/context_processors/templates/context_processors/auth_attrs_no_access.html1
-rw-r--r--tests/regressiontests/context_processors/templates/context_processors/auth_attrs_perms.html1
-rw-r--r--tests/regressiontests/context_processors/templates/context_processors/auth_attrs_test_access.html1
-rw-r--r--tests/regressiontests/context_processors/templates/context_processors/auth_attrs_user.html4
-rw-r--r--tests/regressiontests/context_processors/tests.py76
-rw-r--r--tests/regressiontests/context_processors/urls.py6
-rw-r--r--tests/regressiontests/context_processors/views.py29
-rw-r--r--tests/regressiontests/csrf_tests/__init__.py0
-rw-r--r--tests/regressiontests/csrf_tests/models.py1
-rw-r--r--tests/regressiontests/csrf_tests/tests.py324
-rw-r--r--tests/regressiontests/dateformat/tests.py166
-rw-r--r--tests/regressiontests/m2m_through_regress/models.py8
-rw-r--r--tests/regressiontests/mail/custombackend.py15
-rw-r--r--tests/regressiontests/mail/tests.py234
-rw-r--r--tests/regressiontests/model_formsets_regress/tests.py10
-rw-r--r--tests/regressiontests/model_inheritance_regress/models.py66
-rw-r--r--tests/regressiontests/serializers_regress/models.py8
-rw-r--r--tests/regressiontests/signals_regress/__init__.py0
-rw-r--r--tests/regressiontests/signals_regress/models.py89
-rw-r--r--tests/regressiontests/test_client_regress/models.py17
-rw-r--r--tests/regressiontests/utils/dateformat.py73
-rw-r--r--tests/regressiontests/utils/tests.py78
-rw-r--r--tests/regressiontests/views/tests/generic/date_based.py4
40 files changed, 1302 insertions, 169 deletions
diff --git a/tests/modeltests/invalid_models/models.py b/tests/modeltests/invalid_models/models.py
index c033d31237..af199635e6 100644
--- a/tests/modeltests/invalid_models/models.py
+++ b/tests/modeltests/invalid_models/models.py
@@ -182,6 +182,7 @@ class UniqueM2M(models.Model):
""" Model to test for unique ManyToManyFields, which are invalid. """
unique_people = models.ManyToManyField( Person, unique=True )
+
model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "max_length" attribute.
invalid_models.fielderrors: "decimalfield": DecimalFields require a "decimal_places" attribute.
invalid_models.fielderrors: "decimalfield": DecimalFields require a "max_digits" attribute.
diff --git a/tests/modeltests/lookup/models.py b/tests/modeltests/lookup/models.py
index 11e2b079f0..94c16ff071 100644
--- a/tests/modeltests/lookup/models.py
+++ b/tests/modeltests/lookup/models.py
@@ -17,6 +17,10 @@ class Article(models.Model):
return self.headline
__test__ = {'API_TESTS': r"""
+# We can use .exists() to check that there are none yet
+>>> Article.objects.exists()
+False
+
# Create a couple of Articles.
>>> from datetime import datetime
>>> a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26))
@@ -33,6 +37,10 @@ __test__ = {'API_TESTS': r"""
>>> a6.save()
>>> a7 = Article(headline='Article 7', pub_date=datetime(2005, 7, 27))
>>> a7.save()
+
+# There should be some now!
+>>> Article.objects.exists()
+True
"""}
if settings.DATABASE_ENGINE in ('postgresql', 'postgresql_pysycopg2'):
diff --git a/tests/modeltests/m2m_through/models.py b/tests/modeltests/m2m_through/models.py
index 10aa163343..16f303d02e 100644
--- a/tests/modeltests/m2m_through/models.py
+++ b/tests/modeltests/m2m_through/models.py
@@ -133,7 +133,7 @@ AttributeError: 'ManyRelatedManager' object has no attribute 'add'
>>> rock.members.create(name='Anne')
Traceback (most recent call last):
...
-AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead.
+AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead.
# Remove has similar complications, and is not provided either.
>>> rock.members.remove(jim)
@@ -160,7 +160,7 @@ AttributeError: 'ManyRelatedManager' object has no attribute 'remove'
>>> rock.members = backup
Traceback (most recent call last):
...
-AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead.
+AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead.
# Let's re-save those instances that we've cleared.
>>> m1.save()
@@ -184,7 +184,7 @@ AttributeError: 'ManyRelatedManager' object has no attribute 'add'
>>> bob.group_set.create(name='Funk')
Traceback (most recent call last):
...
-AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead.
+AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead.
# Remove has similar complications, and is not provided either.
>>> jim.group_set.remove(rock)
@@ -209,7 +209,7 @@ AttributeError: 'ManyRelatedManager' object has no attribute 'remove'
>>> jim.group_set = backup
Traceback (most recent call last):
...
-AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead.
+AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead.
# Let's re-save those instances that we've cleared.
>>> m1.save()
@@ -334,4 +334,4 @@ AttributeError: Cannot set values on a ManyToManyField which specifies an interm
# QuerySet's distinct() method can correct this problem.
>>> Person.objects.filter(membership__date_joined__gt=datetime(2004, 1, 1)).distinct()
[<Person: Jane>, <Person: Jim>]
-"""} \ No newline at end of file
+"""}
diff --git a/tests/modeltests/model_package/__init__.py b/tests/modeltests/model_package/__init__.py
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/tests/modeltests/model_package/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tests/modeltests/model_package/models/__init__.py b/tests/modeltests/model_package/models/__init__.py
new file mode 100644
index 0000000000..91e1b02e7d
--- /dev/null
+++ b/tests/modeltests/model_package/models/__init__.py
@@ -0,0 +1,3 @@
+# Import all the models from subpackages
+from article import Article
+from publication import Publication
diff --git a/tests/modeltests/model_package/models/article.py b/tests/modeltests/model_package/models/article.py
new file mode 100644
index 0000000000..c8fae1c72d
--- /dev/null
+++ b/tests/modeltests/model_package/models/article.py
@@ -0,0 +1,10 @@
+from django.db import models
+from django.contrib.sites.models import Site
+
+class Article(models.Model):
+ sites = models.ManyToManyField(Site)
+ headline = models.CharField(max_length=100)
+ publications = models.ManyToManyField("model_package.Publication", null=True, blank=True,)
+
+ class Meta:
+ app_label = 'model_package'
diff --git a/tests/modeltests/model_package/models/publication.py b/tests/modeltests/model_package/models/publication.py
new file mode 100644
index 0000000000..4dc2d6a148
--- /dev/null
+++ b/tests/modeltests/model_package/models/publication.py
@@ -0,0 +1,7 @@
+from django.db import models
+
+class Publication(models.Model):
+ title = models.CharField(max_length=30)
+
+ class Meta:
+ app_label = 'model_package'
diff --git a/tests/modeltests/model_package/tests.py b/tests/modeltests/model_package/tests.py
new file mode 100644
index 0000000000..6e8c158a68
--- /dev/null
+++ b/tests/modeltests/model_package/tests.py
@@ -0,0 +1,34 @@
+"""
+>>> from models.publication import Publication
+>>> from models.article import Article
+>>> from django.contrib.auth.views import Site
+
+>>> p = Publication(title="FooBar")
+>>> p.save()
+>>> p
+<Publication: Publication object>
+
+>>> from django.contrib.sites.models import Site
+>>> current_site = Site.objects.get_current()
+>>> current_site
+<Site: example.com>
+
+# Regression for #12168: models split into subpackages still get M2M tables
+
+>>> a = Article(headline="a foo headline")
+>>> a.save()
+>>> a.publications.add(p)
+>>> a.sites.add(current_site)
+>>> a.save()
+
+>>> a = Article.objects.get(id=1)
+>>> a
+<Article: Article object>
+>>> a.id
+1
+>>> a.sites.count()
+1
+
+"""
+
+
diff --git a/tests/regressiontests/admin_validation/models.py b/tests/regressiontests/admin_validation/models.py
index 1ff89e2502..5506114841 100644
--- a/tests/regressiontests/admin_validation/models.py
+++ b/tests/regressiontests/admin_validation/models.py
@@ -4,9 +4,11 @@ Tests of ModelAdmin validation logic.
from django.db import models
+
class Album(models.Model):
title = models.CharField(max_length=150)
+
class Song(models.Model):
title = models.CharField(max_length=150)
album = models.ForeignKey(Album)
@@ -17,11 +19,19 @@ class Song(models.Model):
def __unicode__(self):
return self.title
+
+class TwoAlbumFKAndAnE(models.Model):
+ album1 = models.ForeignKey(Album, related_name="album1_set")
+ album2 = models.ForeignKey(Album, related_name="album2_set")
+ e = models.CharField(max_length=1)
+
+
+
__test__ = {'API_TESTS':"""
>>> from django import forms
>>> from django.contrib import admin
->>> from django.contrib.admin.validation import validate
+>>> from django.contrib.admin.validation import validate, validate_inline
# Regression test for #8027: custom ModelForms with fields/fieldsets
@@ -58,4 +68,31 @@ Traceback (most recent call last):
...
ImproperlyConfigured: SongInline cannot exclude the field 'album' - this is the foreign key to the parent model Album.
+# Regression test for #11709 - when testing for fk excluding (when exclude is
+# given) make sure fk_name is honored or things blow up when there is more
+# than one fk to the parent model.
+
+>>> class TwoAlbumFKAndAnEInline(admin.TabularInline):
+... model = TwoAlbumFKAndAnE
+... exclude = ("e",)
+... fk_name = "album1"
+
+>>> validate_inline(TwoAlbumFKAndAnEInline, None, Album)
+
+# Ensure inlines validate that they can be used correctly.
+
+>>> class TwoAlbumFKAndAnEInline(admin.TabularInline):
+... model = TwoAlbumFKAndAnE
+
+>>> validate_inline(TwoAlbumFKAndAnEInline, None, Album)
+Traceback (most recent call last):
+ ...
+Exception: <class 'regressiontests.admin_validation.models.TwoAlbumFKAndAnE'> has more than 1 ForeignKey to <class 'regressiontests.admin_validation.models.Album'>
+
+>>> class TwoAlbumFKAndAnEInline(admin.TabularInline):
+... model = TwoAlbumFKAndAnE
+... fk_name = "album1"
+
+>>> validate_inline(TwoAlbumFKAndAnEInline, None, Album)
+
"""}
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
index 7273d3f320..8607589289 100644
--- a/tests/regressiontests/admin_views/tests.py
+++ b/tests/regressiontests/admin_views/tests.py
@@ -885,8 +885,9 @@ class AdminViewListEditable(TestCase):
# 4 action inputs (3 regular checkboxes, 1 checkbox to select all)
# main form submit button = 1
# search field and search submit button = 2
- # 6 + 2 + 1 + 2 = 11 inputs
- self.failUnlessEqual(response.content.count("<input"), 15)
+ # CSRF field = 1
+ # 6 + 2 + 4 + 1 + 2 + 1 = 16 inputs
+ self.failUnlessEqual(response.content.count("<input"), 16)
# 1 select per object = 3 selects
self.failUnlessEqual(response.content.count("<select"), 4)
@@ -1140,6 +1141,16 @@ class AdminActionsTest(TestCase):
'<input type="checkbox" class="action-select"' not in response.content,
"Found an unexpected action toggle checkboxbox in response"
)
+ self.assert_('action-checkbox-column' not in response.content,
+ "Found unexpected action-checkbox-column class in response")
+
+ def test_action_column_class(self):
+ "Tests that the checkbox column class is present in the response"
+ response = self.client.get('/test_admin/admin/admin_views/subscriber/')
+ self.assertNotEquals(response.context["action_form"], None)
+ self.assert_('action-checkbox-column' in response.content,
+ "Expected an action-checkbox-column in response")
+
def test_multiple_actions_form(self):
"""
diff --git a/tests/regressiontests/cache/models.py b/tests/regressiontests/cache/models.py
index e69de29bb2..643fd22629 100644
--- a/tests/regressiontests/cache/models.py
+++ b/tests/regressiontests/cache/models.py
@@ -0,0 +1,11 @@
+from django.db import models
+from datetime import datetime
+
+def expensive_calculation():
+ expensive_calculation.num_runs += 1
+ return datetime.now()
+
+class Poll(models.Model):
+ question = models.CharField(max_length=200)
+ answer = models.CharField(max_length=200)
+ pub_date = models.DateTimeField('date published', default=expensive_calculation)
diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py
index 1b54ab935f..593b7262aa 100644
--- a/tests/regressiontests/cache/tests.py
+++ b/tests/regressiontests/cache/tests.py
@@ -16,6 +16,7 @@ from django.core.cache.backends.base import InvalidCacheBackendError
from django.http import HttpResponse, HttpRequest
from django.utils.cache import patch_vary_headers, get_cache_key, learn_cache_key
from django.utils.hashcompat import md5_constructor
+from regressiontests.cache.models import Poll, expensive_calculation
# functions/classes for complex data type tests
def f():
@@ -211,6 +212,47 @@ class BaseCacheTests(object):
self.cache.set("stuff", stuff)
self.assertEqual(self.cache.get("stuff"), stuff)
+ def test_cache_read_for_model_instance(self):
+ # Don't want fields with callable as default to be called on cache read
+ expensive_calculation.num_runs = 0
+ Poll.objects.all().delete()
+ my_poll = Poll.objects.create(question="Well?")
+ self.assertEqual(Poll.objects.count(), 1)
+ pub_date = my_poll.pub_date
+ self.cache.set('question', my_poll)
+ cached_poll = self.cache.get('question')
+ self.assertEqual(cached_poll.pub_date, pub_date)
+ # We only want the default expensive calculation run once
+ self.assertEqual(expensive_calculation.num_runs, 1)
+
+ def test_cache_write_for_model_instance_with_deferred(self):
+ # Don't want fields with callable as default to be called on cache write
+ expensive_calculation.num_runs = 0
+ Poll.objects.all().delete()
+ my_poll = Poll.objects.create(question="What?")
+ self.assertEqual(expensive_calculation.num_runs, 1)
+ defer_qs = Poll.objects.all().defer('question')
+ self.assertEqual(defer_qs.count(), 1)
+ self.assertEqual(expensive_calculation.num_runs, 1)
+ self.cache.set('deferred_queryset', defer_qs)
+ # cache set should not re-evaluate default functions
+ self.assertEqual(expensive_calculation.num_runs, 1)
+
+ def test_cache_read_for_model_instance_with_deferred(self):
+ # Don't want fields with callable as default to be called on cache read
+ expensive_calculation.num_runs = 0
+ Poll.objects.all().delete()
+ my_poll = Poll.objects.create(question="What?")
+ self.assertEqual(expensive_calculation.num_runs, 1)
+ defer_qs = Poll.objects.all().defer('question')
+ self.assertEqual(defer_qs.count(), 1)
+ self.cache.set('deferred_queryset', defer_qs)
+ self.assertEqual(expensive_calculation.num_runs, 1)
+ runs_before_cache_read = expensive_calculation.num_runs
+ cached_polls = self.cache.get('deferred_queryset')
+ # We only want the default expensive calculation run on creation and set
+ self.assertEqual(expensive_calculation.num_runs, runs_before_cache_read)
+
def test_expiration(self):
# Cache values can be set to expire
self.cache.set('expire1', 'very quickly', 1)
diff --git a/tests/regressiontests/comment_tests/tests/moderation_view_tests.py b/tests/regressiontests/comment_tests/tests/moderation_view_tests.py
index b9eadd78b4..c9b50e2983 100644
--- a/tests/regressiontests/comment_tests/tests/moderation_view_tests.py
+++ b/tests/regressiontests/comment_tests/tests/moderation_view_tests.py
@@ -159,31 +159,32 @@ class ApproveViewTests(CommentTestCase):
response = self.client.get("/approved/", data={"c":pk})
self.assertTemplateUsed(response, "comments/approved.html")
+class AdminActionsTests(CommentTestCase):
+ urls = "regressiontests.comment_tests.urls_admin"
+
+ def setUp(self):
+ super(AdminActionsTests, self).setUp()
+
+ # Make "normaluser" a moderator
+ u = User.objects.get(username="normaluser")
+ u.is_staff = True
+ perms = Permission.objects.filter(
+ content_type__app_label = 'comments',
+ codename__endswith = 'comment'
+ )
+ for perm in perms:
+ u.user_permissions.add(perm)
+ u.save()
-class ModerationQueueTests(CommentTestCase):
-
- def testModerationQueuePermissions(self):
- """Only moderators can view the moderation queue"""
+ def testActionsNonModerator(self):
+ comments = self.createSomeComments()
self.client.login(username="normaluser", password="normaluser")
- response = self.client.get("/moderate/")
- self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/moderate/")
-
- makeModerator("normaluser")
- response = self.client.get("/moderate/")
- self.assertEqual(response.status_code, 200)
+ response = self.client.get("/admin/comments/comment/")
+ self.assertEquals("approve_comments" in response.content, False)
- def testModerationQueueContents(self):
- """Moderation queue should display non-public, non-removed comments."""
- c1, c2, c3, c4 = self.createSomeComments()
+ def testActionsModerator(self):
+ comments = self.createSomeComments()
makeModerator("normaluser")
self.client.login(username="normaluser", password="normaluser")
-
- c1.is_public = c2.is_public = False
- c1.save(); c2.save()
- response = self.client.get("/moderate/")
- self.assertEqual(list(response.context[0]["comments"]), [c1, c2])
-
- c2.is_removed = True
- c2.save()
- response = self.client.get("/moderate/")
- self.assertEqual(list(response.context[0]["comments"]), [c1])
+ response = self.client.get("/admin/comments/comment/")
+ self.assertEquals("approve_comments" in response.content, True)
diff --git a/tests/regressiontests/comment_tests/urls_admin.py b/tests/regressiontests/comment_tests/urls_admin.py
new file mode 100644
index 0000000000..341285d7ef
--- /dev/null
+++ b/tests/regressiontests/comment_tests/urls_admin.py
@@ -0,0 +1,13 @@
+from django.conf.urls.defaults import *
+from django.contrib import admin
+from django.contrib.comments.admin import CommentsAdmin
+from django.contrib.comments.models import Comment
+
+# Make a new AdminSite to avoid picking up the deliberately broken admin
+# modules in other tests.
+admin_site = admin.AdminSite()
+admin_site.register(Comment, CommentsAdmin)
+
+urlpatterns = patterns('',
+ (r'^admin/', include(admin_site.urls)),
+)
diff --git a/tests/regressiontests/context_processors/fixtures/context-processors-users.xml b/tests/regressiontests/context_processors/fixtures/context-processors-users.xml
new file mode 100644
index 0000000000..aba8f4aace
--- /dev/null
+++ b/tests/regressiontests/context_processors/fixtures/context-processors-users.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+ <object pk="100" model="auth.user">
+ <field type="CharField" name="username">super</field>
+ <field type="CharField" name="first_name">Super</field>
+ <field type="CharField" name="last_name">User</field>
+ <field type="CharField" name="email">super@example.com</field>
+ <field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
+ <field type="BooleanField" name="is_staff">True</field>
+ <field type="BooleanField" name="is_active">True</field>
+ <field type="BooleanField" name="is_superuser">True</field>
+ <field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
+ <field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
+ <field to="auth.group" name="groups" rel="ManyToManyRel"></field>
+ <field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
+ </object>
+</django-objects>
diff --git a/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_access.html b/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_access.html
new file mode 100644
index 0000000000..b5c65db28d
--- /dev/null
+++ b/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_access.html
@@ -0,0 +1 @@
+{{ user }}
diff --git a/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_messages.html b/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_messages.html
new file mode 100644
index 0000000000..7b7e448ad2
--- /dev/null
+++ b/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_messages.html
@@ -0,0 +1 @@
+{% for m in messages %}{{ m }}{% endfor %}
diff --git a/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_no_access.html b/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_no_access.html
new file mode 100644
index 0000000000..8d1c8b69c3
--- /dev/null
+++ b/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_no_access.html
@@ -0,0 +1 @@
+
diff --git a/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_perms.html b/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_perms.html
new file mode 100644
index 0000000000..a5db868e9e
--- /dev/null
+++ b/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_perms.html
@@ -0,0 +1 @@
+{% if perms.auth %}Has auth permissions{% endif %}
diff --git a/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_test_access.html b/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_test_access.html
new file mode 100644
index 0000000000..a28ff937f8
--- /dev/null
+++ b/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_test_access.html
@@ -0,0 +1 @@
+{% if session_accessed %}Session accessed{% else %}Session not accessed{% endif %}
diff --git a/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_user.html b/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_user.html
new file mode 100644
index 0000000000..7ed16d7867
--- /dev/null
+++ b/tests/regressiontests/context_processors/templates/context_processors/auth_attrs_user.html
@@ -0,0 +1,4 @@
+unicode: {{ user }}
+id: {{ user.id }}
+username: {{ user.username }}
+url: {% url userpage user %}
diff --git a/tests/regressiontests/context_processors/tests.py b/tests/regressiontests/context_processors/tests.py
index eadd6310b1..004f90eceb 100644
--- a/tests/regressiontests/context_processors/tests.py
+++ b/tests/regressiontests/context_processors/tests.py
@@ -3,8 +3,10 @@ Tests for Django's bundled context processors.
"""
from django.conf import settings
+from django.contrib.auth import authenticate
+from django.db.models import Q
from django.test import TestCase
-
+from django.template import Template
class RequestContextProcessorTests(TestCase):
"""
@@ -36,3 +38,75 @@ class RequestContextProcessorTests(TestCase):
self.assertContains(response, url)
response = self.client.post(url, {'path': '/blah/'})
self.assertContains(response, url)
+
+class AuthContextProcessorTests(TestCase):
+ """
+ Tests for the ``django.core.context_processors.auth`` processor
+ """
+ urls = 'regressiontests.context_processors.urls'
+ fixtures = ['context-processors-users.xml']
+
+ def test_session_not_accessed(self):
+ """
+ Tests that the session is not accessed simply by including
+ the auth context processor
+ """
+ response = self.client.get('/auth_processor_no_attr_access/')
+ self.assertContains(response, "Session not accessed")
+
+ def test_session_is_accessed(self):
+ """
+ Tests that the session is accessed if the auth context processor
+ is used and relevant attributes accessed.
+ """
+ response = self.client.get('/auth_processor_attr_access/')
+ self.assertContains(response, "Session accessed")
+
+ def test_perms_attrs(self):
+ self.client.login(username='super', password='secret')
+ response = self.client.get('/auth_processor_perms/')
+ self.assertContains(response, "Has auth permissions")
+
+ def test_message_attrs(self):
+ self.client.login(username='super', password='secret')
+ response = self.client.get('/auth_processor_messages/')
+ self.assertContains(response, "Message 1")
+
+ def test_user_attrs(self):
+ """
+ Test that the lazy objects returned behave just like the wrapped objects.
+ """
+ # These are 'functional' level tests for common use cases. Direct
+ # testing of the implementation (SimpleLazyObject) is in the 'utils'
+ # tests.
+ self.client.login(username='super', password='secret')
+ user = authenticate(username='super', password='secret')
+ response = self.client.get('/auth_processor_user/')
+ self.assertContains(response, "unicode: super")
+ self.assertContains(response, "id: 100")
+ self.assertContains(response, "username: super")
+ # bug #12037 is tested by the {% url %} in the template:
+ self.assertContains(response, "url: /userpage/super/")
+
+ # See if this object can be used for queries where a Q() comparing
+ # a user can be used with another Q() (in an AND or OR fashion).
+ # This simulates what a template tag might do with the user from the
+ # context. Note that we don't need to execute a query, just build it.
+ #
+ # The failure case (bug #12049) on Python 2.4 with a LazyObject-wrapped
+ # User is a fatal TypeError: "function() takes at least 2 arguments
+ # (0 given)" deep inside deepcopy().
+ #
+ # Python 2.5 and 2.6 succeeded, but logged internally caught exception
+ # spew:
+ #
+ # Exception RuntimeError: 'maximum recursion depth exceeded while
+ # calling a Python object' in <type 'exceptions.AttributeError'>
+ # ignored"
+ query = Q(user=response.context['user']) & Q(someflag=True)
+
+ # Tests for user equality. This is hard because User defines
+ # equality in a non-duck-typing way
+ # See bug #12060
+ self.assertEqual(response.context['user'], user)
+ self.assertEqual(user, response.context['user'])
diff --git a/tests/regressiontests/context_processors/urls.py b/tests/regressiontests/context_processors/urls.py
index 7e8ba967c1..30728c8df8 100644
--- a/tests/regressiontests/context_processors/urls.py
+++ b/tests/regressiontests/context_processors/urls.py
@@ -5,4 +5,10 @@ import views
urlpatterns = patterns('',
(r'^request_attrs/$', views.request_processor),
+ (r'^auth_processor_no_attr_access/$', views.auth_processor_no_attr_access),
+ (r'^auth_processor_attr_access/$', views.auth_processor_attr_access),
+ (r'^auth_processor_user/$', views.auth_processor_user),
+ (r'^auth_processor_perms/$', views.auth_processor_perms),
+ (r'^auth_processor_messages/$', views.auth_processor_messages),
+ url(r'^userpage/(.+)/$', views.userpage, name="userpage"),
)
diff --git a/tests/regressiontests/context_processors/views.py b/tests/regressiontests/context_processors/views.py
index 66e7132c05..3f2dcb037b 100644
--- a/tests/regressiontests/context_processors/views.py
+++ b/tests/regressiontests/context_processors/views.py
@@ -6,3 +6,32 @@ from django.template.context import RequestContext
def request_processor(request):
return render_to_response('context_processors/request_attrs.html',
RequestContext(request, {}, processors=[context_processors.request]))
+
+def auth_processor_no_attr_access(request):
+ r1 = render_to_response('context_processors/auth_attrs_no_access.html',
+ RequestContext(request, {}, processors=[context_processors.auth]))
+ # *After* rendering, we check whether the session was accessed
+ return render_to_response('context_processors/auth_attrs_test_access.html',
+ {'session_accessed':request.session.accessed})
+
+def auth_processor_attr_access(request):
+ r1 = render_to_response('context_processors/auth_attrs_access.html',
+ RequestContext(request, {}, processors=[context_processors.auth]))
+ return render_to_response('context_processors/auth_attrs_test_access.html',
+ {'session_accessed':request.session.accessed})
+
+def auth_processor_user(request):
+ return render_to_response('context_processors/auth_attrs_user.html',
+ RequestContext(request, {}, processors=[context_processors.auth]))
+
+def auth_processor_perms(request):
+ return render_to_response('context_processors/auth_attrs_perms.html',
+ RequestContext(request, {}, processors=[context_processors.auth]))
+
+def auth_processor_messages(request):
+ request.user.message_set.create(message="Message 1")
+ return render_to_response('context_processors/auth_attrs_messages.html',
+ RequestContext(request, {}, processors=[context_processors.auth]))
+
+def userpage(request):
+ pass
diff --git a/tests/regressiontests/csrf_tests/__init__.py b/tests/regressiontests/csrf_tests/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/csrf_tests/__init__.py
diff --git a/tests/regressiontests/csrf_tests/models.py b/tests/regressiontests/csrf_tests/models.py
new file mode 100644
index 0000000000..71abcc5198
--- /dev/null
+++ b/tests/regressiontests/csrf_tests/models.py
@@ -0,0 +1 @@
+# models.py file for tests to run.
diff --git a/tests/regressiontests/csrf_tests/tests.py b/tests/regressiontests/csrf_tests/tests.py
new file mode 100644
index 0000000000..5688293647
--- /dev/null
+++ b/tests/regressiontests/csrf_tests/tests.py
@@ -0,0 +1,324 @@
+# -*- coding: utf-8 -*-
+
+from django.test import TestCase
+from django.http import HttpRequest, HttpResponse
+from django.middleware.csrf import CsrfMiddleware, CsrfViewMiddleware
+from django.views.decorators.csrf import csrf_exempt
+from django.core.context_processors import csrf
+from django.contrib.sessions.middleware import SessionMiddleware
+from django.utils.importlib import import_module
+from django.conf import settings
+from django.template import RequestContext, Template
+
+# Response/views used for CsrfResponseMiddleware and CsrfViewMiddleware tests
+def post_form_response():
+ resp = HttpResponse(content="""
+<html><body><form method="POST"><input type="text" /></form></body></html>
+""", mimetype="text/html")
+ return resp
+
+def post_form_response_non_html():
+ resp = post_form_response()
+ resp["Content-Type"] = "application/xml"
+ return resp
+
+def post_form_view(request):
+ """A view that returns a POST form (without a token)"""
+ return post_form_response()
+
+# Response/views used for template tag tests
+def _token_template():
+ return Template("{% csrf_token %}")
+
+def _render_csrf_token_template(req):
+ context = RequestContext(req, processors=[csrf])
+ template = _token_template()
+ return template.render(context)
+
+def token_view(request):
+ """A view that uses {% csrf_token %}"""
+ return HttpResponse(_render_csrf_token_template(request))
+
+def non_token_view_using_request_processor(request):
+ """
+ A view that doesn't use the token, but does use the csrf view processor.
+ """
+ context = RequestContext(request, processors=[csrf])
+ template = Template("")
+ return HttpResponse(template.render(context))
+
+class TestingHttpRequest(HttpRequest):
+ """
+ A version of HttpRequest that allows us to change some things
+ more easily
+ """
+ def is_secure(self):
+ return getattr(self, '_is_secure', False)
+
+class CsrfMiddlewareTest(TestCase):
+ _csrf_id = "1"
+
+ # This is a valid session token for this ID and secret key. This was generated using
+ # the old code that we're to be backwards-compatible with. Don't use the CSRF code
+ # to generate this hash, or we're merely testing the code against itself and not
+ # checking backwards-compatibility. This is also the output of (echo -n test1 | md5sum).
+ _session_token = "5a105e8b9d40e1329780d62ea2265d8a"
+ _session_id = "1"
+ _secret_key_for_session_test= "test"
+
+ def _get_GET_no_csrf_cookie_request(self):
+ return TestingHttpRequest()
+
+ def _get_GET_csrf_cookie_request(self):
+ req = TestingHttpRequest()
+ req.COOKIES[settings.CSRF_COOKIE_NAME] = self._csrf_id
+ return req
+
+ def _get_POST_csrf_cookie_request(self):
+ req = self._get_GET_csrf_cookie_request()
+ req.method = "POST"
+ return req
+
+ def _get_POST_no_csrf_cookie_request(self):
+ req = self._get_GET_no_csrf_cookie_request()
+ req.method = "POST"
+ return req
+
+ def _get_POST_request_with_token(self):
+ req = self._get_POST_csrf_cookie_request()
+ req.POST['csrfmiddlewaretoken'] = self._csrf_id
+ return req
+
+ def _get_POST_session_request_with_token(self):
+ req = self._get_POST_no_csrf_cookie_request()
+ req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id
+ req.POST['csrfmiddlewaretoken'] = self._session_token
+ return req
+
+ def _get_POST_session_request_no_token(self):
+ req = self._get_POST_no_csrf_cookie_request()
+ req.COOKIES[settings.SESSION_COOKIE_NAME] = self._session_id
+ return req
+
+ def _check_token_present(self, response, csrf_id=None):
+ self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % (csrf_id or self._csrf_id))
+
+ # Check the post processing and outgoing cookie
+ def test_process_response_no_csrf_cookie(self):
+ """
+ When no prior CSRF cookie exists, check that the cookie is created and a
+ token is inserted.
+ """
+ req = self._get_GET_no_csrf_cookie_request()
+ CsrfMiddleware().process_view(req, post_form_view, (), {})
+
+ resp = post_form_response()
+ resp_content = resp.content # needed because process_response modifies resp
+ resp2 = CsrfMiddleware().process_response(req, resp)
+
+ csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
+ self.assertNotEqual(csrf_cookie, False)
+ self.assertNotEqual(resp_content, resp2.content)
+ self._check_token_present(resp2, csrf_cookie.value)
+ # Check the Vary header got patched correctly
+ self.assert_('Cookie' in resp2.get('Vary',''))
+
+ def test_process_response_no_csrf_cookie_view_only_get_token_used(self):
+ """
+ When no prior CSRF cookie exists, check that the cookie is created, even
+ if only CsrfViewMiddleware is used.
+ """
+ # This is checking that CsrfViewMiddleware has the cookie setting
+ # code. Most of the other tests use CsrfMiddleware.
+ req = self._get_GET_no_csrf_cookie_request()
+ # token_view calls get_token() indirectly
+ CsrfViewMiddleware().process_view(req, token_view, (), {})
+ resp = token_view(req)
+ resp2 = CsrfViewMiddleware().process_response(req, resp)
+
+ csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
+ self.assertNotEqual(csrf_cookie, False)
+
+ def test_process_response_get_token_not_used(self):
+ """
+ Check that if get_token() is not called, the view middleware does not
+ add a cookie.
+ """
+ # This is important to make pages cacheable. Pages which do call
+ # get_token(), assuming they use the token, are not cacheable because
+ # the token is specific to the user
+ req = self._get_GET_no_csrf_cookie_request()
+ # non_token_view_using_request_processor does not call get_token(), but
+ # does use the csrf request processor. By using this, we are testing
+ # that the view processor is properly lazy and doesn't call get_token()
+ # until needed.
+ CsrfViewMiddleware().process_view(req, non_token_view_using_request_processor, (), {})
+ resp = non_token_view_using_request_processor(req)
+ resp2 = CsrfViewMiddleware().process_response(req, resp)
+
+ csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
+ self.assertEqual(csrf_cookie, False)
+
+ def test_process_response_existing_csrf_cookie(self):
+ """
+ Check that the token is inserted when a prior CSRF cookie exists
+ """
+ req = self._get_GET_csrf_cookie_request()
+ CsrfMiddleware().process_view(req, post_form_view, (), {})
+
+ resp = post_form_response()
+ resp_content = resp.content # needed because process_response modifies resp
+ resp2 = CsrfMiddleware().process_response(req, resp)
+ self.assertNotEqual(resp_content, resp2.content)
+ self._check_token_present(resp2)
+
+ def test_process_response_non_html(self):
+ """
+ Check the the post-processor does nothing for content-types not in _HTML_TYPES.
+ """
+ req = self._get_GET_no_csrf_cookie_request()
+ CsrfMiddleware().process_view(req, post_form_view, (), {})
+ resp = post_form_response_non_html()
+ resp_content = resp.content # needed because process_response modifies resp
+ resp2 = CsrfMiddleware().process_response(req, resp)
+ self.assertEquals(resp_content, resp2.content)
+
+ def test_process_response_exempt_view(self):
+ """
+ Check that no post processing is done for an exempt view
+ """
+ req = self._get_POST_csrf_cookie_request()
+ resp = csrf_exempt(post_form_view)(req)
+ resp_content = resp.content
+ resp2 = CsrfMiddleware().process_response(req, resp)
+ self.assertEquals(resp_content, resp2.content)
+
+ # Check the request processing
+ def test_process_request_no_session_no_csrf_cookie(self):
+ """
+ Check that if neither a CSRF cookie nor a session cookie are present,
+ the middleware rejects the incoming request. This will stop login CSRF.
+ """
+ req = self._get_POST_no_csrf_cookie_request()
+ req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEquals(403, req2.status_code)
+
+ def test_process_request_csrf_cookie_no_token(self):
+ """
+ Check that if a CSRF cookie is present but no token, the middleware
+ rejects the incoming request.
+ """
+ req = self._get_POST_csrf_cookie_request()
+ req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEquals(403, req2.status_code)
+
+ def test_process_request_csrf_cookie_and_token(self):
+ """
+ Check that if both a cookie and a token is present, the middleware lets it through.
+ """
+ req = self._get_POST_request_with_token()
+ req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEquals(None, req2)
+
+ def test_process_request_session_cookie_no_csrf_cookie_token(self):
+ """
+ When no CSRF cookie exists, but the user has a session, check that a token
+ using the session cookie as a legacy CSRF cookie is accepted.
+ """
+ orig_secret_key = settings.SECRET_KEY
+ settings.SECRET_KEY = self._secret_key_for_session_test
+ try:
+ req = self._get_POST_session_request_with_token()
+ req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEquals(None, req2)
+ finally:
+ settings.SECRET_KEY = orig_secret_key
+
+ def test_process_request_session_cookie_no_csrf_cookie_no_token(self):
+ """
+ Check that if a session cookie is present but no token and no CSRF cookie,
+ the request is rejected.
+ """
+ req = self._get_POST_session_request_no_token()
+ req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEquals(403, req2.status_code)
+
+ def test_process_request_csrf_cookie_no_token_exempt_view(self):
+ """
+ Check that if a CSRF cookie is present and no token, but the csrf_exempt
+ decorator has been applied to the view, the middleware lets it through
+ """
+ req = self._get_POST_csrf_cookie_request()
+ req2 = CsrfMiddleware().process_view(req, csrf_exempt(post_form_view), (), {})
+ self.assertEquals(None, req2)
+
+ def test_ajax_exemption(self):
+ """
+ Check that AJAX requests are automatically exempted.
+ """
+ req = self._get_POST_csrf_cookie_request()
+ req.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
+ req2 = CsrfMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEquals(None, req2)
+
+ # Tests for the template tag method
+ def test_token_node_no_csrf_cookie(self):
+ """
+ Check that CsrfTokenNode works when no CSRF cookie is set
+ """
+ req = self._get_GET_no_csrf_cookie_request()
+ resp = token_view(req)
+ self.assertEquals(u"", resp.content)
+
+ def test_token_node_with_csrf_cookie(self):
+ """
+ Check that CsrfTokenNode works when a CSRF cookie is set
+ """
+ req = self._get_GET_csrf_cookie_request()
+ CsrfViewMiddleware().process_view(req, token_view, (), {})
+ resp = token_view(req)
+ self._check_token_present(resp)
+
+ def test_token_node_with_new_csrf_cookie(self):
+ """
+ Check that CsrfTokenNode works when a CSRF cookie is created by
+ the middleware (when one was not already present)
+ """
+ req = self._get_GET_no_csrf_cookie_request()
+ CsrfViewMiddleware().process_view(req, token_view, (), {})
+ resp = token_view(req)
+ resp2 = CsrfViewMiddleware().process_response(req, resp)
+ csrf_cookie = resp2.cookies[settings.CSRF_COOKIE_NAME]
+ self._check_token_present(resp, csrf_id=csrf_cookie.value)
+
+ def test_response_middleware_without_view_middleware(self):
+ """
+ Check that CsrfResponseMiddleware finishes without error if the view middleware
+ has not been called, as is the case if a request middleware returns a response.
+ """
+ req = self._get_GET_no_csrf_cookie_request()
+ resp = post_form_view(req)
+ CsrfMiddleware().process_response(req, resp)
+
+ def test_https_bad_referer(self):
+ """
+ Test that a POST HTTPS request with a bad referer is rejected
+ """
+ req = self._get_POST_request_with_token()
+ req._is_secure = True
+ req.META['HTTP_HOST'] = 'www.example.com'
+ req.META['HTTP_REFERER'] = 'https://www.evil.org/somepage'
+ req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertNotEqual(None, req2)
+ self.assertEquals(403, req2.status_code)
+
+ def test_https_good_referer(self):
+ """
+ Test that a POST HTTPS request with a good referer is accepted
+ """
+ req = self._get_POST_request_with_token()
+ req._is_secure = True
+ req.META['HTTP_HOST'] = 'www.example.com'
+ req.META['HTTP_REFERER'] = 'https://www.example.com/somepage'
+ req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+ self.assertEquals(None, req2)
diff --git a/tests/regressiontests/dateformat/tests.py b/tests/regressiontests/dateformat/tests.py
index d649d4789c..bbde45b903 100644
--- a/tests/regressiontests/dateformat/tests.py
+++ b/tests/regressiontests/dateformat/tests.py
@@ -1,90 +1,92 @@
-r"""
->>> format(my_birthday, '')
-u''
->>> format(my_birthday, 'a')
-u'p.m.'
->>> format(my_birthday, 'A')
-u'PM'
->>> format(my_birthday, 'd')
-u'08'
->>> format(my_birthday, 'j')
-u'8'
->>> format(my_birthday, 'l')
-u'Sunday'
->>> format(my_birthday, 'L')
-u'False'
->>> format(my_birthday, 'm')
-u'07'
->>> format(my_birthday, 'M')
-u'Jul'
->>> format(my_birthday, 'b')
-u'jul'
->>> format(my_birthday, 'n')
-u'7'
->>> format(my_birthday, 'N')
-u'July'
->>> no_tz or format(my_birthday, 'O') == '+0100'
-True
->>> format(my_birthday, 'P')
-u'10 p.m.'
->>> no_tz or format(my_birthday, 'r') == 'Sun, 8 Jul 1979 22:00:00 +0100'
-True
->>> format(my_birthday, 's')
-u'00'
->>> format(my_birthday, 'S')
-u'th'
->>> format(my_birthday, 't')
-u'31'
->>> no_tz or format(my_birthday, 'T') == 'CET'
-True
->>> no_tz or format(my_birthday, 'U') == '300315600'
-True
->>> format(my_birthday, 'w')
-u'0'
->>> format(my_birthday, 'W')
-u'27'
->>> format(my_birthday, 'y')
-u'79'
->>> format(my_birthday, 'Y')
-u'1979'
->>> format(my_birthday, 'z')
-u'189'
->>> no_tz or format(my_birthday, 'Z') == '3600'
-True
->>> no_tz or format(summertime, 'I') == '1'
-True
->>> no_tz or format(summertime, 'O') == '+0200'
-True
->>> no_tz or format(wintertime, 'I') == '0'
-True
->>> no_tz or format(wintertime, 'O') == '+0100'
-True
+from django.utils import dateformat, translation
+from unittest import TestCase
+import datetime, os, time
->>> format(my_birthday, r'Y z \C\E\T')
-u'1979 189 CET'
+class DateFormatTests(TestCase):
+ def setUp(self):
+ self.old_TZ = os.environ.get('TZ')
+ os.environ['TZ'] = 'Europe/Copenhagen'
+ translation.activate('en-us')
->>> format(my_birthday, r'jS o\f F')
-u'8th of July'
+ try:
+ # Check if a timezone has been set
+ time.tzset()
+ self.tz_tests = True
+ except AttributeError:
+ # No timezone available. Don't run the tests that require a TZ
+ self.tz_tests = False
->>> format(the_future, r'Y')
-u'2100'
-"""
+ def tearDown(self):
+ if self.old_TZ is None:
+ del os.environ['TZ']
+ else:
+ os.environ['TZ'] = self.old_TZ
-from django.utils import dateformat, translation
-import datetime, os, time
+ # Cleanup - force re-evaluation of TZ environment variable.
+ if self.tz_tests:
+ time.tzset()
+
+ def test_empty_format(self):
+ my_birthday = datetime.datetime(1979, 7, 8, 22, 00)
+
+ self.assertEquals(dateformat.format(my_birthday, ''), u'')
+
+ def test_am_pm(self):
+ my_birthday = datetime.datetime(1979, 7, 8, 22, 00)
+
+ self.assertEquals(dateformat.format(my_birthday, 'a'), u'p.m.')
+
+ def test_date_formats(self):
+ my_birthday = datetime.datetime(1979, 7, 8, 22, 00)
+
+ self.assertEquals(dateformat.format(my_birthday, 'A'), u'PM')
+ self.assertEquals(dateformat.format(my_birthday, 'd'), u'08')
+ self.assertEquals(dateformat.format(my_birthday, 'j'), u'8')
+ self.assertEquals(dateformat.format(my_birthday, 'l'), u'Sunday')
+ self.assertEquals(dateformat.format(my_birthday, 'L'), u'False')
+ self.assertEquals(dateformat.format(my_birthday, 'm'), u'07')
+ self.assertEquals(dateformat.format(my_birthday, 'M'), u'Jul')
+ self.assertEquals(dateformat.format(my_birthday, 'b'), u'jul')
+ self.assertEquals(dateformat.format(my_birthday, 'n'), u'7')
+ self.assertEquals(dateformat.format(my_birthday, 'N'), u'July')
+
+ def test_time_formats(self):
+ my_birthday = datetime.datetime(1979, 7, 8, 22, 00)
+
+ self.assertEquals(dateformat.format(my_birthday, 'P'), u'10 p.m.')
+ self.assertEquals(dateformat.format(my_birthday, 's'), u'00')
+ self.assertEquals(dateformat.format(my_birthday, 'S'), u'th')
+ self.assertEquals(dateformat.format(my_birthday, 't'), u'31')
+ self.assertEquals(dateformat.format(my_birthday, 'w'), u'0')
+ self.assertEquals(dateformat.format(my_birthday, 'W'), u'27')
+ self.assertEquals(dateformat.format(my_birthday, 'y'), u'79')
+ self.assertEquals(dateformat.format(my_birthday, 'Y'), u'1979')
+ self.assertEquals(dateformat.format(my_birthday, 'z'), u'189')
+
+ def test_dateformat(self):
+ my_birthday = datetime.datetime(1979, 7, 8, 22, 00)
+
+ self.assertEquals(dateformat.format(my_birthday, r'Y z \C\E\T'), u'1979 189 CET')
+
+ self.assertEquals(dateformat.format(my_birthday, r'jS o\f F'), u'8th of July')
-format = dateformat.format
-os.environ['TZ'] = 'Europe/Copenhagen'
-translation.activate('en-us')
+ def test_futuredates(self):
+ the_future = datetime.datetime(2100, 10, 25, 0, 00)
+ self.assertEquals(dateformat.format(the_future, r'Y'), u'2100')
-try:
- time.tzset()
- no_tz = False
-except AttributeError:
- no_tz = True
+ def test_timezones(self):
+ my_birthday = datetime.datetime(1979, 7, 8, 22, 00)
+ summertime = datetime.datetime(2005, 10, 30, 1, 00)
+ wintertime = datetime.datetime(2005, 10, 30, 4, 00)
-my_birthday = datetime.datetime(1979, 7, 8, 22, 00)
-summertime = datetime.datetime(2005, 10, 30, 1, 00)
-wintertime = datetime.datetime(2005, 10, 30, 4, 00)
-the_future = datetime.datetime(2100, 10, 25, 0, 00)
+ if self.tz_tests:
+ self.assertEquals(dateformat.format(my_birthday, 'O'), u'+0100')
+ self.assertEquals(dateformat.format(my_birthday, 'r'), u'Sun, 8 Jul 1979 22:00:00 +0100')
+ self.assertEquals(dateformat.format(my_birthday, 'T'), u'CET')
+ self.assertEquals(dateformat.format(my_birthday, 'U'), u'300315600')
+ self.assertEquals(dateformat.format(my_birthday, 'Z'), u'3600')
+ self.assertEquals(dateformat.format(summertime, 'I'), u'1')
+ self.assertEquals(dateformat.format(summertime, 'O'), u'+0200')
+ self.assertEquals(dateformat.format(wintertime, 'I'), u'0')
+ self.assertEquals(dateformat.format(wintertime, 'O'), u'+0100')
diff --git a/tests/regressiontests/m2m_through_regress/models.py b/tests/regressiontests/m2m_through_regress/models.py
index dcf5f8115b..56aecd6975 100644
--- a/tests/regressiontests/m2m_through_regress/models.py
+++ b/tests/regressiontests/m2m_through_regress/models.py
@@ -84,22 +84,22 @@ __test__ = {'API_TESTS':"""
>>> bob.group_set = []
Traceback (most recent call last):
...
-AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead.
+AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use m2m_through_regress.Membership's Manager instead.
>>> roll.members = []
Traceback (most recent call last):
...
-AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead.
+AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use m2m_through_regress.Membership's Manager instead.
>>> rock.members.create(name='Anne')
Traceback (most recent call last):
...
-AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead.
+AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through_regress.Membership's Manager instead.
>>> bob.group_set.create(name='Funk')
Traceback (most recent call last):
...
-AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead.
+AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through_regress.Membership's Manager instead.
# Now test that the intermediate with a relationship outside
# the current app (i.e., UserMembership) workds
diff --git a/tests/regressiontests/mail/custombackend.py b/tests/regressiontests/mail/custombackend.py
new file mode 100644
index 0000000000..6b0e15ad0a
--- /dev/null
+++ b/tests/regressiontests/mail/custombackend.py
@@ -0,0 +1,15 @@
+"""A custom backend for testing."""
+
+from django.core.mail.backends.base import BaseEmailBackend
+
+
+class EmailBackend(BaseEmailBackend):
+
+ def __init__(self, *args, **kwargs):
+ super(EmailBackend, self).__init__(*args, **kwargs)
+ self.test_outbox = []
+
+ def send_messages(self, email_messages):
+ # Messages are stored in a instance variable for testing.
+ self.test_outbox.extend(email_messages)
+ return len(email_messages)
diff --git a/tests/regressiontests/mail/tests.py b/tests/regressiontests/mail/tests.py
index e90d77366f..10d2ae7df3 100644
--- a/tests/regressiontests/mail/tests.py
+++ b/tests/regressiontests/mail/tests.py
@@ -1,10 +1,18 @@
# coding: utf-8
+
r"""
# Tests for the django.core.mail.
+>>> import os
+>>> import shutil
+>>> import tempfile
+>>> from StringIO import StringIO
>>> from django.conf import settings
>>> from django.core import mail
>>> from django.core.mail import EmailMessage, mail_admins, mail_managers, EmailMultiAlternatives
+>>> from django.core.mail import send_mail, send_mass_mail
+>>> from django.core.mail.backends.base import BaseEmailBackend
+>>> from django.core.mail.backends import console, dummy, locmem, filebased, smtp
>>> from django.utils.translation import ugettext_lazy
# Test normal ascii character case:
@@ -85,8 +93,6 @@ BadHeaderError: Header values can't contain newlines (got u'Subject\nInjection T
>>> mail_managers('hi','there')
>>> len(mail.outbox)
1
->>> settings.ADMINS = old_admins
->>> settings.MANAGERS = old_managers
# Make sure we can manually set the From header (#9214)
@@ -95,6 +101,17 @@ BadHeaderError: Header values can't contain newlines (got u'Subject\nInjection T
>>> message['From']
'from@example.com'
+# Regression for #11144 - When a to/from/cc header contains unicode,
+# make sure the email addresses are parsed correctly (especially
+# with regards to commas)
+>>> email = EmailMessage('Subject', 'Content', 'from@example.com', ['"Firstname Sürname" <to@example.com>','other@example.com'])
+>>> email.message()['To']
+'=?utf-8?q?Firstname_S=C3=BCrname?= <to@example.com>, other@example.com'
+
+>>> email = EmailMessage('Subject', 'Content', 'from@example.com', ['"Sürname, Firstname" <to@example.com>','other@example.com'])
+>>> email.message()['To']
+'=?utf-8?q?S=C3=BCrname=2C_Firstname?= <to@example.com>, other@example.com'
+
# Handle attachments within an multipart/alternative mail correctly (#9367)
# (test is not as precise/clear as it could be w.r.t. email tree structure,
# but it's good enough.)
@@ -138,4 +155,217 @@ Content-Disposition: attachment; filename="an attachment.pdf"
JVBERi0xLjQuJS4uLg==
...
+# Make sure that the console backend writes to stdout by default
+>>> connection = console.EmailBackend()
+>>> email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
+>>> connection.send_messages([email])
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: quoted-printable
+Subject: Subject
+From: from@example.com
+To: to@example.com
+Date: ...
+Message-ID: ...
+
+Content
+-------------------------------------------------------------------------------
+1
+
+# Test that the console backend can be pointed at an arbitrary stream
+>>> s = StringIO()
+>>> connection = mail.get_connection('django.core.mail.backends.console', stream=s)
+>>> send_mail('Subject', 'Content', 'from@example.com', ['to@example.com'], connection=connection)
+1
+>>> print s.getvalue()
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: quoted-printable
+Subject: Subject
+From: from@example.com
+To: to@example.com
+Date: ...
+Message-ID: ...
+
+Content
+-------------------------------------------------------------------------------
+
+# Make sure that dummy backends returns correct number of sent messages
+>>> connection = dummy.EmailBackend()
+>>> email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
+>>> connection.send_messages([email, email, email])
+3
+
+# Make sure that locmen backend populates the outbox
+>>> mail.outbox = []
+>>> connection = locmem.EmailBackend()
+>>> email1 = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
+>>> email2 = EmailMessage('Subject 2', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
+>>> connection.send_messages([email1, email2])
+2
+>>> len(mail.outbox)
+2
+>>> mail.outbox[0].subject
+'Subject'
+>>> mail.outbox[1].subject
+'Subject 2'
+
+# Make sure that multiple locmem connections share mail.outbox
+>>> mail.outbox = []
+>>> connection1 = locmem.EmailBackend()
+>>> connection2 = locmem.EmailBackend()
+>>> email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
+>>> connection1.send_messages([email])
+1
+>>> connection2.send_messages([email])
+1
+>>> len(mail.outbox)
+2
+
+# Make sure that the file backend write to the right location
+>>> tmp_dir = tempfile.mkdtemp()
+>>> connection = filebased.EmailBackend(file_path=tmp_dir)
+>>> email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
+>>> connection.send_messages([email])
+1
+>>> len(os.listdir(tmp_dir))
+1
+>>> print open(os.path.join(tmp_dir, os.listdir(tmp_dir)[0])).read()
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: quoted-printable
+Subject: Subject
+From: from@example.com
+To: to@example.com
+Date: ...
+Message-ID: ...
+
+Content
+-------------------------------------------------------------------------------
+
+>>> connection2 = filebased.EmailBackend(file_path=tmp_dir)
+>>> connection2.send_messages([email])
+1
+>>> len(os.listdir(tmp_dir))
+2
+>>> connection.send_messages([email])
+1
+>>> len(os.listdir(tmp_dir))
+2
+>>> email.connection = filebased.EmailBackend(file_path=tmp_dir)
+>>> connection_created = connection.open()
+>>> num_sent = email.send()
+>>> len(os.listdir(tmp_dir))
+3
+>>> num_sent = email.send()
+>>> len(os.listdir(tmp_dir))
+3
+>>> connection.close()
+>>> shutil.rmtree(tmp_dir)
+
+# Make sure that get_connection() accepts arbitrary keyword that might be
+# used with custom backends.
+>>> c = mail.get_connection(fail_silently=True, foo='bar')
+>>> c.fail_silently
+True
+
+# Test custom backend defined in this suite.
+>>> conn = mail.get_connection('regressiontests.mail.custombackend')
+>>> hasattr(conn, 'test_outbox')
+True
+>>> email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
+>>> conn.send_messages([email])
+1
+>>> len(conn.test_outbox)
+1
+
+# Test backend argument of mail.get_connection()
+>>> isinstance(mail.get_connection('django.core.mail.backends.smtp'), smtp.EmailBackend)
+True
+>>> isinstance(mail.get_connection('django.core.mail.backends.locmem'), locmem.EmailBackend)
+True
+>>> isinstance(mail.get_connection('django.core.mail.backends.dummy'), dummy.EmailBackend)
+True
+>>> isinstance(mail.get_connection('django.core.mail.backends.console'), console.EmailBackend)
+True
+>>> tmp_dir = tempfile.mkdtemp()
+>>> isinstance(mail.get_connection('django.core.mail.backends.filebased', file_path=tmp_dir), filebased.EmailBackend)
+True
+>>> shutil.rmtree(tmp_dir)
+>>> isinstance(mail.get_connection(), locmem.EmailBackend)
+True
+
+# Test connection argument of send_mail() et al
+>>> connection = mail.get_connection('django.core.mail.backends.console')
+>>> send_mail('Subject', 'Content', 'from@example.com', ['to@example.com'], connection=connection)
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: quoted-printable
+Subject: Subject
+From: from@example.com
+To: to@example.com
+Date: ...
+Message-ID: ...
+
+Content
+-------------------------------------------------------------------------------
+1
+
+>>> send_mass_mail([
+... ('Subject1', 'Content1', 'from1@example.com', ['to1@example.com']),
+... ('Subject2', 'Content2', 'from2@example.com', ['to2@example.com'])
+... ], connection=connection)
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: quoted-printable
+Subject: Subject1
+From: from1@example.com
+To: to1@example.com
+Date: ...
+Message-ID: ...
+
+Content1
+-------------------------------------------------------------------------------
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: quoted-printable
+Subject: Subject2
+From: from2@example.com
+To: to2@example.com
+Date: ...
+Message-ID: ...
+
+Content2
+-------------------------------------------------------------------------------
+2
+
+>>> mail_admins('Subject', 'Content', connection=connection)
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: quoted-printable
+Subject: [Django] Subject
+From: root@localhost
+To: nobody@example.com
+Date: ...
+Message-ID: ...
+
+Content
+-------------------------------------------------------------------------------
+
+>>> mail_managers('Subject', 'Content', connection=connection)
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: quoted-printable
+Subject: [Django] Subject
+From: root@localhost
+To: nobody@example.com
+Date: ...
+Message-ID: ...
+
+Content
+-------------------------------------------------------------------------------
+
+>>> settings.ADMINS = old_admins
+>>> settings.MANAGERS = old_managers
+
"""
diff --git a/tests/regressiontests/model_formsets_regress/tests.py b/tests/regressiontests/model_formsets_regress/tests.py
index f5e9355e42..4dba9fc19f 100644
--- a/tests/regressiontests/model_formsets_regress/tests.py
+++ b/tests/regressiontests/model_formsets_regress/tests.py
@@ -140,3 +140,13 @@ class InlineFormsetTests(TestCase):
self.assertEqual(manager[1]['name'], 'Terry Gilliam')
else:
self.fail('Errors found on formset:%s' % form_set.errors)
+
+ def test_formset_with_none_instance(self):
+ "A formset with instance=None can be created. Regression for #11872"
+ Form = modelform_factory(User)
+ FormSet = inlineformset_factory(User, UserSite)
+
+ # Instantiate the Form and FormSet to prove
+ # you can create a formset with an instance of None
+ form = Form(instance=None)
+ formset = FormSet(instance=None)
diff --git a/tests/regressiontests/model_inheritance_regress/models.py b/tests/regressiontests/model_inheritance_regress/models.py
index a1ee6a2d86..6a804a97c1 100644
--- a/tests/regressiontests/model_inheritance_regress/models.py
+++ b/tests/regressiontests/model_inheritance_regress/models.py
@@ -110,6 +110,36 @@ class DerivedM(BaseM):
return "PK = %d, base_name = %s, derived_name = %s" \
% (self.customPK, self.base_name, self.derived_name)
+# Check that abstract classes don't get m2m tables autocreated.
+class Person(models.Model):
+ name = models.CharField(max_length=100)
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class AbstractEvent(models.Model):
+ name = models.CharField(max_length=100)
+ attendees = models.ManyToManyField(Person, related_name="%(class)s_set")
+
+ class Meta:
+ abstract = True
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+class BirthdayParty(AbstractEvent):
+ pass
+
+class BachelorParty(AbstractEvent):
+ pass
+
+class MessyBachelorParty(BachelorParty):
+ pass
+
__test__ = {'API_TESTS':"""
# Regression for #7350, #7202
# Check that when you create a Parent object with a specific reference to an
@@ -318,5 +348,41 @@ True
>>> ParkingLot3._meta.get_ancestor_link(Place).name # the child->parent link
"parent"
+# Check that many-to-many relations defined on an abstract base class
+# are correctly inherited (and created) on the child class.
+>>> p1 = Person.objects.create(name='Alice')
+>>> p2 = Person.objects.create(name='Bob')
+>>> p3 = Person.objects.create(name='Carol')
+>>> p4 = Person.objects.create(name='Dave')
+
+>>> birthday = BirthdayParty.objects.create(name='Birthday party for Alice')
+>>> birthday.attendees = [p1, p3]
+
+>>> bachelor = BachelorParty.objects.create(name='Bachelor party for Bob')
+>>> bachelor.attendees = [p2, p4]
+
+>>> print p1.birthdayparty_set.all()
+[<BirthdayParty: Birthday party for Alice>]
+
+>>> print p1.bachelorparty_set.all()
+[]
+
+>>> print p2.bachelorparty_set.all()
+[<BachelorParty: Bachelor party for Bob>]
+
+# Check that a subclass of a subclass of an abstract model
+# doesn't get it's own accessor.
+>>> p2.messybachelorparty_set.all()
+Traceback (most recent call last):
+...
+AttributeError: 'Person' object has no attribute 'messybachelorparty_set'
+
+# ... but it does inherit the m2m from it's parent
+>>> messy = MessyBachelorParty.objects.create(name='Bachelor party for Dave')
+>>> messy.attendees = [p4]
+
+>>> p4.bachelorparty_set.all()
+[<BachelorParty: Bachelor party for Bob>, <BachelorParty: Bachelor party for Dave>]
+
"""}
diff --git a/tests/regressiontests/serializers_regress/models.py b/tests/regressiontests/serializers_regress/models.py
index 95119d4b05..313ed8fc3a 100644
--- a/tests/regressiontests/serializers_regress/models.py
+++ b/tests/regressiontests/serializers_regress/models.py
@@ -105,6 +105,9 @@ class Anchor(models.Model):
data = models.CharField(max_length=30)
+ class Meta:
+ ordering = ('id',)
+
class UniqueAnchor(models.Model):
"""This is a model that can be used as
something for other models to point at"""
@@ -135,7 +138,7 @@ class FKDataToO2O(models.Model):
class M2MIntermediateData(models.Model):
data = models.ManyToManyField(Anchor, null=True, through='Intermediate')
-
+
class Intermediate(models.Model):
left = models.ForeignKey(M2MIntermediateData)
right = models.ForeignKey(Anchor)
@@ -242,7 +245,7 @@ class AbstractBaseModel(models.Model):
class InheritAbstractModel(AbstractBaseModel):
child_data = models.IntegerField()
-
+
class BaseModel(models.Model):
parent_data = models.IntegerField()
@@ -252,4 +255,3 @@ class InheritBaseModel(BaseModel):
class ExplicitInheritBaseModel(BaseModel):
parent = models.OneToOneField(BaseModel)
child_data = models.IntegerField()
- \ No newline at end of file
diff --git a/tests/regressiontests/signals_regress/__init__.py b/tests/regressiontests/signals_regress/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/signals_regress/__init__.py
diff --git a/tests/regressiontests/signals_regress/models.py b/tests/regressiontests/signals_regress/models.py
new file mode 100644
index 0000000000..2b40ebc21a
--- /dev/null
+++ b/tests/regressiontests/signals_regress/models.py
@@ -0,0 +1,89 @@
+"""
+Testing signals before/after saving and deleting.
+"""
+
+from django.db import models
+
+class Author(models.Model):
+ name = models.CharField(max_length=20)
+
+ def __unicode__(self):
+ return self.name
+
+class Book(models.Model):
+ name = models.CharField(max_length=20)
+ authors = models.ManyToManyField(Author)
+
+ def __unicode__(self):
+ return self.name
+
+def pre_save_test(signal, sender, instance, **kwargs):
+ print 'pre_save signal,', instance
+ if kwargs.get('raw'):
+ print 'Is raw'
+
+def post_save_test(signal, sender, instance, **kwargs):
+ print 'post_save signal,', instance
+ if 'created' in kwargs:
+ if kwargs['created']:
+ print 'Is created'
+ else:
+ print 'Is updated'
+ if kwargs.get('raw'):
+ print 'Is raw'
+
+def pre_delete_test(signal, sender, instance, **kwargs):
+ print 'pre_delete signal,', instance
+ print 'instance.id is not None: %s' % (instance.id != None)
+
+def post_delete_test(signal, sender, instance, **kwargs):
+ print 'post_delete signal,', instance
+ print 'instance.id is not None: %s' % (instance.id != None)
+
+__test__ = {'API_TESTS':"""
+
+# Save up the number of connected signals so that we can check at the end
+# that all the signals we register get properly unregistered (#9989)
+>>> pre_signals = (len(models.signals.pre_save.receivers),
+... len(models.signals.post_save.receivers),
+... len(models.signals.pre_delete.receivers),
+... len(models.signals.post_delete.receivers))
+
+>>> models.signals.pre_save.connect(pre_save_test)
+>>> models.signals.post_save.connect(post_save_test)
+>>> models.signals.pre_delete.connect(pre_delete_test)
+>>> models.signals.post_delete.connect(post_delete_test)
+
+>>> a1 = Author(name='Neal Stephenson')
+>>> a1.save()
+pre_save signal, Neal Stephenson
+post_save signal, Neal Stephenson
+Is created
+
+>>> b1 = Book(name='Snow Crash')
+>>> b1.save()
+pre_save signal, Snow Crash
+post_save signal, Snow Crash
+Is created
+
+# Assigning to m2m shouldn't generate an m2m signal
+>>> b1.authors = [a1]
+
+# Removing an author from an m2m shouldn't generate an m2m signal
+>>> b1.authors = []
+
+>>> models.signals.post_delete.disconnect(post_delete_test)
+>>> models.signals.pre_delete.disconnect(pre_delete_test)
+>>> models.signals.post_save.disconnect(post_save_test)
+>>> models.signals.pre_save.disconnect(pre_save_test)
+
+# Check that all our signals got disconnected properly.
+>>> post_signals = (len(models.signals.pre_save.receivers),
+... len(models.signals.post_save.receivers),
+... len(models.signals.pre_delete.receivers),
+... len(models.signals.post_delete.receivers))
+
+>>> pre_signals == post_signals
+True
+
+"""}
diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py
index be2cb8fa37..58693cc395 100644
--- a/tests/regressiontests/test_client_regress/models.py
+++ b/tests/regressiontests/test_client_regress/models.py
@@ -574,6 +574,23 @@ class RequestMethodTests(TestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, 'request method: DELETE')
+class RequestMethodStringDataTests(TestCase):
+ def test_post(self):
+ "Request a view with string data via request method POST"
+ # Regression test for #11371
+ data = u'{"test": "json"}'
+ response = self.client.post('/test_client_regress/request_methods/', data=data, content_type='application/json')
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, 'request method: POST')
+
+ def test_put(self):
+ "Request a view with string data via request method PUT"
+ # Regression test for #11371
+ data = u'{"test": "json"}'
+ response = self.client.put('/test_client_regress/request_methods/', data=data, content_type='application/json')
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, 'request method: PUT')
+
class QueryStringTests(TestCase):
def test_get_like_requests(self):
for method_name in ('get','head','options','put','delete'):
diff --git a/tests/regressiontests/utils/dateformat.py b/tests/regressiontests/utils/dateformat.py
index 63b8201752..b80010f8d8 100644
--- a/tests/regressiontests/utils/dateformat.py
+++ b/tests/regressiontests/utils/dateformat.py
@@ -1,48 +1,35 @@
-"""
->>> from datetime import datetime, date
->>> from django.utils.dateformat import format
->>> from django.utils.tzinfo import FixedOffset, LocalTimezone
+import os
+from unittest import TestCase
+from datetime import datetime, date
+from django.utils.dateformat import format
+from django.utils.tzinfo import FixedOffset, LocalTimezone
-# date
->>> d = date(2009, 5, 16)
->>> date.fromtimestamp(int(format(d, 'U'))) == d
-True
+class DateFormatTests(TestCase):
+ def test_date(self):
+ d = date(2009, 5, 16)
+ self.assertEquals(date.fromtimestamp(int(format(d, 'U'))), d)
-# Naive datetime
->>> dt = datetime(2009, 5, 16, 5, 30, 30)
->>> datetime.fromtimestamp(int(format(dt, 'U'))) == dt
-True
+ def test_naive_datetime(self):
+ dt = datetime(2009, 5, 16, 5, 30, 30)
+ self.assertEquals(datetime.fromtimestamp(int(format(dt, 'U'))), dt)
-# datetime with local tzinfo
->>> ltz = LocalTimezone(datetime.now())
->>> dt = datetime(2009, 5, 16, 5, 30, 30, tzinfo=ltz)
->>> datetime.fromtimestamp(int(format(dt, 'U')), ltz) == dt
-True
->>> datetime.fromtimestamp(int(format(dt, 'U'))) == dt.replace(tzinfo=None)
-True
+ def test_datetime_with_local_tzinfo(self):
+ ltz = LocalTimezone(datetime.now())
+ dt = datetime(2009, 5, 16, 5, 30, 30, tzinfo=ltz)
+ self.assertEquals(datetime.fromtimestamp(int(format(dt, 'U')), ltz), dt)
+ self.assertEquals(datetime.fromtimestamp(int(format(dt, 'U'))), dt.replace(tzinfo=None))
-# datetime with arbitrary tzinfo
->>> tz = FixedOffset(-510)
->>> ltz = LocalTimezone(datetime.now())
->>> dt = datetime(2009, 5, 16, 5, 30, 30, tzinfo=tz)
->>> datetime.fromtimestamp(int(format(dt, 'U')), tz) == dt
-True
->>> datetime.fromtimestamp(int(format(dt, 'U')), ltz) == dt
-True
->>> datetime.fromtimestamp(int(format(dt, 'U'))) == dt.astimezone(ltz).replace(tzinfo=None)
-True
->>> datetime.fromtimestamp(int(format(dt, 'U')), tz).utctimetuple() == dt.utctimetuple()
-True
->>> datetime.fromtimestamp(int(format(dt, 'U')), ltz).utctimetuple() == dt.utctimetuple()
-True
+ def test_datetime_with_tzinfo(self):
+ tz = FixedOffset(-510)
+ ltz = LocalTimezone(datetime.now())
+ dt = datetime(2009, 5, 16, 5, 30, 30, tzinfo=tz)
+ self.assertEquals(datetime.fromtimestamp(int(format(dt, 'U')), tz), dt)
+ self.assertEquals(datetime.fromtimestamp(int(format(dt, 'U')), ltz), dt)
+ self.assertEquals(datetime.fromtimestamp(int(format(dt, 'U'))), dt.astimezone(ltz).replace(tzinfo=None))
+ self.assertEquals(datetime.fromtimestamp(int(format(dt, 'U')), tz).utctimetuple(), dt.utctimetuple())
+ self.assertEquals(datetime.fromtimestamp(int(format(dt, 'U')), ltz).utctimetuple(), dt.utctimetuple())
-# Epoch
->>> utc = FixedOffset(0)
->>> udt = datetime(1970, 1, 1, tzinfo=utc)
->>> format(udt, 'U')
-u'0'
-"""
-
-if __name__ == "__main__":
- import doctest
- doctest.testmod()
+ def test_epoch(self):
+ utc = FixedOffset(0)
+ udt = datetime(1970, 1, 1, tzinfo=utc)
+ self.assertEquals(format(udt, 'U'), u'0')
diff --git a/tests/regressiontests/utils/tests.py b/tests/regressiontests/utils/tests.py
index 6d345d9afb..a7a6e4c3a1 100644
--- a/tests/regressiontests/utils/tests.py
+++ b/tests/regressiontests/utils/tests.py
@@ -5,6 +5,7 @@ Tests for django.utils.
from unittest import TestCase
from django.utils import html, checksums
+from django.utils.functional import SimpleLazyObject
import timesince
import datastructures
@@ -23,10 +24,11 @@ except NameError:
__test__ = {
'timesince': timesince,
'datastructures': datastructures,
- 'dateformat': dateformat,
'itercompat': itercompat,
}
+from dateformat import *
+
class TestUtilsHtml(TestCase):
def check_output(self, function, value, output=None):
@@ -161,6 +163,80 @@ class TestUtilsChecksums(TestCase):
for value, output in items:
self.check_output(f, value, output)
+class _ComplexObject(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __eq__(self, other):
+ return self.name == other.name
+
+ def __hash__(self):
+ return hash(self.name)
+
+ def __str__(self):
+ return "I am _ComplexObject(%r)" % self.name
+
+ def __unicode__(self):
+ return unicode(self.name)
+
+ def __repr__(self):
+ return "_ComplexObject(%r)" % self.name
+
+complex_object = lambda: _ComplexObject("joe")
+
+class TestUtilsSimpleLazyObject(TestCase):
+ """
+ Tests for SimpleLazyObject
+ """
+ # Note that concrete use cases for SimpleLazyObject are also found in the
+ # auth context processor tests (unless the implementation of that function
+ # is changed).
+
+ def test_equality(self):
+ self.assertEqual(complex_object(), SimpleLazyObject(complex_object))
+ self.assertEqual(SimpleLazyObject(complex_object), complex_object())
+
+ def test_hash(self):
+ # hash() equality would not be true for many objects, but it should be
+ # for _ComplexObject
+ self.assertEqual(hash(complex_object()),
+ hash(SimpleLazyObject(complex_object)))
+
+ def test_repr(self):
+ # For debugging, it will really confuse things if there is no clue that
+ # SimpleLazyObject is actually a proxy object. So we don't
+ # proxy __repr__
+ self.assert_("SimpleLazyObject" in repr(SimpleLazyObject(complex_object)))
+
+ def test_str(self):
+ self.assertEqual("I am _ComplexObject('joe')", str(SimpleLazyObject(complex_object)))
+
+ def test_unicode(self):
+ self.assertEqual(u"joe", unicode(SimpleLazyObject(complex_object)))
+
+ def test_class(self):
+ # This is important for classes that use __class__ in things like
+ # equality tests.
+ self.assertEqual(_ComplexObject, SimpleLazyObject(complex_object).__class__)
+
+ def test_deepcopy(self):
+ import copy
+ # Check that we *can* do deep copy, and that it returns the right
+ # objects.
+
+ # First, for an unevaluated SimpleLazyObject
+ s = SimpleLazyObject(complex_object)
+ assert s._wrapped is None
+ s2 = copy.deepcopy(s)
+ assert s._wrapped is None # something has gone wrong is s is evaluated
+ self.assertEqual(s2, complex_object())
+
+ # Second, for an evaluated SimpleLazyObject
+ name = s.name # evaluate
+ assert s._wrapped is not None
+ s3 = copy.deepcopy(s)
+ self.assertEqual(s3, complex_object())
+
if __name__ == "__main__":
import doctest
doctest.testmod()
diff --git a/tests/regressiontests/views/tests/generic/date_based.py b/tests/regressiontests/views/tests/generic/date_based.py
index e0bae28ed0..2ca1bfd090 100644
--- a/tests/regressiontests/views/tests/generic/date_based.py
+++ b/tests/regressiontests/views/tests/generic/date_based.py
@@ -100,7 +100,7 @@ class MonthArchiveTest(TestCase):
now = datetime.now()
prev_month = now.date().replace(day=1)
- if prev_month.month == 11:
+ if prev_month.month == 1:
prev_month = prev_month.replace(year=prev_month.year-1, month=12)
else:
prev_month = prev_month.replace(month=prev_month.month-1)
@@ -121,4 +121,4 @@ class DayArchiveTests(TestCase):
article = Article.objects.create(title="example", author=author, date_created=datetime(2004, 1, 21, 0, 0, 1))
response = self.client.get('/views/date_based/archive_day/2004/1/21/')
self.assertEqual(response.status_code, 200)
- self.assertEqual(response.context['object_list'][0], article) \ No newline at end of file
+ self.assertEqual(response.context['object_list'][0], article)