Commit eb973684 authored by Andrew Svetlov's avatar Andrew Svetlov

Issue #11798: TestSuite now drops references to own tests after execution.

parent 6a53af89
......@@ -1470,15 +1470,24 @@ Grouping tests
Tests grouped by a :class:`TestSuite` are always accessed by iteration.
Subclasses can lazily provide tests by overriding :meth:`__iter__`. Note
that this method maybe called several times on a single suite
(for example when counting tests or comparing for equality)
so the tests returned must be the same for repeated iterations.
that this method may be called several times on a single suite (for
example when counting tests or comparing for equality) so the tests
returned by repeated iterations before :meth:`TestSuite.run` must be the
same for each call iteration. After :meth:`TestSuite.run`, callers should
not rely on the tests returned by this method unless the caller uses a
subclass that overrides :meth:`TestSuite._removeTestAtIndex` to preserve
test references.
.. versionchanged:: 3.2
In earlier versions the :class:`TestSuite` accessed tests directly rather
than through iteration, so overriding :meth:`__iter__` wasn't sufficient
for providing tests.
.. versionchanged:: 3.4
In earlier versions the :class:`TestSuite` held references to each
:class:`TestCase` after :meth:`TestSuite.run`. Subclasses can restore
that behavior by overriding :meth:`TestSuite._removeTestAtIndex`.
In the typical usage of a :class:`TestSuite` object, the :meth:`run` method
is invoked by a :class:`TestRunner` rather than by the end-user test harness.
......
......@@ -57,12 +57,21 @@ class BaseTestSuite(object):
self.addTest(test)
def run(self, result):
for test in self:
for index, test in enumerate(self):
if result.shouldStop:
break
test(result)
self._removeTestAtIndex(index)
return result
def _removeTestAtIndex(self, index):
"""Stop holding a reference to the TestCase at index."""
try:
self._tests[index] = None
except TypeError:
# support for suite implementations that have overriden self._test
pass
def __call__(self, *args, **kwds):
return self.run(*args, **kwds)
......@@ -87,7 +96,7 @@ class TestSuite(BaseTestSuite):
if getattr(result, '_testRunEntered', False) is False:
result._testRunEntered = topLevel = True
for test in self:
for index, test in enumerate(self):
if result.shouldStop:
break
......@@ -106,6 +115,8 @@ class TestSuite(BaseTestSuite):
else:
test.debug()
self._removeTestAtIndex(index)
if topLevel:
self._tearDownPreviousClass(None, result)
self._handleModuleTearDown(result)
......
......@@ -494,12 +494,10 @@ class TestSetups(unittest.TestCase):
Test.__module__ = 'Module'
sys.modules['Module'] = Module
_suite = unittest.defaultTestLoader.loadTestsFromTestCase(Test)
suite = unittest.TestSuite()
suite.addTest(_suite)
messages = ('setUpModule', 'tearDownModule', 'setUpClass', 'tearDownClass', 'test_something')
for phase, msg in enumerate(messages):
_suite = unittest.defaultTestLoader.loadTestsFromTestCase(Test)
suite = unittest.TestSuite([_suite])
with self.assertRaisesRegex(Exception, msg):
suite.debug()
......
import unittest
import gc
import sys
import weakref
from .support import LoggingResult, TestEquality
......@@ -300,7 +302,46 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
# when the bug is fixed this line will not crash
suite.run(unittest.TestResult())
def test_remove_test_at_index(self):
suite = unittest.TestSuite()
suite._tests = [1, 2, 3]
suite._removeTestAtIndex(1)
self.assertEqual([1, None, 3], suite._tests)
def test_remove_test_at_index_not_indexable(self):
suite = unittest.TestSuite()
suite._tests = None
# if _removeAtIndex raises for noniterables this next line will break
suite._removeTestAtIndex(2)
def assert_garbage_collect_test_after_run(self, TestSuiteClass):
class Foo(unittest.TestCase):
def test_nothing(self):
pass
test = Foo('test_nothing')
wref = weakref.ref(test)
suite = TestSuiteClass([wref()])
suite.run(unittest.TestResult())
del test
# for the benefit of non-reference counting implementations
gc.collect()
self.assertEqual(suite._tests, [None])
self.assertIsNone(wref())
def test_garbage_collect_test_after_run_BaseTestSuite(self):
self.assert_garbage_collect_test_after_run(unittest.BaseTestSuite)
def test_garbage_collect_test_after_run_TestSuite(self):
self.assert_garbage_collect_test_after_run(unittest.TestSuite)
def test_basetestsuite(self):
class Test(unittest.TestCase):
......@@ -363,6 +404,5 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
self.assertFalse(result._testRunEntered)
if __name__ == '__main__':
unittest.main()
......@@ -814,6 +814,7 @@ Daniel May
Madison May
Lucas Maystre
Arnaud Mazin
Matt McClure
Rebecca McCreary
Kirk McDonald
Chris McDonough
......@@ -1336,6 +1337,7 @@ Kevin Walzer
Rodrigo Steinmuller Wanderley
Ke Wang
Greg Ward
Tom Wardill
Zachary Ware
Jonas Wagner
Barry Warsaw
......
......@@ -51,6 +51,8 @@ Core and Builtins
Library
-------
- Issue #11798: TestSuite now drops references to own tests after execution.
- Issue #16611: http.cookie now correctly parses the 'secure' and 'httponly'
cookie flags.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment