Code

Ticket #13636: migrate-doctests-to-unittests.diff

File migrate-doctests-to-unittests.diff, 55.2 KB (added by ericholscher, 4 years ago)

Updated to pass around streams for stdout and stderr

Line 
1diff --git a/django/core/management/base.py b/django/core/management/base.py
2index a07fc7d..358a44e 100644
3--- a/django/core/management/base.py
4+++ b/django/core/management/base.py
5@@ -213,6 +213,8 @@ class BaseCommand(object):
6                 sys.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e)))
7                 sys.exit(1)
8         try:
9+            self.stdout = options.get('stdout', sys.stdout)
10+            self.stderr = options.get('stderr', sys.stderr)
11             if self.requires_model_validation:
12                 self.validate()
13             output = self.handle(*args, **options)
14@@ -223,12 +225,12 @@ class BaseCommand(object):
15                     from django.db import connections, DEFAULT_DB_ALIAS
16                     connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
17                     if connection.ops.start_transaction_sql():
18-                        print self.style.SQL_KEYWORD(connection.ops.start_transaction_sql())
19-                print output
20+                        self.stdout.write(self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()))
21+                self.stdout.write(output)
22                 if self.output_transaction:
23-                    print self.style.SQL_KEYWORD(connection.ops.end_transaction_sql())
24+                    self.stdout.write(self.style.SQL_KEYWORD("COMMIT;"))
25         except CommandError, e:
26-            sys.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e)))
27+            self.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e)))
28             sys.exit(1)
29 
30     def validate(self, app=None, display_num_errors=False):
31@@ -250,7 +252,7 @@ class BaseCommand(object):
32             error_text = s.read()
33             raise CommandError("One or more models did not validate:\n%s" % error_text)
34         if display_num_errors:
35-            print "%s error%s found" % (num_errors, num_errors != 1 and 's' or '')
36+            self.stdout.write("%s error%s found" % (num_errors, num_errors != 1 and 's' or ''))
37 
38     def handle(self, *args, **options):
39         """
40diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py
41index 6212d61..8f78170 100644
42--- a/django/core/management/commands/loaddata.py
43+++ b/django/core/management/commands/loaddata.py
44@@ -112,7 +112,7 @@ class Command(BaseCommand):
45 
46             if formats:
47                 if verbosity > 1:
48-                    print "Loading '%s' fixtures..." % fixture_name
49+                    self.stdout.write("Loading '%s' fixtures..." % fixture_name)
50             else:
51                 sys.stderr.write(
52                     self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format." %
53@@ -128,7 +128,7 @@ class Command(BaseCommand):
54 
55             for fixture_dir in fixture_dirs:
56                 if verbosity > 1:
57-                    print "Checking %s for fixtures..." % humanize(fixture_dir)
58+                    self.stdout.write("Checking %s for fixtures..." % humanize(fixture_dir))
59 
60                 label_found = False
61                 for combo in product([using, None], formats, compression_formats):
62@@ -141,16 +141,16 @@ class Command(BaseCommand):
63                     )
64 
65                     if verbosity > 1:
66-                        print "Trying %s for %s fixture '%s'..." % \
67-                            (humanize(fixture_dir), file_name, fixture_name)
68+                        self.stdout.write("Trying %s for %s fixture '%s'..." % \
69+                            (humanize(fixture_dir), file_name, fixture_name))
70                     full_path = os.path.join(fixture_dir, file_name)
71                     open_method = compression_types[compression_format]
72                     try:
73                         fixture = open_method(full_path, 'r')
74                         if label_found:
75                             fixture.close()
76-                            print self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting." %
77-                                (fixture_name, humanize(fixture_dir)))
78+                            self.stdout.write(self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting." %
79+                                (fixture_name, humanize(fixture_dir))))
80                             transaction.rollback(using=using)
81                             transaction.leave_transaction_management(using=using)
82                             return
83@@ -158,8 +158,8 @@ class Command(BaseCommand):
84                             fixture_count += 1
85                             objects_in_fixture = 0
86                             if verbosity > 0:
87-                                print "Installing %s fixture '%s' from %s." % \
88-                                    (format, fixture_name, humanize(fixture_dir))
89+                                self.stdout.write("Installing %s fixture '%s' from %s." % \
90+                                    (format, fixture_name, humanize(fixture_dir)))
91                             try:
92                                 objects = serializers.deserialize(format, fixture, using=using)
93                                 for obj in objects:
94@@ -198,8 +198,8 @@ class Command(BaseCommand):
95 
96                     except Exception, e:
97                         if verbosity > 1:
98-                            print "No %s fixture '%s' in %s." % \
99-                                (format, fixture_name, humanize(fixture_dir))
100+                            self.stdout.write("No %s fixture '%s' in %s." % \
101+                                (format, fixture_name, humanize(fixture_dir)))
102 
103         # If we found even one object in a fixture, we need to reset the
104         # database sequences.
105@@ -207,7 +207,7 @@ class Command(BaseCommand):
106             sequence_sql = connection.ops.sequence_reset_sql(self.style, models)
107             if sequence_sql:
108                 if verbosity > 1:
109-                    print "Resetting sequences"
110+                    self.stdout.write("Resetting sequences")
111                 for line in sequence_sql:
112                     cursor.execute(line)
113 
114@@ -217,10 +217,10 @@ class Command(BaseCommand):
115 
116         if object_count == 0:
117             if verbosity > 0:
118-                print "No fixtures found."
119+                self.stdout.write("No fixtures found.")
120         else:
121             if verbosity > 0:
122-                print "Installed %d object(s) from %d fixture(s)" % (object_count, fixture_count)
123+                self.stdout.write("Installed %d object(s) from %d fixture(s)" % (object_count, fixture_count))
124 
125         # Close the DB connection. This is required as a workaround for an
126         # edge case in MySQL: if the same connection is used to
127diff --git a/docs/howto/custom-management-commands.txt b/docs/howto/custom-management-commands.txt
128index f8b173c..8ba08f2 100644
129--- a/docs/howto/custom-management-commands.txt
130+++ b/docs/howto/custom-management-commands.txt
131@@ -62,7 +62,15 @@ look like this:
132                 poll.opened = False
133                 poll.save()
134 
135-                print 'Successfully closed poll "%s"' % poll_id
136+                self.stdout.write('Successfully closed poll "%s"' % poll_id)
137+
138+.. note::
139+    When you are using management commands, ``self.stdout`` and ``self.stderr``
140+    should be used to send output. Strings that are returned from your command
141+    will be output to ``self.stdout``. ``stdout`` and ``stderr`` can be
142+    overridden by passing arguments into ``call_command``, which will be used
143+    mainly used in testing.
144+
145 
146 The new custom command can be called using ``python manage.py closepoll
147 <poll_id>``.
148diff --git a/tests/modeltests/fixtures/models.py b/tests/modeltests/fixtures/models.py
149index 46e07a5..216a8e2 100644
150--- a/tests/modeltests/fixtures/models.py
151+++ b/tests/modeltests/fixtures/models.py
152@@ -90,230 +90,3 @@ class Book(models.Model):
153 
154     class Meta:
155         ordering = ('name',)
156-
157-__test__ = {'API_TESTS': """
158->>> from django.core import management
159->>> from django.db.models import get_app
160-
161-# Reset the database representation of this app.
162-# This will return the database to a clean initial state.
163->>> management.call_command('flush', verbosity=0, interactive=False)
164-
165-# Syncdb introduces 1 initial data object from initial_data.json.
166->>> Article.objects.all()
167-[<Article: Python program becomes self aware>]
168-
169-# Load fixture 1. Single JSON file, with two objects.
170->>> management.call_command('loaddata', 'fixture1.json', verbosity=0)
171->>> Article.objects.all()
172-[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
173-
174-# Dump the current contents of the database as a JSON fixture
175->>> management.call_command('dumpdata', 'fixtures', format='json')
176-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"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"}}]
177-
178-# Try just dumping the contents of fixtures.Category
179->>> management.call_command('dumpdata', 'fixtures.Category', format='json')
180-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]
181-
182-# ...and just fixtures.Article
183->>> management.call_command('dumpdata', 'fixtures.Article', format='json')
184-[{"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"}}]
185-
186-# ...and both
187->>> management.call_command('dumpdata', 'fixtures.Category', 'fixtures.Article', format='json')
188-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"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"}}]
189-
190-# Specify a specific model twice
191->>> management.call_command('dumpdata', 'fixtures.Article', 'fixtures.Article', format='json')
192-[{"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"}}]
193-
194-# Specify a dump that specifies Article both explicitly and implicitly
195->>> management.call_command('dumpdata', 'fixtures.Article', 'fixtures', format='json')
196-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"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"}}]
197-
198-# Same again, but specify in the reverse order
199->>> management.call_command('dumpdata', 'fixtures', 'fixtures.Article', format='json')
200-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"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"}}]
201-
202-# Specify one model from one application, and an entire other application.
203->>> management.call_command('dumpdata', 'fixtures.Category', 'sites', format='json')
204-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}]
205-
206-# Load fixture 2. JSON file imported by default. Overwrites some existing objects
207->>> management.call_command('loaddata', 'fixture2.json', verbosity=0)
208->>> Article.objects.all()
209-[<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>]
210-
211-# Load fixture 3, XML format.
212->>> management.call_command('loaddata', 'fixture3.xml', verbosity=0)
213->>> Article.objects.all()
214-[<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>]
215-
216-# Load fixture 6, JSON file with dynamic ContentType fields. Testing ManyToOne.
217->>> management.call_command('loaddata', 'fixture6.json', verbosity=0)
218->>> Tag.objects.all()
219-[<Tag: <Article: Copyright is fine the way it is> tagged "copyright">, <Tag: <Article: Copyright is fine the way it is> tagged "law">]
220-
221-# Load fixture 7, XML file with dynamic ContentType fields. Testing ManyToOne.
222->>> management.call_command('loaddata', 'fixture7.xml', verbosity=0)
223->>> Tag.objects.all()
224-[<Tag: <Article: Copyright is fine the way it is> tagged "copyright">, <Tag: <Article: Copyright is fine the way it is> tagged "legal">, <Tag: <Article: Django conquers world!> tagged "django">, <Tag: <Article: Django conquers world!> tagged "world domination">]
225-
226-# Load fixture 8, JSON file with dynamic Permission fields. Testing ManyToMany.
227->>> management.call_command('loaddata', 'fixture8.json', verbosity=0)
228->>> Visa.objects.all()
229-[<Visa: Django Reinhardt Can add user, Can change user, Can delete user>, <Visa: Stephane Grappelli Can add user>, <Visa: Prince >]
230-
231-# Load fixture 9, XML file with dynamic Permission fields. Testing ManyToMany.
232->>> management.call_command('loaddata', 'fixture9.xml', verbosity=0)
233->>> Visa.objects.all()
234-[<Visa: Django Reinhardt Can add user, Can change user, Can delete user>, <Visa: Stephane Grappelli Can add user, Can delete user>, <Visa: Artist formerly known as "Prince" Can change user>]
235-
236->>> Book.objects.all()
237-[<Book: Music for all ages by Artist formerly known as "Prince" and Django Reinhardt>]
238-
239-# Load a fixture that doesn't exist
240->>> management.call_command('loaddata', 'unknown.json', verbosity=0)
241-
242-# object list is unaffected
243->>> Article.objects.all()
244-[<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>]
245-
246-# By default, you get raw keys on dumpdata
247->>> management.call_command('dumpdata', 'fixtures.book', format='json')
248-[{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [3, 1]}}]
249-
250-# But you can get natural keys if you ask for them and they are available
251->>> management.call_command('dumpdata', 'fixtures.book', format='json', use_natural_keys=True)
252-[{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]
253-
254-# Dump the current contents of the database as a JSON fixture
255->>> management.call_command('dumpdata', 'fixtures', format='json', use_natural_keys=True)
256-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 5, "model": "fixtures.article", "fields": {"headline": "XML identified as leading cause of cancer", "pub_date": "2006-06-16 16:00:00"}}, {"pk": 4, "model": "fixtures.article", "fields": {"headline": "Django conquers world!", "pub_date": "2006-06-16 15:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16 14:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker on TV is great!", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "legal", "tagged_id": 3}}, {"pk": 3, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "django", "tagged_id": 4}}, {"pk": 4, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "world domination", "tagged_id": 4}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Artist formerly known as \\"Prince\\""}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}, {"pk": 1, "model": "fixtures.visa", "fields": {"person": ["Django Reinhardt"], "permissions": [["add_user", "auth", "user"], ["change_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 2, "model": "fixtures.visa", "fields": {"person": ["Stephane Grappelli"], "permissions": [["add_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 3, "model": "fixtures.visa", "fields": {"person": ["Artist formerly known as \\"Prince\\""], "permissions": [["change_user", "auth", "user"]]}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]
257-
258-# Dump the current contents of the database as an XML fixture
259->>> management.call_command('dumpdata', 'fixtures', format='xml', use_natural_keys=True)
260-<?xml version="1.0" encoding="utf-8"?>
261-<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</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><object pk="4" model="fixtures.article"><field type="CharField" name="headline">Django conquers world!</field><field type="DateTimeField" name="pub_date">2006-06-16 15:00:00</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Copyright is fine the way it is</field><field type="DateTimeField" name="pub_date">2006-06-16 14:00:00</field></object><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="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">legal</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="3" model="fixtures.tag"><field type="CharField" name="name">django</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="4" model="fixtures.tag"><field type="CharField" name="name">world domination</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Artist formerly known as "Prince"</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="1" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Django Reinhardt</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="2" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Stephane Grappelli</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="3" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Artist formerly known as "Prince"</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="1" model="fixtures.book"><field type="CharField" name="name">Music for all ages</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"><object><natural>Artist formerly known as "Prince"</natural></object><object><natural>Django Reinhardt</natural></object></field></object></django-objects>
262-
263-"""}
264-
265-# Database flushing does not work on MySQL with the default storage engine
266-# because it requires transaction support.
267-if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
268-    __test__['API_TESTS'] += \
269-"""
270-# Reset the database representation of this app. This will delete all data.
271->>> management.call_command('flush', verbosity=0, interactive=False)
272->>> Article.objects.all()
273-[<Article: Python program becomes self aware>]
274-
275-# Load fixture 1 again, using format discovery
276->>> management.call_command('loaddata', 'fixture1', verbosity=0)
277->>> Article.objects.all()
278-[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
279-
280-# Try to load fixture 2 using format discovery; this will fail
281-# because there are two fixture2's in the fixtures directory
282->>> management.call_command('loaddata', 'fixture2', verbosity=0) # doctest: +ELLIPSIS
283-Multiple fixtures named 'fixture2' in '...fixtures'. Aborting.
284-
285-# object list is unaffected
286->>> Article.objects.all()
287-[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
288-
289-# Dump the current contents of the database as a JSON fixture
290->>> management.call_command('dumpdata', 'fixtures', format='json')
291-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"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"}}]
292-
293-# Load fixture 4 (compressed), using format discovery
294->>> management.call_command('loaddata', 'fixture4', verbosity=0)
295->>> Article.objects.all()
296-[<Article: Django pets kitten>, <Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
297-
298->>> management.call_command('flush', verbosity=0, interactive=False)
299-
300-# Load fixture 4 (compressed), using format specification
301->>> management.call_command('loaddata', 'fixture4.json', verbosity=0)
302->>> Article.objects.all()
303-[<Article: Django pets kitten>, <Article: Python program becomes self aware>]
304-
305->>> management.call_command('flush', verbosity=0, interactive=False)
306-
307-# Load fixture 5 (compressed), using format *and* compression specification
308->>> management.call_command('loaddata', 'fixture5.json.zip', verbosity=0)
309->>> Article.objects.all()
310-[<Article: WoW subscribers now outnumber readers>, <Article: Python program becomes self aware>]
311-
312->>> management.call_command('flush', verbosity=0, interactive=False)
313-
314-# Load fixture 5 (compressed), only compression specification
315->>> management.call_command('loaddata', 'fixture5.zip', verbosity=0)
316->>> Article.objects.all()
317-[<Article: WoW subscribers now outnumber readers>, <Article: Python program becomes self aware>]
318-
319->>> management.call_command('flush', verbosity=0, interactive=False)
320-
321-# Try to load fixture 5 using format and compression discovery; this will fail
322-# because there are two fixture5's in the fixtures directory
323->>> management.call_command('loaddata', 'fixture5', verbosity=0) # doctest: +ELLIPSIS
324-Multiple fixtures named 'fixture5' in '...fixtures'. Aborting.
325-
326->>> management.call_command('flush', verbosity=0, interactive=False)
327-
328-# Load db fixtures 1 and 2. These will load using the 'default' database identifier implicitly
329->>> management.call_command('loaddata', 'db_fixture_1', verbosity=0)
330->>> management.call_command('loaddata', 'db_fixture_2', verbosity=0)
331->>> Article.objects.all()
332-[<Article: Who needs more than one database?>, <Article: Who needs to use compressed data?>, <Article: Python program becomes self aware>]
333-
334->>> management.call_command('flush', verbosity=0, interactive=False)
335-
336-# Load db fixtures 1 and 2. These will load using the 'default' database identifier explicitly
337->>> management.call_command('loaddata', 'db_fixture_1', verbosity=0, using='default')
338->>> management.call_command('loaddata', 'db_fixture_2', verbosity=0, using='default')
339->>> Article.objects.all()
340-[<Article: Who needs more than one database?>, <Article: Who needs to use compressed data?>, <Article: Python program becomes self aware>]
341-
342->>> management.call_command('flush', verbosity=0, interactive=False)
343-
344-# Try to load db fixture 3. This won't load because the database identifier doesn't match
345->>> management.call_command('loaddata', 'db_fixture_3', verbosity=0)
346->>> Article.objects.all()
347-[<Article: Python program becomes self aware>]
348-
349->>> management.call_command('loaddata', 'db_fixture_3', verbosity=0, using='default')
350->>> Article.objects.all()
351-[<Article: Python program becomes self aware>]
352-
353->>> management.call_command('flush', verbosity=0, interactive=False)
354-
355-# Load back in fixture 1, we need the articles from it
356->>> management.call_command('loaddata', 'fixture1', verbosity=0)
357-
358-# Try to load fixture 6 using format discovery
359->>> management.call_command('loaddata', 'fixture6', verbosity=0)
360->>> Tag.objects.all()
361-[<Tag: <Article: Time to reform copyright> tagged "copyright">, <Tag: <Article: Time to reform copyright> tagged "law">]
362-
363-# Dump the current contents of the database as a JSON fixture
364->>> management.call_command('dumpdata', 'fixtures', format='json', use_natural_keys=True)
365-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"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"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "law", "tagged_id": 3}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Prince"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}]
366-
367-# Dump the current contents of the database as an XML fixture
368->>> management.call_command('dumpdata', 'fixtures', format='xml', use_natural_keys=True)
369-<?xml version="1.0" encoding="utf-8"?>
370-<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Time to reform copyright</field><field type="DateTimeField" name="pub_date">2006-06-16 13:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker has no place on ESPN</field><field type="DateTimeField" name="pub_date">2006-06-16 12:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">law</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Prince</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object></django-objects>
371-
372-"""
373-
374-from django.test import TestCase
375-
376-class SampleTestCase(TestCase):
377-    fixtures = ['fixture1.json', 'fixture2.json']
378-
379-    def testClassFixtures(self):
380-        "Check that test case has installed 4 fixture objects"
381-        self.assertEqual(Article.objects.count(), 4)
382-        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>]")
383diff --git a/tests/modeltests/fixtures/tests.py b/tests/modeltests/fixtures/tests.py
384new file mode 100644
385index 0000000..8d62a02
386--- /dev/null
387+++ b/tests/modeltests/fixtures/tests.py
388@@ -0,0 +1,187 @@
389+import StringIO
390+import sys
391+
392+from django.test import TestCase, TransactionTestCase
393+from django.core import management
394+
395+from models import Article, Blog, Book, Category, Person, Tag, Visa
396+
397+class FixtureLoadingTests(TestCase):
398+    fixtures = ['fixture1.json', 'fixture2.json']
399+
400+    def testClassFixtures(self):
401+        "Check that test case has installed 4 fixture objects"
402+        self.assertEqual(Article.objects.count(), 4)
403+        self.assertQuerysetEqual(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>'])
404+
405+class MainFixtureLoadingTests(TestCase):
406+
407+    def _dumpdata_assert(self, args, output, format='json', natural_keys=False):
408+        new_io = StringIO.StringIO()
409+        management.call_command('dumpdata', *args, format=format, stdout=new_io, use_natural_keys=natural_keys)
410+        command_output = new_io.getvalue().strip()
411+        self.assertEqual(command_output, output)
412+
413+    def test_initial_data(self):
414+        # Syncdb introduces 1 initial data object from initial_data.json.
415+        self.assertQuerysetEqual(Article.objects.all(), ['Python program becomes self aware'], str)
416+
417+    def test_loading_and_dumping(self):
418+        new_io = StringIO.StringIO()
419+
420+        # Load fixture 1. Single JSON file, with two objects.
421+        management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False)
422+        self.assertQuerysetEqual(Article.objects.all(), ['Time to reform copyright', 'Poker has no place on ESPN', 'Python program becomes self aware'], str)
423+
424+        # Dump the current contents of the database as a JSON fixture
425+        self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"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"}}]')
426+
427+        # Try just dumping the contents of fixtures.Category
428+        self._dumpdata_assert(['fixtures.Category'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]')
429+
430+        # ...and just fixtures.Article
431+        self._dumpdata_assert(['fixtures.Article'], '[{"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"}}]')
432+
433+        # ...and both
434+        self._dumpdata_assert(['fixtures.Category', 'fixtures.Article'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"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"}}]')
435+
436+        # Specify a specific model twice
437+        self._dumpdata_assert(['fixtures.Article', 'fixtures.Article'], '[{"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"}}]')
438+
439+        # Specify a dump that specifies Article both explicitly and implicitly
440+        self._dumpdata_assert(['fixtures.Article', 'fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"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"}}]')
441+
442+        # Same again, but specify in the reverse order
443+        self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"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"}}]')
444+
445+        # Specify one model from one application, and an entire other application.
446+        self._dumpdata_assert(['fixtures.Category', 'sites'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}]')
447+
448+        # Load fixture 2. JSON file imported by default. Overwrites some existing objects
449+        management.call_command('loaddata', 'fixture2.json', verbosity=0, commit=False)
450+        self.assertQuerysetEqual(Article.objects.all(), ['Django conquers world!', 'Copyright is fine the way it is', 'Poker has no place on ESPN', 'Python program becomes self aware'], str)
451+
452+        # Load fixture 3, XML format.
453+        management.call_command('loaddata', 'fixture3.xml', verbosity=0, commit=False)
454+        self.assertQuerysetEqual(Article.objects.all(), ['XML identified as leading cause of cancer', 'Django conquers world!', 'Copyright is fine the way it is', 'Poker on TV is great!', 'Python program becomes self aware'], str)
455+
456+        # Load fixture 6, JSON file with dynamic ContentType fields. Testing ManyToOne.
457+        management.call_command('loaddata', 'fixture6.json', verbosity=0, commit=False)
458+        self.assertQuerysetEqual(Tag.objects.all(), ['<Article: Copyright is fine the way it is> tagged "copyright"', '<Article: Copyright is fine the way it is> tagged "law"'], str)
459+
460+        # Load fixture 7, XML file with dynamic ContentType fields. Testing ManyToOne.
461+        management.call_command('loaddata', 'fixture7.xml', verbosity=0, commit=False)
462+        self.assertQuerysetEqual(Tag.objects.all(), ['<Article: Copyright is fine the way it is> tagged "copyright"', '<Article: Copyright is fine the way it is> tagged "legal"', '<Article: Django conquers world!> tagged "django"', '<Article: Django conquers world!> tagged "world domination"'], str)
463+
464+        # Load fixture 8, JSON file with dynamic Permission fields. Testing ManyToMany.
465+        management.call_command('loaddata', 'fixture8.json', verbosity=0, commit=False)
466+        self.assertQuerysetEqual(Visa.objects.all(), ['Django Reinhardt Can add user, Can change user, Can delete user', 'Stephane Grappelli Can add user', 'Prince '], str)
467+
468+        # Load fixture 9, XML file with dynamic Permission fields. Testing ManyToMany.
469+        management.call_command('loaddata', 'fixture9.xml', verbosity=0, commit=False)
470+        self.assertQuerysetEqual(Visa.objects.all(), ['Django Reinhardt Can add user, Can change user, Can delete user', 'Stephane Grappelli Can add user, Can delete user', 'Artist formerly known as "Prince" Can change user'], str)
471+
472+        self.assertQuerysetEqual(Book.objects.all(), ['Music for all ages by Artist formerly known as "Prince" and Django Reinhardt'], str)
473+
474+        # Load a fixture that doesn't exist
475+        management.call_command('loaddata', 'unknown.json', verbosity=0, commit=False)
476+
477+        # object list is unaffected
478+        self.assertQuerysetEqual(Article.objects.all(), ['XML identified as leading cause of cancer', 'Django conquers world!', 'Copyright is fine the way it is', 'Poker on TV is great!', 'Python program becomes self aware'], str)
479+
480+        # By default, you get raw keys on dumpdata
481+        self._dumpdata_assert(['fixtures.book'], '[{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [3, 1]}}]')
482+
483+        # But you can get natural keys if you ask for them and they are available
484+        self._dumpdata_assert(['fixtures.book'], '[{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]', natural_keys=True)
485+
486+        # Dump the current contents of the database as a JSON fixture
487+        self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 5, "model": "fixtures.article", "fields": {"headline": "XML identified as leading cause of cancer", "pub_date": "2006-06-16 16:00:00"}}, {"pk": 4, "model": "fixtures.article", "fields": {"headline": "Django conquers world!", "pub_date": "2006-06-16 15:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16 14:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker on TV is great!", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "legal", "tagged_id": 3}}, {"pk": 3, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "django", "tagged_id": 4}}, {"pk": 4, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "world domination", "tagged_id": 4}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Artist formerly known as \\"Prince\\""}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}, {"pk": 1, "model": "fixtures.visa", "fields": {"person": ["Django Reinhardt"], "permissions": [["add_user", "auth", "user"], ["change_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 2, "model": "fixtures.visa", "fields": {"person": ["Stephane Grappelli"], "permissions": [["add_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 3, "model": "fixtures.visa", "fields": {"person": ["Artist formerly known as \\"Prince\\""], "permissions": [["change_user", "auth", "user"]]}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]', natural_keys=True)
488+
489+        # Dump the current contents of the database as an XML fixture
490+        self._dumpdata_assert(['fixtures'], """<?xml version="1.0" encoding="utf-8"?>
491+<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</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><object pk="4" model="fixtures.article"><field type="CharField" name="headline">Django conquers world!</field><field type="DateTimeField" name="pub_date">2006-06-16 15:00:00</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Copyright is fine the way it is</field><field type="DateTimeField" name="pub_date">2006-06-16 14:00:00</field></object><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="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">legal</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="3" model="fixtures.tag"><field type="CharField" name="name">django</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="4" model="fixtures.tag"><field type="CharField" name="name">world domination</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Artist formerly known as "Prince"</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="1" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Django Reinhardt</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="2" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Stephane Grappelli</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="3" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Artist formerly known as "Prince"</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="1" model="fixtures.book"><field type="CharField" name="name">Music for all ages</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"><object><natural>Artist formerly known as "Prince"</natural></object><object><natural>Django Reinhardt</natural></object></field></object></django-objects>""", format='xml', natural_keys=True)
492+
493+
494+    def test_compress_format_loading(self):
495+        # Load fixture 4 (compressed), using format specification
496+        management.call_command('loaddata', 'fixture4.json', verbosity=0, commit=False)
497+        self.assertQuerysetEqual(Article.objects.all(), ['Django pets kitten', 'Python program becomes self aware'], str)
498+
499+    def test_compressed_specified_loading(self):
500+        # Load fixture 5 (compressed), using format *and* compression specification
501+        management.call_command('loaddata', 'fixture5.json.zip', verbosity=0, commit=False)
502+        self.assertQuerysetEqual(Article.objects.all(), ['WoW subscribers now outnumber readers', 'Python program becomes self aware'], str)
503+
504+    def test_compressed_loading(self):
505+        # Load fixture 5 (compressed), only compression specification
506+        management.call_command('loaddata', 'fixture5.zip', verbosity=0, commit=False)
507+        self.assertQuerysetEqual(Article.objects.all(), ['WoW subscribers now outnumber readers', 'Python program becomes self aware'], str)
508+
509+    def test_db_loading(self):
510+        # Load db fixtures 1 and 2. These will load using the 'default' database identifier implicitly
511+        management.call_command('loaddata', 'db_fixture_1', verbosity=0, commit=False)
512+        management.call_command('loaddata', 'db_fixture_2', verbosity=0, commit=False)
513+        self.assertQuerysetEqual(Article.objects.all(), ['Who needs more than one database?', 'Who needs to use compressed data?', 'Python program becomes self aware'], str)
514+
515+    def test_loading_using(self):
516+
517+        # Load db fixtures 1 and 2. These will load using the 'default' database identifier explicitly
518+        management.call_command('loaddata', 'db_fixture_1', verbosity=0, using='default', commit=False)
519+        management.call_command('loaddata', 'db_fixture_2', verbosity=0, using='default', commit=False)
520+        self.assertQuerysetEqual(Article.objects.all(), ['Who needs more than one database?', 'Who needs to use compressed data?', 'Python program becomes self aware'], str)
521+
522+    def test_unmatched_identifier_loading(self):
523+
524+        # Try to load db fixture 3. This won't load because the database identifier doesn't match
525+        management.call_command('loaddata', 'db_fixture_3', verbosity=0, commit=False)
526+        self.assertQuerysetEqual(Article.objects.all(), ['Python program becomes self aware'], str)
527+
528+        management.call_command('loaddata', 'db_fixture_3', verbosity=0, using='default', commit=False)
529+        self.assertQuerysetEqual(Article.objects.all(), ['Python program becomes self aware'], str)
530+
531+    def test_xml_json_dumping(self):
532+
533+        # Load back in fixture 1, we need the articles from it
534+        management.call_command('loaddata', 'fixture1', verbosity=0, commit=False)
535+
536+        # Try to load fixture 6 using format discovery
537+        management.call_command('loaddata', 'fixture6', verbosity=0, commit=False)
538+        self.assertQuerysetEqual(Tag.objects.all(), ['<Article: Time to reform copyright> tagged "copyright"', '<Article: Time to reform copyright> tagged "law"'], str)
539+
540+        # Dump the current contents of the database as a JSON fixture
541+        self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"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"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "law", "tagged_id": 3}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Prince"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}]', natural_keys=True)
542+
543+        # Dump the current contents of the database as an XML fixture
544+        self._dumpdata_assert(['fixtures'], """<?xml version="1.0" encoding="utf-8"?>
545+<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Time to reform copyright</field><field type="DateTimeField" name="pub_date">2006-06-16 13:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker has no place on ESPN</field><field type="DateTimeField" name="pub_date">2006-06-16 12:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">law</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Prince</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object></django-objects>""", format='xml', natural_keys=True)
546+
547+class FixtureTransactionTests(TransactionTestCase):
548+
549+    def _dumpdata_assert(self, args, output, format='json'):
550+        new_io = StringIO.StringIO()
551+        management.call_command('dumpdata', *args, format=format, stdout=new_io)
552+        command_output = new_io.getvalue().strip()
553+        self.assertEqual(command_output, output)
554+
555+    def test_format_discovery(self):
556+        # Load fixture 1 again, using format discovery
557+        management.call_command('loaddata', 'fixture1', verbosity=0, commit=False)
558+        self.assertQuerysetEqual(Article.objects.all(), ['Time to reform copyright', 'Poker has no place on ESPN', 'Python program becomes self aware'], str)
559+
560+        # Try to load fixture 2 using format discovery; this will fail
561+        # because there are two fixture2's in the fixtures directory
562+        new_io = StringIO.StringIO()
563+        management.call_command('loaddata', 'fixture2', verbosity=0, stdout=new_io)
564+        output = new_io.getvalue().strip()
565+        self.assertTrue("Multiple fixtures named 'fixture2'" in output)
566+
567+        # object list is unaffected
568+        self.assertQuerysetEqual(Article.objects.all(), ['Time to reform copyright', 'Poker has no place on ESPN', 'Python program becomes self aware'], str)
569+
570+        # Dump the current contents of the database as a JSON fixture
571+        self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"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"}}]')
572+
573+        # Load fixture 4 (compressed), using format discovery
574+        management.call_command('loaddata', 'fixture4', verbosity=0, commit=False)
575+        self.assertQuerysetEqual(Article.objects.all(), ['Django pets kitten', 'Time to reform copyright', 'Poker has no place on ESPN', 'Python program becomes self aware'], str)
576\ No newline at end of file