From b46dad1befb1edd680c71f09df952245b3413f20 Mon Sep 17 00:00:00 2001 From: Adam Chainz Date: Sun, 26 Jul 2015 17:42:21 +0100 Subject: [PATCH] [1.8.x] Fixed #25176 -- Prevented TestCase.setUpTestData() exception from leaking transaction. Backport of 0abb06930fc0686cb35079934e5bb40df66f5691 from master --- django/test/testcases.py | 6 +++++- docs/releases/1.8.4.txt | 3 +++ tests/test_utils/tests.py | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/django/test/testcases.py b/django/test/testcases.py index ecf0eda4e85..c960d88828f 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -958,7 +958,11 @@ class TestCase(TransactionTestCase): except Exception: cls._rollback_atomics(cls.cls_atomics) raise - cls.setUpTestData() + try: + cls.setUpTestData() + except Exception: + cls._rollback_atomics(cls.cls_atomics) + raise @classmethod def tearDownClass(cls): diff --git a/docs/releases/1.8.4.txt b/docs/releases/1.8.4.txt index 81b00c1800b..53c58e6a90e 100644 --- a/docs/releases/1.8.4.txt +++ b/docs/releases/1.8.4.txt @@ -18,3 +18,6 @@ Bugfixes * Fixed ``QuerySet.raw()`` so ``InvalidQuery`` is not raised when using the ``db_column`` name of a ``ForeignKey`` field with ``primary_key=True`` (:ticket:`12768`). + +* Prevented an exception in ``TestCase.setUpTestData()`` from leaking the + transaction (:ticket:`25176`). diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index 1c5c12ca5b0..ae8d34b6919 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -914,3 +914,36 @@ class OverrideSettingsTests(TestCase): with self.settings(STATICFILES_DIRS=[test_path]): finder = get_finder('django.contrib.staticfiles.finders.FileSystemFinder') self.assertIn(expected_location, finder.locations) + + +class TestBadSetUpTestData(TestCase): + """ + An exception in setUpTestData() shouldn't leak a transaction which would + cascade across the rest of the test suite. + """ + class MyException(Exception): + pass + + @classmethod + def setUpClass(cls): + try: + super(TestBadSetUpTestData, cls).setUpClass() + except cls.MyException: + cls._in_atomic_block = connection.in_atomic_block + + @classmethod + def tearDownClass(Cls): + # override to avoid a second cls._rollback_atomics() which would fail. + # Normal setUpClass() methods won't have exception handling so this + # method wouldn't typically be run. + pass + + @classmethod + def setUpTestData(cls): + # Simulate a broken setUpTestData() method. + raise cls.MyException() + + def test_failure_in_setUpTestData_should_rollback_transaction(self): + # setUpTestData() should call _rollback_atomics() so that the + # transaction doesn't leak. + self.assertFalse(self._in_atomic_block)