~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Vincent Ladeuil
  • Date: 2012-02-14 17:22:37 UTC
  • mfrom: (6466 +trunk)
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120214172237-7dv7er3n4uy8d5m4
Merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Testing framework extensions"""
18
18
 
19
 
# TODO: Perhaps there should be an API to find out if bzr running under the
20
 
# test suite -- some plugins might want to avoid making intrusive changes if
21
 
# this is the case.  However, we want behaviour under to test to diverge as
22
 
# little as possible, so this should be used rarely if it's added at all.
23
 
# (Suggestion from j-a-meinel, 2005-11-24)
 
19
from __future__ import absolute_import
24
20
 
25
21
# NOTE: Some classes in here use camelCaseNaming() rather than
26
22
# underscore_naming().  That's for consistency with unittest; it's not the
42
38
import random
43
39
import re
44
40
import shlex
 
41
import site
45
42
import stat
46
43
import subprocess
47
44
import sys
67
64
    chk_map,
68
65
    commands as _mod_commands,
69
66
    config,
 
67
    i18n,
70
68
    debug,
71
69
    errors,
72
70
    hooks,
94
92
    memory,
95
93
    pathfilter,
96
94
    )
 
95
from bzrlib.symbol_versioning import (
 
96
    deprecated_function,
 
97
    deprecated_in,
 
98
    )
97
99
from bzrlib.tests import (
 
100
    fixtures,
98
101
    test_server,
99
102
    TestUtil,
100
103
    treeshape,
101
104
    )
102
105
from bzrlib.ui import NullProgressView
103
106
from bzrlib.ui.text import TextUIFactory
 
107
from bzrlib.tests.features import _CompatabilityThunkFeature
104
108
 
105
109
# Mark this python module as being part of the implementation
106
110
# of unittest: this gives us better tracebacks where the last
142
146
    'BZREMAIL': None, # may still be present in the environment
143
147
    'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
144
148
    'BZR_PROGRESS_BAR': None,
145
 
    'BZR_LOG': None,
 
149
    # This should trap leaks to ~/.bzr.log. This occurs when tests use TestCase
 
150
    # as a base class instead of TestCaseInTempDir. Tests inheriting from
 
151
    # TestCase should not use disk resources, BZR_LOG is one.
 
152
    'BZR_LOG': '/you-should-use-TestCaseInTempDir-if-you-need-a-log-file',
146
153
    'BZR_PLUGIN_PATH': None,
147
154
    'BZR_DISABLE_PLUGINS': None,
148
155
    'BZR_PLUGINS_AT': None,
210
217
        osutils.set_or_unset_env(var, value)
211
218
 
212
219
 
 
220
def _clear__type_equality_funcs(test):
 
221
    """Cleanup bound methods stored on TestCase instances
 
222
 
 
223
    Clear the dict breaking a few (mostly) harmless cycles in the affected
 
224
    unittests released with Python 2.6 and initial Python 2.7 versions.
 
225
 
 
226
    For a few revisions between Python 2.7.1 and Python 2.7.2 that annoyingly
 
227
    shipped in Oneiric, an object with no clear method was used, hence the
 
228
    extra complications, see bug 809048 for details.
 
229
    """
 
230
    type_equality_funcs = getattr(test, "_type_equality_funcs", None)
 
231
    if type_equality_funcs is not None:
 
232
        tef_clear = getattr(type_equality_funcs, "clear", None)
 
233
        if tef_clear is None:
 
234
            tef_instance_dict = getattr(type_equality_funcs, "__dict__", None)
 
235
            if tef_instance_dict is not None:
 
236
                tef_clear = tef_instance_dict.clear
 
237
        if tef_clear is not None:
 
238
            tef_clear()
 
239
 
 
240
 
213
241
class ExtendedTestResult(testtools.TextTestResult):
214
242
    """Accepts, reports and accumulates the results of running tests.
215
243
 
219
247
    different types of display.
220
248
 
221
249
    When a test finishes, in whatever way, it calls one of the addSuccess,
222
 
    addFailure or addError classes.  These in turn may redirect to a more
 
250
    addFailure or addError methods.  These in turn may redirect to a more
223
251
    specific case for the special test results supported by our extended
224
252
    tests.
225
253
 
377
405
        if isinstance(test, TestCase):
378
406
            test.addCleanup(self._check_leaked_threads, test)
379
407
 
 
408
    def stopTest(self, test):
 
409
        super(ExtendedTestResult, self).stopTest(test)
 
410
        # Manually break cycles, means touching various private things but hey
 
411
        getDetails = getattr(test, "getDetails", None)
 
412
        if getDetails is not None:
 
413
            getDetails().clear()
 
414
        _clear__type_equality_funcs(test)
 
415
        self._traceback_from_test = None
 
416
 
380
417
    def startTests(self):
381
418
        self.report_tests_starting()
382
419
        self._active_threads = threading.enumerate()
383
420
 
384
 
    def stopTest(self, test):
385
 
        self._traceback_from_test = None
386
 
 
387
421
    def _check_leaked_threads(self, test):
388
422
        """See if any threads have leaked since last call
389
423
 
449
483
        self.known_failure_count += 1
450
484
        self.report_known_failure(test, err)
451
485
 
 
486
    def addUnexpectedSuccess(self, test, details=None):
 
487
        """Tell result the test unexpectedly passed, counting as a failure
 
488
 
 
489
        When the minimum version of testtools required becomes 0.9.8 this
 
490
        can be updated to use the new handling there.
 
491
        """
 
492
        super(ExtendedTestResult, self).addFailure(test, details=details)
 
493
        self.failure_count += 1
 
494
        self.report_unexpected_success(test,
 
495
            "".join(details["reason"].iter_text()))
 
496
        if self.stop_early:
 
497
            self.stop()
 
498
 
452
499
    def addNotSupported(self, test, feature):
453
500
        """The test will not be run because of a missing feature.
454
501
        """
471
518
        self.not_applicable_count += 1
472
519
        self.report_not_applicable(test, reason)
473
520
 
 
521
    def _count_stored_tests(self):
 
522
        """Count of tests instances kept alive due to not succeeding"""
 
523
        return self.error_count + self.failure_count + self.known_failure_count
 
524
 
474
525
    def _post_mortem(self, tb=None):
475
526
        """Start a PDB post mortem session."""
476
527
        if os.environ.get('BZR_TEST_PDB', None):
613
664
    def report_known_failure(self, test, err):
614
665
        pass
615
666
 
 
667
    def report_unexpected_success(self, test, reason):
 
668
        self.stream.write('FAIL: %s\n    %s: %s\n' % (
 
669
            self._test_description(test),
 
670
            "Unexpected success. Should have failed",
 
671
            reason,
 
672
            ))
 
673
 
616
674
    def report_skip(self, test, reason):
617
675
        pass
618
676
 
670
728
                % (self._testTimeString(test),
671
729
                   self._error_summary(err)))
672
730
 
 
731
    def report_unexpected_success(self, test, reason):
 
732
        self.stream.write(' FAIL %s\n%s: %s\n'
 
733
                % (self._testTimeString(test),
 
734
                   "Unexpected success. Should have failed",
 
735
                   reason))
 
736
 
673
737
    def report_success(self, test):
674
738
        self.stream.write('   OK %s\n' % self._testTimeString(test))
675
739
        for bench_called, stats in getattr(test, '_benchcalls', []):
894
958
 
895
959
    The method is really a factory and users are expected to use it as such.
896
960
    """
897
 
    
 
961
 
898
962
    kwargs['setUp'] = isolated_doctest_setUp
899
963
    kwargs['tearDown'] = isolated_doctest_tearDown
900
964
    return doctest.DocTestSuite(*args, **kwargs)
935
999
 
936
1000
    def setUp(self):
937
1001
        super(TestCase, self).setUp()
 
1002
 
 
1003
        timeout = config.GlobalStack().get('selftest.timeout')
 
1004
        if timeout:
 
1005
            timeout_fixture = fixtures.TimeoutFixture(timeout)
 
1006
            timeout_fixture.setUp()
 
1007
            self.addCleanup(timeout_fixture.cleanUp)
 
1008
 
938
1009
        for feature in getattr(self, '_test_needs_features', []):
939
1010
            self.requireFeature(feature)
940
 
        self._log_contents = None
941
 
        self.addDetail("log", content.Content(content.ContentType("text",
942
 
            "plain", {"charset": "utf8"}),
943
 
            lambda:[self._get_log(keep_log_file=True)]))
944
1011
        self._cleanEnvironment()
 
1012
 
 
1013
        if bzrlib.global_state is not None:
 
1014
            self.overrideAttr(bzrlib.global_state, 'cmdline_overrides',
 
1015
                              config.CommandLineStore())
 
1016
 
945
1017
        self._silenceUI()
946
1018
        self._startLogFile()
947
1019
        self._benchcalls = []
954
1026
        # between tests.  We should get rid of this altogether: bug 656694. --
955
1027
        # mbp 20101008
956
1028
        self.overrideAttr(bzrlib.trace, '_verbosity_level', 0)
 
1029
        # Isolate config option expansion until its default value for bzrlib is
 
1030
        # settled on or a the FIXME associated with _get_expand_default_value
 
1031
        # is addressed -- vila 20110219
 
1032
        self.overrideAttr(config, '_expand_default_value', None)
 
1033
        self._log_files = set()
 
1034
        # Each key in the ``_counters`` dict holds a value for a different
 
1035
        # counter. When the test ends, addDetail() should be used to output the
 
1036
        # counter values. This happens in install_counter_hook().
 
1037
        self._counters = {}
 
1038
        if 'config_stats' in selftest_debug_flags:
 
1039
            self._install_config_stats_hooks()
 
1040
        # Do not use i18n for tests (unless the test reverses this)
 
1041
        i18n.disable_i18n()
957
1042
 
958
1043
    def debug(self):
959
1044
        # debug a frame up.
960
1045
        import pdb
961
 
        pdb.Pdb().set_trace(sys._getframe().f_back)
 
1046
        # The sys preserved stdin/stdout should allow blackbox tests debugging
 
1047
        pdb.Pdb(stdin=sys.__stdin__, stdout=sys.__stdout__
 
1048
                ).set_trace(sys._getframe().f_back)
962
1049
 
963
1050
    def discardDetail(self, name):
964
1051
        """Extend the addDetail, getDetails api so we can remove a detail.
976
1063
        if name in details:
977
1064
            del details[name]
978
1065
 
 
1066
    def install_counter_hook(self, hooks, name, counter_name=None):
 
1067
        """Install a counting hook.
 
1068
 
 
1069
        Any hook can be counted as long as it doesn't need to return a value.
 
1070
 
 
1071
        :param hooks: Where the hook should be installed.
 
1072
 
 
1073
        :param name: The hook name that will be counted.
 
1074
 
 
1075
        :param counter_name: The counter identifier in ``_counters``, defaults
 
1076
            to ``name``.
 
1077
        """
 
1078
        _counters = self._counters # Avoid closing over self
 
1079
        if counter_name is None:
 
1080
            counter_name = name
 
1081
        if _counters.has_key(counter_name):
 
1082
            raise AssertionError('%s is already used as a counter name'
 
1083
                                  % (counter_name,))
 
1084
        _counters[counter_name] = 0
 
1085
        self.addDetail(counter_name, content.Content(content.UTF8_TEXT,
 
1086
            lambda: ['%d' % (_counters[counter_name],)]))
 
1087
        def increment_counter(*args, **kwargs):
 
1088
            _counters[counter_name] += 1
 
1089
        label = 'count %s calls' % (counter_name,)
 
1090
        hooks.install_named_hook(name, increment_counter, label)
 
1091
        self.addCleanup(hooks.uninstall_named_hook, name, label)
 
1092
 
 
1093
    def _install_config_stats_hooks(self):
 
1094
        """Install config hooks to count hook calls.
 
1095
 
 
1096
        """
 
1097
        for hook_name in ('get', 'set', 'remove', 'load', 'save'):
 
1098
            self.install_counter_hook(config.ConfigHooks, hook_name,
 
1099
                                       'config.%s' % (hook_name,))
 
1100
 
 
1101
        # The OldConfigHooks are private and need special handling to protect
 
1102
        # against recursive tests (tests that run other tests), so we just do
 
1103
        # manually what registering them into _builtin_known_hooks will provide
 
1104
        # us.
 
1105
        self.overrideAttr(config, 'OldConfigHooks', config._OldConfigHooks())
 
1106
        for hook_name in ('get', 'set', 'remove', 'load', 'save'):
 
1107
            self.install_counter_hook(config.OldConfigHooks, hook_name,
 
1108
                                      'old_config.%s' % (hook_name,))
 
1109
 
979
1110
    def _clear_debug_flags(self):
980
1111
        """Prevent externally set debug flags affecting tests.
981
1112
 
996
1127
        for key, (parent, name) in known_hooks.iter_parent_objects():
997
1128
            current_hooks = getattr(parent, name)
998
1129
            self._preserved_hooks[parent] = (name, current_hooks)
 
1130
        self._preserved_lazy_hooks = hooks._lazy_hooks
 
1131
        hooks._lazy_hooks = {}
999
1132
        self.addCleanup(self._restoreHooks)
1000
1133
        for key, (parent, name) in known_hooks.iter_parent_objects():
1001
1134
            factory = known_hooks.get(key)
1033
1166
        # break some locks on purpose and should be taken into account by
1034
1167
        # considering that breaking a lock is just a dirty way of releasing it.
1035
1168
        if len(acquired_locks) != (len(released_locks) + len(broken_locks)):
1036
 
            message = ('Different number of acquired and '
1037
 
                       'released or broken locks. (%s, %s + %s)' %
1038
 
                       (acquired_locks, released_locks, broken_locks))
 
1169
            message = (
 
1170
                'Different number of acquired and '
 
1171
                'released or broken locks.\n'
 
1172
                'acquired=%s\n'
 
1173
                'released=%s\n'
 
1174
                'broken=%s\n' %
 
1175
                (acquired_locks, released_locks, broken_locks))
1039
1176
            if not self._lock_check_thorough:
1040
1177
                # Rather than fail, just warn
1041
1178
                print "Broken test %s: %s" % (self, message)
1069
1206
 
1070
1207
    def permit_dir(self, name):
1071
1208
        """Permit a directory to be used by this test. See permit_url."""
1072
 
        name_transport = _mod_transport.get_transport(name)
 
1209
        name_transport = _mod_transport.get_transport_from_path(name)
1073
1210
        self.permit_url(name)
1074
1211
        self.permit_url(name_transport.base)
1075
1212
 
1154
1291
        self.addCleanup(transport_server.stop_server)
1155
1292
        # Obtain a real transport because if the server supplies a password, it
1156
1293
        # will be hidden from the base on the client side.
1157
 
        t = _mod_transport.get_transport(transport_server.get_url())
 
1294
        t = _mod_transport.get_transport_from_url(transport_server.get_url())
1158
1295
        # Some transport servers effectively chroot the backing transport;
1159
1296
        # others like SFTPServer don't - users of the transport can walk up the
1160
1297
        # transport to read the entire backing transport. This wouldn't matter
1261
1398
                         'st_mtime did not match')
1262
1399
        self.assertEqual(expected.st_ctime, actual.st_ctime,
1263
1400
                         'st_ctime did not match')
1264
 
        if sys.platform != 'win32':
 
1401
        if sys.platform == 'win32':
1265
1402
            # On Win32 both 'dev' and 'ino' cannot be trusted. In python2.4 it
1266
1403
            # is 'dev' that varies, in python 2.5 (6?) it is st_ino that is
1267
 
            # odd. Regardless we shouldn't actually try to assert anything
1268
 
            # about their values
 
1404
            # odd. We just force it to always be 0 to avoid any problems.
 
1405
            self.assertEqual(0, expected.st_dev)
 
1406
            self.assertEqual(0, actual.st_dev)
 
1407
            self.assertEqual(0, expected.st_ino)
 
1408
            self.assertEqual(0, actual.st_ino)
 
1409
        else:
1269
1410
            self.assertEqual(expected.st_dev, actual.st_dev,
1270
1411
                             'st_dev did not match')
1271
1412
            self.assertEqual(expected.st_ino, actual.st_ino,
1280
1421
                length, len(obj_with_len), obj_with_len))
1281
1422
 
1282
1423
    def assertLogsError(self, exception_class, func, *args, **kwargs):
1283
 
        """Assert that func(*args, **kwargs) quietly logs a specific exception.
 
1424
        """Assert that `func(*args, **kwargs)` quietly logs a specific error.
1284
1425
        """
1285
1426
        captured = []
1286
1427
        orig_log_exception_quietly = trace.log_exception_quietly
1287
1428
        try:
1288
1429
            def capture():
1289
1430
                orig_log_exception_quietly()
1290
 
                captured.append(sys.exc_info())
 
1431
                captured.append(sys.exc_info()[1])
1291
1432
            trace.log_exception_quietly = capture
1292
1433
            func(*args, **kwargs)
1293
1434
        finally:
1294
1435
            trace.log_exception_quietly = orig_log_exception_quietly
1295
1436
        self.assertLength(1, captured)
1296
 
        err = captured[0][1]
 
1437
        err = captured[0]
1297
1438
        self.assertIsInstance(err, exception_class)
1298
1439
        return err
1299
1440
 
1434
1575
 
1435
1576
    def assertFileEqual(self, content, path):
1436
1577
        """Fail if path does not contain 'content'."""
1437
 
        self.failUnlessExists(path)
 
1578
        self.assertPathExists(path)
1438
1579
        f = file(path, 'rb')
1439
1580
        try:
1440
1581
            s = f.read()
1450
1591
        else:
1451
1592
            self.assertEqual(expected_docstring, obj.__doc__)
1452
1593
 
 
1594
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1453
1595
    def failUnlessExists(self, path):
 
1596
        return self.assertPathExists(path)
 
1597
 
 
1598
    def assertPathExists(self, path):
1454
1599
        """Fail unless path or paths, which may be abs or relative, exist."""
1455
1600
        if not isinstance(path, basestring):
1456
1601
            for p in path:
1457
 
                self.failUnlessExists(p)
 
1602
                self.assertPathExists(p)
1458
1603
        else:
1459
 
            self.failUnless(osutils.lexists(path),path+" does not exist")
 
1604
            self.assertTrue(osutils.lexists(path),
 
1605
                path + " does not exist")
1460
1606
 
 
1607
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1461
1608
    def failIfExists(self, path):
 
1609
        return self.assertPathDoesNotExist(path)
 
1610
 
 
1611
    def assertPathDoesNotExist(self, path):
1462
1612
        """Fail if path or paths, which may be abs or relative, exist."""
1463
1613
        if not isinstance(path, basestring):
1464
1614
            for p in path:
1465
 
                self.failIfExists(p)
 
1615
                self.assertPathDoesNotExist(p)
1466
1616
        else:
1467
 
            self.failIf(osutils.lexists(path),path+" exists")
 
1617
            self.assertFalse(osutils.lexists(path),
 
1618
                path + " exists")
1468
1619
 
1469
1620
    def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
1470
1621
        """A helper for callDeprecated and applyDeprecated.
1496
1647
        not other callers that go direct to the warning module.
1497
1648
 
1498
1649
        To test that a deprecated method raises an error, do something like
1499
 
        this::
 
1650
        this (remember that both assertRaises and applyDeprecated delays *args
 
1651
        and **kwargs passing)::
1500
1652
 
1501
1653
            self.assertRaises(errors.ReservedId,
1502
1654
                self.applyDeprecated,
1580
1732
        return result
1581
1733
 
1582
1734
    def _startLogFile(self):
1583
 
        """Send bzr and test log messages to a temporary file.
1584
 
 
1585
 
        The file is removed as the test is torn down.
1586
 
        """
1587
 
        self._log_file = StringIO()
 
1735
        """Setup a in-memory target for bzr and testcase log messages"""
 
1736
        pseudo_log_file = StringIO()
 
1737
        def _get_log_contents_for_weird_testtools_api():
 
1738
            return [pseudo_log_file.getvalue().decode(
 
1739
                "utf-8", "replace").encode("utf-8")]
 
1740
        self.addDetail("log", content.Content(content.ContentType("text",
 
1741
            "plain", {"charset": "utf8"}),
 
1742
            _get_log_contents_for_weird_testtools_api))
 
1743
        self._log_file = pseudo_log_file
1588
1744
        self._log_memento = trace.push_log_file(self._log_file)
1589
1745
        self.addCleanup(self._finishLogFile)
1590
1746
 
1591
1747
    def _finishLogFile(self):
1592
 
        """Finished with the log file.
1593
 
 
1594
 
        Close the file and delete it, unless setKeepLogfile was called.
1595
 
        """
 
1748
        """Flush and dereference the in-memory log for this testcase"""
1596
1749
        if trace._trace_file:
1597
1750
            # flush the log file, to get all content
1598
1751
            trace._trace_file.flush()
1599
1752
        trace.pop_log_file(self._log_memento)
1600
 
        # Cache the log result and delete the file on disk
1601
 
        self._get_log(False)
 
1753
        # The logging module now tracks references for cleanup so discard ours
 
1754
        del self._log_memento
1602
1755
 
1603
1756
    def thisFailsStrictLockCheck(self):
1604
1757
        """It is known that this test would fail with -Dstrict_locks.
1616
1769
    def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1617
1770
        """Overrides an object attribute restoring it after the test.
1618
1771
 
 
1772
        :note: This should be used with discretion; you should think about
 
1773
        whether it's better to make the code testable without monkey-patching.
 
1774
 
1619
1775
        :param obj: The object that will be mutated.
1620
1776
 
1621
1777
        :param attr_name: The attribute name we want to preserve/override in
1646
1802
        self.addCleanup(osutils.set_or_unset_env, name, value)
1647
1803
        return value
1648
1804
 
 
1805
    def recordCalls(self, obj, attr_name):
 
1806
        """Monkeypatch in a wrapper that will record calls.
 
1807
 
 
1808
        The monkeypatch is automatically removed when the test concludes.
 
1809
 
 
1810
        :param obj: The namespace holding the reference to be replaced;
 
1811
            typically a module, class, or object.
 
1812
        :param attr_name: A string for the name of the attribute to 
 
1813
            patch.
 
1814
        :returns: A list that will be extended with one item every time the
 
1815
            function is called, with a tuple of (args, kwargs).
 
1816
        """
 
1817
        calls = []
 
1818
 
 
1819
        def decorator(*args, **kwargs):
 
1820
            calls.append((args, kwargs))
 
1821
            return orig(*args, **kwargs)
 
1822
        orig = self.overrideAttr(obj, attr_name, decorator)
 
1823
        return calls
 
1824
 
1649
1825
    def _cleanEnvironment(self):
1650
1826
        for name, value in isolated_environ.iteritems():
1651
1827
            self.overrideEnv(name, value)
1653
1829
    def _restoreHooks(self):
1654
1830
        for klass, (name, hooks) in self._preserved_hooks.items():
1655
1831
            setattr(klass, name, hooks)
 
1832
        self._preserved_hooks.clear()
 
1833
        bzrlib.hooks._lazy_hooks = self._preserved_lazy_hooks
 
1834
        self._preserved_lazy_hooks.clear()
1656
1835
 
1657
1836
    def knownFailure(self, reason):
1658
 
        """This test has failed for some known reason."""
1659
 
        raise KnownFailure(reason)
 
1837
        """Declare that this test fails for a known reason
 
1838
 
 
1839
        Tests that are known to fail should generally be using expectedFailure
 
1840
        with an appropriate reverse assertion if a change could cause the test
 
1841
        to start passing. Conversely if the test has no immediate prospect of
 
1842
        succeeding then using skip is more suitable.
 
1843
 
 
1844
        When this method is called while an exception is being handled, that
 
1845
        traceback will be used, otherwise a new exception will be thrown to
 
1846
        provide one but won't be reported.
 
1847
        """
 
1848
        self._add_reason(reason)
 
1849
        try:
 
1850
            exc_info = sys.exc_info()
 
1851
            if exc_info != (None, None, None):
 
1852
                self._report_traceback(exc_info)
 
1853
            else:
 
1854
                try:
 
1855
                    raise self.failureException(reason)
 
1856
                except self.failureException:
 
1857
                    exc_info = sys.exc_info()
 
1858
            # GZ 02-08-2011: Maybe cleanup this err.exc_info attribute too?
 
1859
            raise testtools.testcase._ExpectedFailure(exc_info)
 
1860
        finally:
 
1861
            del exc_info
1660
1862
 
1661
1863
    def _suppress_log(self):
1662
1864
        """Remove the log info from details."""
1750
1952
    def log(self, *args):
1751
1953
        trace.mutter(*args)
1752
1954
 
1753
 
    def _get_log(self, keep_log_file=False):
1754
 
        """Internal helper to get the log from bzrlib.trace for this test.
1755
 
 
1756
 
        Please use self.getDetails, or self.get_log to access this in test case
1757
 
        code.
1758
 
 
1759
 
        :param keep_log_file: When True, if the log is still a file on disk
1760
 
            leave it as a file on disk. When False, if the log is still a file
1761
 
            on disk, the log file is deleted and the log preserved as
1762
 
            self._log_contents.
1763
 
        :return: A string containing the log.
1764
 
        """
1765
 
        if self._log_contents is not None:
1766
 
            try:
1767
 
                self._log_contents.decode('utf8')
1768
 
            except UnicodeDecodeError:
1769
 
                unicodestr = self._log_contents.decode('utf8', 'replace')
1770
 
                self._log_contents = unicodestr.encode('utf8')
1771
 
            return self._log_contents
1772
 
        if self._log_file is not None:
1773
 
            log_contents = self._log_file.getvalue()
1774
 
            try:
1775
 
                log_contents.decode('utf8')
1776
 
            except UnicodeDecodeError:
1777
 
                unicodestr = log_contents.decode('utf8', 'replace')
1778
 
                log_contents = unicodestr.encode('utf8')
1779
 
            if not keep_log_file:
1780
 
                self._log_file = None
1781
 
                # Permit multiple calls to get_log until we clean it up in
1782
 
                # finishLogFile
1783
 
                self._log_contents = log_contents
1784
 
            return log_contents
1785
 
        else:
1786
 
            return "No log file content."
1787
 
 
1788
1955
    def get_log(self):
1789
1956
        """Get a unicode string containing the log from bzrlib.trace.
1790
1957
 
1825
1992
 
1826
1993
        self.log('run bzr: %r', args)
1827
1994
        # FIXME: don't call into logging here
1828
 
        handler = logging.StreamHandler(stderr)
1829
 
        handler.setLevel(logging.INFO)
 
1995
        handler = trace.EncodedStreamHandler(stderr, errors="replace",
 
1996
            level=logging.INFO)
1830
1997
        logger = logging.getLogger('')
1831
1998
        logger.addHandler(handler)
1832
1999
        old_ui_factory = ui.ui_factory
1990
2157
    def start_bzr_subprocess(self, process_args, env_changes=None,
1991
2158
                             skip_if_plan_to_signal=False,
1992
2159
                             working_dir=None,
1993
 
                             allow_plugins=False):
 
2160
                             allow_plugins=False, stderr=subprocess.PIPE):
1994
2161
        """Start bzr in a subprocess for testing.
1995
2162
 
1996
2163
        This starts a new Python interpreter and runs bzr in there.
2008
2175
        :param skip_if_plan_to_signal: raise TestSkipped when true and system
2009
2176
            doesn't support signalling subprocesses.
2010
2177
        :param allow_plugins: If False (default) pass --no-plugins to bzr.
 
2178
        :param stderr: file to use for the subprocess's stderr.  Valid values
 
2179
            are those valid for the stderr argument of `subprocess.Popen`.
 
2180
            Default value is ``subprocess.PIPE``.
2011
2181
 
2012
2182
        :returns: Popen object for the started process.
2013
2183
        """
2017
2187
 
2018
2188
        if env_changes is None:
2019
2189
            env_changes = {}
 
2190
        # Because $HOME is set to a tempdir for the context of a test, modules
 
2191
        # installed in the user dir will not be found unless $PYTHONUSERBASE
 
2192
        # gets set to the computed directory of this parent process.
 
2193
        if site.USER_BASE is not None:
 
2194
            env_changes["PYTHONUSERBASE"] = site.USER_BASE
2020
2195
        old_env = {}
2021
2196
 
2022
2197
        def cleanup_environment():
2039
2214
            # so we will avoid using it on all platforms, just to
2040
2215
            # make sure the code path is used, and we don't break on win32
2041
2216
            cleanup_environment()
 
2217
            # Include the subprocess's log file in the test details, in case
 
2218
            # the test fails due to an error in the subprocess.
 
2219
            self._add_subprocess_log(trace._get_bzr_log_filename())
2042
2220
            command = [sys.executable]
2043
2221
            # frozen executables don't need the path to bzr
2044
2222
            if getattr(sys, "frozen", None) is None:
2048
2226
            command.extend(process_args)
2049
2227
            process = self._popen(command, stdin=subprocess.PIPE,
2050
2228
                                  stdout=subprocess.PIPE,
2051
 
                                  stderr=subprocess.PIPE)
 
2229
                                  stderr=stderr)
2052
2230
        finally:
2053
2231
            restore_environment()
2054
2232
            if cwd is not None:
2056
2234
 
2057
2235
        return process
2058
2236
 
 
2237
    def _add_subprocess_log(self, log_file_path):
 
2238
        if len(self._log_files) == 0:
 
2239
            # Register an addCleanup func.  We do this on the first call to
 
2240
            # _add_subprocess_log rather than in TestCase.setUp so that this
 
2241
            # addCleanup is registered after any cleanups for tempdirs that
 
2242
            # subclasses might create, which will probably remove the log file
 
2243
            # we want to read.
 
2244
            self.addCleanup(self._subprocess_log_cleanup)
 
2245
        # self._log_files is a set, so if a log file is reused we won't grab it
 
2246
        # twice.
 
2247
        self._log_files.add(log_file_path)
 
2248
 
 
2249
    def _subprocess_log_cleanup(self):
 
2250
        for count, log_file_path in enumerate(self._log_files):
 
2251
            # We use buffer_now=True to avoid holding the file open beyond
 
2252
            # the life of this function, which might interfere with e.g.
 
2253
            # cleaning tempdirs on Windows.
 
2254
            # XXX: Testtools 0.9.5 doesn't have the content_from_file helper
 
2255
            #detail_content = content.content_from_file(
 
2256
            #    log_file_path, buffer_now=True)
 
2257
            with open(log_file_path, 'rb') as log_file:
 
2258
                log_file_bytes = log_file.read()
 
2259
            detail_content = content.Content(content.ContentType("text",
 
2260
                "plain", {"charset": "utf8"}), lambda: [log_file_bytes])
 
2261
            self.addDetail("start_bzr_subprocess-log-%d" % (count,),
 
2262
                detail_content)
 
2263
 
2059
2264
    def _popen(self, *args, **kwargs):
2060
2265
        """Place a call to Popen.
2061
2266
 
2104
2309
                      % (process_args, retcode, process.returncode))
2105
2310
        return [out, err]
2106
2311
 
2107
 
    def check_inventory_shape(self, inv, shape):
2108
 
        """Compare an inventory to a list of expected names.
 
2312
    def check_tree_shape(self, tree, shape):
 
2313
        """Compare a tree to a list of expected names.
2109
2314
 
2110
2315
        Fail if they are not precisely equal.
2111
2316
        """
2112
2317
        extras = []
2113
2318
        shape = list(shape)             # copy
2114
 
        for path, ie in inv.entries():
 
2319
        for path, ie in tree.iter_entries_by_dir():
2115
2320
            name = path.replace('\\', '/')
2116
2321
            if ie.kind == 'directory':
2117
2322
                name = name + '/'
2118
 
            if name in shape:
 
2323
            if name == "/":
 
2324
                pass # ignore root entry
 
2325
            elif name in shape:
2119
2326
                shape.remove(name)
2120
2327
            else:
2121
2328
                extras.append(name)
2181
2388
        from bzrlib.smart import request
2182
2389
        request_handlers = request.request_handlers
2183
2390
        orig_method = request_handlers.get(verb)
 
2391
        orig_info = request_handlers.get_info(verb)
2184
2392
        request_handlers.remove(verb)
2185
 
        self.addCleanup(request_handlers.register, verb, orig_method)
 
2393
        self.addCleanup(request_handlers.register, verb, orig_method,
 
2394
            info=orig_info)
2186
2395
 
2187
2396
 
2188
2397
class CapturedCall(object):
2211
2420
class TestCaseWithMemoryTransport(TestCase):
2212
2421
    """Common test class for tests that do not need disk resources.
2213
2422
 
2214
 
    Tests that need disk resources should derive from TestCaseWithTransport.
 
2423
    Tests that need disk resources should derive from TestCaseInTempDir
 
2424
    orTestCaseWithTransport.
2215
2425
 
2216
2426
    TestCaseWithMemoryTransport sets the TEST_ROOT variable for all bzr tests.
2217
2427
 
2218
 
    For TestCaseWithMemoryTransport the test_home_dir is set to the name of
 
2428
    For TestCaseWithMemoryTransport the ``test_home_dir`` is set to the name of
2219
2429
    a directory which does not exist. This serves to help ensure test isolation
2220
 
    is preserved. test_dir is set to the TEST_ROOT, as is cwd, because they
2221
 
    must exist. However, TestCaseWithMemoryTransport does not offer local
2222
 
    file defaults for the transport in tests, nor does it obey the command line
 
2430
    is preserved. ``test_dir`` is set to the TEST_ROOT, as is cwd, because they
 
2431
    must exist. However, TestCaseWithMemoryTransport does not offer local file
 
2432
    defaults for the transport in tests, nor does it obey the command line
2223
2433
    override, so tests that accidentally write to the common directory should
2224
2434
    be rare.
2225
2435
 
2226
 
    :cvar TEST_ROOT: Directory containing all temporary directories, plus
2227
 
    a .bzr directory that stops us ascending higher into the filesystem.
 
2436
    :cvar TEST_ROOT: Directory containing all temporary directories, plus a
 
2437
        ``.bzr`` directory that stops us ascending higher into the filesystem.
2228
2438
    """
2229
2439
 
2230
2440
    TEST_ROOT = None
2248
2458
 
2249
2459
        :param relpath: a path relative to the base url.
2250
2460
        """
2251
 
        t = _mod_transport.get_transport(self.get_url(relpath))
 
2461
        t = _mod_transport.get_transport_from_url(self.get_url(relpath))
2252
2462
        self.assertFalse(t.is_readonly())
2253
2463
        return t
2254
2464
 
2260
2470
 
2261
2471
        :param relpath: a path relative to the base url.
2262
2472
        """
2263
 
        t = _mod_transport.get_transport(self.get_readonly_url(relpath))
 
2473
        t = _mod_transport.get_transport_from_url(
 
2474
            self.get_readonly_url(relpath))
2264
2475
        self.assertTrue(t.is_readonly())
2265
2476
        return t
2266
2477
 
2387
2598
        real branch.
2388
2599
        """
2389
2600
        root = TestCaseWithMemoryTransport.TEST_ROOT
2390
 
        bzrdir.BzrDir.create_standalone_workingtree(root)
 
2601
        # Make sure we get a readable and accessible home for .bzr.log
 
2602
        # and/or config files, and not fallback to weird defaults (see
 
2603
        # http://pad.lv/825027).
 
2604
        self.assertIs(None, os.environ.get('BZR_HOME', None))
 
2605
        os.environ['BZR_HOME'] = root
 
2606
        wt = bzrdir.BzrDir.create_standalone_workingtree(root)
 
2607
        del os.environ['BZR_HOME']
 
2608
        # Hack for speed: remember the raw bytes of the dirstate file so that
 
2609
        # we don't need to re-open the wt to check it hasn't changed.
 
2610
        TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE = (
 
2611
            wt.control_transport.get_bytes('dirstate'))
2391
2612
 
2392
2613
    def _check_safety_net(self):
2393
2614
        """Check that the safety .bzr directory have not been touched.
2396
2617
        propagating. This method ensures than a test did not leaked.
2397
2618
        """
2398
2619
        root = TestCaseWithMemoryTransport.TEST_ROOT
2399
 
        self.permit_url(_mod_transport.get_transport(root).base)
2400
 
        wt = workingtree.WorkingTree.open(root)
2401
 
        last_rev = wt.last_revision()
2402
 
        if last_rev != 'null:':
 
2620
        t = _mod_transport.get_transport_from_path(root)
 
2621
        self.permit_url(t.base)
 
2622
        if (t.get_bytes('.bzr/checkout/dirstate') != 
 
2623
                TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE):
2403
2624
            # The current test have modified the /bzr directory, we need to
2404
2625
            # recreate a new one or all the followng tests will fail.
2405
2626
            # If you need to inspect its content uncomment the following line
2440
2661
    def make_branch(self, relpath, format=None):
2441
2662
        """Create a branch on the transport at relpath."""
2442
2663
        repo = self.make_repository(relpath, format=format)
2443
 
        return repo.bzrdir.create_branch()
 
2664
        return repo.bzrdir.create_branch(append_revisions_only=False)
 
2665
 
 
2666
    def get_default_format(self):
 
2667
        return 'default'
 
2668
 
 
2669
    def resolve_format(self, format):
 
2670
        """Resolve an object to a ControlDir format object.
 
2671
 
 
2672
        The initial format object can either already be
 
2673
        a ControlDirFormat, None (for the default format),
 
2674
        or a string with the name of the control dir format.
 
2675
 
 
2676
        :param format: Object to resolve
 
2677
        :return A ControlDirFormat instance
 
2678
        """
 
2679
        if format is None:
 
2680
            format = self.get_default_format()
 
2681
        if isinstance(format, basestring):
 
2682
            format = bzrdir.format_registry.make_bzrdir(format)
 
2683
        return format
2444
2684
 
2445
2685
    def make_bzrdir(self, relpath, format=None):
2446
2686
        try:
2450
2690
            t = _mod_transport.get_transport(maybe_a_url)
2451
2691
            if len(segments) > 1 and segments[-1] not in ('', '.'):
2452
2692
                t.ensure_base()
2453
 
            if format is None:
2454
 
                format = 'default'
2455
 
            if isinstance(format, basestring):
2456
 
                format = bzrdir.format_registry.make_bzrdir(format)
 
2693
            format = self.resolve_format(format)
2457
2694
            return format.initialize_on_transport(t)
2458
2695
        except errors.UninitializableFormat:
2459
2696
            raise TestSkipped("Format %s is not initializable." % format)
2460
2697
 
2461
 
    def make_repository(self, relpath, shared=False, format=None):
 
2698
    def make_repository(self, relpath, shared=None, format=None):
2462
2699
        """Create a repository on our default transport at relpath.
2463
2700
 
2464
2701
        Note that relpath must be a relative path, not a full url.
2475
2712
            backing_server = self.get_server()
2476
2713
        smart_server = test_server.SmartTCPServer_for_testing()
2477
2714
        self.start_server(smart_server, backing_server)
2478
 
        remote_transport = _mod_transport.get_transport(smart_server.get_url()
 
2715
        remote_transport = _mod_transport.get_transport_from_url(smart_server.get_url()
2479
2716
                                                   ).clone(path)
2480
2717
        return remote_transport
2481
2718
 
2497
2734
 
2498
2735
    def setUp(self):
2499
2736
        super(TestCaseWithMemoryTransport, self).setUp()
2500
 
        # Ensure that ConnectedTransport doesn't leak sockets
2501
 
        def get_transport_with_cleanup(*args, **kwargs):
2502
 
            t = orig_get_transport(*args, **kwargs)
2503
 
            if isinstance(t, _mod_transport.ConnectedTransport):
2504
 
                self.addCleanup(t.disconnect)
2505
 
            return t
2506
 
 
2507
 
        orig_get_transport = self.overrideAttr(_mod_transport, 'get_transport',
2508
 
                                               get_transport_with_cleanup)
 
2737
 
 
2738
        def _add_disconnect_cleanup(transport):
 
2739
            """Schedule disconnection of given transport at test cleanup
 
2740
 
 
2741
            This needs to happen for all connected transports or leaks occur.
 
2742
 
 
2743
            Note reconnections may mean we call disconnect multiple times per
 
2744
            transport which is suboptimal but seems harmless.
 
2745
            """
 
2746
            self.addCleanup(transport.disconnect)
 
2747
 
 
2748
        _mod_transport.Transport.hooks.install_named_hook('post_connect',
 
2749
            _add_disconnect_cleanup, None)
 
2750
 
2509
2751
        self._make_test_root()
2510
2752
        self.addCleanup(os.chdir, os.getcwdu())
2511
2753
        self.makeAndChdirToTestDir()
2517
2759
    def setup_smart_server_with_call_log(self):
2518
2760
        """Sets up a smart server as the transport server with a call log."""
2519
2761
        self.transport_server = test_server.SmartTCPServer_for_testing
 
2762
        self.hpss_connections = []
2520
2763
        self.hpss_calls = []
2521
2764
        import traceback
2522
2765
        # Skip the current stack down to the caller of
2525
2768
        def capture_hpss_call(params):
2526
2769
            self.hpss_calls.append(
2527
2770
                CapturedCall(params, prefix_length))
 
2771
        def capture_connect(transport):
 
2772
            self.hpss_connections.append(transport)
2528
2773
        client._SmartClient.hooks.install_named_hook(
2529
2774
            'call', capture_hpss_call, None)
 
2775
        _mod_transport.Transport.hooks.install_named_hook(
 
2776
            'post_connect', capture_connect, None)
2530
2777
 
2531
2778
    def reset_smart_call_log(self):
2532
2779
        self.hpss_calls = []
 
2780
        self.hpss_connections = []
2533
2781
 
2534
2782
 
2535
2783
class TestCaseInTempDir(TestCaseWithMemoryTransport):
2554
2802
 
2555
2803
    OVERRIDE_PYTHON = 'python'
2556
2804
 
 
2805
    def setUp(self):
 
2806
        super(TestCaseInTempDir, self).setUp()
 
2807
        # Remove the protection set in isolated_environ, we have a proper
 
2808
        # access to disk resources now.
 
2809
        self.overrideEnv('BZR_LOG', None)
 
2810
 
2557
2811
    def check_file_contents(self, filename, expect):
2558
2812
        self.log("check contents of file %s" % filename)
2559
2813
        f = file(filename)
2640
2894
                "a list or a tuple. Got %r instead" % (shape,))
2641
2895
        # It's OK to just create them using forward slashes on windows.
2642
2896
        if transport is None or transport.is_readonly():
2643
 
            transport = _mod_transport.get_transport(".")
 
2897
            transport = _mod_transport.get_transport_from_path(".")
2644
2898
        for name in shape:
2645
2899
            self.assertIsInstance(name, basestring)
2646
2900
            if name[-1] == '/':
2731
2985
        # this obviously requires a format that supports branch references
2732
2986
        # so check for that by checking bzrdir.BzrDirFormat.get_default_format()
2733
2987
        # RBC 20060208
 
2988
        format = self.resolve_format(format=format)
 
2989
        if not format.supports_workingtrees:
 
2990
            b = self.make_branch(relpath+'.branch', format=format)
 
2991
            return b.create_checkout(relpath, lightweight=True)
2734
2992
        b = self.make_branch(relpath, format=format)
2735
2993
        try:
2736
2994
            return b.bzrdir.create_workingtree()
3035
3293
                            result_decorators=result_decorators,
3036
3294
                            )
3037
3295
    runner.stop_on_failure=stop_on_failure
 
3296
    if isinstance(suite, unittest.TestSuite):
 
3297
        # Empty out _tests list of passed suite and populate new TestSuite
 
3298
        suite._tests[:], suite = [], TestSuite(suite)
3038
3299
    # built in decorator factories:
3039
3300
    decorators = [
3040
3301
        random_order(random_seed, runner),
3138
3399
 
3139
3400
class TestDecorator(TestUtil.TestSuite):
3140
3401
    """A decorator for TestCase/TestSuite objects.
3141
 
    
3142
 
    Usually, subclasses should override __iter__(used when flattening test
3143
 
    suites), which we do to filter, reorder, parallelise and so on, run() and
3144
 
    debug().
 
3402
 
 
3403
    Contains rather than flattening suite passed on construction
3145
3404
    """
3146
3405
 
3147
 
    def __init__(self, suite):
3148
 
        TestUtil.TestSuite.__init__(self)
3149
 
        self.addTest(suite)
3150
 
 
3151
 
    def countTestCases(self):
3152
 
        cases = 0
3153
 
        for test in self:
3154
 
            cases += test.countTestCases()
3155
 
        return cases
3156
 
 
3157
 
    def debug(self):
3158
 
        for test in self:
3159
 
            test.debug()
3160
 
 
3161
 
    def run(self, result):
3162
 
        # Use iteration on self, not self._tests, to allow subclasses to hook
3163
 
        # into __iter__.
3164
 
        for test in self:
3165
 
            if result.shouldStop:
3166
 
                break
3167
 
            test.run(result)
3168
 
        return result
 
3406
    def __init__(self, suite=None):
 
3407
        super(TestDecorator, self).__init__()
 
3408
        if suite is not None:
 
3409
            self.addTest(suite)
 
3410
 
 
3411
    # Don't need subclass run method with suite emptying
 
3412
    run = unittest.TestSuite.run
3169
3413
 
3170
3414
 
3171
3415
class CountingDecorator(TestDecorator):
3182
3426
    """A decorator which excludes test matching an exclude pattern."""
3183
3427
 
3184
3428
    def __init__(self, suite, exclude_pattern):
3185
 
        TestDecorator.__init__(self, suite)
3186
 
        self.exclude_pattern = exclude_pattern
3187
 
        self.excluded = False
3188
 
 
3189
 
    def __iter__(self):
3190
 
        if self.excluded:
3191
 
            return iter(self._tests)
3192
 
        self.excluded = True
3193
 
        suite = exclude_tests_by_re(self, self.exclude_pattern)
3194
 
        del self._tests[:]
3195
 
        self.addTests(suite)
3196
 
        return iter(self._tests)
 
3429
        super(ExcludeDecorator, self).__init__(
 
3430
            exclude_tests_by_re(suite, exclude_pattern))
3197
3431
 
3198
3432
 
3199
3433
class FilterTestsDecorator(TestDecorator):
3200
3434
    """A decorator which filters tests to those matching a pattern."""
3201
3435
 
3202
3436
    def __init__(self, suite, pattern):
3203
 
        TestDecorator.__init__(self, suite)
3204
 
        self.pattern = pattern
3205
 
        self.filtered = False
3206
 
 
3207
 
    def __iter__(self):
3208
 
        if self.filtered:
3209
 
            return iter(self._tests)
3210
 
        self.filtered = True
3211
 
        suite = filter_suite_by_re(self, self.pattern)
3212
 
        del self._tests[:]
3213
 
        self.addTests(suite)
3214
 
        return iter(self._tests)
 
3437
        super(FilterTestsDecorator, self).__init__(
 
3438
            filter_suite_by_re(suite, pattern))
3215
3439
 
3216
3440
 
3217
3441
class RandomDecorator(TestDecorator):
3218
3442
    """A decorator which randomises the order of its tests."""
3219
3443
 
3220
3444
    def __init__(self, suite, random_seed, stream):
3221
 
        TestDecorator.__init__(self, suite)
3222
 
        self.random_seed = random_seed
3223
 
        self.randomised = False
3224
 
        self.stream = stream
3225
 
 
3226
 
    def __iter__(self):
3227
 
        if self.randomised:
3228
 
            return iter(self._tests)
3229
 
        self.randomised = True
3230
 
        self.stream.write("Randomizing test order using seed %s\n\n" %
3231
 
            (self.actual_seed()))
 
3445
        random_seed = self.actual_seed(random_seed)
 
3446
        stream.write("Randomizing test order using seed %s\n\n" %
 
3447
            (random_seed,))
3232
3448
        # Initialise the random number generator.
3233
 
        random.seed(self.actual_seed())
3234
 
        suite = randomize_suite(self)
3235
 
        del self._tests[:]
3236
 
        self.addTests(suite)
3237
 
        return iter(self._tests)
 
3449
        random.seed(random_seed)
 
3450
        super(RandomDecorator, self).__init__(randomize_suite(suite))
3238
3451
 
3239
 
    def actual_seed(self):
3240
 
        if self.random_seed == "now":
 
3452
    @staticmethod
 
3453
    def actual_seed(seed):
 
3454
        if seed == "now":
3241
3455
            # We convert the seed to a long to make it reuseable across
3242
3456
            # invocations (because the user can reenter it).
3243
 
            self.random_seed = long(time.time())
 
3457
            return long(time.time())
3244
3458
        else:
3245
3459
            # Convert the seed to a long if we can
3246
3460
            try:
3247
 
                self.random_seed = long(self.random_seed)
3248
 
            except:
 
3461
                return long(seed)
 
3462
            except (TypeError, ValueError):
3249
3463
                pass
3250
 
        return self.random_seed
 
3464
        return seed
3251
3465
 
3252
3466
 
3253
3467
class TestFirstDecorator(TestDecorator):
3254
3468
    """A decorator which moves named tests to the front."""
3255
3469
 
3256
3470
    def __init__(self, suite, pattern):
3257
 
        TestDecorator.__init__(self, suite)
3258
 
        self.pattern = pattern
3259
 
        self.filtered = False
3260
 
 
3261
 
    def __iter__(self):
3262
 
        if self.filtered:
3263
 
            return iter(self._tests)
3264
 
        self.filtered = True
3265
 
        suites = split_suite_by_re(self, self.pattern)
3266
 
        del self._tests[:]
3267
 
        self.addTests(suites)
3268
 
        return iter(self._tests)
 
3471
        super(TestFirstDecorator, self).__init__()
 
3472
        self.addTests(split_suite_by_re(suite, pattern))
3269
3473
 
3270
3474
 
3271
3475
def partition_tests(suite, count):
3303
3507
    """
3304
3508
    concurrency = osutils.local_concurrency()
3305
3509
    result = []
3306
 
    from subunit import TestProtocolClient, ProtocolTestCase
 
3510
    from subunit import ProtocolTestCase
3307
3511
    from subunit.test_results import AutoTimingTestResultDecorator
3308
3512
    class TestInOtherProcess(ProtocolTestCase):
3309
3513
        # Should be in subunit, I think. RBC.
3315
3519
            try:
3316
3520
                ProtocolTestCase.run(self, result)
3317
3521
            finally:
3318
 
                os.waitpid(self.pid, 0)
 
3522
                pid, status = os.waitpid(self.pid, 0)
 
3523
            # GZ 2011-10-18: If status is nonzero, should report to the result
 
3524
            #                that something went wrong.
3319
3525
 
3320
3526
    test_blocks = partition_tests(suite, concurrency)
 
3527
    # Clear the tests from the original suite so it doesn't keep them alive
 
3528
    suite._tests[:] = []
3321
3529
    for process_tests in test_blocks:
3322
 
        process_suite = TestUtil.TestSuite()
3323
 
        process_suite.addTests(process_tests)
 
3530
        process_suite = TestUtil.TestSuite(process_tests)
 
3531
        # Also clear each split list so new suite has only reference
 
3532
        process_tests[:] = []
3324
3533
        c2pread, c2pwrite = os.pipe()
3325
3534
        pid = os.fork()
3326
3535
        if pid == 0:
3327
 
            workaround_zealous_crypto_random()
3328
3536
            try:
 
3537
                stream = os.fdopen(c2pwrite, 'wb', 1)
 
3538
                workaround_zealous_crypto_random()
3329
3539
                os.close(c2pread)
3330
3540
                # Leave stderr and stdout open so we can see test noise
3331
3541
                # Close stdin so that the child goes away if it decides to
3332
3542
                # read from stdin (otherwise its a roulette to see what
3333
3543
                # child actually gets keystrokes for pdb etc).
3334
3544
                sys.stdin.close()
3335
 
                sys.stdin = None
3336
 
                stream = os.fdopen(c2pwrite, 'wb', 1)
3337
3545
                subunit_result = AutoTimingTestResultDecorator(
3338
 
                    TestProtocolClient(stream))
 
3546
                    SubUnitBzrProtocolClient(stream))
3339
3547
                process_suite.run(subunit_result)
3340
 
            finally:
3341
 
                os._exit(0)
 
3548
            except:
 
3549
                # Try and report traceback on stream, but exit with error even
 
3550
                # if stream couldn't be created or something else goes wrong.
 
3551
                # The traceback is formatted to a string and written in one go
 
3552
                # to avoid interleaving lines from multiple failing children.
 
3553
                try:
 
3554
                    stream.write(traceback.format_exc())
 
3555
                finally:
 
3556
                    os._exit(1)
 
3557
            os._exit(0)
3342
3558
        else:
3343
3559
            os.close(c2pwrite)
3344
3560
            stream = os.fdopen(c2pread, 'rb', 1)
3450
3666
#                           with proper exclusion rules.
3451
3667
#   -Ethreads               Will display thread ident at creation/join time to
3452
3668
#                           help track thread leaks
 
3669
#   -Euncollected_cases     Display the identity of any test cases that weren't
 
3670
#                           deallocated after being completed.
 
3671
#   -Econfig_stats          Will collect statistics using addDetail
3453
3672
selftest_debug_flags = set()
3454
3673
 
3455
3674
 
3707
3926
        'bzrlib.tests.per_repository',
3708
3927
        'bzrlib.tests.per_repository_chk',
3709
3928
        'bzrlib.tests.per_repository_reference',
 
3929
        'bzrlib.tests.per_repository_vf',
3710
3930
        'bzrlib.tests.per_uifactory',
3711
3931
        'bzrlib.tests.per_versionedfile',
3712
3932
        'bzrlib.tests.per_workingtree',
3746
3966
        'bzrlib.tests.test_commit_merge',
3747
3967
        'bzrlib.tests.test_config',
3748
3968
        'bzrlib.tests.test_conflicts',
 
3969
        'bzrlib.tests.test_controldir',
3749
3970
        'bzrlib.tests.test_counted_lock',
3750
3971
        'bzrlib.tests.test_crash',
3751
3972
        'bzrlib.tests.test_decorators',
3752
3973
        'bzrlib.tests.test_delta',
3753
3974
        'bzrlib.tests.test_debug',
3754
 
        'bzrlib.tests.test_deprecated_graph',
3755
3975
        'bzrlib.tests.test_diff',
3756
3976
        'bzrlib.tests.test_directory_service',
3757
3977
        'bzrlib.tests.test_dirstate',
3758
3978
        'bzrlib.tests.test_email_message',
3759
3979
        'bzrlib.tests.test_eol_filters',
3760
3980
        'bzrlib.tests.test_errors',
 
3981
        'bzrlib.tests.test_estimate_compressed_size',
3761
3982
        'bzrlib.tests.test_export',
 
3983
        'bzrlib.tests.test_export_pot',
3762
3984
        'bzrlib.tests.test_extract',
 
3985
        'bzrlib.tests.test_features',
3763
3986
        'bzrlib.tests.test_fetch',
3764
3987
        'bzrlib.tests.test_fixtures',
3765
3988
        'bzrlib.tests.test_fifo_cache',
3766
3989
        'bzrlib.tests.test_filters',
 
3990
        'bzrlib.tests.test_filter_tree',
3767
3991
        'bzrlib.tests.test_ftp_transport',
3768
3992
        'bzrlib.tests.test_foreign',
3769
3993
        'bzrlib.tests.test_generate_docs',
3778
4002
        'bzrlib.tests.test_http',
3779
4003
        'bzrlib.tests.test_http_response',
3780
4004
        'bzrlib.tests.test_https_ca_bundle',
 
4005
        'bzrlib.tests.test_https_urllib',
 
4006
        'bzrlib.tests.test_i18n',
3781
4007
        'bzrlib.tests.test_identitymap',
3782
4008
        'bzrlib.tests.test_ignores',
3783
4009
        'bzrlib.tests.test_index',
3831
4057
        'bzrlib.tests.test_revisiontree',
3832
4058
        'bzrlib.tests.test_rio',
3833
4059
        'bzrlib.tests.test_rules',
 
4060
        'bzrlib.tests.test_url_policy_open',
3834
4061
        'bzrlib.tests.test_sampler',
3835
4062
        'bzrlib.tests.test_scenarios',
3836
4063
        'bzrlib.tests.test_script',
3843
4070
        'bzrlib.tests.test_smart',
3844
4071
        'bzrlib.tests.test_smart_add',
3845
4072
        'bzrlib.tests.test_smart_request',
 
4073
        'bzrlib.tests.test_smart_signals',
3846
4074
        'bzrlib.tests.test_smart_transport',
3847
4075
        'bzrlib.tests.test_smtp_connection',
3848
4076
        'bzrlib.tests.test_source',
3875
4103
        'bzrlib.tests.test_upgrade',
3876
4104
        'bzrlib.tests.test_upgrade_stacked',
3877
4105
        'bzrlib.tests.test_urlutils',
 
4106
        'bzrlib.tests.test_utextwrap',
3878
4107
        'bzrlib.tests.test_version',
3879
4108
        'bzrlib.tests.test_version_info',
3880
4109
        'bzrlib.tests.test_versionedfile',
 
4110
        'bzrlib.tests.test_vf_search',
3881
4111
        'bzrlib.tests.test_weave',
3882
4112
        'bzrlib.tests.test_whitebox',
3883
4113
        'bzrlib.tests.test_win32utils',
3897
4127
        'bzrlib',
3898
4128
        'bzrlib.branchbuilder',
3899
4129
        'bzrlib.decorators',
3900
 
        'bzrlib.export',
3901
4130
        'bzrlib.inventory',
3902
4131
        'bzrlib.iterablefile',
3903
4132
        'bzrlib.lockdir',
4159
4388
        the module is available.
4160
4389
    """
4161
4390
 
 
4391
    from bzrlib.tests.features import ModuleAvailableFeature
4162
4392
    py_module = pyutils.get_named_object(py_module_name)
4163
4393
    scenarios = [
4164
4394
        ('python', {'module': py_module}),
4205
4435
                         % (os.path.basename(dirname), printable_e))
4206
4436
 
4207
4437
 
4208
 
class Feature(object):
4209
 
    """An operating system Feature."""
4210
 
 
4211
 
    def __init__(self):
4212
 
        self._available = None
4213
 
 
4214
 
    def available(self):
4215
 
        """Is the feature available?
4216
 
 
4217
 
        :return: True if the feature is available.
4218
 
        """
4219
 
        if self._available is None:
4220
 
            self._available = self._probe()
4221
 
        return self._available
4222
 
 
4223
 
    def _probe(self):
4224
 
        """Implement this method in concrete features.
4225
 
 
4226
 
        :return: True if the feature is available.
4227
 
        """
4228
 
        raise NotImplementedError
4229
 
 
4230
 
    def __str__(self):
4231
 
        if getattr(self, 'feature_name', None):
4232
 
            return self.feature_name()
4233
 
        return self.__class__.__name__
4234
 
 
4235
 
 
4236
 
class _SymlinkFeature(Feature):
4237
 
 
4238
 
    def _probe(self):
4239
 
        return osutils.has_symlinks()
4240
 
 
4241
 
    def feature_name(self):
4242
 
        return 'symlinks'
4243
 
 
4244
 
SymlinkFeature = _SymlinkFeature()
4245
 
 
4246
 
 
4247
 
class _HardlinkFeature(Feature):
4248
 
 
4249
 
    def _probe(self):
4250
 
        return osutils.has_hardlinks()
4251
 
 
4252
 
    def feature_name(self):
4253
 
        return 'hardlinks'
4254
 
 
4255
 
HardlinkFeature = _HardlinkFeature()
4256
 
 
4257
 
 
4258
 
class _OsFifoFeature(Feature):
4259
 
 
4260
 
    def _probe(self):
4261
 
        return getattr(os, 'mkfifo', None)
4262
 
 
4263
 
    def feature_name(self):
4264
 
        return 'filesystem fifos'
4265
 
 
4266
 
OsFifoFeature = _OsFifoFeature()
4267
 
 
4268
 
 
4269
 
class _UnicodeFilenameFeature(Feature):
4270
 
    """Does the filesystem support Unicode filenames?"""
4271
 
 
4272
 
    def _probe(self):
4273
 
        try:
4274
 
            # Check for character combinations unlikely to be covered by any
4275
 
            # single non-unicode encoding. We use the characters
4276
 
            # - greek small letter alpha (U+03B1) and
4277
 
            # - braille pattern dots-123456 (U+283F).
4278
 
            os.stat(u'\u03b1\u283f')
4279
 
        except UnicodeEncodeError:
4280
 
            return False
4281
 
        except (IOError, OSError):
4282
 
            # The filesystem allows the Unicode filename but the file doesn't
4283
 
            # exist.
4284
 
            return True
4285
 
        else:
4286
 
            # The filesystem allows the Unicode filename and the file exists,
4287
 
            # for some reason.
4288
 
            return True
4289
 
 
4290
 
UnicodeFilenameFeature = _UnicodeFilenameFeature()
4291
 
 
4292
 
 
4293
 
class _CompatabilityThunkFeature(Feature):
4294
 
    """This feature is just a thunk to another feature.
4295
 
 
4296
 
    It issues a deprecation warning if it is accessed, to let you know that you
4297
 
    should really use a different feature.
4298
 
    """
4299
 
 
4300
 
    def __init__(self, dep_version, module, name,
4301
 
                 replacement_name, replacement_module=None):
4302
 
        super(_CompatabilityThunkFeature, self).__init__()
4303
 
        self._module = module
4304
 
        if replacement_module is None:
4305
 
            replacement_module = module
4306
 
        self._replacement_module = replacement_module
4307
 
        self._name = name
4308
 
        self._replacement_name = replacement_name
4309
 
        self._dep_version = dep_version
4310
 
        self._feature = None
4311
 
 
4312
 
    def _ensure(self):
4313
 
        if self._feature is None:
4314
 
            depr_msg = self._dep_version % ('%s.%s'
4315
 
                                            % (self._module, self._name))
4316
 
            use_msg = ' Use %s.%s instead.' % (self._replacement_module,
4317
 
                                               self._replacement_name)
4318
 
            symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning)
4319
 
            # Import the new feature and use it as a replacement for the
4320
 
            # deprecated one.
4321
 
            self._feature = pyutils.get_named_object(
4322
 
                self._replacement_module, self._replacement_name)
4323
 
 
4324
 
    def _probe(self):
4325
 
        self._ensure()
4326
 
        return self._feature._probe()
4327
 
 
4328
 
 
4329
 
class ModuleAvailableFeature(Feature):
4330
 
    """This is a feature than describes a module we want to be available.
4331
 
 
4332
 
    Declare the name of the module in __init__(), and then after probing, the
4333
 
    module will be available as 'self.module'.
4334
 
 
4335
 
    :ivar module: The module if it is available, else None.
4336
 
    """
4337
 
 
4338
 
    def __init__(self, module_name):
4339
 
        super(ModuleAvailableFeature, self).__init__()
4340
 
        self.module_name = module_name
4341
 
 
4342
 
    def _probe(self):
4343
 
        try:
4344
 
            self._module = __import__(self.module_name, {}, {}, [''])
4345
 
            return True
4346
 
        except ImportError:
4347
 
            return False
4348
 
 
4349
 
    @property
4350
 
    def module(self):
4351
 
        if self.available(): # Make sure the probe has been done
4352
 
            return self._module
4353
 
        return None
4354
 
 
4355
 
    def feature_name(self):
4356
 
        return self.module_name
4357
 
 
4358
 
 
4359
4438
def probe_unicode_in_user_encoding():
4360
4439
    """Try to encode several unicode strings to use in unicode-aware tests.
4361
4440
    Return first successfull match.
4389
4468
    return None
4390
4469
 
4391
4470
 
4392
 
class _HTTPSServerFeature(Feature):
4393
 
    """Some tests want an https Server, check if one is available.
4394
 
 
4395
 
    Right now, the only way this is available is under python2.6 which provides
4396
 
    an ssl module.
4397
 
    """
4398
 
 
4399
 
    def _probe(self):
4400
 
        try:
4401
 
            import ssl
4402
 
            return True
4403
 
        except ImportError:
4404
 
            return False
4405
 
 
4406
 
    def feature_name(self):
4407
 
        return 'HTTPSServer'
4408
 
 
4409
 
 
4410
 
HTTPSServerFeature = _HTTPSServerFeature()
4411
 
 
4412
 
 
4413
 
class _UnicodeFilename(Feature):
4414
 
    """Does the filesystem support Unicode filenames?"""
4415
 
 
4416
 
    def _probe(self):
4417
 
        try:
4418
 
            os.stat(u'\u03b1')
4419
 
        except UnicodeEncodeError:
4420
 
            return False
4421
 
        except (IOError, OSError):
4422
 
            # The filesystem allows the Unicode filename but the file doesn't
4423
 
            # exist.
4424
 
            return True
4425
 
        else:
4426
 
            # The filesystem allows the Unicode filename and the file exists,
4427
 
            # for some reason.
4428
 
            return True
4429
 
 
4430
 
UnicodeFilename = _UnicodeFilename()
4431
 
 
4432
 
 
4433
 
class _ByteStringNamedFilesystem(Feature):
4434
 
    """Is the filesystem based on bytes?"""
4435
 
 
4436
 
    def _probe(self):
4437
 
        if os.name == "posix":
4438
 
            return True
4439
 
        return False
4440
 
 
4441
 
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
4442
 
 
4443
 
 
4444
 
class _UTF8Filesystem(Feature):
4445
 
    """Is the filesystem UTF-8?"""
4446
 
 
4447
 
    def _probe(self):
4448
 
        if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
4449
 
            return True
4450
 
        return False
4451
 
 
4452
 
UTF8Filesystem = _UTF8Filesystem()
4453
 
 
4454
 
 
4455
 
class _BreakinFeature(Feature):
4456
 
    """Does this platform support the breakin feature?"""
4457
 
 
4458
 
    def _probe(self):
4459
 
        from bzrlib import breakin
4460
 
        if breakin.determine_signal() is None:
4461
 
            return False
4462
 
        if sys.platform == 'win32':
4463
 
            # Windows doesn't have os.kill, and we catch the SIGBREAK signal.
4464
 
            # We trigger SIGBREAK via a Console api so we need ctypes to
4465
 
            # access the function
4466
 
            try:
4467
 
                import ctypes
4468
 
            except OSError:
4469
 
                return False
4470
 
        return True
4471
 
 
4472
 
    def feature_name(self):
4473
 
        return "SIGQUIT or SIGBREAK w/ctypes on win32"
4474
 
 
4475
 
 
4476
 
BreakinFeature = _BreakinFeature()
4477
 
 
4478
 
 
4479
 
class _CaseInsCasePresFilenameFeature(Feature):
4480
 
    """Is the file-system case insensitive, but case-preserving?"""
4481
 
 
4482
 
    def _probe(self):
4483
 
        fileno, name = tempfile.mkstemp(prefix='MixedCase')
4484
 
        try:
4485
 
            # first check truly case-preserving for created files, then check
4486
 
            # case insensitive when opening existing files.
4487
 
            name = osutils.normpath(name)
4488
 
            base, rel = osutils.split(name)
4489
 
            found_rel = osutils.canonical_relpath(base, name)
4490
 
            return (found_rel == rel
4491
 
                    and os.path.isfile(name.upper())
4492
 
                    and os.path.isfile(name.lower()))
4493
 
        finally:
4494
 
            os.close(fileno)
4495
 
            os.remove(name)
4496
 
 
4497
 
    def feature_name(self):
4498
 
        return "case-insensitive case-preserving filesystem"
4499
 
 
4500
 
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
4501
 
 
4502
 
 
4503
 
class _CaseInsensitiveFilesystemFeature(Feature):
4504
 
    """Check if underlying filesystem is case-insensitive but *not* case
4505
 
    preserving.
4506
 
    """
4507
 
    # Note that on Windows, Cygwin, MacOS etc, the file-systems are far
4508
 
    # more likely to be case preserving, so this case is rare.
4509
 
 
4510
 
    def _probe(self):
4511
 
        if CaseInsCasePresFilenameFeature.available():
4512
 
            return False
4513
 
 
4514
 
        if TestCaseWithMemoryTransport.TEST_ROOT is None:
4515
 
            root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
4516
 
            TestCaseWithMemoryTransport.TEST_ROOT = root
4517
 
        else:
4518
 
            root = TestCaseWithMemoryTransport.TEST_ROOT
4519
 
        tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
4520
 
            dir=root)
4521
 
        name_a = osutils.pathjoin(tdir, 'a')
4522
 
        name_A = osutils.pathjoin(tdir, 'A')
4523
 
        os.mkdir(name_a)
4524
 
        result = osutils.isdir(name_A)
4525
 
        _rmtree_temp_dir(tdir)
4526
 
        return result
4527
 
 
4528
 
    def feature_name(self):
4529
 
        return 'case-insensitive filesystem'
4530
 
 
4531
 
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
4532
 
 
4533
 
 
4534
 
class _CaseSensitiveFilesystemFeature(Feature):
4535
 
 
4536
 
    def _probe(self):
4537
 
        if CaseInsCasePresFilenameFeature.available():
4538
 
            return False
4539
 
        elif CaseInsensitiveFilesystemFeature.available():
4540
 
            return False
4541
 
        else:
4542
 
            return True
4543
 
 
4544
 
    def feature_name(self):
4545
 
        return 'case-sensitive filesystem'
4546
 
 
4547
 
# new coding style is for feature instances to be lowercase
4548
 
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
4549
 
 
4550
 
 
4551
4471
# Only define SubUnitBzrRunner if subunit is available.
4552
4472
try:
4553
4473
    from subunit import TestProtocolClient
4554
4474
    from subunit.test_results import AutoTimingTestResultDecorator
4555
4475
    class SubUnitBzrProtocolClient(TestProtocolClient):
4556
4476
 
 
4477
        def stopTest(self, test):
 
4478
            super(SubUnitBzrProtocolClient, self).stopTest(test)
 
4479
            _clear__type_equality_funcs(test)
 
4480
 
4557
4481
        def addSuccess(self, test, details=None):
4558
4482
            # The subunit client always includes the details in the subunit
4559
4483
            # stream, but we don't want to include it in ours.
4571
4495
except ImportError:
4572
4496
    pass
4573
4497
 
4574
 
class _PosixPermissionsFeature(Feature):
4575
 
 
4576
 
    def _probe(self):
4577
 
        def has_perms():
4578
 
            # create temporary file and check if specified perms are maintained.
4579
 
            import tempfile
4580
 
 
4581
 
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
4582
 
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
4583
 
            fd, name = f
4584
 
            os.close(fd)
4585
 
            os.chmod(name, write_perms)
4586
 
 
4587
 
            read_perms = os.stat(name).st_mode & 0777
4588
 
            os.unlink(name)
4589
 
            return (write_perms == read_perms)
4590
 
 
4591
 
        return (os.name == 'posix') and has_perms()
4592
 
 
4593
 
    def feature_name(self):
4594
 
        return 'POSIX permissions support'
4595
 
 
4596
 
posix_permissions_feature = _PosixPermissionsFeature()
 
4498
 
 
4499
# API compatibility for old plugins; see bug 892622.
 
4500
for name in [
 
4501
    'Feature',
 
4502
    'HTTPServerFeature', 
 
4503
    'ModuleAvailableFeature',
 
4504
    'HTTPSServerFeature', 'SymlinkFeature', 'HardlinkFeature',
 
4505
    'OsFifoFeature', 'UnicodeFilenameFeature',
 
4506
    'ByteStringNamedFilesystem', 'UTF8Filesystem',
 
4507
    'BreakinFeature', 'CaseInsCasePresFilenameFeature',
 
4508
    'CaseInsensitiveFilesystemFeature', 'case_sensitive_filesystem_feature',
 
4509
    'posix_permissions_feature',
 
4510
    ]:
 
4511
    globals()[name] = _CompatabilityThunkFeature(
 
4512
        symbol_versioning.deprecated_in((2, 5, 0)),
 
4513
        'bzrlib.tests', name,
 
4514
        name, 'bzrlib.tests.features')
 
4515
 
 
4516
 
 
4517
for (old_name, new_name) in [
 
4518
    ('UnicodeFilename', 'UnicodeFilenameFeature'),
 
4519
    ]:
 
4520
    globals()[name] = _CompatabilityThunkFeature(
 
4521
        symbol_versioning.deprecated_in((2, 5, 0)),
 
4522
        'bzrlib.tests', old_name,
 
4523
        new_name, 'bzrlib.tests.features')