diff options
| author | Jeremy Dunck <jdunck@gmail.com> | 2007-03-06 14:21:30 +0000 |
|---|---|---|
| committer | Jeremy Dunck <jdunck@gmail.com> | 2007-03-06 14:21:30 +0000 |
| commit | 5514d8731955466dad0cdaf395ddd4da1c101274 (patch) | |
| tree | 7a51066204f4bacb3bfb30a74d089d3309959177 /tests | |
| parent | d60c44319459ea6aeb7d2c77d2efd8b4b4683296 (diff) | |
gis: Merged revisions 4564-4668 via svnmerge from
http://code.djangoproject.com/svn/django/trunk
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@4669 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests')
35 files changed, 802 insertions, 19 deletions
diff --git a/tests/modeltests/fixtures/__init__.py b/tests/modeltests/fixtures/__init__.py new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/tests/modeltests/fixtures/__init__.py @@ -0,0 +1,2 @@ + + diff --git a/tests/modeltests/fixtures/fixtures/fixture1.json b/tests/modeltests/fixtures/fixtures/fixture1.json new file mode 100644 index 0000000000..cc11a3e926 --- /dev/null +++ b/tests/modeltests/fixtures/fixtures/fixture1.json @@ -0,0 +1,18 @@ +[ + { + "pk": "2", + "model": "fixtures.article", + "fields": { + "headline": "Poker has no place on ESPN", + "pub_date": "2006-06-16 12:00:00" + } + }, + { + "pk": "3", + "model": "fixtures.article", + "fields": { + "headline": "Time to reform copyright", + "pub_date": "2006-06-16 13:00:00" + } + } +]
\ No newline at end of file diff --git a/tests/modeltests/fixtures/fixtures/fixture2.json b/tests/modeltests/fixtures/fixtures/fixture2.json new file mode 100644 index 0000000000..01b40d7535 --- /dev/null +++ b/tests/modeltests/fixtures/fixtures/fixture2.json @@ -0,0 +1,18 @@ +[ + { + "pk": "3", + "model": "fixtures.article", + "fields": { + "headline": "Copyright is fine the way it is", + "pub_date": "2006-06-16 14:00:00" + } + }, + { + "pk": "4", + "model": "fixtures.article", + "fields": { + "headline": "Django conquers world!", + "pub_date": "2006-06-16 15:00:00" + } + } +]
\ No newline at end of file diff --git a/tests/modeltests/fixtures/fixtures/fixture2.xml b/tests/modeltests/fixtures/fixtures/fixture2.xml new file mode 100644 index 0000000000..9ced78162e --- /dev/null +++ b/tests/modeltests/fixtures/fixtures/fixture2.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<django-objects version="1.0"> + <object pk="2" model="fixtures.article"> + <field type="CharField" name="headline">Poker on TV is great!</field> + <field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field> + </object> + <object pk="5" model="fixtures.article"> + <field type="CharField" name="headline">XML identified as leading cause of cancer</field> + <field type="DateTimeField" name="pub_date">2006-06-16 16:00:00</field> + </object> +</django-objects>
\ No newline at end of file diff --git a/tests/modeltests/fixtures/fixtures/fixture3.xml b/tests/modeltests/fixtures/fixtures/fixture3.xml new file mode 100644 index 0000000000..9ced78162e --- /dev/null +++ b/tests/modeltests/fixtures/fixtures/fixture3.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<django-objects version="1.0"> + <object pk="2" model="fixtures.article"> + <field type="CharField" name="headline">Poker on TV is great!</field> + <field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field> + </object> + <object pk="5" model="fixtures.article"> + <field type="CharField" name="headline">XML identified as leading cause of cancer</field> + <field type="DateTimeField" name="pub_date">2006-06-16 16:00:00</field> + </object> +</django-objects>
\ No newline at end of file diff --git a/tests/modeltests/fixtures/fixtures/initial_data.json b/tests/modeltests/fixtures/fixtures/initial_data.json new file mode 100644 index 0000000000..477d781dbc --- /dev/null +++ b/tests/modeltests/fixtures/fixtures/initial_data.json @@ -0,0 +1,10 @@ +[ + { + "pk": "1", + "model": "fixtures.article", + "fields": { + "headline": "Python program becomes self aware", + "pub_date": "2006-06-16 11:00:00" + } + } +]
\ No newline at end of file diff --git a/tests/modeltests/fixtures/models.py b/tests/modeltests/fixtures/models.py new file mode 100644 index 0000000000..d82886a6c4 --- /dev/null +++ b/tests/modeltests/fixtures/models.py @@ -0,0 +1,88 @@ +""" +39. Fixtures. + +Fixtures are a way of loading data into the database in bulk. Fixure data +can be stored in any serializable format (including JSON and XML). Fixtures +are identified by name, and are stored in either a directory named 'fixtures' +in the application directory, on in one of the directories named in the +FIXTURE_DIRS setting. +""" + +from django.db import models + +class Article(models.Model): + headline = models.CharField(maxlength=100, default='Default headline') + pub_date = models.DateTimeField() + + def __str__(self): + return self.headline + + class Meta: + ordering = ('-pub_date', 'headline') + +__test__ = {'API_TESTS': """ +>>> from django.core import management +>>> from django.db.models import get_app + +# Reset the database representation of this app. +# This will return the database to a clean initial state. +>>> management.flush(verbosity=0, interactive=False) + +# Syncdb introduces 1 initial data object from initial_data.json. +>>> Article.objects.all() +[<Article: Python program becomes self aware>] + +# Load fixture 1. Single JSON file, with two objects. +>>> management.load_data(['fixture1.json'], verbosity=0) +>>> Article.objects.all() +[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>] + +# Load fixture 2. JSON file imported by default. Overwrites some existing objects +>>> management.load_data(['fixture2.json'], verbosity=0) +>>> Article.objects.all() +[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>] + +# Load fixture 3, XML format. +>>> management.load_data(['fixture3.xml'], verbosity=0) +>>> Article.objects.all() +[<Article: XML identified as leading cause of cancer>, <Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker on TV is great!>, <Article: Python program becomes self aware>] + +# Load a fixture that doesn't exist +>>> management.load_data(['unknown.json'], verbosity=0) + +# object list is unaffected +>>> Article.objects.all() +[<Article: XML identified as leading cause of cancer>, <Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker on TV is great!>, <Article: Python program becomes self aware>] + +# Reset the database representation of this app. This will delete all data. +>>> management.flush(verbosity=0, interactive=False) +>>> Article.objects.all() +[<Article: Python program becomes self aware>] + +# Load fixture 1 again, using format discovery +>>> management.load_data(['fixture1'], verbosity=0) +>>> Article.objects.all() +[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>] + +# Try to load fixture 2 using format discovery; this will fail +# because there are two fixture2's in the fixtures directory +>>> management.load_data(['fixture2'], verbosity=0) # doctest: +ELLIPSIS +Multiple fixtures named 'fixture2' in '.../fixtures'. Aborting. + +>>> Article.objects.all() +[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>] + +# Dump the current contents of the database as a JSON fixture +>>> management.dump_data(['fixtures'], format='json') +[{"pk": "3", "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": "2", "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": "1", "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}] +"""} + +from django.test import TestCase + +class SampleTestCase(TestCase): + fixtures = ['fixture1.json', 'fixture2.json'] + + def testClassFixtures(self): + "Check that test case has installed 4 fixture objects" + self.assertEqual(Article.objects.count(), 4) + self.assertEquals(str(Article.objects.all()), "[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]") diff --git a/tests/modeltests/select_related/__init__.py b/tests/modeltests/select_related/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/modeltests/select_related/__init__.py diff --git a/tests/modeltests/select_related/models.py b/tests/modeltests/select_related/models.py new file mode 100644 index 0000000000..8a19267870 --- /dev/null +++ b/tests/modeltests/select_related/models.py @@ -0,0 +1,152 @@ +""" +XXX. Tests for ``select_related()`` + +``select_related()`` follows all relationships and pre-caches any foreign key +values so that complex trees can be fetched in a single query. However, this +isn't always a good idea, so the ``depth`` argument control how many "levels" +the select-related behavior will traverse. +""" + +from django.db import models + +# Who remembers high school biology? + +class Domain(models.Model): + name = models.CharField(maxlength=50) + def __str__(self): + return self.name + +class Kingdom(models.Model): + name = models.CharField(maxlength=50) + domain = models.ForeignKey(Domain) + def __str__(self): + return self.name + +class Phylum(models.Model): + name = models.CharField(maxlength=50) + kingdom = models.ForeignKey(Kingdom) + def __str__(self): + return self.name + +class Klass(models.Model): + name = models.CharField(maxlength=50) + phylum = models.ForeignKey(Phylum) + def __str__(self): + return self.name + +class Order(models.Model): + name = models.CharField(maxlength=50) + klass = models.ForeignKey(Klass) + def __str__(self): + return self.name + +class Family(models.Model): + name = models.CharField(maxlength=50) + order = models.ForeignKey(Order) + def __str__(self): + return self.name + +class Genus(models.Model): + name = models.CharField(maxlength=50) + family = models.ForeignKey(Family) + def __str__(self): + return self.name + +class Species(models.Model): + name = models.CharField(maxlength=50) + genus = models.ForeignKey(Genus) + def __str__(self): + return self.name + +def create_tree(stringtree): + """Helper to create a complete tree""" + names = stringtree.split() + models = [Domain, Kingdom, Phylum, Klass, Order, Family, Genus, Species] + assert len(names) == len(models), (names, models) + + parent = None + for name, model in zip(names, models): + try: + obj = model.objects.get(name=name) + except model.DoesNotExist: + obj = model(name=name) + if parent: + setattr(obj, parent.__class__.__name__.lower(), parent) + obj.save() + parent = obj + +__test__ = {'API_TESTS':""" + +# Set up. +# The test runner sets settings.DEBUG to False, but we want to gather queries +# so we'll set it to True here and reset it at the end of the test suite. +>>> from django.conf import settings +>>> settings.DEBUG = True + +>>> create_tree("Eukaryota Animalia Anthropoda Insecta Diptera Drosophilidae Drosophila melanogaster") +>>> create_tree("Eukaryota Animalia Chordata Mammalia Primates Hominidae Homo sapiens") +>>> create_tree("Eukaryota Plantae Magnoliophyta Magnoliopsida Fabales Fabaceae Pisum sativum") +>>> create_tree("Eukaryota Fungi Basidiomycota Homobasidiomycatae Agaricales Amanitacae Amanita muscaria") + +>>> from django import db + +# Normally, accessing FKs doesn't fill in related objects: +>>> db.reset_queries() +>>> fly = Species.objects.get(name="melanogaster") +>>> fly.genus.family.order.klass.phylum.kingdom.domain +<Domain: Eukaryota> +>>> len(db.connection.queries) +8 + +# However, a select_related() call will fill in those related objects without any extra queries: +>>> db.reset_queries() +>>> person = Species.objects.select_related().get(name="sapiens") +>>> person.genus.family.order.klass.phylum.kingdom.domain +<Domain: Eukaryota> +>>> len(db.connection.queries) +1 + +# select_related() also of course applies to entire lists, not just items. +# Without select_related() +>>> db.reset_queries() +>>> world = Species.objects.all() +>>> [o.genus.family for o in world] +[<Family: Drosophilidae>, <Family: Hominidae>, <Family: Fabaceae>, <Family: Amanitacae>] +>>> len(db.connection.queries) +9 + +# With select_related(): +>>> db.reset_queries() +>>> world = Species.objects.all().select_related() +>>> [o.genus.family for o in world] +[<Family: Drosophilidae>, <Family: Hominidae>, <Family: Fabaceae>, <Family: Amanitacae>] +>>> len(db.connection.queries) +1 + +# The "depth" argument to select_related() will stop the descent at a particular level: +>>> db.reset_queries() +>>> pea = Species.objects.select_related(depth=1).get(name="sativum") +>>> pea.genus.family.order.klass.phylum.kingdom.domain +<Domain: Eukaryota> + +# Notice: one few query than above because of depth=1 +>>> len(db.connection.queries) +7 + +>>> db.reset_queries() +>>> pea = Species.objects.select_related(depth=5).get(name="sativum") +>>> pea.genus.family.order.klass.phylum.kingdom.domain +<Domain: Eukaryota> +>>> len(db.connection.queries) +3 + +>>> db.reset_queries() +>>> world = Species.objects.all().select_related(depth=2) +>>> [o.genus.family.order for o in world] +[<Order: Diptera>, <Order: Primates>, <Order: Fabales>, <Order: Agaricales>] +>>> len(db.connection.queries) +5 + +# Reset DEBUG to where we found it. +>>> settings.DEBUG = False +"""} diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py index e24ff537c1..e86546c6fe 100644 --- a/tests/modeltests/serializers/models.py +++ b/tests/modeltests/serializers/models.py @@ -139,4 +139,24 @@ __test__ = {'API_TESTS':""" ... print obj <DeserializedObject: Profile of Joe> +# Objects ids can be referenced before they are defined in the serialization data +# However, the deserialization process will need to be contained within a transaction +>>> json = '[{"pk": "3", "model": "serializers.article", "fields": {"headline": "Forward references pose no problem", "pub_date": "2006-06-16 15:00:00", "categories": [4, 1], "author": 4}}, {"pk": "4", "model": "serializers.category", "fields": {"name": "Reference"}}, {"pk": "4", "model": "serializers.author", "fields": {"name": "Agnes"}}]' +>>> from django.db import transaction +>>> transaction.enter_transaction_management() +>>> transaction.managed(True) +>>> for obj in serializers.deserialize("json", json): +... obj.save() + +>>> transaction.commit() +>>> transaction.leave_transaction_management() + +>>> article = Article.objects.get(pk=3) +>>> article +<Article: Forward references pose no problem> +>>> article.categories.all() +[<Category: Reference>, <Category: Sports>] +>>> article.author +<Author: Agnes> + """} diff --git a/tests/modeltests/test_client/fixtures/testdata.json b/tests/modeltests/test_client/fixtures/testdata.json new file mode 100644 index 0000000000..5c9e415240 --- /dev/null +++ b/tests/modeltests/test_client/fixtures/testdata.json @@ -0,0 +1,20 @@ +[ + { + "pk": "1", + "model": "auth.user", + "fields": { + "username": "testclient", + "first_name": "Test", + "last_name": "Client", + "is_active": true, + "is_superuser": false, + "is_staff": false, + "last_login": "2006-12-17 07:03:31", + "groups": [], + "user_permissions": [], + "password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161", + "email": "testclient@example.com", + "date_joined": "2006-12-17 07:03:31" + } + } +]
\ No newline at end of file diff --git a/tests/modeltests/test_client/management.py b/tests/modeltests/test_client/management.py deleted file mode 100644 index 9b5a5c498e..0000000000 --- a/tests/modeltests/test_client/management.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.dispatch import dispatcher -from django.db.models import signals -import models as test_client_app -from django.contrib.auth.models import User - -def setup_test(app, created_models, verbosity): - # Create a user account for the login-based tests - User.objects.create_user('testclient','testclient@example.com', 'password') - -dispatcher.connect(setup_test, sender=test_client_app, signal=signals.post_syncdb) diff --git a/tests/modeltests/test_client/models.py b/tests/modeltests/test_client/models.py index c3febfb99f..a3a9749162 100644 --- a/tests/modeltests/test_client/models.py +++ b/tests/modeltests/test_client/models.py @@ -19,10 +19,11 @@ testing against the contexts and templates produced by a view, rather than the HTML rendered to the end-user. """ -from django.test.client import Client -import unittest +from django.test import Client, TestCase -class ClientTest(unittest.TestCase): +class ClientTest(TestCase): + fixtures = ['testdata.json'] + def setUp(self): "Set up test environment" self.client = Client() @@ -97,7 +98,7 @@ class ClientTest(unittest.TestCase): # Request a page that requires a login response = self.client.login('/test_client/login_protected_view/', 'testclient', 'password') - self.assertTrue(response) + self.failUnless(response) self.assertEqual(response.status_code, 200) self.assertEqual(response.context['user'].username, 'testclient') self.assertEqual(response.template.name, 'Login Template') @@ -106,7 +107,7 @@ class ClientTest(unittest.TestCase): "Request a page that is protected with @login, but use bad credentials" response = self.client.login('/test_client/login_protected_view/', 'otheruser', 'nopassword') - self.assertFalse(response) + self.failIf(response) def test_session_modifying_view(self): "Request a page that modifies the session" diff --git a/tests/modeltests/validation/models.py b/tests/modeltests/validation/models.py index a9a3d3f485..1066faca4f 100644 --- a/tests/modeltests/validation/models.py +++ b/tests/modeltests/validation/models.py @@ -146,4 +146,8 @@ u'john@example.com' >>> p.validate() {'email': ['Enter a valid e-mail address.']} +# Make sure that Date and DateTime return validation errors and don't raise Python errors. +>>> Person(name='John Doe', is_child=True, email='abc@def.com').validate() +{'favorite_moment': ['This field is required.'], 'birthdate': ['This field is required.']} + """} diff --git a/tests/regressiontests/bug639/__init__.py b/tests/regressiontests/bug639/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/bug639/__init__.py diff --git a/tests/regressiontests/bug639/models.py b/tests/regressiontests/bug639/models.py new file mode 100644 index 0000000000..7cfdfc82ef --- /dev/null +++ b/tests/regressiontests/bug639/models.py @@ -0,0 +1,16 @@ +import tempfile +from django.db import models + +class Photo(models.Model): + title = models.CharField(maxlength=30) + image = models.FileField(upload_to=tempfile.gettempdir()) + + # Support code for the tests; this keeps track of how many times save() gets + # called on each instance. + def __init__(self, *args, **kwargs): + super(Photo, self).__init__(*args, **kwargs) + self._savecount = 0 + + def save(self): + super(Photo, self).save() + self._savecount +=1
\ No newline at end of file diff --git a/tests/regressiontests/bug639/test.jpg b/tests/regressiontests/bug639/test.jpg Binary files differnew file mode 100644 index 0000000000..391b57a0f3 --- /dev/null +++ b/tests/regressiontests/bug639/test.jpg diff --git a/tests/regressiontests/bug639/tests.py b/tests/regressiontests/bug639/tests.py new file mode 100644 index 0000000000..f9596d06cb --- /dev/null +++ b/tests/regressiontests/bug639/tests.py @@ -0,0 +1,42 @@ +""" +Tests for file field behavior, and specifically #639, in which Model.save() gets +called *again* for each FileField. This test will fail if calling an +auto-manipulator's save() method causes Model.save() to be called more than once. +""" + +import os +import unittest +from regressiontests.bug639.models import Photo +from django.http import QueryDict +from django.utils.datastructures import MultiValueDict + +class Bug639Test(unittest.TestCase): + + def testBug639(self): + """ + Simulate a file upload and check how many times Model.save() gets called. + """ + # Grab an image for testing + img = open(os.path.join(os.path.dirname(__file__), "test.jpg"), "rb").read() + + # Fake a request query dict with the file + qd = QueryDict("title=Testing&image=", mutable=True) + qd["image_file"] = { + "filename" : "test.jpg", + "content-type" : "image/jpeg", + "content" : img + } + + manip = Photo.AddManipulator() + manip.do_html2python(qd) + p = manip.save(qd) + + # Check the savecount stored on the object (see the model) + self.assertEqual(p._savecount, 1) + + def tearDown(self): + """ + Make sure to delete the "uploaded" file to avoid clogging /tmp. + """ + p = Photo.objects.get() + os.unlink(p.get_image_filename())
\ No newline at end of file diff --git a/tests/regressiontests/datastructures/__init__.py b/tests/regressiontests/datastructures/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/datastructures/__init__.py diff --git a/tests/regressiontests/datastructures/models.py b/tests/regressiontests/datastructures/models.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/datastructures/models.py diff --git a/tests/regressiontests/datastructures/tests.py b/tests/regressiontests/datastructures/tests.py new file mode 100644 index 0000000000..624e7a50bf --- /dev/null +++ b/tests/regressiontests/datastructures/tests.py @@ -0,0 +1,34 @@ +""" +# Tests for stuff in django.utils.datastructures. + +>>> from django.utils.datastructures import * + +### MergeDict ################################################################# + +>>> d1 = {'chris':'cool','camri':'cute','cotton':'adorable','tulip':'snuggable', 'twoofme':'firstone'} +>>> d2 = {'chris2':'cool2','camri2':'cute2','cotton2':'adorable2','tulip2':'snuggable2'} +>>> d3 = {'chris3':'cool3','camri3':'cute3','cotton3':'adorable3','tulip3':'snuggable3'} +>>> d4 = {'twoofme':'secondone'} +>>> md = MergeDict( d1,d2,d3 ) +>>> md['chris'] +'cool' +>>> md['camri'] +'cute' +>>> md['twoofme'] +'firstone' +>>> md2 = md.copy() +>>> md2['chris'] +'cool' + +### MultiValueDict ########################################################## + +>>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']}) +>>> d['name'] +'Simon' +>>> d.getlist('name') +['Adrian', 'Simon'] +>>> d.get('lastname', 'nonexistent') +'nonexistent' +>>> d.setlist('lastname', ['Holovaty', 'Willison']) + +"""
\ No newline at end of file diff --git a/tests/regressiontests/dateformat/tests.py b/tests/regressiontests/dateformat/tests.py index 6e28759a92..f9f84145c5 100644 --- a/tests/regressiontests/dateformat/tests.py +++ b/tests/regressiontests/dateformat/tests.py @@ -17,6 +17,8 @@ r""" '07' >>> format(my_birthday, 'M') 'Jul' +>>> format(my_birthday, 'b') +'jul' >>> format(my_birthday, 'n') '7' >>> format(my_birthday, 'N') diff --git a/tests/regressiontests/dispatch/__init__.py b/tests/regressiontests/dispatch/__init__.py new file mode 100644 index 0000000000..679895bb5c --- /dev/null +++ b/tests/regressiontests/dispatch/__init__.py @@ -0,0 +1,2 @@ +"""Unit-tests for the dispatch project +""" diff --git a/tests/regressiontests/dispatch/models.py b/tests/regressiontests/dispatch/models.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/dispatch/models.py diff --git a/tests/regressiontests/dispatch/tests/__init__.py b/tests/regressiontests/dispatch/tests/__init__.py new file mode 100644 index 0000000000..0fdefe48a7 --- /dev/null +++ b/tests/regressiontests/dispatch/tests/__init__.py @@ -0,0 +1,7 @@ +""" +Unit-tests for the dispatch project +""" + +from test_dispatcher import * +from test_robustapply import * +from test_saferef import * diff --git a/tests/regressiontests/dispatch/tests/test_dispatcher.py b/tests/regressiontests/dispatch/tests/test_dispatcher.py new file mode 100644 index 0000000000..0bc99a1505 --- /dev/null +++ b/tests/regressiontests/dispatch/tests/test_dispatcher.py @@ -0,0 +1,144 @@ +from django.dispatch.dispatcher import * +from django.dispatch import dispatcher, robust +import unittest +import copy + +def x(a): + return a + +class Dummy(object): + pass + +class Callable(object): + def __call__(self, a): + return a + + def a(self, a): + return a + +class DispatcherTests(unittest.TestCase): + """Test suite for dispatcher (barely started)""" + + def setUp(self): + # track the initial state, since it's possible that others have bleed receivers in + self.sendersBack = copy.copy(dispatcher.sendersBack) + self.connections = copy.copy(dispatcher.connections) + self.senders = copy.copy(dispatcher.senders) + + def _testIsClean(self): + """Assert that everything has been cleaned up automatically""" + self.assertEqual(dispatcher.sendersBack, self.sendersBack) + self.assertEqual(dispatcher.connections, self.connections) + self.assertEqual(dispatcher.senders, self.senders) + + def testExact(self): + a = Dummy() + signal = 'this' + connect(x, signal, a) + expected = [(x,a)] + result = send('this',a, a=a) + self.assertEqual(result, expected) + disconnect(x, signal, a) + self.assertEqual(list(getAllReceivers(a,signal)), []) + self._testIsClean() + + def testAnonymousSend(self): + a = Dummy() + signal = 'this' + connect(x, signal) + expected = [(x,a)] + result = send(signal,None, a=a) + self.assertEqual(result, expected) + disconnect(x, signal) + self.assertEqual(list(getAllReceivers(None,signal)), []) + self._testIsClean() + + def testAnyRegistration(self): + a = Dummy() + signal = 'this' + connect(x, signal, Any) + expected = [(x,a)] + result = send('this',object(), a=a) + self.assertEqual(result, expected) + disconnect(x, signal, Any) + expected = [] + result = send('this',object(), a=a) + self.assertEqual(result, expected) + self.assertEqual(list(getAllReceivers(Any,signal)), []) + + self._testIsClean() + + def testAnyRegistration2(self): + a = Dummy() + signal = 'this' + connect(x, Any, a) + expected = [(x,a)] + result = send('this',a, a=a) + self.assertEqual(result, expected) + disconnect(x, Any, a) + self.assertEqual(list(getAllReceivers(a,Any)), []) + self._testIsClean() + + def testGarbageCollected(self): + a = Callable() + b = Dummy() + signal = 'this' + connect(a.a, signal, b) + expected = [] + del a + result = send('this',b, a=b) + self.assertEqual(result, expected) + self.assertEqual(list(getAllReceivers(b,signal)), []) + self._testIsClean() + + def testGarbageCollectedObj(self): + class x: + def __call__(self, a): + return a + a = Callable() + b = Dummy() + signal = 'this' + connect(a, signal, b) + expected = [] + del a + result = send('this',b, a=b) + self.assertEqual(result, expected) + self.assertEqual(list(getAllReceivers(b,signal)), []) + self._testIsClean() + + + def testMultipleRegistration(self): + a = Callable() + b = Dummy() + signal = 'this' + connect(a, signal, b) + connect(a, signal, b) + connect(a, signal, b) + connect(a, signal, b) + connect(a, signal, b) + connect(a, signal, b) + result = send('this',b, a=b) + self.assertEqual(len(result), 1) + self.assertEqual(len(list(getAllReceivers(b,signal))), 1) + del a + del b + del result + self._testIsClean() + + def testRobust(self): + """Test the sendRobust function""" + def fails(): + raise ValueError('this') + a = object() + signal = 'this' + connect(fails, Any, a) + result = robust.sendRobust('this',a, a=a) + err = result[0][1] + self.assert_(isinstance(err, ValueError)) + self.assertEqual(err.args, ('this',)) + +def getSuite(): + return unittest.makeSuite(DispatcherTests,'test') + +if __name__ == "__main__": + unittest.main () diff --git a/tests/regressiontests/dispatch/tests/test_robustapply.py b/tests/regressiontests/dispatch/tests/test_robustapply.py new file mode 100644 index 0000000000..499450eec4 --- /dev/null +++ b/tests/regressiontests/dispatch/tests/test_robustapply.py @@ -0,0 +1,34 @@ +from django.dispatch.robustapply import * + +import unittest + +def noArgument(): + pass + +def oneArgument(blah): + pass + +def twoArgument(blah, other): + pass + +class TestCases(unittest.TestCase): + def test01(self): + robustApply(noArgument) + + def test02(self): + self.assertRaises(TypeError, robustApply, noArgument, "this") + + def test03(self): + self.assertRaises(TypeError, robustApply, oneArgument) + + def test04(self): + """Raise error on duplication of a particular argument""" + self.assertRaises(TypeError, robustApply, oneArgument, "this", blah = "that") + +def getSuite(): + return unittest.makeSuite(TestCases,'test') + + +if __name__ == "__main__": + unittest.main() + diff --git a/tests/regressiontests/dispatch/tests/test_saferef.py b/tests/regressiontests/dispatch/tests/test_saferef.py new file mode 100644 index 0000000000..233a2023e0 --- /dev/null +++ b/tests/regressiontests/dispatch/tests/test_saferef.py @@ -0,0 +1,77 @@ +from django.dispatch.saferef import * + +import unittest + +class Test1(object): + def x(self): + pass + +def test2(obj): + pass + +class Test2(object): + def __call__(self, obj): + pass + +class Tester(unittest.TestCase): + def setUp(self): + ts = [] + ss = [] + for x in xrange(5000): + t = Test1() + ts.append(t) + s = safeRef(t.x, self._closure) + ss.append(s) + ts.append(test2) + ss.append(safeRef(test2, self._closure)) + for x in xrange(30): + t = Test2() + ts.append(t) + s = safeRef(t, self._closure) + ss.append(s) + self.ts = ts + self.ss = ss + self.closureCount = 0 + + def tearDown(self): + del self.ts + del self.ss + + def testIn(self): + """Test the "in" operator for safe references (cmp)""" + for t in self.ts[:50]: + self.assert_(safeRef(t.x) in self.ss) + + def testValid(self): + """Test that the references are valid (return instance methods)""" + for s in self.ss: + self.assert_(s()) + + def testShortCircuit (self): + """Test that creation short-circuits to reuse existing references""" + sd = {} + for s in self.ss: + sd[s] = 1 + for t in self.ts: + if hasattr(t, 'x'): + self.assert_(sd.has_key(safeRef(t.x))) + else: + self.assert_(sd.has_key(safeRef(t))) + + def testRepresentation (self): + """Test that the reference object's representation works + + XXX Doesn't currently check the results, just that no error + is raised + """ + repr(self.ss[-1]) + + def _closure(self, ref): + """Dumb utility mechanism to increment deletion counter""" + self.closureCount +=1 + +def getSuite(): + return unittest.makeSuite(Tester,'test') + +if __name__ == "__main__": + unittest.main() diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py index db3969429e..a9ce8d23b3 100644 --- a/tests/regressiontests/forms/tests.py +++ b/tests/regressiontests/forms/tests.py @@ -3476,6 +3476,7 @@ as its choices. <option value="ME">Maine</option> <option value="MH">Marshall Islands</option> <option value="MD">Maryland</option> +<option value="MA">Massachusetts</option> <option value="MI">Michigan</option> <option value="MN">Minnesota</option> <option value="MS">Mississippi</option> diff --git a/tests/regressiontests/humanize/__init__.py b/tests/regressiontests/humanize/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/humanize/__init__.py diff --git a/tests/regressiontests/humanize/models.py b/tests/regressiontests/humanize/models.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/humanize/models.py diff --git a/tests/regressiontests/humanize/tests.py b/tests/regressiontests/humanize/tests.py new file mode 100644 index 0000000000..e342d7ded8 --- /dev/null +++ b/tests/regressiontests/humanize/tests.py @@ -0,0 +1,52 @@ +import unittest +from django.template import Template, Context, add_to_builtins + +add_to_builtins('django.contrib.humanize.templatetags.humanize') + +class HumanizeTests(unittest.TestCase): + + def humanize_tester(self, test_list, result_list, method): + # Using max below ensures we go through both lists + # However, if the lists are not equal length, this raises an exception + for index in xrange(len(max(test_list,result_list))): + test_content = test_list[index] + t = Template('{{ test_content|%s }}' % method) + rendered = t.render(Context(locals())).strip() + self.assertEqual(rendered, result_list[index], + msg="""%s test failed, produced %s, +should've produced %s""" % (method, rendered, result_list[index])) + + def test_ordinal(self): + test_list = ('1','2','3','4','11','12', + '13','101','102','103','111', + 'something else') + result_list = ('1st', '2nd', '3rd', '4th', '11th', + '12th', '13th', '101st', '102nd', '103rd', + '111th', 'something else') + + self.humanize_tester(test_list, result_list, 'ordinal') + + def test_intcomma(self): + test_list = ('100','1000','10123','10311','1000000') + result_list = ('100', '1,000', '10,123', '10,311', '1,000,000') + + self.humanize_tester(test_list, result_list, 'intcomma') + + def test_intword(self): + test_list = ('100', '1000000', '1200000', '1290000', + '1000000000','2000000000','6000000000000') + result_list = ('100', '1.0 million', '1.2 million', '1.3 million', + '1.0 billion', '2.0 billion', '6.0 trillion') + + self.humanize_tester(test_list, result_list, 'intword') + + def test_apnumber(self): + test_list = [str(x) for x in xrange(1,11)] + result_list = ('one', 'two', 'three', 'four', 'five', 'six', + 'seven', 'eight', 'nine', '10') + + self.humanize_tester(test_list, result_list, 'apnumber') + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index 3bae6a2609..0165824951 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -127,6 +127,29 @@ class Templates(unittest.TestCase): # Fail silently when accessing a non-simple method 'basic-syntax20': ("{{ var.method2 }}", {"var": SomeClass()}, ("","INVALID")), + # List-index syntax allows a template to access a certain item of a subscriptable object. + 'list-index01': ("{{ var.1 }}", {"var": ["first item", "second item"]}, "second item"), + + # Fail silently when the list index is out of range. + 'list-index02': ("{{ var.5 }}", {"var": ["first item", "second item"]}, ("", "INVALID")), + + # Fail silently when the variable is not a subscriptable object. + 'list-index03': ("{{ var.1 }}", {"var": None}, ("", "INVALID")), + + # Fail silently when variable is a dict without the specified key. + 'list-index04': ("{{ var.1 }}", {"var": {}}, ("", "INVALID")), + + # Dictionary lookup wins out when dict's key is a string. + 'list-index05': ("{{ var.1 }}", {"var": {'1': "hello"}}, "hello"), + + # But list-index lookup wins out when dict's key is an int, which + # behind the scenes is really a dictionary lookup (for a dict) + # after converting the key to an int. + 'list-index06': ("{{ var.1 }}", {"var": {1: "hello"}}, "hello"), + + # Dictionary lookup wins out when there is a string and int version of the key. + 'list-index07': ("{{ var.1 }}", {"var": {'1': "hello", 1: "world"}}, "hello"), + # Basic filter usage 'basic-syntax21': ("{{ var|upper }}", {"var": "Django is the greatest!"}, "DJANGO IS THE GREATEST!"), @@ -167,7 +190,7 @@ class Templates(unittest.TestCase): 'basic-syntax33': (r'1{{ var.method3 }}2', {"var": SomeClass()}, ("12", "1INVALID2")), # In methods that raise an exception without a "silent_variable_attribute" set to True, - # the exception propogates + # the exception propagates 'basic-syntax34': (r'1{{ var.method4 }}2', {"var": SomeClass()}, SomeOtherException), # Escaped backslash in argument diff --git a/tests/runtests.py b/tests/runtests.py index 1c15f18510..a111ef1436 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -124,7 +124,9 @@ def django_tests(verbosity, tests_to_run): # Run the test suite, including the extra validation tests. from django.test.simple import run_tests - run_tests(test_models, verbosity, extra_tests=extra_tests) + failures = run_tests(test_models, verbosity, extra_tests=extra_tests) + if failures: + sys.exit(failures) # Restore the old settings. settings.INSTALLED_APPS = old_installed_apps @@ -146,5 +148,7 @@ if __name__ == "__main__": options, args = parser.parse_args() if options.settings: os.environ['DJANGO_SETTINGS_MODULE'] = options.settings - + elif "DJANGO_SETTINGS_MODULE" not in os.environ: + parser.error("DJANGO_SETTINGS_MODULE is not set in the environment. " + "Set it or use --settings.") django_tests(int(options.verbosity), args) diff --git a/tests/urls.py b/tests/urls.py index 9fefd93624..9dcdca944e 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -6,7 +6,7 @@ urlpatterns = patterns('', # Always provide the auth system login and logout views (r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}), - (r'^accounts/logout/$', 'django.contrib.auth.views.login'), + (r'^accounts/logout/$', 'django.contrib.auth.views.logout'), # test urlconf for {% url %} template tag (r'^url_tag/', include('regressiontests.templates.urls')), |
