diff --git a/CHANGES.txt b/CHANGES.txt index b2d815c..7cac0ea 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,12 @@ CHANGES ======= +3.4.3 (2008-07-25) +------------------ + +- Fix memory leak in all functional tests. + see: https://bugs.launchpad.net/zope3/+bug/251273 + 3.4.2 (2008-02-02) ------------------ diff --git a/buildout.cfg b/buildout.cfg index 2c2df0d..90b4d8d 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -1,8 +1,11 @@ [buildout] -develop = . -parts = test +develop = . ../zope.component +parts = test tags [test] recipe = zc.recipe.testrunner eggs = zope.app.testing [test] +[tags] +recipe = z3c.recipe.tag:tags +eggs = zope.app.testing diff --git a/src/zope/app/testing/functional.py b/src/zope/app/testing/functional.py index 08fbac1..66c4016 100644 --- a/src/zope/app/testing/functional.py +++ b/src/zope/app/testing/functional.py @@ -29,6 +29,7 @@ from transaction import abort, commit from ZODB.DB import DB from ZODB.DemoStorage import DemoStorage +from ZODB.interfaces import IDatabase from zope import component from zope.publisher.browser import BrowserRequest, setDefaultSkin @@ -231,12 +232,17 @@ def _set_base_storage(self, value): base_storage = property(_get_base_storage, _set_base_storage) + def _close_databases(self): + base = component.getGlobalSiteManager() + for name, db in self.db.databases.iteritems(): + db.close() + base.unregisterUtility(db, IDatabase, name) + def setUp(self): """Prepares for a functional test case.""" # Tear down the old demo storages (if any) and create fresh ones abort() - for db in self.db.databases.itervalues(): - db.close() + self._close_databases() self.db = self.app.db = multi_database( DerivedDatabaseFactory(name, self._base_storages) for name in self._database_names @@ -249,8 +255,7 @@ def tearDown(self): if self.connection: self.connection.close() self.connection = None - for db in self.db.databases.itervalues(): - db.close() + self._close_databases() setSite(None) def tearDownCompletely(self): diff --git a/src/zope/app/testing/tests.py b/src/zope/app/testing/tests.py index 839215f..fc0d478 100644 --- a/src/zope/app/testing/tests.py +++ b/src/zope/app/testing/tests.py @@ -22,6 +22,8 @@ from zope.testing.doctestunit import DocTestSuite from zope.testing.renormalizing import RENormalizing +from zope.component import getAllUtilitiesRegisteredFor +from ZODB.interfaces import IDatabase import zope.app.testing from zope.app.publication.requestpublicationregistry import factoryRegistry @@ -32,6 +34,7 @@ from zope.app.testing.functional import SampleFunctionalTest, BrowserTestCase from zope.app.testing.functional import FunctionalDocFileSuite from zope.app.testing.functional import FunctionalTestCase +from zope.app.testing.functional import FunctionalTestSetup from zope.app.testing.testing import AppTestingLayer from zope.app.testing.testing import FailingKlass @@ -400,6 +403,30 @@ def test_retryOnConflictErrorBrowser(self): self.assertNotEqual(response.getStatus(), 599) self.assertEqual(response.getStatus(), 500) + +ftesting_zcml = os.path.join(os.path.split(zope.app.testing.__file__)[0], + 'ftesting.zcml') + +def doctest_FunctionalTestSetup_clears_global_utilities(): + """Test that FunctionalTestSetup doesn't leave global utilities. + + Leaving global IDatabase utilities makes a nice juicy memory leak. + See https://bugs.launchpad.net/zope3/+bug/251273 + + >>> setup = FunctionalTestSetup(ftesting_zcml) + >>> setup.setUp() + >>> setup.tearDown() + + >>> len(getAllUtilitiesRegisteredFor(IDatabase)) + 0 + + Clean up: + + >>> setup.tearDownCompletely() + + """ + + def test_suite(): checker = RENormalizing([ (re.compile(r'^HTTP/1.1 (\d{3}) .*?\n'), 'HTTP/1.1 \\1\n')