Ticket #8138: django_transaction_tests_3.diff

File django_transaction_tests_3.diff, 14.8 KB (added by Marc Remolt, 16 years ago)
  • src/django/test/client.py

     
    1818from django.utils.encoding import smart_str
    1919from django.utils.http import urlencode
    2020from django.utils.itercompat import is_iterable
     21from django.db import transaction
    2122
    2223BOUNDARY = 'BoUnDaRyStRiNg'
    2324MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY
     
    6061
    6162        signals.request_started.send(sender=self.__class__)
    6263        try:
     64            transaction.enter_transaction_management()
     65            transaction.managed(True)
    6366            request = WSGIRequest(environ)
    6467            response = self.get_response(request)
     68            transaction.commit()
     69            transaction.leave_transaction_management()
    6570
    6671            # Apply response middleware.
    6772            for middleware_method in self._response_middleware:
     
    134139        '',
    135140        file.read()
    136141    ]
    137    
     142
    138143class Client:
    139144    """
    140145    A class that can act as a client for testing purposes.
     
    170175        Obtains the current session variables.
    171176        """
    172177        if 'django.contrib.sessions' in settings.INSTALLED_APPS:
     178            # If a session db change hangs in a transaction, commit,
     179            # just to be sure.
     180            if transaction.is_dirty():
     181                transaction.commit()
    173182            engine = __import__(settings.SESSION_ENGINE, {}, {}, [''])
    174183            cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None)
    175184            if cookie:
  • src/django/test/testcases.py

     
     1
    12import re
    23import unittest
    34from urlparse import urlsplit, urlunsplit
     
    89from django.core.management import call_command
    910from django.core.urlresolvers import clear_url_caches
    1011from django.db import transaction
     12from django.db.models.signals import post_save
    1113from django.http import QueryDict
    1214from django.test import _doctest as doctest
    1315from django.test.client import Client
     
    5557        """Tries to do a 'xml-comparision' of want and got.  Plain string
    5658        comparision doesn't always work because, for example, attribute
    5759        ordering should not be important.
    58        
     60
    5961        Based on http://codespeak.net/svn/lxml/trunk/src/lxml/doctestcompare.py
    6062        """
    6163        _norm_whitespace_re = re.compile(r'[ \t\n][ \t\n]+')
     
    102104            wrapper = '<root>%s</root>'
    103105            want = wrapper % want
    104106            got = wrapper % got
    105            
     107
    106108        # Parse the want and got strings, and compare the parsings.
    107109        try:
    108110            want_root = parseString(want).firstChild
     
    169171        # side effects on other tests.
    170172        transaction.rollback_unless_managed()
    171173
    172 class TestCase(unittest.TestCase):
     174    def run(self, test, compileflags=None, out=None, clear_globs=True):
     175        """
     176        Wraps the parent run() and encloses it in a transaction.
     177        """
     178        transaction.enter_transaction_management()
     179        transaction.managed(True)
     180        result = doctest.DocTestRunner.run(self, test, compileflags, out, clear_globs)
     181        transaction.rollback()
     182        transaction.leave_transaction_management()
     183        return result
     184
     185class TransactionTestCase(unittest.TestCase):
    173186    def _pre_setup(self):
    174187        """Performs any pre-test setup. This includes:
    175188
    176189            * Flushing the database.
    177             * If the Test Case class has a 'fixtures' member, installing the 
     190            * If the Test Case class has a 'fixtures' member, installing the
    178191              named fixtures.
    179192            * If the Test Case class has a 'urls' member, replace the
    180193              ROOT_URLCONF with it.
    181194            * Clearing the mail test outbox.
    182195        """
     196        self._fixture_setup()
     197        self._urlconf_setup()
     198        mail.outbox = []
     199
     200    def _fixture_setup(self):
    183201        call_command('flush', verbosity=0, interactive=False)
    184202        if hasattr(self, 'fixtures'):
    185203            # We have to use this slightly awkward syntax due to the fact
    186204            # that we're using *args and **kwargs together.
    187205            call_command('loaddata', *self.fixtures, **{'verbosity': 0})
     206
     207    def _urlconf_setup(self):
    188208        if hasattr(self, 'urls'):
    189209            self._old_root_urlconf = settings.ROOT_URLCONF
    190210            settings.ROOT_URLCONF = self.urls
    191211            clear_url_caches()
    192         mail.outbox = []
    193212
    194213    def __call__(self, result=None):
    195214        """
     
    206225            import sys
    207226            result.addError(self, sys.exc_info())
    208227            return
    209         super(TestCase, self).__call__(result)
     228        super(TransactionTestCase, self).__call__(result)
    210229        try:
    211230            self._post_teardown()
    212231        except (KeyboardInterrupt, SystemExit):
     
    221240
    222241            * Putting back the original ROOT_URLCONF if it was changed.
    223242        """
     243        self._fixture_teardown()
     244        self._urlconf_teardown()
     245
     246    def _fixture_teardown(self):
     247        pass
     248
     249    def _urlconf_teardown(self):
    224250        if hasattr(self, '_old_root_urlconf'):
    225251            settings.ROOT_URLCONF = self._old_root_urlconf
    226252            clear_url_caches()
     
    354380        self.failIf(template_name in template_names,
    355381            (u"Template '%s' was used unexpectedly in rendering the"
    356382             u" response") % template_name)
     383
     384class TestCase(TransactionTestCase):
     385    """
     386    Does basically the same as TransactionTestCase, but surrounds every test
     387    with a transaction. You have to use TransactionTestCase, if you need
     388    transaction management inside a test.
     389    """
     390
     391    # has the db been changed during the test
     392    db_was_changed = False
     393
     394    def _fixture_setup(self):
     395        transaction.enter_transaction_management()
     396        transaction.managed(True)
     397
     398        # whenever a save occured, the db must be dirty
     399        post_save.connect(self._set_db_was_changed)
     400        # this seems more elegant than patching ClientHandler
     401        #request_started.connect(self._do_commit)
     402        #request_finished.connect(self._do_commit)
     403
     404        if hasattr(self, 'fixtures'):
     405            call_command('loaddata', *self.fixtures, **{
     406                                                        'verbosity': 0,
     407                                                        'no_commit': True
     408                                                        })
     409            # TODO: find out, if loaddata does emit a post_save signal
     410            self._set_db_was_changed()
     411
     412    def _fixture_teardown(self):
     413        # If the transaction is not dirty, but the DB was changed,
     414        # a commit must have happened, so flush instead of rollback.
     415        # This currently doesn't catch the following case:
     416        # Inside a test a commit happens and after that more data is changed.
     417        if not transaction.is_dirty() and self.db_was_changed:
     418            transaction.leave_transaction_management()
     419            call_command('flush', verbosity=0, interactive=False)
     420        else:
     421            transaction.rollback()
     422            transaction.leave_transaction_management()
     423
     424    def _set_db_was_changed(self, *args, **kwargs):
     425        self.db_was_changed = True
     426
     427    def _do_commit(self, *args, **kwargs):
     428        transaction.commit()
  • src/django/test/__init__.py

     
    33"""
    44
    55from django.test.client import Client
    6 from django.test.testcases import TestCase
     6from django.test.testcases import TestCase, TransactionTestCase
  • src/django/core/management/commands/loaddata.py

     
    2828
    2929        verbosity = int(options.get('verbosity', 1))
    3030        show_traceback = options.get('traceback', False)
     31        no_commit = options.get('no_commit', False)
    3132
    3233        # Keep a count of the installed objects and fixtures
    3334        fixture_count = 0
     
    4445
    4546        # Start transaction management. All fixtures are installed in a
    4647        # single transaction to ensure that all references are resolved.
    47         transaction.commit_unless_managed()
    48         transaction.enter_transaction_management()
    49         transaction.managed(True)
     48        if not no_commit:
     49            transaction.commit_unless_managed()
     50            transaction.enter_transaction_management()
     51            transaction.managed(True)
    5052
    5153        app_fixtures = [os.path.join(os.path.dirname(app.__file__), 'fixtures') for app in get_apps()]
    5254        for fixture_label in fixture_labels:
     
    133135                                (format, fixture_name, humanize(fixture_dir))
    134136
    135137
    136         # If any of the fixtures we loaded contain 0 objects, assume that an 
     138        # If any of the fixtures we loaded contain 0 objects, assume that an
    137139        # error was encountered during fixture loading.
    138140        if 0 in objects_per_fixture:
    139141            sys.stderr.write(
     
    142144            transaction.rollback()
    143145            transaction.leave_transaction_management()
    144146            return
    145            
    146         # If we found even one object in a fixture, we need to reset the 
     147
     148        # If we found even one object in a fixture, we need to reset the
    147149        # database sequences.
    148150        if object_count > 0:
    149151            sequence_sql = connection.ops.sequence_reset_sql(self.style, models)
     
    152154                    print "Resetting sequences"
    153155                for line in sequence_sql:
    154156                    cursor.execute(line)
    155            
    156         transaction.commit()
    157         transaction.leave_transaction_management()
    158157
     158        if not no_commit:
     159            transaction.commit()
     160            transaction.leave_transaction_management()
     161
    159162        if object_count == 0:
    160163            if verbosity > 1:
    161164                print "No fixtures found."
    162165        else:
    163166            if verbosity > 0:
    164167                print "Installed %d object(s) from %d fixture(s)" % (object_count, fixture_count)
    165                
     168
    166169        # Close the DB connection. This is required as a workaround for an
    167170        # edge case in MySQL: if the same connection is used to
    168171        # create tables, load data, and query, the query can return
    169172        # incorrect results. See Django #7572, MySQL #37735.
    170         connection.close()
     173        if not no_commit:
     174            connection.close()
  • src/tests/regressiontests/admin_views/tests.py

     
    11# coding: utf-8
    22
    3 from django.test import TestCase
     3from django.test import TestCase, TransactionTestCase
    44from django.contrib.auth.models import User, Permission
    55from django.contrib.contenttypes.models import ContentType
    66from django.contrib.admin.models import LogEntry
     
    1313
    1414class AdminViewBasicTest(TestCase):
    1515    fixtures = ['admin-views-users.xml']
    16    
     16
    1717    def setUp(self):
    1818        self.client.login(username='super', password='secret')
    19    
     19
    2020    def tearDown(self):
    2121        self.client.logout()
    22    
     22
    2323    def testTrailingSlashRequired(self):
    2424        """
    2525        If you leave off the trailing slash, app should redirect and add it.
     
    2828        self.assertRedirects(request,
    2929            '/test_admin/admin/admin_views/article/add/'
    3030        )
    31    
     31
    3232    def testBasicAddGet(self):
    3333        """
    3434        A smoke test to ensure GET on the add_view works.
    3535        """
    3636        response = self.client.get('/test_admin/admin/admin_views/section/add/')
    3737        self.failUnlessEqual(response.status_code, 200)
    38    
     38
    3939    def testBasicEditGet(self):
    4040        """
    4141        A smoke test to ensureGET on the change_view works.
    4242        """
    4343        response = self.client.get('/test_admin/admin/admin_views/section/1/')
    4444        self.failUnlessEqual(response.status_code, 200)
    45    
     45
    4646    def testBasicAddPost(self):
    4747        """
    4848        A smoke test to ensure POST on add_view works.
     
    5555        }
    5656        response = self.client.post('/test_admin/admin/admin_views/section/add/', post_data)
    5757        self.failUnlessEqual(response.status_code, 302) # redirect somewhere
    58    
     58
    5959    def testBasicEditPost(self):
    6060        """
    6161        A smoke test to ensure POST on edit_view works.
     
    9696    ct = ContentType.objects.get_for_model(Model)
    9797    return Permission.objects.get(content_type=ct, codename=perm)
    9898
    99 class AdminViewPermissionsTest(TestCase):
    100     """Tests for Admin Views Permissions."""
     99class AdminViewPermissionsTest(TransactionTestCase):
     100    """
     101    Tests for Admin Views Permissions.
    101102
     103    We need TransactionTestCase here, because some data is lodaed manually
     104    via the ORM, not via fixtures and test.Client is used.
     105    """
     106
    102107    fixtures = ['admin-views-users.xml']
    103108
    104109    def setUp(self):
     
    438443        should_contain = """<a href="../../%s/">%s</a>""" % (quote(self.pk), escape(self.pk))
    439444        self.assertContains(response, should_contain)
    440445
    441 class SecureViewTest(TestCase):
     446class SecureViewTest(TransactionTestCase):
    442447    fixtures = ['admin-views-users.xml']
    443448
    444449    def setUp(self):
     
    471476                     LOGIN_FORM_KEY: 1,
    472477                     'username': 'joepublic',
    473478                     'password': 'secret'}
    474    
     479
    475480    def tearDown(self):
    476481        self.client.logout()
    477    
     482
    478483    def test_secure_view_shows_login_if_not_logged_in(self):
    479484        "Ensure that we see the login form"
    480485        response = self.client.get('/test_admin/admin/secure-view/' )
    481486        self.assertTemplateUsed(response, 'admin/login.html')
    482    
     487
    483488    def test_secure_view_login_successfully_redirects_to_original_url(self):
    484489        request = self.client.get('/test_admin/admin/secure-view/')
    485490        self.failUnlessEqual(request.status_code, 200)
    486491        query_string = "the-answer=42"
    487492        login = self.client.post('/test_admin/admin/secure-view/', self.super_login, QUERY_STRING = query_string )
    488493        self.assertRedirects(login, '/test_admin/admin/secure-view/?%s' % query_string)
    489    
     494
    490495    def test_staff_member_required_decorator_works_as_per_admin_login(self):
    491496        """
    492497        Make sure only staff members can log in.
    493498
    494499        Successful posts to the login page will redirect to the orignal url.
    495         Unsuccessfull attempts will continue to render the login page with 
     500        Unsuccessfull attempts will continue to render the login page with
    496501        a 200 status code.
    497502        """
    498503        # Super User
Back to Top