~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_selftest.py

  • Committer: Richard Wilbur
  • Date: 2016-02-04 19:07:28 UTC
  • mto: This revision was merged to the branch mainline in revision 6618.
  • Revision ID: richard.wilbur@gmail.com-20160204190728-p0zvfii6zase0fw7
Update COPYING.txt from the original http://www.gnu.org/licenses/gpl-2.0.txt  (Only differences were in whitespace.)  Thanks to Petr Stodulka for pointing out the discrepancy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
"""Tests for the test framework."""
18
18
 
19
19
from cStringIO import StringIO
 
20
import gc
20
21
import doctest
21
22
import os
22
23
import signal
36
37
    DocTestMatches,
37
38
    Equals,
38
39
    )
39
 
import testtools.tests.helpers
 
40
import testtools.testresult.doubles
40
41
 
41
42
import bzrlib
42
43
from bzrlib import (
43
44
    branchbuilder,
44
45
    bzrdir,
 
46
    controldir,
45
47
    errors,
 
48
    hooks,
46
49
    lockdir,
47
50
    memorytree,
48
51
    osutils,
52
55
    tests,
53
56
    transport,
54
57
    workingtree,
 
58
    workingtree_3,
 
59
    workingtree_4,
55
60
    )
56
61
from bzrlib.repofmt import (
57
62
    groupcompress_repo,
90
95
            DocTestMatches(u"...a test message\n", doctest.ELLIPSIS))
91
96
 
92
97
 
93
 
class TestUnicodeFilename(tests.TestCase):
94
 
 
95
 
    def test_probe_passes(self):
96
 
        """UnicodeFilename._probe passes."""
97
 
        # We can't test much more than that because the behaviour depends
98
 
        # on the platform.
99
 
        tests.UnicodeFilename._probe()
100
 
 
101
 
 
102
98
class TestTreeShape(tests.TestCaseInTempDir):
103
99
 
104
100
    def test_unicode_paths(self):
105
 
        self.requireFeature(tests.UnicodeFilename)
 
101
        self.requireFeature(features.UnicodeFilenameFeature)
106
102
 
107
103
        filename = u'hell\u00d8'
108
104
        self.build_tree_contents([(filename, 'contents of hello')])
338
334
        from bzrlib.tests.per_workingtree import make_scenarios
339
335
        server1 = "a"
340
336
        server2 = "b"
341
 
        formats = [workingtree.WorkingTreeFormat4(),
342
 
                   workingtree.WorkingTreeFormat3(),]
343
 
        scenarios = make_scenarios(server1, server2, formats)
 
337
        formats = [workingtree_4.WorkingTreeFormat4(),
 
338
                   workingtree_3.WorkingTreeFormat3(),
 
339
                   workingtree_4.WorkingTreeFormat6()]
 
340
        scenarios = make_scenarios(server1, server2, formats,
 
341
            remote_server='c', remote_readonly_server='d',
 
342
            remote_backing_server='e')
344
343
        self.assertEqual([
345
344
            ('WorkingTreeFormat4',
346
345
             {'bzrdir_format': formats[0]._matchingbzrdir,
351
350
             {'bzrdir_format': formats[1]._matchingbzrdir,
352
351
              'transport_readonly_server': 'b',
353
352
              'transport_server': 'a',
354
 
              'workingtree_format': formats[1]})],
355
 
            scenarios)
 
353
              'workingtree_format': formats[1]}),
 
354
            ('WorkingTreeFormat6',
 
355
             {'bzrdir_format': formats[2]._matchingbzrdir,
 
356
              'transport_readonly_server': 'b',
 
357
              'transport_server': 'a',
 
358
              'workingtree_format': formats[2]}),
 
359
            ('WorkingTreeFormat6,remote',
 
360
             {'bzrdir_format': formats[2]._matchingbzrdir,
 
361
              'repo_is_remote': True,
 
362
              'transport_readonly_server': 'd',
 
363
              'transport_server': 'c',
 
364
              'vfs_transport_factory': 'e',
 
365
              'workingtree_format': formats[2]}),
 
366
            ], scenarios)
356
367
 
357
368
 
358
369
class TestTreeScenarios(tests.TestCase):
359
370
 
360
371
    def test_scenarios(self):
361
372
        # the tree implementation scenario generator is meant to setup one
362
 
        # instance for each working tree format, and one additional instance
 
373
        # instance for each working tree format, one additional instance
363
374
        # that will use the default wt format, but create a revision tree for
364
 
        # the tests.  this means that the wt ones should have the
365
 
        # workingtree_to_test_tree attribute set to 'return_parameter' and the
366
 
        # revision one set to revision_tree_from_workingtree.
 
375
        # the tests, and one more that uses the default wt format as a
 
376
        # lightweight checkout of a remote repository.  This means that the wt
 
377
        # ones should have the workingtree_to_test_tree attribute set to
 
378
        # 'return_parameter' and the revision one set to
 
379
        # revision_tree_from_workingtree.
367
380
 
368
381
        from bzrlib.tests.per_tree import (
369
382
            _dirstate_tree_from_workingtree,
375
388
            )
376
389
        server1 = "a"
377
390
        server2 = "b"
378
 
        formats = [workingtree.WorkingTreeFormat4(),
379
 
                   workingtree.WorkingTreeFormat3(),]
 
391
        smart_server = test_server.SmartTCPServer_for_testing
 
392
        smart_readonly_server = test_server.ReadonlySmartTCPServer_for_testing
 
393
        mem_server = memory.MemoryServer
 
394
        formats = [workingtree_4.WorkingTreeFormat4(),
 
395
                   workingtree_3.WorkingTreeFormat3(),]
380
396
        scenarios = make_scenarios(server1, server2, formats)
381
 
        self.assertEqual(7, len(scenarios))
 
397
        self.assertEqual(8, len(scenarios))
382
398
        default_wt_format = workingtree.format_registry.get_default()
383
 
        wt4_format = workingtree.WorkingTreeFormat4()
384
 
        wt5_format = workingtree.WorkingTreeFormat5()
 
399
        wt4_format = workingtree_4.WorkingTreeFormat4()
 
400
        wt5_format = workingtree_4.WorkingTreeFormat5()
 
401
        wt6_format = workingtree_4.WorkingTreeFormat6()
385
402
        expected_scenarios = [
386
403
            ('WorkingTreeFormat4',
387
404
             {'bzrdir_format': formats[0]._matchingbzrdir,
397
414
              'workingtree_format': formats[1],
398
415
              '_workingtree_to_test_tree': return_parameter,
399
416
             }),
 
417
            ('WorkingTreeFormat6,remote',
 
418
             {'bzrdir_format': wt6_format._matchingbzrdir,
 
419
              'repo_is_remote': True,
 
420
              'transport_readonly_server': smart_readonly_server,
 
421
              'transport_server': smart_server,
 
422
              'vfs_transport_factory': mem_server,
 
423
              'workingtree_format': wt6_format,
 
424
              '_workingtree_to_test_tree': return_parameter,
 
425
             }),
400
426
            ('RevisionTree',
401
427
             {'_workingtree_to_test_tree': revision_tree_from_workingtree,
402
428
              'bzrdir_format': default_wt_format._matchingbzrdir,
453
479
        from bzrlib.tests.per_intertree import (
454
480
            make_scenarios,
455
481
            )
456
 
        from bzrlib.workingtree import WorkingTreeFormat3, WorkingTreeFormat4
 
482
        from bzrlib.workingtree_3 import WorkingTreeFormat3
 
483
        from bzrlib.workingtree_4 import WorkingTreeFormat4
457
484
        input_test = TestInterTreeScenarios(
458
485
            "test_scenarios")
459
486
        server1 = "a"
613
640
        # Guard against regression into MemoryTransport leaking
614
641
        # files to disk instead of keeping them in memory.
615
642
        self.assertFalse(osutils.lexists('dir'))
616
 
        dir_format = bzrdir.format_registry.make_bzrdir('knit')
 
643
        dir_format = controldir.format_registry.make_bzrdir('knit')
617
644
        self.assertEqual(dir_format.repository_format.__class__,
618
645
                         the_branch.repository._format.__class__)
619
646
        self.assertEqual('Bazaar-NG Knit Repository Format 1',
623
650
    def test_dangling_locks_cause_failures(self):
624
651
        class TestDanglingLock(tests.TestCaseWithMemoryTransport):
625
652
            def test_function(self):
626
 
                t = self.get_transport('.')
 
653
                t = self.get_transport_from_path('.')
627
654
                l = lockdir.LockDir(t, 'lock')
628
655
                l.create()
629
656
                l.attempt_lock()
649
676
        # for the server
650
677
        url = self.get_readonly_url()
651
678
        url2 = self.get_readonly_url('foo/bar')
652
 
        t = transport.get_transport(url)
653
 
        t2 = transport.get_transport(url2)
 
679
        t = transport.get_transport_from_url(url)
 
680
        t2 = transport.get_transport_from_url(url2)
654
681
        self.assertIsInstance(t, ReadonlyTransportDecorator)
655
682
        self.assertIsInstance(t2, ReadonlyTransportDecorator)
656
683
        self.assertEqual(t2.base[:-1], t.abspath('foo/bar'))
664
691
        url = self.get_readonly_url()
665
692
        url2 = self.get_readonly_url('foo/bar')
666
693
        # the transport returned may be any HttpTransportBase subclass
667
 
        t = transport.get_transport(url)
668
 
        t2 = transport.get_transport(url2)
 
694
        t = transport.get_transport_from_url(url)
 
695
        t2 = transport.get_transport_from_url(url2)
669
696
        self.assertIsInstance(t, HttpTransportBase)
670
697
        self.assertIsInstance(t2, HttpTransportBase)
671
698
        self.assertEqual(t2.base[:-1], t.abspath('foo/bar'))
682
709
        builder = self.make_branch_builder('dir')
683
710
        rev_id = builder.build_commit()
684
711
        self.assertPathExists('dir')
685
 
        a_dir = bzrdir.BzrDir.open('dir')
 
712
        a_dir = controldir.ControlDir.open('dir')
686
713
        self.assertRaises(errors.NoWorkingTree, a_dir.open_workingtree)
687
714
        a_branch = a_dir.open_branch()
688
715
        builder_branch = builder.get_branch()
709
736
class TestChrootedTest(tests.ChrootedTestCase):
710
737
 
711
738
    def test_root_is_root(self):
712
 
        t = transport.get_transport(self.get_readonly_url())
 
739
        t = transport.get_transport_from_url(self.get_readonly_url())
713
740
        url = t.base
714
741
        self.assertEqual(url, t.clone('..').base)
715
742
 
717
744
class TestProfileResult(tests.TestCase):
718
745
 
719
746
    def test_profiles_tests(self):
720
 
        self.requireFeature(test_lsprof.LSProfFeature)
721
 
        terminal = testtools.tests.helpers.ExtendedTestResult()
 
747
        self.requireFeature(features.lsprof_feature)
 
748
        terminal = testtools.testresult.doubles.ExtendedTestResult()
722
749
        result = tests.ProfileResult(terminal)
723
750
        class Sample(tests.TestCase):
724
751
            def a(self):
741
768
                descriptions=0,
742
769
                verbosity=1,
743
770
                )
744
 
        capture = testtools.tests.helpers.ExtendedTestResult()
 
771
        capture = testtools.testresult.doubles.ExtendedTestResult()
745
772
        test_case.run(MultiTestResult(result, capture))
746
773
        run_case = capture._events[0][1]
747
774
        timed_string = result._testTimeString(run_case)
778
805
 
779
806
    def test_lsprofiling(self):
780
807
        """Verbose test result prints lsprof statistics from test cases."""
781
 
        self.requireFeature(test_lsprof.LSProfFeature)
 
808
        self.requireFeature(features.lsprof_feature)
782
809
        result_stream = StringIO()
783
810
        result = bzrlib.tests.VerboseTestResult(
784
811
            result_stream,
829
856
        self.assertEndsWith(sio.getvalue(), "OK    50002ms\n")
830
857
 
831
858
    def test_known_failure(self):
832
 
        """A KnownFailure being raised should trigger several result actions."""
 
859
        """Using knownFailure should trigger several result actions."""
833
860
        class InstrumentedTestResult(tests.ExtendedTestResult):
834
861
            def stopTestRun(self): pass
835
862
            def report_tests_starting(self): pass
838
865
        result = InstrumentedTestResult(None, None, None, None)
839
866
        class Test(tests.TestCase):
840
867
            def test_function(self):
841
 
                raise tests.KnownFailure('failed!')
 
868
                self.knownFailure('failed!')
842
869
        test = Test("test_function")
843
870
        test.run(result)
844
871
        # it should invoke 'report_known_failure'.
860
887
            descriptions=0,
861
888
            verbosity=2,
862
889
            )
863
 
        test = self.get_passing_test()
864
 
        result.startTest(test)
865
 
        prefix = len(result_stream.getvalue())
866
 
        # the err parameter has the shape:
867
 
        # (class, exception object, traceback)
868
 
        # KnownFailures dont get their tracebacks shown though, so we
869
 
        # can skip that.
870
 
        err = (tests.KnownFailure, tests.KnownFailure('foo'), None)
871
 
        result.report_known_failure(test, err)
872
 
        output = result_stream.getvalue()[prefix:]
873
 
        lines = output.splitlines()
874
 
        self.assertContainsRe(lines[0], r'XFAIL *\d+ms$')
875
 
        if sys.version_info > (2, 7):
876
 
            self.expectFailure("_ExpectedFailure on 2.7 loses the message",
877
 
                self.assertNotEqual, lines[1], '    ')
878
 
        self.assertEqual(lines[1], '    foo')
879
 
        self.assertEqual(2, len(lines))
 
890
        _get_test("test_xfail").run(result)
 
891
        self.assertContainsRe(result_stream.getvalue(),
 
892
            "\n\\S+\\.test_xfail\\s+XFAIL\\s+\\d+ms\n"
 
893
            "\\s*(?:Text attachment: )?reason"
 
894
            "(?:\n-+\n|: {{{)"
 
895
            "this_fails"
 
896
            "(?:\n-+\n|}}}\n)")
880
897
 
881
898
    def get_passing_test(self):
882
899
        """Return a test object that can't be run usefully."""
893
910
                self._call = test, feature
894
911
        result = InstrumentedTestResult(None, None, None, None)
895
912
        test = SampleTestCase('_test_pass')
896
 
        feature = tests.Feature()
 
913
        feature = features.Feature()
897
914
        result.startTest(test)
898
915
        result.addNotSupported(test, feature)
899
916
        # it should invoke 'report_unsupported'.
918
935
            verbosity=2,
919
936
            )
920
937
        test = self.get_passing_test()
921
 
        feature = tests.Feature()
 
938
        feature = features.Feature()
922
939
        result.startTest(test)
923
940
        prefix = len(result_stream.getvalue())
924
941
        result.report_unsupported(test, feature)
937
954
            def addNotSupported(self, test, feature):
938
955
                self._call = test, feature
939
956
        result = InstrumentedTestResult(None, None, None, None)
940
 
        feature = tests.Feature()
 
957
        feature = features.Feature()
941
958
        class Test(tests.TestCase):
942
959
            def test_function(self):
943
960
                raise tests.UnavailableFeature(feature)
962
979
    def test_strict_with_known_failure(self):
963
980
        result = bzrlib.tests.TextTestResult(self._log_file, descriptions=0,
964
981
                                             verbosity=1)
965
 
        test = self.get_passing_test()
966
 
        err = (tests.KnownFailure, tests.KnownFailure('foo'), None)
967
 
        result.addExpectedFailure(test, err)
 
982
        test = _get_test("test_xfail")
 
983
        test.run(result)
968
984
        self.assertFalse(result.wasStrictlySuccessful())
969
985
        self.assertEqual(None, result._extractBenchmarkTime(test))
970
986
 
1002
1018
        self.assertEquals(2, result.count)
1003
1019
 
1004
1020
 
1005
 
class TestUnicodeFilenameFeature(tests.TestCase):
1006
 
 
1007
 
    def test_probe_passes(self):
1008
 
        """UnicodeFilenameFeature._probe passes."""
1009
 
        # We can't test much more than that because the behaviour depends
1010
 
        # on the platform.
1011
 
        tests.UnicodeFilenameFeature._probe()
1012
 
 
1013
 
 
1014
1021
class TestRunner(tests.TestCase):
1015
1022
 
1016
1023
    def dummy_test(self):
1042
1049
        test = unittest.TestSuite()
1043
1050
        test.addTest(Test("known_failure_test"))
1044
1051
        def failing_test():
1045
 
            self.fail('foo')
 
1052
            raise AssertionError('foo')
1046
1053
        test.addTest(unittest.FunctionTestCase(failing_test))
1047
1054
        stream = StringIO()
1048
1055
        runner = tests.TextTestRunner(stream=stream)
1056
1063
            '^----------------------------------------------------------------------\n'
1057
1064
            'Traceback \\(most recent call last\\):\n'
1058
1065
            '  .*' # File .*, line .*, in failing_test' - but maybe not from .pyc
1059
 
            '    self.fail\\(\'foo\'\\)\n'
 
1066
            '    raise AssertionError\\(\'foo\'\\)\n'
1060
1067
            '.*'
1061
1068
            '^----------------------------------------------------------------------\n'
1062
1069
            '.*'
1068
1075
        # the final output.
1069
1076
        class Test(tests.TestCase):
1070
1077
            def known_failure_test(self):
1071
 
                self.expectFailure('failed', self.assertTrue, False)
 
1078
                self.knownFailure("Never works...")
1072
1079
        test = Test("known_failure_test")
1073
1080
        stream = StringIO()
1074
1081
        runner = tests.TextTestRunner(stream=stream)
1080
1087
            '\n'
1081
1088
            'OK \\(known_failures=1\\)\n')
1082
1089
 
 
1090
    def test_unexpected_success_bad(self):
 
1091
        class Test(tests.TestCase):
 
1092
            def test_truth(self):
 
1093
                self.expectFailure("No absolute truth", self.assertTrue, True)
 
1094
        runner = tests.TextTestRunner(stream=StringIO())
 
1095
        result = self.run_test_runner(runner, Test("test_truth"))
 
1096
        self.assertContainsRe(runner.stream.getvalue(),
 
1097
            "=+\n"
 
1098
            "FAIL: \\S+\.test_truth\n"
 
1099
            "-+\n"
 
1100
            "(?:.*\n)*"
 
1101
            "\\s*(?:Text attachment: )?reason"
 
1102
            "(?:\n-+\n|: {{{)"
 
1103
            "No absolute truth"
 
1104
            "(?:\n-+\n|}}}\n)"
 
1105
            "(?:.*\n)*"
 
1106
            "-+\n"
 
1107
            "Ran 1 test in .*\n"
 
1108
            "\n"
 
1109
            "FAILED \\(failures=1\\)\n\\Z")
 
1110
 
1083
1111
    def test_result_decorator(self):
1084
1112
        # decorate results
1085
1113
        calls = []
1133
1161
        class SkippedTest(tests.TestCase):
1134
1162
 
1135
1163
            def setUp(self):
1136
 
                tests.TestCase.setUp(self)
 
1164
                super(SkippedTest, self).setUp()
1137
1165
                calls.append('setUp')
1138
1166
                self.addCleanup(self.cleanup)
1139
1167
 
1169
1197
 
1170
1198
    def test_unsupported_features_listed(self):
1171
1199
        """When unsupported features are encountered they are detailed."""
1172
 
        class Feature1(tests.Feature):
 
1200
        class Feature1(features.Feature):
1173
1201
            def _probe(self): return False
1174
 
        class Feature2(tests.Feature):
 
1202
        class Feature2(features.Feature):
1175
1203
            def _probe(self): return False
1176
1204
        # create sample tests
1177
1205
        test1 = SampleTestCase('_test_pass')
1244
1272
        result = self.run_test_runner(tests.TextTestRunner(stream=out),
1245
1273
            FailureWithUnicode("test_log_unicode"))
1246
1274
        self.assertContainsRe(out.getvalue(),
1247
 
            "Text attachment: log\n"
1248
 
            "-+\n"
1249
 
            "\d+\.\d+  \\\\u2606\n"
1250
 
            "-+\n")
 
1275
            "(?:Text attachment: )?log"
 
1276
            "(?:\n-+\n|: {{{)"
 
1277
            "\d+\.\d+  \\\\u2606"
 
1278
            "(?:\n-+\n|}}}\n)")
1251
1279
 
1252
1280
 
1253
1281
class SampleTestCase(tests.TestCase):
1454
1482
 
1455
1483
        Each self.time() call is individually and separately profiled.
1456
1484
        """
1457
 
        self.requireFeature(test_lsprof.LSProfFeature)
 
1485
        self.requireFeature(features.lsprof_feature)
1458
1486
        # overrides the class member with an instance member so no cleanup
1459
1487
        # needed.
1460
1488
        self._gather_lsprof_in_benchmarks = True
1479
1507
        transport_server = memory.MemoryServer()
1480
1508
        transport_server.start_server()
1481
1509
        self.addCleanup(transport_server.stop_server)
1482
 
        t = transport.get_transport(transport_server.get_url())
1483
 
        bzrdir.BzrDir.create(t.base)
 
1510
        t = transport.get_transport_from_url(transport_server.get_url())
 
1511
        controldir.ControlDir.create(t.base)
1484
1512
        self.assertRaises(errors.BzrError,
1485
 
            bzrdir.BzrDir.open_from_transport, t)
 
1513
            controldir.ControlDir.open_from_transport, t)
1486
1514
        # But if we declare this as safe, we can open the bzrdir.
1487
1515
        self.permit_url(t.base)
1488
1516
        self._bzr_selftest_roots.append(t.base)
1489
 
        bzrdir.BzrDir.open_from_transport(t)
 
1517
        controldir.ControlDir.open_from_transport(t)
1490
1518
 
1491
1519
    def test_requireFeature_available(self):
1492
1520
        """self.requireFeature(available) is a no-op."""
1493
 
        class Available(tests.Feature):
 
1521
        class Available(features.Feature):
1494
1522
            def _probe(self):return True
1495
1523
        feature = Available()
1496
1524
        self.requireFeature(feature)
1497
1525
 
1498
1526
    def test_requireFeature_unavailable(self):
1499
1527
        """self.requireFeature(unavailable) raises UnavailableFeature."""
1500
 
        class Unavailable(tests.Feature):
 
1528
        class Unavailable(features.Feature):
1501
1529
            def _probe(self):return False
1502
1530
        feature = Unavailable()
1503
1531
        self.assertRaises(tests.UnavailableFeature,
1626
1654
        self.assertRaises(AssertionError,
1627
1655
            self.assertListRaises, _TestException, success_generator)
1628
1656
 
 
1657
    def _run_successful_test(self, test):
 
1658
        result = testtools.TestResult()
 
1659
        test.run(result)
 
1660
        self.assertTrue(result.wasSuccessful())
 
1661
        return result
 
1662
 
1629
1663
    def test_overrideAttr_without_value(self):
1630
1664
        self.test_attr = 'original' # Define a test attribute
1631
1665
        obj = self # Make 'obj' visible to the embedded test
1632
1666
        class Test(tests.TestCase):
1633
1667
 
1634
1668
            def setUp(self):
1635
 
                tests.TestCase.setUp(self)
 
1669
                super(Test, self).setUp()
1636
1670
                self.orig = self.overrideAttr(obj, 'test_attr')
1637
1671
 
1638
1672
            def test_value(self):
1641
1675
                obj.test_attr = 'modified'
1642
1676
                self.assertEqual('modified', obj.test_attr)
1643
1677
 
1644
 
        test = Test('test_value')
1645
 
        test.run(unittest.TestResult())
 
1678
        self._run_successful_test(Test('test_value'))
1646
1679
        self.assertEqual('original', obj.test_attr)
1647
1680
 
1648
1681
    def test_overrideAttr_with_value(self):
1651
1684
        class Test(tests.TestCase):
1652
1685
 
1653
1686
            def setUp(self):
1654
 
                tests.TestCase.setUp(self)
 
1687
                super(Test, self).setUp()
1655
1688
                self.orig = self.overrideAttr(obj, 'test_attr', new='modified')
1656
1689
 
1657
1690
            def test_value(self):
1658
1691
                self.assertEqual('original', self.orig)
1659
1692
                self.assertEqual('modified', obj.test_attr)
1660
1693
 
1661
 
        test = Test('test_value')
1662
 
        test.run(unittest.TestResult())
 
1694
        self._run_successful_test(Test('test_value'))
1663
1695
        self.assertEqual('original', obj.test_attr)
1664
1696
 
1665
 
 
1666
 
class _MissingFeature(tests.Feature):
 
1697
    def test_overrideAttr_with_no_existing_value_and_value(self):
 
1698
        # Do not define the test_attribute
 
1699
        obj = self # Make 'obj' visible to the embedded test
 
1700
        class Test(tests.TestCase):
 
1701
 
 
1702
            def setUp(self):
 
1703
                tests.TestCase.setUp(self)
 
1704
                self.orig = self.overrideAttr(obj, 'test_attr', new='modified')
 
1705
 
 
1706
            def test_value(self):
 
1707
                self.assertEqual(tests._unitialized_attr, self.orig)
 
1708
                self.assertEqual('modified', obj.test_attr)
 
1709
 
 
1710
        self._run_successful_test(Test('test_value'))
 
1711
        self.assertRaises(AttributeError, getattr, obj, 'test_attr')
 
1712
 
 
1713
    def test_overrideAttr_with_no_existing_value_and_no_value(self):
 
1714
        # Do not define the test_attribute
 
1715
        obj = self # Make 'obj' visible to the embedded test
 
1716
        class Test(tests.TestCase):
 
1717
 
 
1718
            def setUp(self):
 
1719
                tests.TestCase.setUp(self)
 
1720
                self.orig = self.overrideAttr(obj, 'test_attr')
 
1721
 
 
1722
            def test_value(self):
 
1723
                self.assertEqual(tests._unitialized_attr, self.orig)
 
1724
                self.assertRaises(AttributeError, getattr, obj, 'test_attr')
 
1725
 
 
1726
        self._run_successful_test(Test('test_value'))
 
1727
        self.assertRaises(AttributeError, getattr, obj, 'test_attr')
 
1728
 
 
1729
    def test_recordCalls(self):
 
1730
        from bzrlib.tests import test_selftest
 
1731
        calls = self.recordCalls(
 
1732
            test_selftest, '_add_numbers')
 
1733
        self.assertEqual(test_selftest._add_numbers(2, 10),
 
1734
            12)
 
1735
        self.assertEquals(calls, [((2, 10), {})])
 
1736
 
 
1737
 
 
1738
def _add_numbers(a, b):
 
1739
    return a + b
 
1740
 
 
1741
 
 
1742
class _MissingFeature(features.Feature):
1667
1743
    def _probe(self):
1668
1744
        return False
1669
1745
missing_feature = _MissingFeature()
1720
1796
        result = self._run_test('test_fail')
1721
1797
        self.assertEqual(1, len(result.failures))
1722
1798
        result_content = result.failures[0][1]
1723
 
        self.assertContainsRe(result_content, 'Text attachment: log')
 
1799
        self.assertContainsRe(result_content,
 
1800
            '(?m)^(?:Text attachment: )?log(?:$|: )')
1724
1801
        self.assertContainsRe(result_content, 'this was a failing test')
1725
1802
 
1726
1803
    def test_error_has_log(self):
1727
1804
        result = self._run_test('test_error')
1728
1805
        self.assertEqual(1, len(result.errors))
1729
1806
        result_content = result.errors[0][1]
1730
 
        self.assertContainsRe(result_content, 'Text attachment: log')
 
1807
        self.assertContainsRe(result_content,
 
1808
            '(?m)^(?:Text attachment: )?log(?:$|: )')
1731
1809
        self.assertContainsRe(result_content, 'this test errored')
1732
1810
 
1733
1811
    def test_skip_has_no_log(self):
1752
1830
        result = self._run_test('test_xfail')
1753
1831
        self.assertEqual(1, len(result.expectedFailures))
1754
1832
        result_content = result.expectedFailures[0][1]
1755
 
        self.assertNotContainsRe(result_content, 'Text attachment: log')
 
1833
        self.assertNotContainsRe(result_content,
 
1834
            '(?m)^(?:Text attachment: )?log(?:$|: )')
1756
1835
        self.assertNotContainsRe(result_content, 'test with expected failure')
1757
1836
 
1758
1837
    def test_unexpected_success_has_log(self):
1931
2010
    def test_make_branch_and_tree_with_format(self):
1932
2011
        # we should be able to supply a format to make_branch_and_tree
1933
2012
        self.make_branch_and_tree('a', format=bzrlib.bzrdir.BzrDirMetaFormat1())
1934
 
        self.assertIsInstance(bzrlib.bzrdir.BzrDir.open('a')._format,
 
2013
        self.assertIsInstance(bzrlib.controldir.ControlDir.open('a')._format,
1935
2014
                              bzrlib.bzrdir.BzrDirMetaFormat1)
1936
2015
 
1937
2016
    def test_make_branch_and_memory_tree(self):
2015
2094
        self.assertLength(2, output.readlines())
2016
2095
 
2017
2096
    def test_lsprof_tests(self):
2018
 
        self.requireFeature(test_lsprof.LSProfFeature)
2019
 
        calls = []
 
2097
        self.requireFeature(features.lsprof_feature)
 
2098
        results = []
2020
2099
        class Test(object):
2021
2100
            def __call__(test, result):
2022
2101
                test.run(result)
2023
2102
            def run(test, result):
2024
 
                self.assertIsInstance(result, ExtendedToOriginalDecorator)
2025
 
                calls.append("called")
 
2103
                results.append(result)
2026
2104
            def countTestCases(self):
2027
2105
                return 1
2028
2106
        self.run_selftest(test_suite_factory=Test, lsprof_tests=True)
2029
 
        self.assertLength(1, calls)
 
2107
        self.assertLength(1, results)
 
2108
        self.assertIsInstance(results.pop(), ExtendedToOriginalDecorator)
2030
2109
 
2031
2110
    def test_random(self):
2032
2111
        # test randomising by listing a number of tests.
2167
2246
        self.assertNotContainsRe(content, 'test with expected failure')
2168
2247
        self.assertEqual(1, len(result.expectedFailures))
2169
2248
        result_content = result.expectedFailures[0][1]
2170
 
        self.assertNotContainsRe(result_content, 'Text attachment: log')
 
2249
        self.assertNotContainsRe(result_content,
 
2250
            '(?m)^(?:Text attachment: )?log(?:$|: )')
2171
2251
        self.assertNotContainsRe(result_content, 'test with expected failure')
2172
2252
 
2173
2253
    def test_unexpected_success_has_log(self):
2174
2254
        content, result = self.run_subunit_stream('test_unexpected_success')
2175
2255
        self.assertContainsRe(content, '(?m)^log$')
2176
2256
        self.assertContainsRe(content, 'test with unexpected success')
2177
 
        self.expectFailure('subunit treats "unexpectedSuccess"'
2178
 
                           ' as a plain success',
2179
 
            self.assertEqual, 1, len(result.unexpectedSuccesses))
 
2257
        # GZ 2011-05-18: Old versions of subunit treat unexpected success as a
 
2258
        #                success, if a min version check is added remove this
 
2259
        from subunit import TestProtocolClient as _Client
 
2260
        if _Client.addUnexpectedSuccess.im_func is _Client.addSuccess.im_func:
 
2261
            self.expectFailure('subunit treats "unexpectedSuccess"'
 
2262
                               ' as a plain success',
 
2263
                self.assertEqual, 1, len(result.unexpectedSuccesses))
2180
2264
        self.assertEqual(1, len(result.unexpectedSuccesses))
2181
2265
        test = result.unexpectedSuccesses[0]
2182
2266
        # RemotedTestCase doesn't preserve the "details"
2364
2448
    """Base class for tests testing how we might run bzr."""
2365
2449
 
2366
2450
    def setUp(self):
2367
 
        tests.TestCaseWithTransport.setUp(self)
 
2451
        super(TestWithFakedStartBzrSubprocess, self).setUp()
2368
2452
        self.subprocess_calls = []
2369
2453
 
2370
2454
    def start_bzr_subprocess(self, process_args, env_changes=None,
2480
2564
 
2481
2565
 
2482
2566
class TestStartBzrSubProcess(tests.TestCase):
 
2567
    """Stub test start_bzr_subprocess."""
2483
2568
 
2484
 
    def check_popen_state(self):
2485
 
        """Replace to make assertions when popen is called."""
 
2569
    def _subprocess_log_cleanup(self):
 
2570
        """Inhibits the base version as we don't produce a log file."""
2486
2571
 
2487
2572
    def _popen(self, *args, **kwargs):
2488
 
        """Record the command that is run, so that we can ensure it is correct"""
 
2573
        """Override the base version to record the command that is run.
 
2574
 
 
2575
        From there we can ensure it is correct without spawning a real process.
 
2576
        """
2489
2577
        self.check_popen_state()
2490
2578
        self._popen_args = args
2491
2579
        self._popen_kwargs = kwargs
2492
2580
        raise _DontSpawnProcess()
2493
2581
 
 
2582
    def check_popen_state(self):
 
2583
        """Replace to make assertions when popen is called."""
 
2584
 
2494
2585
    def test_run_bzr_subprocess_no_plugins(self):
2495
2586
        self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [])
2496
2587
        command = self._popen_args[0]
2500
2591
 
2501
2592
    def test_allow_plugins(self):
2502
2593
        self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
2503
 
            allow_plugins=True)
 
2594
                          allow_plugins=True)
2504
2595
        command = self._popen_args[0]
2505
2596
        self.assertEqual([], command[2:])
2506
2597
 
2511
2602
            self.assertEqual('set variable', os.environ['EXISTANT_ENV_VAR'])
2512
2603
        self.check_popen_state = check_environment
2513
2604
        self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
2514
 
            env_changes={'EXISTANT_ENV_VAR':'set variable'})
 
2605
                          env_changes={'EXISTANT_ENV_VAR':'set variable'})
2515
2606
        # not set in theparent
2516
2607
        self.assertFalse('EXISTANT_ENV_VAR' in os.environ)
2517
2608
 
2523
2614
        os.environ['EXISTANT_ENV_VAR'] = 'set variable'
2524
2615
        self.check_popen_state = check_environment
2525
2616
        self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
2526
 
            env_changes={'EXISTANT_ENV_VAR':None})
 
2617
                          env_changes={'EXISTANT_ENV_VAR':None})
2527
2618
        # Still set in parent
2528
2619
        self.assertEqual('set variable', os.environ['EXISTANT_ENV_VAR'])
2529
2620
        del os.environ['EXISTANT_ENV_VAR']
2534
2625
            self.assertFalse('NON_EXISTANT_ENV_VAR' in os.environ)
2535
2626
        self.check_popen_state = check_environment
2536
2627
        self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
2537
 
            env_changes={'NON_EXISTANT_ENV_VAR':None})
 
2628
                          env_changes={'NON_EXISTANT_ENV_VAR':None})
2538
2629
 
2539
2630
    def test_working_dir(self):
2540
2631
        """Test that we can specify the working dir for the child"""
2543
2634
        chdirs = []
2544
2635
        def chdir(path):
2545
2636
            chdirs.append(path)
2546
 
        os.chdir = chdir
2547
 
        try:
2548
 
            def getcwd():
2549
 
                return 'current'
2550
 
            osutils.getcwd = getcwd
2551
 
            try:
2552
 
                self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
2553
 
                    working_dir='foo')
2554
 
            finally:
2555
 
                osutils.getcwd = orig_getcwd
2556
 
        finally:
2557
 
            os.chdir = orig_chdir
 
2637
        self.overrideAttr(os, 'chdir', chdir)
 
2638
        def getcwd():
 
2639
            return 'current'
 
2640
        self.overrideAttr(osutils, 'getcwd', getcwd)
 
2641
        self.assertRaises(_DontSpawnProcess, self.start_bzr_subprocess, [],
 
2642
                          working_dir='foo')
2558
2643
        self.assertEqual(['foo', 'current'], chdirs)
2559
2644
 
2560
2645
    def test_get_bzr_path_with_cwd_bzrlib(self):
2580
2665
        self.assertEqual('bzr: interrupted\n', result[1])
2581
2666
 
2582
2667
 
2583
 
class TestFeature(tests.TestCase):
2584
 
 
2585
 
    def test_caching(self):
2586
 
        """Feature._probe is called by the feature at most once."""
2587
 
        class InstrumentedFeature(tests.Feature):
2588
 
            def __init__(self):
2589
 
                super(InstrumentedFeature, self).__init__()
2590
 
                self.calls = []
2591
 
            def _probe(self):
2592
 
                self.calls.append('_probe')
2593
 
                return False
2594
 
        feature = InstrumentedFeature()
2595
 
        feature.available()
2596
 
        self.assertEqual(['_probe'], feature.calls)
2597
 
        feature.available()
2598
 
        self.assertEqual(['_probe'], feature.calls)
2599
 
 
2600
 
    def test_named_str(self):
2601
 
        """Feature.__str__ should thunk to feature_name()."""
2602
 
        class NamedFeature(tests.Feature):
2603
 
            def feature_name(self):
2604
 
                return 'symlinks'
2605
 
        feature = NamedFeature()
2606
 
        self.assertEqual('symlinks', str(feature))
2607
 
 
2608
 
    def test_default_str(self):
2609
 
        """Feature.__str__ should default to __class__.__name__."""
2610
 
        class NamedFeature(tests.Feature):
2611
 
            pass
2612
 
        feature = NamedFeature()
2613
 
        self.assertEqual('NamedFeature', str(feature))
2614
 
 
2615
 
 
2616
 
class TestUnavailableFeature(tests.TestCase):
2617
 
 
2618
 
    def test_access_feature(self):
2619
 
        feature = tests.Feature()
2620
 
        exception = tests.UnavailableFeature(feature)
2621
 
        self.assertIs(feature, exception.args[0])
2622
 
 
2623
 
 
2624
 
simple_thunk_feature = tests._CompatabilityThunkFeature(
2625
 
    deprecated_in((2, 1, 0)),
2626
 
    'bzrlib.tests.test_selftest',
2627
 
    'simple_thunk_feature','UnicodeFilename',
2628
 
    replacement_module='bzrlib.tests'
2629
 
    )
2630
 
 
2631
 
class Test_CompatibilityFeature(tests.TestCase):
2632
 
 
2633
 
    def test_does_thunk(self):
2634
 
        res = self.callDeprecated(
2635
 
            ['bzrlib.tests.test_selftest.simple_thunk_feature was deprecated'
2636
 
             ' in version 2.1.0. Use bzrlib.tests.UnicodeFilename instead.'],
2637
 
            simple_thunk_feature.available)
2638
 
        self.assertEqual(tests.UnicodeFilename.available(), res)
2639
 
 
2640
 
 
2641
 
class TestModuleAvailableFeature(tests.TestCase):
2642
 
 
2643
 
    def test_available_module(self):
2644
 
        feature = tests.ModuleAvailableFeature('bzrlib.tests')
2645
 
        self.assertEqual('bzrlib.tests', feature.module_name)
2646
 
        self.assertEqual('bzrlib.tests', str(feature))
2647
 
        self.assertTrue(feature.available())
2648
 
        self.assertIs(tests, feature.module)
2649
 
 
2650
 
    def test_unavailable_module(self):
2651
 
        feature = tests.ModuleAvailableFeature('bzrlib.no_such_module_exists')
2652
 
        self.assertEqual('bzrlib.no_such_module_exists', str(feature))
2653
 
        self.assertFalse(feature.available())
2654
 
        self.assertIs(None, feature.module)
2655
 
 
2656
 
 
2657
2668
class TestSelftestFiltering(tests.TestCase):
2658
2669
 
2659
2670
    def setUp(self):
2660
 
        tests.TestCase.setUp(self)
 
2671
        super(TestSelftestFiltering, self).setUp()
2661
2672
        self.suite = TestUtil.TestSuite()
2662
2673
        self.loader = TestUtil.TestLoader()
2663
2674
        self.suite.addTest(self.loader.loadTestsFromModule(
3358
3369
        self.assertLength(1, calls)
3359
3370
 
3360
3371
 
 
3372
class _Selftest(object):
 
3373
    """Mixin for tests needing full selftest output"""
 
3374
 
 
3375
    def _inject_stream_into_subunit(self, stream):
 
3376
        """To be overridden by subclasses that run tests out of process"""
 
3377
 
 
3378
    def _run_selftest(self, **kwargs):
 
3379
        sio = StringIO()
 
3380
        self._inject_stream_into_subunit(sio)
 
3381
        tests.selftest(stream=sio, stop_on_failure=False, **kwargs)
 
3382
        return sio.getvalue()
 
3383
 
 
3384
 
 
3385
class _ForkedSelftest(_Selftest):
 
3386
    """Mixin for tests needing full selftest output with forked children"""
 
3387
 
 
3388
    _test_needs_features = [features.subunit]
 
3389
 
 
3390
    def _inject_stream_into_subunit(self, stream):
 
3391
        """Monkey-patch subunit so the extra output goes to stream not stdout
 
3392
 
 
3393
        Some APIs need rewriting so this kind of bogus hackery can be replaced
 
3394
        by passing the stream param from run_tests down into ProtocolTestCase.
 
3395
        """
 
3396
        from subunit import ProtocolTestCase
 
3397
        _original_init = ProtocolTestCase.__init__
 
3398
        def _init_with_passthrough(self, *args, **kwargs):
 
3399
            _original_init(self, *args, **kwargs)
 
3400
            self._passthrough = stream
 
3401
        self.overrideAttr(ProtocolTestCase, "__init__", _init_with_passthrough)
 
3402
 
 
3403
    def _run_selftest(self, **kwargs):
 
3404
        # GZ 2011-05-26: Add a PosixSystem feature so this check can go away
 
3405
        if getattr(os, "fork", None) is None:
 
3406
            raise tests.TestNotApplicable("Platform doesn't support forking")
 
3407
        # Make sure the fork code is actually invoked by claiming two cores
 
3408
        self.overrideAttr(osutils, "local_concurrency", lambda: 2)
 
3409
        kwargs.setdefault("suite_decorators", []).append(tests.fork_decorator)
 
3410
        return super(_ForkedSelftest, self)._run_selftest(**kwargs)
 
3411
 
 
3412
 
 
3413
class TestParallelFork(_ForkedSelftest, tests.TestCase):
 
3414
    """Check operation of --parallel=fork selftest option"""
 
3415
 
 
3416
    def test_error_in_child_during_fork(self):
 
3417
        """Error in a forked child during test setup should get reported"""
 
3418
        class Test(tests.TestCase):
 
3419
            def testMethod(self):
 
3420
                pass
 
3421
        # We don't care what, just break something that a child will run
 
3422
        self.overrideAttr(tests, "workaround_zealous_crypto_random", None)
 
3423
        out = self._run_selftest(test_suite_factory=Test)
 
3424
        # Lines from the tracebacks of the two child processes may be mixed
 
3425
        # together due to the way subunit parses and forwards the streams,
 
3426
        # so permit extra lines between each part of the error output.
 
3427
        self.assertContainsRe(out,
 
3428
            "Traceback.*:\n"
 
3429
            "(?:.*\n)*"
 
3430
            ".+ in fork_for_tests\n"
 
3431
            "(?:.*\n)*"
 
3432
            "\s*workaround_zealous_crypto_random\(\)\n"
 
3433
            "(?:.*\n)*"
 
3434
            "TypeError:")
 
3435
 
 
3436
 
 
3437
class TestUncollectedWarnings(_Selftest, tests.TestCase):
 
3438
    """Check a test case still alive after being run emits a warning"""
 
3439
 
 
3440
    class Test(tests.TestCase):
 
3441
        def test_pass(self):
 
3442
            pass
 
3443
        def test_self_ref(self):
 
3444
            self.also_self = self.test_self_ref
 
3445
        def test_skip(self):
 
3446
            self.skip("Don't need")
 
3447
 
 
3448
    def _get_suite(self):
 
3449
        return TestUtil.TestSuite([
 
3450
            self.Test("test_pass"),
 
3451
            self.Test("test_self_ref"),
 
3452
            self.Test("test_skip"),
 
3453
            ])
 
3454
 
 
3455
    def _run_selftest_with_suite(self, **kwargs):
 
3456
        old_flags = tests.selftest_debug_flags
 
3457
        tests.selftest_debug_flags = old_flags.union(["uncollected_cases"])
 
3458
        gc_on = gc.isenabled()
 
3459
        if gc_on:
 
3460
            gc.disable()
 
3461
        try:
 
3462
            output = self._run_selftest(test_suite_factory=self._get_suite,
 
3463
                **kwargs)
 
3464
        finally:
 
3465
            if gc_on:
 
3466
                gc.enable()
 
3467
            tests.selftest_debug_flags = old_flags
 
3468
        self.assertNotContainsRe(output, "Uncollected test case.*test_pass")
 
3469
        self.assertContainsRe(output, "Uncollected test case.*test_self_ref")
 
3470
        return output
 
3471
 
 
3472
    def test_testsuite(self):
 
3473
        self._run_selftest_with_suite()
 
3474
 
 
3475
    def test_pattern(self):
 
3476
        out = self._run_selftest_with_suite(pattern="test_(?:pass|self_ref)$")
 
3477
        self.assertNotContainsRe(out, "test_skip")
 
3478
 
 
3479
    def test_exclude_pattern(self):
 
3480
        out = self._run_selftest_with_suite(exclude_pattern="test_skip$")
 
3481
        self.assertNotContainsRe(out, "test_skip")
 
3482
 
 
3483
    def test_random_seed(self):
 
3484
        self._run_selftest_with_suite(random_seed="now")
 
3485
 
 
3486
    def test_matching_tests_first(self):
 
3487
        self._run_selftest_with_suite(matching_tests_first=True,
 
3488
            pattern="test_self_ref$")
 
3489
 
 
3490
    def test_starting_with_and_exclude(self):
 
3491
        out = self._run_selftest_with_suite(starting_with=["bt."],
 
3492
            exclude_pattern="test_skip$")
 
3493
        self.assertNotContainsRe(out, "test_skip")
 
3494
 
 
3495
    def test_additonal_decorator(self):
 
3496
        out = self._run_selftest_with_suite(
 
3497
            suite_decorators=[tests.TestDecorator])
 
3498
 
 
3499
 
 
3500
class TestUncollectedWarningsSubunit(TestUncollectedWarnings):
 
3501
    """Check warnings from tests staying alive are emitted with subunit"""
 
3502
 
 
3503
    _test_needs_features = [features.subunit]
 
3504
 
 
3505
    def _run_selftest_with_suite(self, **kwargs):
 
3506
        return TestUncollectedWarnings._run_selftest_with_suite(self,
 
3507
            runner_class=tests.SubUnitBzrRunner, **kwargs)
 
3508
 
 
3509
 
 
3510
class TestUncollectedWarningsForked(_ForkedSelftest, TestUncollectedWarnings):
 
3511
    """Check warnings from tests staying alive are emitted when forking"""
 
3512
 
 
3513
 
3361
3514
class TestEnvironHandling(tests.TestCase):
3362
3515
 
3363
3516
    def test_overrideEnv_None_called_twice_doesnt_leak(self):
3497
3650
        self.assertDocTestStringFails(doctest.DocTestSuite, test)
3498
3651
        # tests.DocTestSuite sees None
3499
3652
        self.assertDocTestStringSucceds(tests.IsolatedDocTestSuite, test)
 
3653
 
 
3654
 
 
3655
class TestSelftestExcludePatterns(tests.TestCase):
 
3656
 
 
3657
    def setUp(self):
 
3658
        super(TestSelftestExcludePatterns, self).setUp()
 
3659
        self.overrideAttr(tests, 'test_suite', self.suite_factory)
 
3660
 
 
3661
    def suite_factory(self, keep_only=None, starting_with=None):
 
3662
        """A test suite factory with only a few tests."""
 
3663
        class Test(tests.TestCase):
 
3664
            def id(self):
 
3665
                # We don't need the full class path
 
3666
                return self._testMethodName
 
3667
            def a(self):
 
3668
                pass
 
3669
            def b(self):
 
3670
                pass
 
3671
            def c(self):
 
3672
                pass
 
3673
        return TestUtil.TestSuite([Test("a"), Test("b"), Test("c")])
 
3674
 
 
3675
    def assertTestList(self, expected, *selftest_args):
 
3676
        # We rely on setUp installing the right test suite factory so we can
 
3677
        # test at the command level without loading the whole test suite
 
3678
        out, err = self.run_bzr(('selftest', '--list') + selftest_args)
 
3679
        actual = out.splitlines()
 
3680
        self.assertEquals(expected, actual)
 
3681
 
 
3682
    def test_full_list(self):
 
3683
        self.assertTestList(['a', 'b', 'c'])
 
3684
 
 
3685
    def test_single_exclude(self):
 
3686
        self.assertTestList(['b', 'c'], '-x', 'a')
 
3687
 
 
3688
    def test_mutiple_excludes(self):
 
3689
        self.assertTestList(['c'], '-x', 'a', '-x', 'b')
 
3690
 
 
3691
 
 
3692
class TestCounterHooks(tests.TestCase, SelfTestHelper):
 
3693
 
 
3694
    _test_needs_features = [features.subunit]
 
3695
 
 
3696
    def setUp(self):
 
3697
        super(TestCounterHooks, self).setUp()
 
3698
        class Test(tests.TestCase):
 
3699
 
 
3700
            def setUp(self):
 
3701
                super(Test, self).setUp()
 
3702
                self.hooks = hooks.Hooks()
 
3703
                self.hooks.add_hook('myhook', 'Foo bar blah', (2,4))
 
3704
                self.install_counter_hook(self.hooks, 'myhook')
 
3705
 
 
3706
            def no_hook(self):
 
3707
                pass
 
3708
 
 
3709
            def run_hook_once(self):
 
3710
                for hook in self.hooks['myhook']:
 
3711
                    hook(self)
 
3712
 
 
3713
        self.test_class = Test
 
3714
 
 
3715
    def assertHookCalls(self, expected_calls, test_name):
 
3716
        test = self.test_class(test_name)
 
3717
        result = unittest.TestResult()
 
3718
        test.run(result)
 
3719
        self.assertTrue(hasattr(test, '_counters'))
 
3720
        self.assertTrue(test._counters.has_key('myhook'))
 
3721
        self.assertEquals(expected_calls, test._counters['myhook'])
 
3722
 
 
3723
    def test_no_hook(self):
 
3724
        self.assertHookCalls(0, 'no_hook')
 
3725
 
 
3726
    def test_run_hook_once(self):
 
3727
        tt = features.testtools
 
3728
        if tt.module.__version__ < (0, 9, 8):
 
3729
            raise tests.TestSkipped('testtools-0.9.8 required for addDetail')
 
3730
        self.assertHookCalls(1, 'run_hook_once')