Added a new TestCase class, subclassing django.test.TestCase, in order to

be able to add fixtures once and for all for all tests, instead of loading
them again and again for each test, if running on a database that supports
transaction rollbacks.  In this case, fixtures specified in the perma_fixtures
class attribute will be loaded permanently, and not re-loaded.  Fixtures
specifice as before, in a fixtures class attribute, will be treated as
before.

The downside of this is that as fixtures are loaded and not unloaded, they
can conflict with each other.  The requirements on consistency becomes much
greater.  The effect of this has been to require quite a bit of changes to
the simplified creations of various objects in make_test_data() in cases
where identically named objects occur in fixtures.  Where completely
fictitious objects are created, no conflicts appear.

Also re-wrote parts of test_runner.py to permit global fixtures, loaded
before any tests are run and shared by all.
 - Legacy-Id: 6318
This commit is contained in:
Henrik Levkowetz 2013-09-30 20:22:24 +00:00
parent 4805b4b12e
commit a07f47a4a0
4 changed files with 138 additions and 51 deletions

View file

@ -3,6 +3,7 @@
from log import log
from cache_foreign_key import FKAsOneToOne
from draft_search import normalize_draftname
from test_utils import TestCase
# See http://docs.python.org/tut/node8.html regarding the use of __all__ and
# also regarding the practice of using "from xxx import *" in interactive

View file

@ -10,6 +10,8 @@ from ietf.name.models import *
from ietf.group.models import *
from ietf.person.models import *
import debug
def make_test_data():
# telechat dates
t = datetime.date.today()
@ -21,7 +23,7 @@ def make_test_data():
# groups
secretariat, created = Group.objects.get_or_create(
name="Secretariat",
name="IETF Secretariat",
acronym="secretariat",
state_id="active",
type_id="ietf",
@ -32,27 +34,39 @@ def make_test_data():
state_id="active",
type_id="ietf",
parent=None)
for x in ["irtf", "iab", "ise", "iesg"]:
Group.objects.get_or_create(
name=x.upper(),
acronym=x,
state_id="active",
type_id="ietf",
parent=None)
area, created = Group.objects.get_or_create(
# XXX As given below, the group objects created doesn't match what's in the
# fixtures, so if both are used, things blow up. The code below should
# probably be updated to match what's in the fixtures, making the fixtures
# unnecessary for a number of test cases.
# irtf, created = Group.objects.get_or_create(
# name="IRTF",
# acronym="irtf",
# state_id="active",
# type_id="irtf",
# parent=None)
# for g,t,n,p in [("iab","ietf", "Internet Architecture Board",1), ("ise","ietf", "Independent Submission Editor", None), ("iesg","ietf", "Internet Engineering Steering Group", 1), ]:
# Group.objects.get_or_create(
# name=n,
# acronym=g,
# state_id="active",
# type_id=t,
# parent_id=p)
area = Group.objects.create(
name="Far Future",
acronym="farfut",
state_id="active",
type_id="area",
parent=ietf)
individ, created = Group.objects.get_or_create(
name="Individual submissions",
acronym="none",
state_id="active",
type_id="individ",
parent=None)
# individ, created = Group.objects.get_or_create(
# name="Individual submissions",
# acronym="none",
# state_id="active",
# type_id="individ",
# parent=None)
# mars WG
group, created = Group.objects.get_or_create(
group = Group.objects.create(
name="Martian Special Interest Group",
acronym="mars",
state_id="active",
@ -61,7 +75,7 @@ def make_test_data():
list_email="mars-wg@ietf.org",
)
mars_wg = group
charter, created = Document.objects.get_or_create(
charter = Document.objects.create(
name="charter-ietf-" + group.acronym,
type_id="charter",
title=group.name,
@ -111,7 +125,7 @@ def make_test_data():
# persons
# system
system_person = Person.objects.create(
system_person, created = Person.objects.get_or_create(
# id=0, # special value
name="(System)",
ascii="(System)",
@ -120,36 +134,35 @@ def make_test_data():
system_person.save()
# IANA and RFC Editor groups
iana = Group.objects.create(
iana, created = Group.objects.get_or_create(
name="IANA",
acronym="iana",
state_id="active",
type_id="ietf",
parent=None,
)
rfc_editor = Group.objects.create(
rfc_editor, created = Group.objects.get_or_create(
name="RFC Editor",
acronym="rfceditor",
state_id="active",
type_id="ietf",
type_id="rfcedtyp",
parent=None,
)
if system_person.id != 0: # work around bug in Django
Person.objects.filter(id=system_person.id).update(id=0)
system_person = Person.objects.get(id=0)
alias = Alias(person=system_person, name=system_person.name)
alias.save()
# if system_person.id != 0: # work around bug in Django
# Person.objects.filter(id=system_person.id).update(id=0)
# system_person = Person.objects.get(id=0)
Alias.objects.get_or_create(person=system_person, name=system_person.name)
Email.objects.get_or_create(address="", person=system_person)
# plain IETF'er
u = User.objects.create(username="plain")
plainman = Person.objects.create(
u, created = User.objects.get_or_create(username="plain")
plainman, created = Person.objects.get_or_create(
name="Plain Man",
ascii="Plain Man",
user=u)
email = Email.objects.create(
email, created = Email.objects.get_or_create(
address="plain@example.com",
person=plainman)
@ -308,15 +321,15 @@ def make_test_data():
)
# Secretariat user
u = User.objects.create(id=509, username="wnl")
p = Person.objects.create(
u, created = User.objects.get_or_create(id=509, username="wnl")
p, created = Person.objects.get_or_create(
name="Wanda Lo",
ascii="Wanda Lo",
user=u)
email = Email.objects.create(
email, created = Email.objects.get_or_create(
address="wnl@amsl.com",
person=p)
Role.objects.create(
Role.objects.get_or_create(
name_id="auth",
group=secretariat,
email=email,

View file

@ -37,6 +37,7 @@ import socket
from django.conf import settings
from django.template import TemplateDoesNotExist
from django.test.simple import run_tests as django_run_tests
from django.core.management import call_command
import debug
@ -50,12 +51,14 @@ old_create = None
def safe_create_1(self, verbosity, *args, **kwargs):
global test_database_name, old_create
print " Creating test database..."
settings.DATABASES["default"]["OPTIONS"] = settings.DATABASE_TEST_OPTIONS
print " Using OPTIONS: %s" % settings.DATABASES["default"]["OPTIONS"]
x = old_create(self, 0, *args, **kwargs)
print " Saving test database name "+settings.DATABASES["default"]["NAME"]+"..."
test_database_name = settings.DATABASES["default"]["NAME"]
return x
if settings.DATABASES["default"]["ENGINE"] == 'django.db.backends.mysql':
settings.DATABASES["default"]["OPTIONS"] = settings.DATABASE_TEST_OPTIONS
print " Using OPTIONS: %s" % settings.DATABASES["default"]["OPTIONS"]
test_database_name = old_create(self, 0, *args, **kwargs)
if settings.GLOBAL_TEST_FIXTURES:
print " Loading global test fixtures: %s" % ", ".join(settings.GLOBAL_TEST_FIXTURES)
call_command('loaddata', *settings.GLOBAL_TEST_FIXTURES, verbosity=0, commit=False, database="default")
return test_database_name
def safe_destroy_0_1(*args, **kwargs):
global test_database_name, old_destroy

View file

@ -35,17 +35,21 @@
import os
import re
import sys
import django
from django.db import connection
from django.test import TestCase
from django.test.client import Client
import ietf.settings
from django.conf import settings
from datetime import datetime
import urllib2 as urllib
from difflib import unified_diff
real_database_name = ietf.settings.DATABASES["default"]["NAME"]
import django.test
from django.db import connection, connections, transaction, DEFAULT_DB_ALIAS
from django.test.testcases import connections_support_transactions
from django.test.testcases import disable_transaction_methods
from django.test.client import Client
from django.conf import settings
from django.core.management import call_command
import debug
real_database_name = settings.DATABASES["default"]["NAME"]
import traceback
@ -57,7 +61,7 @@ class RealDatabaseTest:
self._setDatabaseName(newdb)
def tearDownRealDatabase(self):
curdb = self._getDatabaseName()
#curdb = self._getDatabaseName()
#print " Switching database from "+curdb+" to "+self._original_testdb
self._setDatabaseName(self._original_testdb)
@ -105,7 +109,7 @@ def split_url(url):
args = {}
return url, args
class SimpleUrlTestCase(TestCase,RealDatabaseTest):
class SimpleUrlTestCase(django.test.TestCase,RealDatabaseTest):
def setUp(self):
self.setUpRealDatabase()
@ -158,7 +162,8 @@ class SimpleUrlTestCase(TestCase,RealDatabaseTest):
if code in codes:
sys.stdout.write(".")
else:
sys.stdout.write("E")
sys.stdout.write("F")
failed = True
elif self.verbosity > 1:
if code in codes:
print "OK %s %s" % (code, url)
@ -232,7 +237,72 @@ def login_testing_unauthorized(tc, remote_user, url):
tc.client.login(remote_user=remote_user)
class ReverseLazyTest(TestCase):
class ReverseLazyTest(django.test.TestCase):
def test_redirect_with_lazy_reverse(self):
response = self.client.get('/ipr/update/')
self.assertRedirects(response, "/ipr/", status_code=301)
loaded_fixtures = []
class TestCase(django.test.TestCase):
"""
Does basically the same as django.test.TestCase, but if the test database has
support for transactions, it loads perma_fixtures before the transaction which
surrounds every test. This causes the perma_fixtures to stay in the database
after the transaction which surrounds each test is rolled back, which lets us
load each preload_fixture only once. However, it requires that all the
perma_fixtures are consistent with each other and with all regular fixtures
across all applications. You could view the perma_fixtures as on_the_fly
initial_data for tests.
Regular fixtures are re-loaded for each TestCase, as usual.
We don't flush the database, as that triggers a re-load of initial_data.
"""
def _fixture_setup(self):
global loaded_fixtures
if not connections_support_transactions():
if hasattr(self, 'perma_fixtures'):
if not hasattr(self, 'fixtures'):
self.fixtures = self.perma_fixtures
else:
self.fixtures += self.perma_fixtures
return super(TestCase, self)._fixture_setup()
# If the test case has a multi_db=True flag, setup all databases.
# Otherwise, just use default.
if getattr(self, 'multi_db', False):
databases = connections
else:
databases = [DEFAULT_DB_ALIAS]
for db in databases:
if hasattr(self, 'perma_fixtures'):
fixtures = [ fixture for fixture in self.perma_fixtures if not fixture in loaded_fixtures ]
if fixtures:
call_command('loaddata', *fixtures, **{
'verbosity': 0,
'commit': False,
'database': db
})
loaded_fixtures += fixtures
for db in databases:
transaction.enter_transaction_management(using=db)
transaction.managed(True, using=db)
disable_transaction_methods()
from django.contrib.sites.models import Site
Site.objects.clear_cache()
for db in databases:
if hasattr(self, 'fixtures'):
call_command('loaddata', *self.fixtures, **{
'verbosity': 0,
'commit': False,
'database': db
})