Django

Code

Ticket #8138: django_transaction_tests_3.diff

File django_transaction_tests_3.diff, 14.8 kB (added by mremolt, 5 months ago)
  • src/django/test/client.py

    old new  
    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

    old new  
     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

    old new  
    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

    old new  
    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

    old new  
    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