Rev 5981: (vila) Add -Econfig_stats for selftest (Vincent Ladeuil) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Thu Jun 16 20:47:36 UTC 2011
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 5981 [merge]
revision-id: pqm at pqm.ubuntu.com-20110616204733-tm28wqjjdlz0dhla
parent: pqm at pqm.ubuntu.com-20110616195708-gdi3z3j38uigwawx
parent: v.ladeuil+lp at free.fr-20110616190024-4bvpexewiw162a1r
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Thu 2011-06-16 20:47:33 +0000
message:
(vila) Add -Econfig_stats for selftest (Vincent Ladeuil)
added:
tools/subunit-sum subunitsum-20110616103751-x5ovn49fga91b9zg-1
modified:
bzrlib/tests/__init__.py selftest.py-20050531073622-8d0e3c8845c97a64
bzrlib/tests/features.py features.py-20090820042958-jglgza3wrn03ha9e-1
bzrlib/tests/test_selftest.py test_selftest.py-20051202044319-c110a115d8c0456a
=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py 2011-06-10 07:32:35 +0000
+++ b/bzrlib/tests/__init__.py 2011-06-16 19:00:24 +0000
@@ -989,6 +989,12 @@
# is addressed -- vila 20110219
self.overrideAttr(config, '_expand_default_value', None)
self._log_files = set()
+ # Each key in the ``_counters`` dict holds a value for a different
+ # counter. When the test ends, addDetail() should be used to output the
+ # counter values. This happens in install_counter_hook().
+ self._counters = {}
+ if 'config_stats' in selftest_debug_flags:
+ self._install_config_stats_hooks()
def debug(self):
# debug a frame up.
@@ -1011,6 +1017,50 @@
if name in details:
del details[name]
+ def install_counter_hook(self, hooks, name, counter_name=None):
+ """Install a counting hook.
+
+ Any hook can be counted as long as it doesn't need to return a value.
+
+ :param hooks: Where the hook should be installed.
+
+ :param name: The hook name that will be counted.
+
+ :param counter_name: The counter identifier in ``_counters``, defaults
+ to ``name``.
+ """
+ _counters = self._counters # Avoid closing over self
+ if counter_name is None:
+ counter_name = name
+ if _counters.has_key(counter_name):
+ raise AssertionError('%s is already used as a counter name'
+ % (counter_name,))
+ _counters[counter_name] = 0
+ self.addDetail(counter_name, content.Content(content.UTF8_TEXT,
+ lambda: ['%d' % (_counters[counter_name],)]))
+ def increment_counter(*args, **kwargs):
+ _counters[counter_name] += 1
+ label = 'count %s calls' % (counter_name,)
+ hooks.install_named_hook(name, increment_counter, label)
+ self.addCleanup(hooks.uninstall_named_hook, name, label)
+
+ def _install_config_stats_hooks(self):
+ """Install config hooks to count hook calls.
+
+ """
+ for hook_name in ('get', 'set', 'remove', 'load', 'save'):
+ self.install_counter_hook(config.ConfigHooks, hook_name,
+ 'config.%s' % (hook_name,))
+
+ # The OldConfigHooks are private and need special handling to protect
+ # against recursive tests (tests that run other tests), so we just do
+ # manually what registering them into _builtin_known_hooks will provide
+ # us.
+ self.overrideAttr(config, 'OldConfigHooks', config._OldConfigHooks())
+ for hook_name in ('get', 'set', 'remove', 'load', 'save'):
+ self.install_counter_hook(config.OldConfigHooks, hook_name,
+ 'old_config.%s' % (hook_name,))
+
def _clear_debug_flags(self):
"""Prevent externally set debug flags affecting tests.
@@ -3514,6 +3564,8 @@
# with proper exclusion rules.
# -Ethreads Will display thread ident at creation/join time to
# help track thread leaks
+
+# -Econfig_stats Will collect statistics using addDetail
selftest_debug_flags = set()
=== modified file 'bzrlib/tests/features.py'
--- a/bzrlib/tests/features.py 2011-06-16 18:34:26 +0000
+++ b/bzrlib/tests/features.py 2011-06-16 20:47:33 +0000
@@ -50,6 +50,7 @@
pywintypes = tests.ModuleAvailableFeature('pywintypes')
sphinx = tests.ModuleAvailableFeature('sphinx')
subunit = tests.ModuleAvailableFeature('subunit')
+testtools = tests.ModuleAvailableFeature('testtools')
class _BackslashDirSeparatorFeature(tests.Feature):
=== modified file 'bzrlib/tests/test_selftest.py'
--- a/bzrlib/tests/test_selftest.py 2011-06-09 15:13:54 +0000
+++ b/bzrlib/tests/test_selftest.py 2011-06-16 18:58:12 +0000
@@ -43,6 +43,7 @@
branchbuilder,
bzrdir,
errors,
+ hooks,
lockdir,
memorytree,
osutils,
@@ -3559,3 +3560,45 @@
def test_mutiple_excludes(self):
self.assertTestList(['c'], '-x', 'a', '-x', 'b')
+
+
+class TestCounterHooks(tests.TestCase, SelfTestHelper):
+
+ _test_needs_features = [features.subunit]
+
+ def setUp(self):
+ super(TestCounterHooks, self).setUp()
+ class Test(tests.TestCase):
+
+ def setUp(self):
+ super(Test, self).setUp()
+ self.hooks = hooks.Hooks()
+ self.hooks.add_hook('myhook', 'Foo bar blah', (2,4))
+ self.install_counter_hook(self.hooks, 'myhook')
+
+ def no_hook(self):
+ pass
+
+ def run_hook_once(self):
+ for hook in self.hooks['myhook']:
+ hook(self)
+
+ self.test_class = Test
+
+ def assertHookCalls(self, expected_calls, test_name):
+ test = self.test_class(test_name)
+ result = unittest.TestResult()
+ test.run(result)
+ self.assertTrue(hasattr(test, '_counters'))
+ self.assertTrue(test._counters.has_key('myhook'))
+ self.assertEquals(expected_calls, test._counters['myhook'])
+
+ def test_no_hook(self):
+ self.assertHookCalls(0, 'no_hook')
+
+ def test_run_hook_once(self):
+ tt = features.testtools
+ if tt.module.__version__ < (0, 9, 8):
+ raise tests.TestSkipped('testtools-0.9.8 required for addDetail')
+ self.debug()
+ self.assertHookCalls(1, 'run_hook_once')
=== added file 'tools/subunit-sum'
--- a/tools/subunit-sum 1970-01-01 00:00:00 +0000
+++ b/tools/subunit-sum 2011-06-16 10:45:17 +0000
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+# Copyright (C) 2011 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""Displays the sum of a counter found in a subunit stream.
+
+Each test have (or not) the counter as a named detail in the stream.
+"""
+
+import subunit
+import sys
+import testtools
+import unittest
+
+
+class TestSumCounter(testtools.TestResult):
+
+ def __init__(self, counter_names):
+ """Create a FilterResult object outputting to stream."""
+ testtools.TestResult.__init__(self)
+ self.counter_names = counter_names
+ self.totals = {}
+ self.longest = 0
+ for name in counter_names:
+ l = len(name)
+ if l > self.longest: self.longest = l
+ self.totals[name] = 0
+
+ def addSuccess(self, test, details):
+ for name in self.counter_names:
+ try:
+ counter_text = ''.join(details[name].iter_text())
+ except KeyError, e:
+ # this counter doesn't exist for the test
+ continue
+ self.totals[name] += int(counter_text)
+
+ def display_totals(self, stream):
+ for name in self.counter_names:
+ stream.write('%-*s: %s\n'
+ % (self.longest, name, self.totals[name]))
+
+
+counter_names = sys.argv[1:]
+result = TestSumCounter(counter_names)
+test = subunit.ProtocolTestCase(sys.stdin)
+test.run(result)
+result.display_totals(sys.stdout)
More information about the bazaar-commits
mailing list