[MERGE] Discard most of TestCase instances __dict__ after successful tests to reduce memory consumption

Andrew Bennetts andrew at canonical.com
Tue Feb 19 13:23:23 GMT 2008


Hi all,

This quick hack throws away most of the contents of a bzrlib.tests.TestCase's
__dict__ after running a successful test.  Currently we keep things like log
file contents indefinitely, even though for successful tests we don't care about
them.

Aside from noticeably reducing memory consumption (it keeps a full test suite
run down to 52MB RSS on my laptop), it also makes it faster I think.  I haven't
taken care to make sure these numbers are reproducible, but it reduced the time
from ~750s to ~650s for me.

-Andrew.

-------------- next part --------------
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: andrew.bennetts at canonical.com-20080219012302-\
#   89l9om1golof6ka0
# target_branch: http://bazaar-vcs.org/bzr/bzr.dev
# testament_sha1: 505712d1db10889288734f9812c43b3d5c9ac1de
# timestamp: 2008-02-20 00:17:12 +1100
# source_branch: http://people.ubuntu.com/~andrew/bzr/test-suite-\
#   memory-reduction
# base_revision_id: pqm at pqm.ubuntu.com-20080213062701-74mocrhfz0n4uj4r
# 
# Begin patch
=== modified file 'bzrlib/tests/__init__.py'
--- bzrlib/tests/__init__.py	2008-02-06 00:41:04 +0000
+++ bzrlib/tests/__init__.py	2008-02-19 01:23:02 +0000
@@ -314,6 +314,8 @@
         self.report_success(test)
         self._cleanupLogFile(test)
         unittest.TestResult.addSuccess(self, test)
+        if getattr(test, '_log_contents', '') != '':
+            del test._log_contents
 
     def _testConcluded(self, test):
         """Common code when a test has finished.
@@ -356,6 +358,8 @@
             # seems best to treat this as success from point-of-view of unittest
             # -- it actually does nothing so it barely matters :)
             unittest.TestResult.addSuccess(self, test)
+            if getattr(test, '_log_contents', '') != '':
+                del test._log_contents
 
     def printErrorList(self, flavour, errors):
         for test, err in errors:
@@ -789,6 +793,8 @@
     _keep_log_file = False
     # record lsprof data when performing benchmark calls.
     _gather_lsprof_in_benchmarks = False
+    attrs_to_keep = ('_testMethodName', '_testMethodDoc',
+                     '_log_contents', '_log_file_name', '_benchtime')
 
     def __init__(self, methodName='testMethod'):
         super(TestCase, self).__init__(methodName)
@@ -1281,7 +1287,16 @@
                     result.addSuccess(self)
                 result.stopTest(self)
                 return
-        return unittest.TestCase.run(self, result)
+        try:
+            return unittest.TestCase.run(self, result)
+        finally:
+            saved_attrs = {}
+            not_found = object()
+            for attr_name in self.attrs_to_keep:
+                attr = getattr(self, attr_name, not_found)
+                if attr is not not_found:
+                    saved_attrs[attr_name] = attr
+            self.__dict__ = saved_attrs
 
     def tearDown(self):
         self._runCleanups()

=== modified file 'bzrlib/tests/test_selftest.py'
--- bzrlib/tests/test_selftest.py	2008-01-29 08:21:19 +0000
+++ bzrlib/tests/test_selftest.py	2008-02-19 01:23:02 +0000
@@ -1120,10 +1120,11 @@
         self.assertTrue(result.wasSuccessful())
 
     def test_skipped_from_setup(self):
+        calls = []
         class SkippedSetupTest(TestCase):
 
             def setUp(self):
-                self.counter = 1
+                calls.append('setUp')
                 self.addCleanup(self.cleanup)
                 raise TestSkipped('skipped setup')
 
@@ -1131,34 +1132,35 @@
                 self.fail('test reached')
 
             def cleanup(self):
-                self.counter -= 1
+                calls.append('cleanup')
 
         runner = TextTestRunner(stream=self._log_file)
         test = SkippedSetupTest('test_skip')
         result = self.run_test_runner(runner, test)
         self.assertTrue(result.wasSuccessful())
         # Check if cleanup was called the right number of times.
-        self.assertEqual(0, test.counter)
+        self.assertEqual(['setUp', 'cleanup'], calls)
 
     def test_skipped_from_test(self):
+        calls = []
         class SkippedTest(TestCase):
 
             def setUp(self):
-                self.counter = 1
+                calls.append('setUp')
                 self.addCleanup(self.cleanup)
 
             def test_skip(self):
                 raise TestSkipped('skipped test')
 
             def cleanup(self):
-                self.counter -= 1
+                calls.append('cleanup')
 
         runner = TextTestRunner(stream=self._log_file)
         test = SkippedTest('test_skip')
         result = self.run_test_runner(runner, test)
         self.assertTrue(result.wasSuccessful())
         # Check if cleanup was called the right number of times.
-        self.assertEqual(0, test.counter)
+        self.assertEqual(['setUp', 'cleanup'], calls)
 
     def test_not_applicable(self):
         # run a test that is skipped because it's not applicable

# Begin bundle
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


More information about the bazaar mailing list