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