/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_selftest.py

  • Committer: Vincent Ladeuil
  • Date: 2011-07-06 09:22:00 UTC
  • mfrom: (6008 +trunk)
  • mto: (6012.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 6013.
  • Revision ID: v.ladeuil+lp@free.fr-20110706092200-7iai2mwzc0sqdsvf
MergingĀ inĀ trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
36
36
    DocTestMatches,
37
37
    Equals,
38
38
    )
39
 
import testtools.tests.helpers
 
39
import testtools.testresult.doubles
40
40
 
41
41
import bzrlib
42
42
from bzrlib import (
43
43
    branchbuilder,
44
44
    bzrdir,
45
45
    errors,
 
46
    hooks,
46
47
    lockdir,
47
48
    memorytree,
48
49
    osutils,
52
53
    tests,
53
54
    transport,
54
55
    workingtree,
 
56
    workingtree_3,
 
57
    workingtree_4,
55
58
    )
56
59
from bzrlib.repofmt import (
57
60
    groupcompress_repo,
58
 
    weaverepo,
59
61
    )
60
62
from bzrlib.symbol_versioning import (
61
63
    deprecated_function,
339
341
        from bzrlib.tests.per_workingtree import make_scenarios
340
342
        server1 = "a"
341
343
        server2 = "b"
342
 
        formats = [workingtree.WorkingTreeFormat2(),
343
 
                   workingtree.WorkingTreeFormat3(),]
 
344
        formats = [workingtree_4.WorkingTreeFormat4(),
 
345
                   workingtree_3.WorkingTreeFormat3(),]
344
346
        scenarios = make_scenarios(server1, server2, formats)
345
347
        self.assertEqual([
346
 
            ('WorkingTreeFormat2',
 
348
            ('WorkingTreeFormat4',
347
349
             {'bzrdir_format': formats[0]._matchingbzrdir,
348
350
              'transport_readonly_server': 'b',
349
351
              'transport_server': 'a',
376
378
            )
377
379
        server1 = "a"
378
380
        server2 = "b"
379
 
        formats = [workingtree.WorkingTreeFormat2(),
380
 
                   workingtree.WorkingTreeFormat3(),]
 
381
        formats = [workingtree_4.WorkingTreeFormat4(),
 
382
                   workingtree_3.WorkingTreeFormat3(),]
381
383
        scenarios = make_scenarios(server1, server2, formats)
382
384
        self.assertEqual(7, len(scenarios))
383
 
        default_wt_format = workingtree.WorkingTreeFormat4._default_format
384
 
        wt4_format = workingtree.WorkingTreeFormat4()
385
 
        wt5_format = workingtree.WorkingTreeFormat5()
 
385
        default_wt_format = workingtree.format_registry.get_default()
 
386
        wt4_format = workingtree_4.WorkingTreeFormat4()
 
387
        wt5_format = workingtree_4.WorkingTreeFormat5()
386
388
        expected_scenarios = [
387
 
            ('WorkingTreeFormat2',
 
389
            ('WorkingTreeFormat4',
388
390
             {'bzrdir_format': formats[0]._matchingbzrdir,
389
391
              'transport_readonly_server': 'b',
390
392
              'transport_server': 'a',
450
452
        # ones to add.
451
453
        from bzrlib.tests.per_tree import (
452
454
            return_parameter,
453
 
            revision_tree_from_workingtree
454
455
            )
455
456
        from bzrlib.tests.per_intertree import (
456
457
            make_scenarios,
457
458
            )
458
 
        from bzrlib.workingtree import WorkingTreeFormat2, WorkingTreeFormat3
 
459
        from bzrlib.workingtree_3 import WorkingTreeFormat3
 
460
        from bzrlib.workingtree_4 import WorkingTreeFormat4
459
461
        input_test = TestInterTreeScenarios(
460
462
            "test_scenarios")
461
463
        server1 = "a"
462
464
        server2 = "b"
463
 
        format1 = WorkingTreeFormat2()
 
465
        format1 = WorkingTreeFormat4()
464
466
        format2 = WorkingTreeFormat3()
465
467
        formats = [("1", str, format1, format2, "converter1"),
466
468
            ("2", int, format2, format1, "converter2")]
512
514
        self.assertRaises(AssertionError, self.assertEqualStat,
513
515
            os.lstat("foo"), os.lstat("longname"))
514
516
 
 
517
    def test_failUnlessExists(self):
 
518
        """Deprecated failUnlessExists and failIfExists"""
 
519
        self.applyDeprecated(
 
520
            deprecated_in((2, 4)),
 
521
            self.failUnlessExists, '.')
 
522
        self.build_tree(['foo/', 'foo/bar'])
 
523
        self.applyDeprecated(
 
524
            deprecated_in((2, 4)),
 
525
            self.failUnlessExists, 'foo/bar')
 
526
        self.applyDeprecated(
 
527
            deprecated_in((2, 4)),
 
528
            self.failIfExists, 'foo/foo')
 
529
 
515
530
    def test_assertPathExists(self):
516
531
        self.assertPathExists('.')
517
532
        self.build_tree(['foo/', 'foo/bar'])
563
578
    def test_make_branch_and_memory_tree_with_format(self):
564
579
        """make_branch_and_memory_tree should accept a format option."""
565
580
        format = bzrdir.BzrDirMetaFormat1()
566
 
        format.repository_format = weaverepo.RepositoryFormat7()
 
581
        format.repository_format = repository.format_registry.get_default()
567
582
        tree = self.make_branch_and_memory_tree('dir', format=format)
568
583
        # Guard against regression into MemoryTransport leaking
569
584
        # files to disk instead of keeping them in memory.
583
598
        # Use a repo layout that doesn't conform to a 'named' layout, to ensure
584
599
        # that the format objects are used.
585
600
        format = bzrdir.BzrDirMetaFormat1()
586
 
        repo_format = weaverepo.RepositoryFormat7()
 
601
        repo_format = repository.format_registry.get_default()
587
602
        format.repository_format = repo_format
588
603
        builder = self.make_branch_builder('dir', format=format)
589
604
        the_branch = builder.get_branch()
707
722
 
708
723
    def test_profiles_tests(self):
709
724
        self.requireFeature(test_lsprof.LSProfFeature)
710
 
        terminal = testtools.tests.helpers.ExtendedTestResult()
 
725
        terminal = testtools.testresult.doubles.ExtendedTestResult()
711
726
        result = tests.ProfileResult(terminal)
712
727
        class Sample(tests.TestCase):
713
728
            def a(self):
730
745
                descriptions=0,
731
746
                verbosity=1,
732
747
                )
733
 
        capture = testtools.tests.helpers.ExtendedTestResult()
 
748
        capture = testtools.testresult.doubles.ExtendedTestResult()
734
749
        test_case.run(MultiTestResult(result, capture))
735
750
        run_case = capture._events[0][1]
736
751
        timed_string = result._testTimeString(run_case)
757
772
        self.check_timing(ShortDelayTestCase('test_short_delay'),
758
773
                          r"^ +[0-9]+ms$")
759
774
 
760
 
    def _patch_get_bzr_source_tree(self):
761
 
        # Reading from the actual source tree breaks isolation, but we don't
762
 
        # want to assume that thats *all* that would happen.
763
 
        self.overrideAttr(bzrlib.version, '_get_bzr_source_tree', lambda: None)
764
 
 
765
 
    def test_assigned_benchmark_file_stores_date(self):
766
 
        self._patch_get_bzr_source_tree()
767
 
        output = StringIO()
768
 
        result = bzrlib.tests.TextTestResult(self._log_file,
769
 
                                        descriptions=0,
770
 
                                        verbosity=1,
771
 
                                        bench_history=output
772
 
                                        )
773
 
        output_string = output.getvalue()
774
 
        # if you are wondering about the regexp please read the comment in
775
 
        # test_bench_history (bzrlib.tests.test_selftest.TestRunner)
776
 
        # XXX: what comment?  -- Andrew Bennetts
777
 
        self.assertContainsRe(output_string, "--date [0-9.]+")
778
 
 
779
 
    def test_benchhistory_records_test_times(self):
780
 
        self._patch_get_bzr_source_tree()
781
 
        result_stream = StringIO()
782
 
        result = bzrlib.tests.TextTestResult(
783
 
            self._log_file,
784
 
            descriptions=0,
785
 
            verbosity=1,
786
 
            bench_history=result_stream
787
 
            )
788
 
 
789
 
        # we want profile a call and check that its test duration is recorded
790
 
        # make a new test instance that when run will generate a benchmark
791
 
        example_test_case = TestTestResult("_time_hello_world_encoding")
792
 
        # execute the test, which should succeed and record times
793
 
        example_test_case.run(result)
794
 
        lines = result_stream.getvalue().splitlines()
795
 
        self.assertEqual(2, len(lines))
796
 
        self.assertContainsRe(lines[1],
797
 
            " *[0-9]+ms bzrlib.tests.test_selftest.TestTestResult"
798
 
            "._time_hello_world_encoding")
799
 
 
800
775
    def _time_hello_world_encoding(self):
801
776
        """Profile two sleep calls
802
777
 
1071
1046
        test = unittest.TestSuite()
1072
1047
        test.addTest(Test("known_failure_test"))
1073
1048
        def failing_test():
1074
 
            self.fail('foo')
 
1049
            raise AssertionError('foo')
1075
1050
        test.addTest(unittest.FunctionTestCase(failing_test))
1076
1051
        stream = StringIO()
1077
1052
        runner = tests.TextTestRunner(stream=stream)
1085
1060
            '^----------------------------------------------------------------------\n'
1086
1061
            'Traceback \\(most recent call last\\):\n'
1087
1062
            '  .*' # File .*, line .*, in failing_test' - but maybe not from .pyc
1088
 
            '    self.fail\\(\'foo\'\\)\n'
 
1063
            '    raise AssertionError\\(\'foo\'\\)\n'
1089
1064
            '.*'
1090
1065
            '^----------------------------------------------------------------------\n'
1091
1066
            '.*'
1097
1072
        # the final output.
1098
1073
        class Test(tests.TestCase):
1099
1074
            def known_failure_test(self):
1100
 
                self.expectFailure('failed', self.assertTrue, False)
 
1075
                self.knownFailure("Never works...")
1101
1076
        test = Test("known_failure_test")
1102
1077
        stream = StringIO()
1103
1078
        runner = tests.TextTestRunner(stream=stream)
1109
1084
            '\n'
1110
1085
            'OK \\(known_failures=1\\)\n')
1111
1086
 
 
1087
    def test_unexpected_success_bad(self):
 
1088
        class Test(tests.TestCase):
 
1089
            def test_truth(self):
 
1090
                self.expectFailure("No absolute truth", self.assertTrue, True)
 
1091
        runner = tests.TextTestRunner(stream=StringIO())
 
1092
        result = self.run_test_runner(runner, Test("test_truth"))
 
1093
        self.assertContainsRe(runner.stream.getvalue(),
 
1094
            "=+\n"
 
1095
            "FAIL: \\S+\.test_truth\n"
 
1096
            "-+\n"
 
1097
            "(?:.*\n)*"
 
1098
            "No absolute truth\n"
 
1099
            "(?:.*\n)*"
 
1100
            "-+\n"
 
1101
            "Ran 1 test in .*\n"
 
1102
            "\n"
 
1103
            "FAILED \\(failures=1\\)\n\\Z")
 
1104
 
1112
1105
    def test_result_decorator(self):
1113
1106
        # decorate results
1114
1107
        calls = []
1221
1214
            ],
1222
1215
            lines[-3:])
1223
1216
 
1224
 
    def _patch_get_bzr_source_tree(self):
1225
 
        # Reading from the actual source tree breaks isolation, but we don't
1226
 
        # want to assume that thats *all* that would happen.
1227
 
        self._get_source_tree_calls = []
1228
 
        def new_get():
1229
 
            self._get_source_tree_calls.append("called")
1230
 
            return None
1231
 
        self.overrideAttr(bzrlib.version, '_get_bzr_source_tree',  new_get)
1232
 
 
1233
 
    def test_bench_history(self):
1234
 
        # tests that the running the benchmark passes bench_history into
1235
 
        # the test result object. We can tell that happens if
1236
 
        # _get_bzr_source_tree is called.
1237
 
        self._patch_get_bzr_source_tree()
1238
 
        test = TestRunner('dummy_test')
1239
 
        output = StringIO()
1240
 
        runner = tests.TextTestRunner(stream=self._log_file,
1241
 
                                      bench_history=output)
1242
 
        result = self.run_test_runner(runner, test)
1243
 
        output_string = output.getvalue()
1244
 
        self.assertContainsRe(output_string, "--date [0-9.]+")
1245
 
        self.assertLength(1, self._get_source_tree_calls)
1246
 
 
1247
1217
    def test_verbose_test_count(self):
1248
1218
        """A verbose test run reports the right test count at the start"""
1249
1219
        suite = TestUtil.TestSuite([
1494
1464
        # Note this test won't fail with hooks that the core library doesn't
1495
1465
        # use - but it trigger with a plugin that adds hooks, so its still a
1496
1466
        # useful warning in that case.
1497
 
        self.assertEqual(bzrlib.branch.BranchHooks(),
1498
 
            bzrlib.branch.Branch.hooks)
1499
 
        self.assertEqual(bzrlib.smart.server.SmartServerHooks(),
 
1467
        self.assertEqual(bzrlib.branch.BranchHooks(), bzrlib.branch.Branch.hooks)
 
1468
        self.assertEqual(
 
1469
            bzrlib.smart.server.SmartServerHooks(),
1500
1470
            bzrlib.smart.server.SmartTCPServer.hooks)
1501
 
        self.assertEqual(bzrlib.commands.CommandHooks(),
1502
 
            bzrlib.commands.Command.hooks)
 
1471
        self.assertEqual(
 
1472
            bzrlib.commands.CommandHooks(), bzrlib.commands.Command.hooks)
1503
1473
 
1504
1474
    def test__gather_lsprof_in_benchmarks(self):
1505
1475
        """When _gather_lsprof_in_benchmarks is on, accumulate profile data.
1983
1953
    def test_make_branch_and_tree_with_format(self):
1984
1954
        # we should be able to supply a format to make_branch_and_tree
1985
1955
        self.make_branch_and_tree('a', format=bzrlib.bzrdir.BzrDirMetaFormat1())
1986
 
        self.make_branch_and_tree('b', format=bzrlib.bzrdir.BzrDirFormat6())
1987
1956
        self.assertIsInstance(bzrlib.bzrdir.BzrDir.open('a')._format,
1988
1957
                              bzrlib.bzrdir.BzrDirMetaFormat1)
1989
 
        self.assertIsInstance(bzrlib.bzrdir.BzrDir.open('b')._format,
1990
 
                              bzrlib.bzrdir.BzrDirFormat6)
1991
1958
 
1992
1959
    def test_make_branch_and_memory_tree(self):
1993
1960
        # we should be able to get a new branch and a mutable tree from
2071
2038
 
2072
2039
    def test_lsprof_tests(self):
2073
2040
        self.requireFeature(test_lsprof.LSProfFeature)
2074
 
        calls = []
 
2041
        results = []
2075
2042
        class Test(object):
2076
2043
            def __call__(test, result):
2077
2044
                test.run(result)
2078
2045
            def run(test, result):
2079
 
                self.assertIsInstance(result, ExtendedToOriginalDecorator)
2080
 
                calls.append("called")
 
2046
                results.append(result)
2081
2047
            def countTestCases(self):
2082
2048
                return 1
2083
2049
        self.run_selftest(test_suite_factory=Test, lsprof_tests=True)
2084
 
        self.assertLength(1, calls)
 
2050
        self.assertLength(1, results)
 
2051
        self.assertIsInstance(results.pop(), ExtendedToOriginalDecorator)
2085
2052
 
2086
2053
    def test_random(self):
2087
2054
        # test randomising by listing a number of tests.
2229
2196
        content, result = self.run_subunit_stream('test_unexpected_success')
2230
2197
        self.assertContainsRe(content, '(?m)^log$')
2231
2198
        self.assertContainsRe(content, 'test with unexpected success')
2232
 
        self.expectFailure('subunit treats "unexpectedSuccess"'
2233
 
                           ' as a plain success',
2234
 
            self.assertEqual, 1, len(result.unexpectedSuccesses))
 
2199
        # GZ 2011-05-18: Old versions of subunit treat unexpected success as a
 
2200
        #                success, if a min version check is added remove this
 
2201
        from subunit import TestProtocolClient as _Client
 
2202
        if _Client.addUnexpectedSuccess.im_func is _Client.addSuccess.im_func:
 
2203
            self.expectFailure('subunit treats "unexpectedSuccess"'
 
2204
                               ' as a plain success',
 
2205
                self.assertEqual, 1, len(result.unexpectedSuccesses))
2235
2206
        self.assertEqual(1, len(result.unexpectedSuccesses))
2236
2207
        test = result.unexpectedSuccesses[0]
2237
2208
        # RemotedTestCase doesn't preserve the "details"
2535
2506
 
2536
2507
 
2537
2508
class TestStartBzrSubProcess(tests.TestCase):
 
2509
    """Stub test start_bzr_subprocess."""
2538
2510
 
2539
 
    def check_popen_state(self):
2540
 
        """Replace to make assertions when popen is called."""
 
2511
    def _subprocess_log_cleanup(self):
 
2512
        """Inhibits the base version as we don't produce a log file."""
2541
2513
 
2542
2514
    def _popen(self, *args, **kwargs):
2543
 
        """Record the command that is run, so that we can ensure it is correct"""
 
2515
        """Override the base version to record the command that is run.
 
2516
 
 
2517
        From there we can ensure it is correct without spawning a real process.
 
2518
        """
2544
2519
        self.check_popen_state()
2545
2520
        self._popen_args = args
2546
2521
        self._popen_kwargs = kwargs
2547
2522
        raise _DontSpawnProcess()
2548
2523
 
 
2524
    def check_popen_state(self):
 
2525
        """Replace to make assertions when popen is called."""
 
2526
 
2549
2527
    def test_run_bzr_subprocess_no_plugins(self):
2550
2528
        self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [])
2551
2529
        command = self._popen_args[0]
2555
2533
 
2556
2534
    def test_allow_plugins(self):
2557
2535
        self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
2558
 
            allow_plugins=True)
 
2536
                          allow_plugins=True)
2559
2537
        command = self._popen_args[0]
2560
2538
        self.assertEqual([], command[2:])
2561
2539
 
2566
2544
            self.assertEqual('set variable', os.environ['EXISTANT_ENV_VAR'])
2567
2545
        self.check_popen_state = check_environment
2568
2546
        self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
2569
 
            env_changes={'EXISTANT_ENV_VAR':'set variable'})
 
2547
                          env_changes={'EXISTANT_ENV_VAR':'set variable'})
2570
2548
        # not set in theparent
2571
2549
        self.assertFalse('EXISTANT_ENV_VAR' in os.environ)
2572
2550
 
2578
2556
        os.environ['EXISTANT_ENV_VAR'] = 'set variable'
2579
2557
        self.check_popen_state = check_environment
2580
2558
        self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
2581
 
            env_changes={'EXISTANT_ENV_VAR':None})
 
2559
                          env_changes={'EXISTANT_ENV_VAR':None})
2582
2560
        # Still set in parent
2583
2561
        self.assertEqual('set variable', os.environ['EXISTANT_ENV_VAR'])
2584
2562
        del os.environ['EXISTANT_ENV_VAR']
2589
2567
            self.assertFalse('NON_EXISTANT_ENV_VAR' in os.environ)
2590
2568
        self.check_popen_state = check_environment
2591
2569
        self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
2592
 
            env_changes={'NON_EXISTANT_ENV_VAR':None})
 
2570
                          env_changes={'NON_EXISTANT_ENV_VAR':None})
2593
2571
 
2594
2572
    def test_working_dir(self):
2595
2573
        """Test that we can specify the working dir for the child"""
2598
2576
        chdirs = []
2599
2577
        def chdir(path):
2600
2578
            chdirs.append(path)
2601
 
        os.chdir = chdir
2602
 
        try:
2603
 
            def getcwd():
2604
 
                return 'current'
2605
 
            osutils.getcwd = getcwd
2606
 
            try:
2607
 
                self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
2608
 
                    working_dir='foo')
2609
 
            finally:
2610
 
                osutils.getcwd = orig_getcwd
2611
 
        finally:
2612
 
            os.chdir = orig_chdir
 
2579
        self.overrideAttr(os, 'chdir', chdir)
 
2580
        def getcwd():
 
2581
            return 'current'
 
2582
        self.overrideAttr(osutils, 'getcwd', getcwd)
 
2583
        self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
 
2584
                          working_dir='foo')
2613
2585
        self.assertEqual(['foo', 'current'], chdirs)
2614
2586
 
2615
2587
    def test_get_bzr_path_with_cwd_bzrlib(self):
2865
2837
        self.assertEqual(remaining_names, _test_ids(split_suite[1]))
2866
2838
 
2867
2839
 
2868
 
class TestCheckInventoryShape(tests.TestCaseWithTransport):
 
2840
class TestCheckTreeShape(tests.TestCaseWithTransport):
2869
2841
 
2870
 
    def test_check_inventory_shape(self):
 
2842
    def test_check_tree_shape(self):
2871
2843
        files = ['a', 'b/', 'b/c']
2872
2844
        tree = self.make_branch_and_tree('.')
2873
2845
        self.build_tree(files)
2874
2846
        tree.add(files)
2875
2847
        tree.lock_read()
2876
2848
        try:
2877
 
            self.check_inventory_shape(tree.inventory, files)
 
2849
            self.check_tree_shape(tree, files)
2878
2850
        finally:
2879
2851
            tree.unlock()
2880
2852
 
3552
3524
        self.assertDocTestStringFails(doctest.DocTestSuite, test)
3553
3525
        # tests.DocTestSuite sees None
3554
3526
        self.assertDocTestStringSucceds(tests.IsolatedDocTestSuite, test)
 
3527
 
 
3528
 
 
3529
class TestSelftestExcludePatterns(tests.TestCase):
 
3530
 
 
3531
    def setUp(self):
 
3532
        super(TestSelftestExcludePatterns, self).setUp()
 
3533
        self.overrideAttr(tests, 'test_suite', self.suite_factory)
 
3534
 
 
3535
    def suite_factory(self, keep_only=None, starting_with=None):
 
3536
        """A test suite factory with only a few tests."""
 
3537
        class Test(tests.TestCase):
 
3538
            def id(self):
 
3539
                # We don't need the full class path
 
3540
                return self._testMethodName
 
3541
            def a(self):
 
3542
                pass
 
3543
            def b(self):
 
3544
                pass
 
3545
            def c(self):
 
3546
                pass
 
3547
        return TestUtil.TestSuite([Test("a"), Test("b"), Test("c")])
 
3548
 
 
3549
    def assertTestList(self, expected, *selftest_args):
 
3550
        # We rely on setUp installing the right test suite factory so we can
 
3551
        # test at the command level without loading the whole test suite
 
3552
        out, err = self.run_bzr(('selftest', '--list') + selftest_args)
 
3553
        actual = out.splitlines()
 
3554
        self.assertEquals(expected, actual)
 
3555
 
 
3556
    def test_full_list(self):
 
3557
        self.assertTestList(['a', 'b', 'c'])
 
3558
 
 
3559
    def test_single_exclude(self):
 
3560
        self.assertTestList(['b', 'c'], '-x', 'a')
 
3561
 
 
3562
    def test_mutiple_excludes(self):
 
3563
        self.assertTestList(['c'], '-x', 'a', '-x', 'b')
 
3564
 
 
3565
 
 
3566
class TestCounterHooks(tests.TestCase, SelfTestHelper):
 
3567
 
 
3568
    _test_needs_features = [features.subunit]
 
3569
 
 
3570
    def setUp(self):
 
3571
        super(TestCounterHooks, self).setUp()
 
3572
        class Test(tests.TestCase):
 
3573
 
 
3574
            def setUp(self):
 
3575
                super(Test, self).setUp()
 
3576
                self.hooks = hooks.Hooks()
 
3577
                self.hooks.add_hook('myhook', 'Foo bar blah', (2,4))
 
3578
                self.install_counter_hook(self.hooks, 'myhook')
 
3579
 
 
3580
            def no_hook(self):
 
3581
                pass
 
3582
 
 
3583
            def run_hook_once(self):
 
3584
                for hook in self.hooks['myhook']:
 
3585
                    hook(self)
 
3586
 
 
3587
        self.test_class = Test
 
3588
 
 
3589
    def assertHookCalls(self, expected_calls, test_name):
 
3590
        test = self.test_class(test_name)
 
3591
        result = unittest.TestResult()
 
3592
        test.run(result)
 
3593
        self.assertTrue(hasattr(test, '_counters'))
 
3594
        self.assertTrue(test._counters.has_key('myhook'))
 
3595
        self.assertEquals(expected_calls, test._counters['myhook'])
 
3596
 
 
3597
    def test_no_hook(self):
 
3598
        self.assertHookCalls(0, 'no_hook')
 
3599
 
 
3600
    def test_run_hook_once(self):
 
3601
        tt = features.testtools
 
3602
        if tt.module.__version__ < (0, 9, 8):
 
3603
            raise tests.TestSkipped('testtools-0.9.8 required for addDetail')
 
3604
        self.assertHookCalls(1, 'run_hook_once')