diff --git a/ietf/utils/__init__.py b/ietf/utils/__init__.py index 19ad8d4e9..8a22d0761 100644 --- a/ietf/utils/__init__.py +++ b/ietf/utils/__init__.py @@ -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 diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index 1e9ffc83c..8d697b278 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -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, diff --git a/ietf/utils/test_runner.py b/ietf/utils/test_runner.py index 064874473..30206647b 100644 --- a/ietf/utils/test_runner.py +++ b/ietf/utils/test_runner.py @@ -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 diff --git a/ietf/utils/test_utils.py b/ietf/utils/test_utils.py index ed9c4fbcd..cee8870af 100644 --- a/ietf/utils/test_utils.py +++ b/ietf/utils/test_utils.py @@ -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 + }) + +