~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Patch Queue Manager
  • Date: 2014-04-03 07:45:32 UTC
  • mfrom: (6591.1.3 lp1030521)
  • Revision ID: pqm@pqm.ubuntu.com-20140403074532-0sdwrky6ie4y20l4
(vila) Use LooseVersion from distutils to check Pyrex/Cython version in
 order to handle non-integers in the version string. (Andrew Starr-Bochicchio)

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
63
60
import bzrlib
64
61
from bzrlib import (
65
62
    branchbuilder,
66
 
    bzrdir,
 
63
    controldir,
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
133
137
isolated_environ = {
134
138
    'BZR_HOME': None,
135
139
    'HOME': None,
 
140
    'XDG_CONFIG_HOME': None,
136
141
    # bzr now uses the Win32 API and doesn't rely on APPDATA, but the
137
142
    # tests do check our impls match APPDATA
138
143
    'BZR_EDITOR': None, # test_msgeditor manipulates this variable
142
147
    'BZREMAIL': None, # may still be present in the environment
143
148
    'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
144
149
    'BZR_PROGRESS_BAR': None,
145
 
    'BZR_LOG': None,
 
150
    # This should trap leaks to ~/.bzr.log. This occurs when tests use TestCase
 
151
    # as a base class instead of TestCaseInTempDir. Tests inheriting from
 
152
    # TestCase should not use disk resources, BZR_LOG is one.
 
153
    'BZR_LOG': '/you-should-use-TestCaseInTempDir-if-you-need-a-log-file',
146
154
    'BZR_PLUGIN_PATH': None,
147
155
    'BZR_DISABLE_PLUGINS': None,
148
156
    'BZR_PLUGINS_AT': None,
210
218
        osutils.set_or_unset_env(var, value)
211
219
 
212
220
 
 
221
def _clear__type_equality_funcs(test):
 
222
    """Cleanup bound methods stored on TestCase instances
 
223
 
 
224
    Clear the dict breaking a few (mostly) harmless cycles in the affected
 
225
    unittests released with Python 2.6 and initial Python 2.7 versions.
 
226
 
 
227
    For a few revisions between Python 2.7.1 and Python 2.7.2 that annoyingly
 
228
    shipped in Oneiric, an object with no clear method was used, hence the
 
229
    extra complications, see bug 809048 for details.
 
230
    """
 
231
    type_equality_funcs = getattr(test, "_type_equality_funcs", None)
 
232
    if type_equality_funcs is not None:
 
233
        tef_clear = getattr(type_equality_funcs, "clear", None)
 
234
        if tef_clear is None:
 
235
            tef_instance_dict = getattr(type_equality_funcs, "__dict__", None)
 
236
            if tef_instance_dict is not None:
 
237
                tef_clear = tef_instance_dict.clear
 
238
        if tef_clear is not None:
 
239
            tef_clear()
 
240
 
 
241
 
213
242
class ExtendedTestResult(testtools.TextTestResult):
214
243
    """Accepts, reports and accumulates the results of running tests.
215
244
 
219
248
    different types of display.
220
249
 
221
250
    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
 
251
    addFailure or addError methods.  These in turn may redirect to a more
223
252
    specific case for the special test results supported by our extended
224
253
    tests.
225
254
 
377
406
        if isinstance(test, TestCase):
378
407
            test.addCleanup(self._check_leaked_threads, test)
379
408
 
 
409
    def stopTest(self, test):
 
410
        super(ExtendedTestResult, self).stopTest(test)
 
411
        # Manually break cycles, means touching various private things but hey
 
412
        getDetails = getattr(test, "getDetails", None)
 
413
        if getDetails is not None:
 
414
            getDetails().clear()
 
415
        _clear__type_equality_funcs(test)
 
416
        self._traceback_from_test = None
 
417
 
380
418
    def startTests(self):
381
419
        self.report_tests_starting()
382
420
        self._active_threads = threading.enumerate()
383
421
 
384
 
    def stopTest(self, test):
385
 
        self._traceback_from_test = None
386
 
 
387
422
    def _check_leaked_threads(self, test):
388
423
        """See if any threads have leaked since last call
389
424
 
449
484
        self.known_failure_count += 1
450
485
        self.report_known_failure(test, err)
451
486
 
 
487
    def addUnexpectedSuccess(self, test, details=None):
 
488
        """Tell result the test unexpectedly passed, counting as a failure
 
489
 
 
490
        When the minimum version of testtools required becomes 0.9.8 this
 
491
        can be updated to use the new handling there.
 
492
        """
 
493
        super(ExtendedTestResult, self).addFailure(test, details=details)
 
494
        self.failure_count += 1
 
495
        self.report_unexpected_success(test,
 
496
            "".join(details["reason"].iter_text()))
 
497
        if self.stop_early:
 
498
            self.stop()
 
499
 
452
500
    def addNotSupported(self, test, feature):
453
501
        """The test will not be run because of a missing feature.
454
502
        """
471
519
        self.not_applicable_count += 1
472
520
        self.report_not_applicable(test, reason)
473
521
 
 
522
    def _count_stored_tests(self):
 
523
        """Count of tests instances kept alive due to not succeeding"""
 
524
        return self.error_count + self.failure_count + self.known_failure_count
 
525
 
474
526
    def _post_mortem(self, tb=None):
475
527
        """Start a PDB post mortem session."""
476
528
        if os.environ.get('BZR_TEST_PDB', None):
613
665
    def report_known_failure(self, test, err):
614
666
        pass
615
667
 
 
668
    def report_unexpected_success(self, test, reason):
 
669
        self.stream.write('FAIL: %s\n    %s: %s\n' % (
 
670
            self._test_description(test),
 
671
            "Unexpected success. Should have failed",
 
672
            reason,
 
673
            ))
 
674
 
616
675
    def report_skip(self, test, reason):
617
676
        pass
618
677
 
670
729
                % (self._testTimeString(test),
671
730
                   self._error_summary(err)))
672
731
 
 
732
    def report_unexpected_success(self, test, reason):
 
733
        self.stream.write(' FAIL %s\n%s: %s\n'
 
734
                % (self._testTimeString(test),
 
735
                   "Unexpected success. Should have failed",
 
736
                   reason))
 
737
 
673
738
    def report_success(self, test):
674
739
        self.stream.write('   OK %s\n' % self._testTimeString(test))
675
740
        for bench_called, stats in getattr(test, '_benchcalls', []):
894
959
 
895
960
    The method is really a factory and users are expected to use it as such.
896
961
    """
897
 
    
 
962
 
898
963
    kwargs['setUp'] = isolated_doctest_setUp
899
964
    kwargs['tearDown'] = isolated_doctest_tearDown
900
965
    return doctest.DocTestSuite(*args, **kwargs)
935
1000
 
936
1001
    def setUp(self):
937
1002
        super(TestCase, self).setUp()
 
1003
 
 
1004
        # At this point we're still accessing the config files in $BZR_HOME (as
 
1005
        # set by the user running selftest).
 
1006
        timeout = config.GlobalStack().get('selftest.timeout')
 
1007
        if timeout:
 
1008
            timeout_fixture = fixtures.TimeoutFixture(timeout)
 
1009
            timeout_fixture.setUp()
 
1010
            self.addCleanup(timeout_fixture.cleanUp)
 
1011
 
938
1012
        for feature in getattr(self, '_test_needs_features', []):
939
1013
            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
1014
        self._cleanEnvironment()
 
1015
 
 
1016
        if bzrlib.global_state is not None:
 
1017
            self.overrideAttr(bzrlib.global_state, 'cmdline_overrides',
 
1018
                              config.CommandLineStore())
 
1019
 
945
1020
        self._silenceUI()
946
1021
        self._startLogFile()
947
1022
        self._benchcalls = []
954
1029
        # between tests.  We should get rid of this altogether: bug 656694. --
955
1030
        # mbp 20101008
956
1031
        self.overrideAttr(bzrlib.trace, '_verbosity_level', 0)
957
 
        # Isolate config option expansion until its default value for bzrlib is
958
 
        # settled on or a the FIXME associated with _get_expand_default_value
959
 
        # is addressed -- vila 20110219
960
 
        self.overrideAttr(config, '_expand_default_value', None)
 
1032
        self._log_files = set()
 
1033
        # Each key in the ``_counters`` dict holds a value for a different
 
1034
        # counter. When the test ends, addDetail() should be used to output the
 
1035
        # counter values. This happens in install_counter_hook().
 
1036
        self._counters = {}
 
1037
        if 'config_stats' in selftest_debug_flags:
 
1038
            self._install_config_stats_hooks()
 
1039
        # Do not use i18n for tests (unless the test reverses this)
 
1040
        i18n.disable_i18n()
961
1041
 
962
1042
    def debug(self):
963
1043
        # debug a frame up.
964
1044
        import pdb
965
 
        pdb.Pdb().set_trace(sys._getframe().f_back)
 
1045
        # The sys preserved stdin/stdout should allow blackbox tests debugging
 
1046
        pdb.Pdb(stdin=sys.__stdin__, stdout=sys.__stdout__
 
1047
                ).set_trace(sys._getframe().f_back)
966
1048
 
967
1049
    def discardDetail(self, name):
968
1050
        """Extend the addDetail, getDetails api so we can remove a detail.
980
1062
        if name in details:
981
1063
            del details[name]
982
1064
 
 
1065
    def install_counter_hook(self, hooks, name, counter_name=None):
 
1066
        """Install a counting hook.
 
1067
 
 
1068
        Any hook can be counted as long as it doesn't need to return a value.
 
1069
 
 
1070
        :param hooks: Where the hook should be installed.
 
1071
 
 
1072
        :param name: The hook name that will be counted.
 
1073
 
 
1074
        :param counter_name: The counter identifier in ``_counters``, defaults
 
1075
            to ``name``.
 
1076
        """
 
1077
        _counters = self._counters # Avoid closing over self
 
1078
        if counter_name is None:
 
1079
            counter_name = name
 
1080
        if _counters.has_key(counter_name):
 
1081
            raise AssertionError('%s is already used as a counter name'
 
1082
                                  % (counter_name,))
 
1083
        _counters[counter_name] = 0
 
1084
        self.addDetail(counter_name, content.Content(content.UTF8_TEXT,
 
1085
            lambda: ['%d' % (_counters[counter_name],)]))
 
1086
        def increment_counter(*args, **kwargs):
 
1087
            _counters[counter_name] += 1
 
1088
        label = 'count %s calls' % (counter_name,)
 
1089
        hooks.install_named_hook(name, increment_counter, label)
 
1090
        self.addCleanup(hooks.uninstall_named_hook, name, label)
 
1091
 
 
1092
    def _install_config_stats_hooks(self):
 
1093
        """Install config hooks to count hook calls.
 
1094
 
 
1095
        """
 
1096
        for hook_name in ('get', 'set', 'remove', 'load', 'save'):
 
1097
            self.install_counter_hook(config.ConfigHooks, hook_name,
 
1098
                                       'config.%s' % (hook_name,))
 
1099
 
 
1100
        # The OldConfigHooks are private and need special handling to protect
 
1101
        # against recursive tests (tests that run other tests), so we just do
 
1102
        # manually what registering them into _builtin_known_hooks will provide
 
1103
        # us.
 
1104
        self.overrideAttr(config, 'OldConfigHooks', config._OldConfigHooks())
 
1105
        for hook_name in ('get', 'set', 'remove', 'load', 'save'):
 
1106
            self.install_counter_hook(config.OldConfigHooks, hook_name,
 
1107
                                      'old_config.%s' % (hook_name,))
 
1108
 
983
1109
    def _clear_debug_flags(self):
984
1110
        """Prevent externally set debug flags affecting tests.
985
1111
 
1039
1165
        # break some locks on purpose and should be taken into account by
1040
1166
        # considering that breaking a lock is just a dirty way of releasing it.
1041
1167
        if len(acquired_locks) != (len(released_locks) + len(broken_locks)):
1042
 
            message = ('Different number of acquired and '
1043
 
                       'released or broken locks. (%s, %s + %s)' %
1044
 
                       (acquired_locks, released_locks, broken_locks))
 
1168
            message = (
 
1169
                'Different number of acquired and '
 
1170
                'released or broken locks.\n'
 
1171
                'acquired=%s\n'
 
1172
                'released=%s\n'
 
1173
                'broken=%s\n' %
 
1174
                (acquired_locks, released_locks, broken_locks))
1045
1175
            if not self._lock_check_thorough:
1046
1176
                # Rather than fail, just warn
1047
1177
                print "Broken test %s: %s" % (self, message)
1075
1205
 
1076
1206
    def permit_dir(self, name):
1077
1207
        """Permit a directory to be used by this test. See permit_url."""
1078
 
        name_transport = _mod_transport.get_transport(name)
 
1208
        name_transport = _mod_transport.get_transport_from_path(name)
1079
1209
        self.permit_url(name)
1080
1210
        self.permit_url(name_transport.base)
1081
1211
 
1160
1290
        self.addCleanup(transport_server.stop_server)
1161
1291
        # Obtain a real transport because if the server supplies a password, it
1162
1292
        # will be hidden from the base on the client side.
1163
 
        t = _mod_transport.get_transport(transport_server.get_url())
 
1293
        t = _mod_transport.get_transport_from_url(transport_server.get_url())
1164
1294
        # Some transport servers effectively chroot the backing transport;
1165
1295
        # others like SFTPServer don't - users of the transport can walk up the
1166
1296
        # transport to read the entire backing transport. This wouldn't matter
1197
1327
        # hook into bzr dir opening. This leaves a small window of error for
1198
1328
        # transport tests, but they are well known, and we can improve on this
1199
1329
        # step.
1200
 
        bzrdir.BzrDir.hooks.install_named_hook("pre_open",
 
1330
        controldir.ControlDir.hooks.install_named_hook("pre_open",
1201
1331
            self._preopen_isolate_transport, "Check bzr directories are safe.")
1202
1332
 
1203
1333
    def _ndiff_strings(self, a, b):
1290
1420
                length, len(obj_with_len), obj_with_len))
1291
1421
 
1292
1422
    def assertLogsError(self, exception_class, func, *args, **kwargs):
1293
 
        """Assert that func(*args, **kwargs) quietly logs a specific exception.
 
1423
        """Assert that `func(*args, **kwargs)` quietly logs a specific error.
1294
1424
        """
1295
1425
        captured = []
1296
1426
        orig_log_exception_quietly = trace.log_exception_quietly
1297
1427
        try:
1298
1428
            def capture():
1299
1429
                orig_log_exception_quietly()
1300
 
                captured.append(sys.exc_info())
 
1430
                captured.append(sys.exc_info()[1])
1301
1431
            trace.log_exception_quietly = capture
1302
1432
            func(*args, **kwargs)
1303
1433
        finally:
1304
1434
            trace.log_exception_quietly = orig_log_exception_quietly
1305
1435
        self.assertLength(1, captured)
1306
 
        err = captured[0][1]
 
1436
        err = captured[0]
1307
1437
        self.assertIsInstance(err, exception_class)
1308
1438
        return err
1309
1439
 
1516
1646
        not other callers that go direct to the warning module.
1517
1647
 
1518
1648
        To test that a deprecated method raises an error, do something like
1519
 
        this::
 
1649
        this (remember that both assertRaises and applyDeprecated delays *args
 
1650
        and **kwargs passing)::
1520
1651
 
1521
1652
            self.assertRaises(errors.ReservedId,
1522
1653
                self.applyDeprecated,
1600
1731
        return result
1601
1732
 
1602
1733
    def _startLogFile(self):
1603
 
        """Send bzr and test log messages to a temporary file.
1604
 
 
1605
 
        The file is removed as the test is torn down.
1606
 
        """
1607
 
        self._log_file = StringIO()
 
1734
        """Setup a in-memory target for bzr and testcase log messages"""
 
1735
        pseudo_log_file = StringIO()
 
1736
        def _get_log_contents_for_weird_testtools_api():
 
1737
            return [pseudo_log_file.getvalue().decode(
 
1738
                "utf-8", "replace").encode("utf-8")]
 
1739
        self.addDetail("log", content.Content(content.ContentType("text",
 
1740
            "plain", {"charset": "utf8"}),
 
1741
            _get_log_contents_for_weird_testtools_api))
 
1742
        self._log_file = pseudo_log_file
1608
1743
        self._log_memento = trace.push_log_file(self._log_file)
1609
1744
        self.addCleanup(self._finishLogFile)
1610
1745
 
1611
1746
    def _finishLogFile(self):
1612
 
        """Finished with the log file.
1613
 
 
1614
 
        Close the file and delete it, unless setKeepLogfile was called.
1615
 
        """
 
1747
        """Flush and dereference the in-memory log for this testcase"""
1616
1748
        if trace._trace_file:
1617
1749
            # flush the log file, to get all content
1618
1750
            trace._trace_file.flush()
1619
1751
        trace.pop_log_file(self._log_memento)
1620
 
        # Cache the log result and delete the file on disk
1621
 
        self._get_log(False)
 
1752
        # The logging module now tracks references for cleanup so discard ours
 
1753
        del self._log_memento
1622
1754
 
1623
1755
    def thisFailsStrictLockCheck(self):
1624
1756
        """It is known that this test would fail with -Dstrict_locks.
1636
1768
    def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1637
1769
        """Overrides an object attribute restoring it after the test.
1638
1770
 
 
1771
        :note: This should be used with discretion; you should think about
 
1772
        whether it's better to make the code testable without monkey-patching.
 
1773
 
1639
1774
        :param obj: The object that will be mutated.
1640
1775
 
1641
1776
        :param attr_name: The attribute name we want to preserve/override in
1645
1780
 
1646
1781
        :returns: The actual attr value.
1647
1782
        """
1648
 
        value = getattr(obj, attr_name)
1649
1783
        # The actual value is captured by the call below
1650
 
        self.addCleanup(setattr, obj, attr_name, value)
 
1784
        value = getattr(obj, attr_name, _unitialized_attr)
 
1785
        if value is _unitialized_attr:
 
1786
            # When the test completes, the attribute should not exist, but if
 
1787
            # we aren't setting a value, we don't need to do anything.
 
1788
            if new is not _unitialized_attr:
 
1789
                self.addCleanup(delattr, obj, attr_name)
 
1790
        else:
 
1791
            self.addCleanup(setattr, obj, attr_name, value)
1651
1792
        if new is not _unitialized_attr:
1652
1793
            setattr(obj, attr_name, new)
1653
1794
        return value
1666
1807
        self.addCleanup(osutils.set_or_unset_env, name, value)
1667
1808
        return value
1668
1809
 
 
1810
    def recordCalls(self, obj, attr_name):
 
1811
        """Monkeypatch in a wrapper that will record calls.
 
1812
 
 
1813
        The monkeypatch is automatically removed when the test concludes.
 
1814
 
 
1815
        :param obj: The namespace holding the reference to be replaced;
 
1816
            typically a module, class, or object.
 
1817
        :param attr_name: A string for the name of the attribute to 
 
1818
            patch.
 
1819
        :returns: A list that will be extended with one item every time the
 
1820
            function is called, with a tuple of (args, kwargs).
 
1821
        """
 
1822
        calls = []
 
1823
 
 
1824
        def decorator(*args, **kwargs):
 
1825
            calls.append((args, kwargs))
 
1826
            return orig(*args, **kwargs)
 
1827
        orig = self.overrideAttr(obj, attr_name, decorator)
 
1828
        return calls
 
1829
 
1669
1830
    def _cleanEnvironment(self):
1670
1831
        for name, value in isolated_environ.iteritems():
1671
1832
            self.overrideEnv(name, value)
1673
1834
    def _restoreHooks(self):
1674
1835
        for klass, (name, hooks) in self._preserved_hooks.items():
1675
1836
            setattr(klass, name, hooks)
1676
 
        hooks._lazy_hooks = self._preserved_lazy_hooks
 
1837
        self._preserved_hooks.clear()
 
1838
        bzrlib.hooks._lazy_hooks = self._preserved_lazy_hooks
 
1839
        self._preserved_lazy_hooks.clear()
1677
1840
 
1678
1841
    def knownFailure(self, reason):
1679
 
        """This test has failed for some known reason."""
1680
 
        raise KnownFailure(reason)
 
1842
        """Declare that this test fails for a known reason
 
1843
 
 
1844
        Tests that are known to fail should generally be using expectedFailure
 
1845
        with an appropriate reverse assertion if a change could cause the test
 
1846
        to start passing. Conversely if the test has no immediate prospect of
 
1847
        succeeding then using skip is more suitable.
 
1848
 
 
1849
        When this method is called while an exception is being handled, that
 
1850
        traceback will be used, otherwise a new exception will be thrown to
 
1851
        provide one but won't be reported.
 
1852
        """
 
1853
        self._add_reason(reason)
 
1854
        try:
 
1855
            exc_info = sys.exc_info()
 
1856
            if exc_info != (None, None, None):
 
1857
                self._report_traceback(exc_info)
 
1858
            else:
 
1859
                try:
 
1860
                    raise self.failureException(reason)
 
1861
                except self.failureException:
 
1862
                    exc_info = sys.exc_info()
 
1863
            # GZ 02-08-2011: Maybe cleanup this err.exc_info attribute too?
 
1864
            raise testtools.testcase._ExpectedFailure(exc_info)
 
1865
        finally:
 
1866
            del exc_info
1681
1867
 
1682
1868
    def _suppress_log(self):
1683
1869
        """Remove the log info from details."""
1771
1957
    def log(self, *args):
1772
1958
        trace.mutter(*args)
1773
1959
 
1774
 
    def _get_log(self, keep_log_file=False):
1775
 
        """Internal helper to get the log from bzrlib.trace for this test.
1776
 
 
1777
 
        Please use self.getDetails, or self.get_log to access this in test case
1778
 
        code.
1779
 
 
1780
 
        :param keep_log_file: When True, if the log is still a file on disk
1781
 
            leave it as a file on disk. When False, if the log is still a file
1782
 
            on disk, the log file is deleted and the log preserved as
1783
 
            self._log_contents.
1784
 
        :return: A string containing the log.
1785
 
        """
1786
 
        if self._log_contents is not None:
1787
 
            try:
1788
 
                self._log_contents.decode('utf8')
1789
 
            except UnicodeDecodeError:
1790
 
                unicodestr = self._log_contents.decode('utf8', 'replace')
1791
 
                self._log_contents = unicodestr.encode('utf8')
1792
 
            return self._log_contents
1793
 
        if self._log_file is not None:
1794
 
            log_contents = self._log_file.getvalue()
1795
 
            try:
1796
 
                log_contents.decode('utf8')
1797
 
            except UnicodeDecodeError:
1798
 
                unicodestr = log_contents.decode('utf8', 'replace')
1799
 
                log_contents = unicodestr.encode('utf8')
1800
 
            if not keep_log_file:
1801
 
                self._log_file = None
1802
 
                # Permit multiple calls to get_log until we clean it up in
1803
 
                # finishLogFile
1804
 
                self._log_contents = log_contents
1805
 
            return log_contents
1806
 
        else:
1807
 
            return "No log file content."
1808
 
 
1809
1960
    def get_log(self):
1810
1961
        """Get a unicode string containing the log from bzrlib.trace.
1811
1962
 
1846
1997
 
1847
1998
        self.log('run bzr: %r', args)
1848
1999
        # FIXME: don't call into logging here
1849
 
        handler = logging.StreamHandler(stderr)
1850
 
        handler.setLevel(logging.INFO)
 
2000
        handler = trace.EncodedStreamHandler(stderr, errors="replace",
 
2001
            level=logging.INFO)
1851
2002
        logger = logging.getLogger('')
1852
2003
        logger.addHandler(handler)
1853
2004
        old_ui_factory = ui.ui_factory
2011
2162
    def start_bzr_subprocess(self, process_args, env_changes=None,
2012
2163
                             skip_if_plan_to_signal=False,
2013
2164
                             working_dir=None,
2014
 
                             allow_plugins=False):
 
2165
                             allow_plugins=False, stderr=subprocess.PIPE):
2015
2166
        """Start bzr in a subprocess for testing.
2016
2167
 
2017
2168
        This starts a new Python interpreter and runs bzr in there.
2029
2180
        :param skip_if_plan_to_signal: raise TestSkipped when true and system
2030
2181
            doesn't support signalling subprocesses.
2031
2182
        :param allow_plugins: If False (default) pass --no-plugins to bzr.
 
2183
        :param stderr: file to use for the subprocess's stderr.  Valid values
 
2184
            are those valid for the stderr argument of `subprocess.Popen`.
 
2185
            Default value is ``subprocess.PIPE``.
2032
2186
 
2033
2187
        :returns: Popen object for the started process.
2034
2188
        """
2038
2192
 
2039
2193
        if env_changes is None:
2040
2194
            env_changes = {}
 
2195
        # Because $HOME is set to a tempdir for the context of a test, modules
 
2196
        # installed in the user dir will not be found unless $PYTHONUSERBASE
 
2197
        # gets set to the computed directory of this parent process.
 
2198
        if site.USER_BASE is not None:
 
2199
            env_changes["PYTHONUSERBASE"] = site.USER_BASE
2041
2200
        old_env = {}
2042
2201
 
2043
2202
        def cleanup_environment():
2060
2219
            # so we will avoid using it on all platforms, just to
2061
2220
            # make sure the code path is used, and we don't break on win32
2062
2221
            cleanup_environment()
 
2222
            # Include the subprocess's log file in the test details, in case
 
2223
            # the test fails due to an error in the subprocess.
 
2224
            self._add_subprocess_log(trace._get_bzr_log_filename())
2063
2225
            command = [sys.executable]
2064
2226
            # frozen executables don't need the path to bzr
2065
2227
            if getattr(sys, "frozen", None) is None:
2069
2231
            command.extend(process_args)
2070
2232
            process = self._popen(command, stdin=subprocess.PIPE,
2071
2233
                                  stdout=subprocess.PIPE,
2072
 
                                  stderr=subprocess.PIPE)
 
2234
                                  stderr=stderr)
2073
2235
        finally:
2074
2236
            restore_environment()
2075
2237
            if cwd is not None:
2077
2239
 
2078
2240
        return process
2079
2241
 
 
2242
    def _add_subprocess_log(self, log_file_path):
 
2243
        if len(self._log_files) == 0:
 
2244
            # Register an addCleanup func.  We do this on the first call to
 
2245
            # _add_subprocess_log rather than in TestCase.setUp so that this
 
2246
            # addCleanup is registered after any cleanups for tempdirs that
 
2247
            # subclasses might create, which will probably remove the log file
 
2248
            # we want to read.
 
2249
            self.addCleanup(self._subprocess_log_cleanup)
 
2250
        # self._log_files is a set, so if a log file is reused we won't grab it
 
2251
        # twice.
 
2252
        self._log_files.add(log_file_path)
 
2253
 
 
2254
    def _subprocess_log_cleanup(self):
 
2255
        for count, log_file_path in enumerate(self._log_files):
 
2256
            # We use buffer_now=True to avoid holding the file open beyond
 
2257
            # the life of this function, which might interfere with e.g.
 
2258
            # cleaning tempdirs on Windows.
 
2259
            # XXX: Testtools 0.9.5 doesn't have the content_from_file helper
 
2260
            #detail_content = content.content_from_file(
 
2261
            #    log_file_path, buffer_now=True)
 
2262
            with open(log_file_path, 'rb') as log_file:
 
2263
                log_file_bytes = log_file.read()
 
2264
            detail_content = content.Content(content.ContentType("text",
 
2265
                "plain", {"charset": "utf8"}), lambda: [log_file_bytes])
 
2266
            self.addDetail("start_bzr_subprocess-log-%d" % (count,),
 
2267
                detail_content)
 
2268
 
2080
2269
    def _popen(self, *args, **kwargs):
2081
2270
        """Place a call to Popen.
2082
2271
 
2204
2393
        from bzrlib.smart import request
2205
2394
        request_handlers = request.request_handlers
2206
2395
        orig_method = request_handlers.get(verb)
 
2396
        orig_info = request_handlers.get_info(verb)
2207
2397
        request_handlers.remove(verb)
2208
 
        self.addCleanup(request_handlers.register, verb, orig_method)
 
2398
        self.addCleanup(request_handlers.register, verb, orig_method,
 
2399
            info=orig_info)
2209
2400
 
2210
2401
 
2211
2402
class CapturedCall(object):
2234
2425
class TestCaseWithMemoryTransport(TestCase):
2235
2426
    """Common test class for tests that do not need disk resources.
2236
2427
 
2237
 
    Tests that need disk resources should derive from TestCaseWithTransport.
 
2428
    Tests that need disk resources should derive from TestCaseInTempDir
 
2429
    orTestCaseWithTransport.
2238
2430
 
2239
2431
    TestCaseWithMemoryTransport sets the TEST_ROOT variable for all bzr tests.
2240
2432
 
2241
 
    For TestCaseWithMemoryTransport the test_home_dir is set to the name of
 
2433
    For TestCaseWithMemoryTransport the ``test_home_dir`` is set to the name of
2242
2434
    a directory which does not exist. This serves to help ensure test isolation
2243
 
    is preserved. test_dir is set to the TEST_ROOT, as is cwd, because they
2244
 
    must exist. However, TestCaseWithMemoryTransport does not offer local
2245
 
    file defaults for the transport in tests, nor does it obey the command line
 
2435
    is preserved. ``test_dir`` is set to the TEST_ROOT, as is cwd, because they
 
2436
    must exist. However, TestCaseWithMemoryTransport does not offer local file
 
2437
    defaults for the transport in tests, nor does it obey the command line
2246
2438
    override, so tests that accidentally write to the common directory should
2247
2439
    be rare.
2248
2440
 
2249
 
    :cvar TEST_ROOT: Directory containing all temporary directories, plus
2250
 
    a .bzr directory that stops us ascending higher into the filesystem.
 
2441
    :cvar TEST_ROOT: Directory containing all temporary directories, plus a
 
2442
        ``.bzr`` directory that stops us ascending higher into the filesystem.
2251
2443
    """
2252
2444
 
2253
2445
    TEST_ROOT = None
2263
2455
        self.transport_readonly_server = None
2264
2456
        self.__vfs_server = None
2265
2457
 
 
2458
    def setUp(self):
 
2459
        super(TestCaseWithMemoryTransport, self).setUp()
 
2460
 
 
2461
        def _add_disconnect_cleanup(transport):
 
2462
            """Schedule disconnection of given transport at test cleanup
 
2463
 
 
2464
            This needs to happen for all connected transports or leaks occur.
 
2465
 
 
2466
            Note reconnections may mean we call disconnect multiple times per
 
2467
            transport which is suboptimal but seems harmless.
 
2468
            """
 
2469
            self.addCleanup(transport.disconnect)
 
2470
 
 
2471
        _mod_transport.Transport.hooks.install_named_hook('post_connect',
 
2472
            _add_disconnect_cleanup, None)
 
2473
 
 
2474
        self._make_test_root()
 
2475
        self.addCleanup(os.chdir, os.getcwdu())
 
2476
        self.makeAndChdirToTestDir()
 
2477
        self.overrideEnvironmentForTesting()
 
2478
        self.__readonly_server = None
 
2479
        self.__server = None
 
2480
        self.reduceLockdirTimeout()
 
2481
        # Each test may use its own config files even if the local config files
 
2482
        # don't actually exist. They'll rightly fail if they try to create them
 
2483
        # though.
 
2484
        self.overrideAttr(config, '_shared_stores', {})
 
2485
 
2266
2486
    def get_transport(self, relpath=None):
2267
2487
        """Return a writeable transport.
2268
2488
 
2271
2491
 
2272
2492
        :param relpath: a path relative to the base url.
2273
2493
        """
2274
 
        t = _mod_transport.get_transport(self.get_url(relpath))
 
2494
        t = _mod_transport.get_transport_from_url(self.get_url(relpath))
2275
2495
        self.assertFalse(t.is_readonly())
2276
2496
        return t
2277
2497
 
2283
2503
 
2284
2504
        :param relpath: a path relative to the base url.
2285
2505
        """
2286
 
        t = _mod_transport.get_transport(self.get_readonly_url(relpath))
 
2506
        t = _mod_transport.get_transport_from_url(
 
2507
            self.get_readonly_url(relpath))
2287
2508
        self.assertTrue(t.is_readonly())
2288
2509
        return t
2289
2510
 
2410
2631
        real branch.
2411
2632
        """
2412
2633
        root = TestCaseWithMemoryTransport.TEST_ROOT
2413
 
        bzrdir.BzrDir.create_standalone_workingtree(root)
 
2634
        try:
 
2635
            # Make sure we get a readable and accessible home for .bzr.log
 
2636
            # and/or config files, and not fallback to weird defaults (see
 
2637
            # http://pad.lv/825027).
 
2638
            self.assertIs(None, os.environ.get('BZR_HOME', None))
 
2639
            os.environ['BZR_HOME'] = root
 
2640
            wt = controldir.ControlDir.create_standalone_workingtree(root)
 
2641
            del os.environ['BZR_HOME']
 
2642
        except Exception, e:
 
2643
            self.fail("Fail to initialize the safety net: %r\n" % (e,))
 
2644
        # Hack for speed: remember the raw bytes of the dirstate file so that
 
2645
        # we don't need to re-open the wt to check it hasn't changed.
 
2646
        TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE = (
 
2647
            wt.control_transport.get_bytes('dirstate'))
2414
2648
 
2415
2649
    def _check_safety_net(self):
2416
2650
        """Check that the safety .bzr directory have not been touched.
2419
2653
        propagating. This method ensures than a test did not leaked.
2420
2654
        """
2421
2655
        root = TestCaseWithMemoryTransport.TEST_ROOT
2422
 
        self.permit_url(_mod_transport.get_transport(root).base)
2423
 
        wt = workingtree.WorkingTree.open(root)
2424
 
        last_rev = wt.last_revision()
2425
 
        if last_rev != 'null:':
 
2656
        t = _mod_transport.get_transport_from_path(root)
 
2657
        self.permit_url(t.base)
 
2658
        if (t.get_bytes('.bzr/checkout/dirstate') != 
 
2659
                TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE):
2426
2660
            # The current test have modified the /bzr directory, we need to
2427
2661
            # recreate a new one or all the followng tests will fail.
2428
2662
            # If you need to inspect its content uncomment the following line
2460
2694
        self.test_home_dir = self.test_dir + "/MemoryTransportMissingHomeDir"
2461
2695
        self.permit_dir(self.test_dir)
2462
2696
 
2463
 
    def make_branch(self, relpath, format=None):
 
2697
    def make_branch(self, relpath, format=None, name=None):
2464
2698
        """Create a branch on the transport at relpath."""
2465
2699
        repo = self.make_repository(relpath, format=format)
2466
 
        return repo.bzrdir.create_branch()
 
2700
        return repo.bzrdir.create_branch(append_revisions_only=False,
 
2701
                                         name=name)
 
2702
 
 
2703
    def get_default_format(self):
 
2704
        return 'default'
 
2705
 
 
2706
    def resolve_format(self, format):
 
2707
        """Resolve an object to a ControlDir format object.
 
2708
 
 
2709
        The initial format object can either already be
 
2710
        a ControlDirFormat, None (for the default format),
 
2711
        or a string with the name of the control dir format.
 
2712
 
 
2713
        :param format: Object to resolve
 
2714
        :return A ControlDirFormat instance
 
2715
        """
 
2716
        if format is None:
 
2717
            format = self.get_default_format()
 
2718
        if isinstance(format, basestring):
 
2719
            format = controldir.format_registry.make_bzrdir(format)
 
2720
        return format
2467
2721
 
2468
2722
    def make_bzrdir(self, relpath, format=None):
2469
2723
        try:
2473
2727
            t = _mod_transport.get_transport(maybe_a_url)
2474
2728
            if len(segments) > 1 and segments[-1] not in ('', '.'):
2475
2729
                t.ensure_base()
2476
 
            if format is None:
2477
 
                format = 'default'
2478
 
            if isinstance(format, basestring):
2479
 
                format = bzrdir.format_registry.make_bzrdir(format)
 
2730
            format = self.resolve_format(format)
2480
2731
            return format.initialize_on_transport(t)
2481
2732
        except errors.UninitializableFormat:
2482
2733
            raise TestSkipped("Format %s is not initializable." % format)
2483
2734
 
2484
 
    def make_repository(self, relpath, shared=False, format=None):
 
2735
    def make_repository(self, relpath, shared=None, format=None):
2485
2736
        """Create a repository on our default transport at relpath.
2486
2737
 
2487
2738
        Note that relpath must be a relative path, not a full url.
2498
2749
            backing_server = self.get_server()
2499
2750
        smart_server = test_server.SmartTCPServer_for_testing()
2500
2751
        self.start_server(smart_server, backing_server)
2501
 
        remote_transport = _mod_transport.get_transport(smart_server.get_url()
 
2752
        remote_transport = _mod_transport.get_transport_from_url(smart_server.get_url()
2502
2753
                                                   ).clone(path)
2503
2754
        return remote_transport
2504
2755
 
2518
2769
        self.overrideEnv('HOME', test_home_dir)
2519
2770
        self.overrideEnv('BZR_HOME', test_home_dir)
2520
2771
 
2521
 
    def setUp(self):
2522
 
        super(TestCaseWithMemoryTransport, self).setUp()
2523
 
        # Ensure that ConnectedTransport doesn't leak sockets
2524
 
        def get_transport_with_cleanup(*args, **kwargs):
2525
 
            t = orig_get_transport(*args, **kwargs)
2526
 
            if isinstance(t, _mod_transport.ConnectedTransport):
2527
 
                self.addCleanup(t.disconnect)
2528
 
            return t
2529
 
 
2530
 
        orig_get_transport = self.overrideAttr(_mod_transport, 'get_transport',
2531
 
                                               get_transport_with_cleanup)
2532
 
        self._make_test_root()
2533
 
        self.addCleanup(os.chdir, os.getcwdu())
2534
 
        self.makeAndChdirToTestDir()
2535
 
        self.overrideEnvironmentForTesting()
2536
 
        self.__readonly_server = None
2537
 
        self.__server = None
2538
 
        self.reduceLockdirTimeout()
2539
 
 
2540
2772
    def setup_smart_server_with_call_log(self):
2541
2773
        """Sets up a smart server as the transport server with a call log."""
2542
2774
        self.transport_server = test_server.SmartTCPServer_for_testing
 
2775
        self.hpss_connections = []
2543
2776
        self.hpss_calls = []
2544
2777
        import traceback
2545
2778
        # Skip the current stack down to the caller of
2548
2781
        def capture_hpss_call(params):
2549
2782
            self.hpss_calls.append(
2550
2783
                CapturedCall(params, prefix_length))
 
2784
        def capture_connect(transport):
 
2785
            self.hpss_connections.append(transport)
2551
2786
        client._SmartClient.hooks.install_named_hook(
2552
2787
            'call', capture_hpss_call, None)
 
2788
        _mod_transport.Transport.hooks.install_named_hook(
 
2789
            'post_connect', capture_connect, None)
2553
2790
 
2554
2791
    def reset_smart_call_log(self):
2555
2792
        self.hpss_calls = []
 
2793
        self.hpss_connections = []
2556
2794
 
2557
2795
 
2558
2796
class TestCaseInTempDir(TestCaseWithMemoryTransport):
2577
2815
 
2578
2816
    OVERRIDE_PYTHON = 'python'
2579
2817
 
 
2818
    def setUp(self):
 
2819
        super(TestCaseInTempDir, self).setUp()
 
2820
        # Remove the protection set in isolated_environ, we have a proper
 
2821
        # access to disk resources now.
 
2822
        self.overrideEnv('BZR_LOG', None)
 
2823
 
2580
2824
    def check_file_contents(self, filename, expect):
2581
2825
        self.log("check contents of file %s" % filename)
2582
2826
        f = file(filename)
2622
2866
        # stacking policy to honour; create a bzr dir with an unshared
2623
2867
        # repository (but not a branch - our code would be trying to escape
2624
2868
        # then!) to stop them, and permit it to be read.
2625
 
        # control = bzrdir.BzrDir.create(self.test_base_dir)
 
2869
        # control = controldir.ControlDir.create(self.test_base_dir)
2626
2870
        # control.create_repository()
2627
2871
        self.test_home_dir = self.test_base_dir + '/home'
2628
2872
        os.mkdir(self.test_home_dir)
2663
2907
                "a list or a tuple. Got %r instead" % (shape,))
2664
2908
        # It's OK to just create them using forward slashes on windows.
2665
2909
        if transport is None or transport.is_readonly():
2666
 
            transport = _mod_transport.get_transport(".")
 
2910
            transport = _mod_transport.get_transport_from_path(".")
2667
2911
        for name in shape:
2668
2912
            self.assertIsInstance(name, basestring)
2669
2913
            if name[-1] == '/':
2717
2961
    readwrite one must both define get_url() as resolving to os.getcwd().
2718
2962
    """
2719
2963
 
 
2964
    def setUp(self):
 
2965
        super(TestCaseWithTransport, self).setUp()
 
2966
        self.__vfs_server = None
 
2967
 
2720
2968
    def get_vfs_only_server(self):
2721
2969
        """See TestCaseWithMemoryTransport.
2722
2970
 
2754
3002
        # this obviously requires a format that supports branch references
2755
3003
        # so check for that by checking bzrdir.BzrDirFormat.get_default_format()
2756
3004
        # RBC 20060208
 
3005
        format = self.resolve_format(format=format)
 
3006
        if not format.supports_workingtrees:
 
3007
            b = self.make_branch(relpath+'.branch', format=format)
 
3008
            return b.create_checkout(relpath, lightweight=True)
2757
3009
        b = self.make_branch(relpath, format=format)
2758
3010
        try:
2759
3011
            return b.bzrdir.create_workingtree()
2764
3016
            if self.vfs_transport_factory is test_server.LocalURLServer:
2765
3017
                # the branch is colocated on disk, we cannot create a checkout.
2766
3018
                # hopefully callers will expect this.
2767
 
                local_controldir= bzrdir.BzrDir.open(self.get_vfs_only_url(relpath))
 
3019
                local_controldir = controldir.ControlDir.open(
 
3020
                    self.get_vfs_only_url(relpath))
2768
3021
                wt = local_controldir.create_workingtree()
2769
3022
                if wt.branch._format != b._format:
2770
3023
                    wt._branch = b
2800
3053
        self.assertFalse(differences.has_changed(),
2801
3054
            "Trees %r and %r are different: %r" % (left, right, differences))
2802
3055
 
2803
 
    def setUp(self):
2804
 
        super(TestCaseWithTransport, self).setUp()
2805
 
        self.__vfs_server = None
2806
 
 
2807
3056
    def disable_missing_extensions_warning(self):
2808
3057
        """Some tests expect a precise stderr content.
2809
3058
 
2810
3059
        There is no point in forcing them to duplicate the extension related
2811
3060
        warning.
2812
3061
        """
2813
 
        config.GlobalConfig().set_user_option('ignore_missing_extensions', True)
 
3062
        config.GlobalStack().set('ignore_missing_extensions', True)
2814
3063
 
2815
3064
 
2816
3065
class ChrootedTestCase(TestCaseWithTransport):
3058
3307
                            result_decorators=result_decorators,
3059
3308
                            )
3060
3309
    runner.stop_on_failure=stop_on_failure
 
3310
    if isinstance(suite, unittest.TestSuite):
 
3311
        # Empty out _tests list of passed suite and populate new TestSuite
 
3312
        suite._tests[:], suite = [], TestSuite(suite)
3061
3313
    # built in decorator factories:
3062
3314
    decorators = [
3063
3315
        random_order(random_seed, runner),
3161
3413
 
3162
3414
class TestDecorator(TestUtil.TestSuite):
3163
3415
    """A decorator for TestCase/TestSuite objects.
3164
 
    
3165
 
    Usually, subclasses should override __iter__(used when flattening test
3166
 
    suites), which we do to filter, reorder, parallelise and so on, run() and
3167
 
    debug().
 
3416
 
 
3417
    Contains rather than flattening suite passed on construction
3168
3418
    """
3169
3419
 
3170
 
    def __init__(self, suite):
3171
 
        TestUtil.TestSuite.__init__(self)
3172
 
        self.addTest(suite)
3173
 
 
3174
 
    def countTestCases(self):
3175
 
        cases = 0
3176
 
        for test in self:
3177
 
            cases += test.countTestCases()
3178
 
        return cases
3179
 
 
3180
 
    def debug(self):
3181
 
        for test in self:
3182
 
            test.debug()
3183
 
 
3184
 
    def run(self, result):
3185
 
        # Use iteration on self, not self._tests, to allow subclasses to hook
3186
 
        # into __iter__.
3187
 
        for test in self:
3188
 
            if result.shouldStop:
3189
 
                break
3190
 
            test.run(result)
3191
 
        return result
 
3420
    def __init__(self, suite=None):
 
3421
        super(TestDecorator, self).__init__()
 
3422
        if suite is not None:
 
3423
            self.addTest(suite)
 
3424
 
 
3425
    # Don't need subclass run method with suite emptying
 
3426
    run = unittest.TestSuite.run
3192
3427
 
3193
3428
 
3194
3429
class CountingDecorator(TestDecorator):
3205
3440
    """A decorator which excludes test matching an exclude pattern."""
3206
3441
 
3207
3442
    def __init__(self, suite, exclude_pattern):
3208
 
        TestDecorator.__init__(self, suite)
3209
 
        self.exclude_pattern = exclude_pattern
3210
 
        self.excluded = False
3211
 
 
3212
 
    def __iter__(self):
3213
 
        if self.excluded:
3214
 
            return iter(self._tests)
3215
 
        self.excluded = True
3216
 
        suite = exclude_tests_by_re(self, self.exclude_pattern)
3217
 
        del self._tests[:]
3218
 
        self.addTests(suite)
3219
 
        return iter(self._tests)
 
3443
        super(ExcludeDecorator, self).__init__(
 
3444
            exclude_tests_by_re(suite, exclude_pattern))
3220
3445
 
3221
3446
 
3222
3447
class FilterTestsDecorator(TestDecorator):
3223
3448
    """A decorator which filters tests to those matching a pattern."""
3224
3449
 
3225
3450
    def __init__(self, suite, pattern):
3226
 
        TestDecorator.__init__(self, suite)
3227
 
        self.pattern = pattern
3228
 
        self.filtered = False
3229
 
 
3230
 
    def __iter__(self):
3231
 
        if self.filtered:
3232
 
            return iter(self._tests)
3233
 
        self.filtered = True
3234
 
        suite = filter_suite_by_re(self, self.pattern)
3235
 
        del self._tests[:]
3236
 
        self.addTests(suite)
3237
 
        return iter(self._tests)
 
3451
        super(FilterTestsDecorator, self).__init__(
 
3452
            filter_suite_by_re(suite, pattern))
3238
3453
 
3239
3454
 
3240
3455
class RandomDecorator(TestDecorator):
3241
3456
    """A decorator which randomises the order of its tests."""
3242
3457
 
3243
3458
    def __init__(self, suite, random_seed, stream):
3244
 
        TestDecorator.__init__(self, suite)
3245
 
        self.random_seed = random_seed
3246
 
        self.randomised = False
3247
 
        self.stream = stream
3248
 
 
3249
 
    def __iter__(self):
3250
 
        if self.randomised:
3251
 
            return iter(self._tests)
3252
 
        self.randomised = True
3253
 
        self.stream.write("Randomizing test order using seed %s\n\n" %
3254
 
            (self.actual_seed()))
 
3459
        random_seed = self.actual_seed(random_seed)
 
3460
        stream.write("Randomizing test order using seed %s\n\n" %
 
3461
            (random_seed,))
3255
3462
        # Initialise the random number generator.
3256
 
        random.seed(self.actual_seed())
3257
 
        suite = randomize_suite(self)
3258
 
        del self._tests[:]
3259
 
        self.addTests(suite)
3260
 
        return iter(self._tests)
 
3463
        random.seed(random_seed)
 
3464
        super(RandomDecorator, self).__init__(randomize_suite(suite))
3261
3465
 
3262
 
    def actual_seed(self):
3263
 
        if self.random_seed == "now":
 
3466
    @staticmethod
 
3467
    def actual_seed(seed):
 
3468
        if seed == "now":
3264
3469
            # We convert the seed to a long to make it reuseable across
3265
3470
            # invocations (because the user can reenter it).
3266
 
            self.random_seed = long(time.time())
 
3471
            return long(time.time())
3267
3472
        else:
3268
3473
            # Convert the seed to a long if we can
3269
3474
            try:
3270
 
                self.random_seed = long(self.random_seed)
3271
 
            except:
 
3475
                return long(seed)
 
3476
            except (TypeError, ValueError):
3272
3477
                pass
3273
 
        return self.random_seed
 
3478
        return seed
3274
3479
 
3275
3480
 
3276
3481
class TestFirstDecorator(TestDecorator):
3277
3482
    """A decorator which moves named tests to the front."""
3278
3483
 
3279
3484
    def __init__(self, suite, pattern):
3280
 
        TestDecorator.__init__(self, suite)
3281
 
        self.pattern = pattern
3282
 
        self.filtered = False
3283
 
 
3284
 
    def __iter__(self):
3285
 
        if self.filtered:
3286
 
            return iter(self._tests)
3287
 
        self.filtered = True
3288
 
        suites = split_suite_by_re(self, self.pattern)
3289
 
        del self._tests[:]
3290
 
        self.addTests(suites)
3291
 
        return iter(self._tests)
 
3485
        super(TestFirstDecorator, self).__init__()
 
3486
        self.addTests(split_suite_by_re(suite, pattern))
3292
3487
 
3293
3488
 
3294
3489
def partition_tests(suite, count):
3326
3521
    """
3327
3522
    concurrency = osutils.local_concurrency()
3328
3523
    result = []
3329
 
    from subunit import TestProtocolClient, ProtocolTestCase
 
3524
    from subunit import ProtocolTestCase
3330
3525
    from subunit.test_results import AutoTimingTestResultDecorator
3331
3526
    class TestInOtherProcess(ProtocolTestCase):
3332
3527
        # Should be in subunit, I think. RBC.
3338
3533
            try:
3339
3534
                ProtocolTestCase.run(self, result)
3340
3535
            finally:
3341
 
                os.waitpid(self.pid, 0)
 
3536
                pid, status = os.waitpid(self.pid, 0)
 
3537
            # GZ 2011-10-18: If status is nonzero, should report to the result
 
3538
            #                that something went wrong.
3342
3539
 
3343
3540
    test_blocks = partition_tests(suite, concurrency)
 
3541
    # Clear the tests from the original suite so it doesn't keep them alive
 
3542
    suite._tests[:] = []
3344
3543
    for process_tests in test_blocks:
3345
 
        process_suite = TestUtil.TestSuite()
3346
 
        process_suite.addTests(process_tests)
 
3544
        process_suite = TestUtil.TestSuite(process_tests)
 
3545
        # Also clear each split list so new suite has only reference
 
3546
        process_tests[:] = []
3347
3547
        c2pread, c2pwrite = os.pipe()
3348
3548
        pid = os.fork()
3349
3549
        if pid == 0:
3350
 
            workaround_zealous_crypto_random()
3351
3550
            try:
 
3551
                stream = os.fdopen(c2pwrite, 'wb', 1)
 
3552
                workaround_zealous_crypto_random()
3352
3553
                os.close(c2pread)
3353
3554
                # Leave stderr and stdout open so we can see test noise
3354
3555
                # Close stdin so that the child goes away if it decides to
3355
3556
                # read from stdin (otherwise its a roulette to see what
3356
3557
                # child actually gets keystrokes for pdb etc).
3357
3558
                sys.stdin.close()
3358
 
                sys.stdin = None
3359
 
                stream = os.fdopen(c2pwrite, 'wb', 1)
3360
3559
                subunit_result = AutoTimingTestResultDecorator(
3361
 
                    TestProtocolClient(stream))
 
3560
                    SubUnitBzrProtocolClient(stream))
3362
3561
                process_suite.run(subunit_result)
3363
 
            finally:
3364
 
                os._exit(0)
 
3562
            except:
 
3563
                # Try and report traceback on stream, but exit with error even
 
3564
                # if stream couldn't be created or something else goes wrong.
 
3565
                # The traceback is formatted to a string and written in one go
 
3566
                # to avoid interleaving lines from multiple failing children.
 
3567
                try:
 
3568
                    stream.write(traceback.format_exc())
 
3569
                finally:
 
3570
                    os._exit(1)
 
3571
            os._exit(0)
3365
3572
        else:
3366
3573
            os.close(c2pwrite)
3367
3574
            stream = os.fdopen(c2pread, 'rb', 1)
3473
3680
#                           with proper exclusion rules.
3474
3681
#   -Ethreads               Will display thread ident at creation/join time to
3475
3682
#                           help track thread leaks
 
3683
#   -Euncollected_cases     Display the identity of any test cases that weren't
 
3684
#                           deallocated after being completed.
 
3685
#   -Econfig_stats          Will collect statistics using addDetail
3476
3686
selftest_debug_flags = set()
3477
3687
 
3478
3688
 
3582
3792
 
3583
3793
    :return: (absents, duplicates) absents is a list containing the test found
3584
3794
        in id_list but not in test_suite, duplicates is a list containing the
3585
 
        test found multiple times in test_suite.
 
3795
        tests found multiple times in test_suite.
3586
3796
 
3587
3797
    When using a prefined test id list, it may occurs that some tests do not
3588
3798
    exist anymore or that some tests use the same id. This function warns the
3712
3922
        'bzrlib.doc',
3713
3923
        'bzrlib.tests.blackbox',
3714
3924
        'bzrlib.tests.commands',
3715
 
        'bzrlib.tests.doc_generate',
3716
3925
        'bzrlib.tests.per_branch',
3717
3926
        'bzrlib.tests.per_bzrdir',
3718
3927
        'bzrlib.tests.per_controldir',
3782
3991
        'bzrlib.tests.test_email_message',
3783
3992
        'bzrlib.tests.test_eol_filters',
3784
3993
        'bzrlib.tests.test_errors',
 
3994
        'bzrlib.tests.test_estimate_compressed_size',
3785
3995
        'bzrlib.tests.test_export',
 
3996
        'bzrlib.tests.test_export_pot',
3786
3997
        'bzrlib.tests.test_extract',
 
3998
        'bzrlib.tests.test_features',
3787
3999
        'bzrlib.tests.test_fetch',
3788
4000
        'bzrlib.tests.test_fixtures',
3789
4001
        'bzrlib.tests.test_fifo_cache',
3790
4002
        'bzrlib.tests.test_filters',
 
4003
        'bzrlib.tests.test_filter_tree',
3791
4004
        'bzrlib.tests.test_ftp_transport',
3792
4005
        'bzrlib.tests.test_foreign',
3793
4006
        'bzrlib.tests.test_generate_docs',
3802
4015
        'bzrlib.tests.test_http',
3803
4016
        'bzrlib.tests.test_http_response',
3804
4017
        'bzrlib.tests.test_https_ca_bundle',
 
4018
        'bzrlib.tests.test_https_urllib',
 
4019
        'bzrlib.tests.test_i18n',
3805
4020
        'bzrlib.tests.test_identitymap',
3806
4021
        'bzrlib.tests.test_ignores',
3807
4022
        'bzrlib.tests.test_index',
3855
4070
        'bzrlib.tests.test_revisiontree',
3856
4071
        'bzrlib.tests.test_rio',
3857
4072
        'bzrlib.tests.test_rules',
 
4073
        'bzrlib.tests.test_url_policy_open',
3858
4074
        'bzrlib.tests.test_sampler',
3859
4075
        'bzrlib.tests.test_scenarios',
3860
4076
        'bzrlib.tests.test_script',
3867
4083
        'bzrlib.tests.test_smart',
3868
4084
        'bzrlib.tests.test_smart_add',
3869
4085
        'bzrlib.tests.test_smart_request',
 
4086
        'bzrlib.tests.test_smart_signals',
3870
4087
        'bzrlib.tests.test_smart_transport',
3871
4088
        'bzrlib.tests.test_smtp_connection',
3872
4089
        'bzrlib.tests.test_source',
3899
4116
        'bzrlib.tests.test_upgrade',
3900
4117
        'bzrlib.tests.test_upgrade_stacked',
3901
4118
        'bzrlib.tests.test_urlutils',
 
4119
        'bzrlib.tests.test_utextwrap',
3902
4120
        'bzrlib.tests.test_version',
3903
4121
        'bzrlib.tests.test_version_info',
3904
4122
        'bzrlib.tests.test_versionedfile',
 
4123
        'bzrlib.tests.test_vf_search',
3905
4124
        'bzrlib.tests.test_weave',
3906
4125
        'bzrlib.tests.test_whitebox',
3907
4126
        'bzrlib.tests.test_win32utils',
4122
4341
    """Copy test and apply scenario to it.
4123
4342
 
4124
4343
    :param test: A test to adapt.
4125
 
    :param scenario: A tuple describing the scenarion.
 
4344
    :param scenario: A tuple describing the scenario.
4126
4345
        The first element of the tuple is the new test id.
4127
4346
        The second element is a dict containing attributes to set on the
4128
4347
        test.
4182
4401
        the module is available.
4183
4402
    """
4184
4403
 
 
4404
    from bzrlib.tests.features import ModuleAvailableFeature
4185
4405
    py_module = pyutils.get_named_object(py_module_name)
4186
4406
    scenarios = [
4187
4407
        ('python', {'module': py_module}),
4228
4448
                         % (os.path.basename(dirname), printable_e))
4229
4449
 
4230
4450
 
4231
 
class Feature(object):
4232
 
    """An operating system Feature."""
4233
 
 
4234
 
    def __init__(self):
4235
 
        self._available = None
4236
 
 
4237
 
    def available(self):
4238
 
        """Is the feature available?
4239
 
 
4240
 
        :return: True if the feature is available.
4241
 
        """
4242
 
        if self._available is None:
4243
 
            self._available = self._probe()
4244
 
        return self._available
4245
 
 
4246
 
    def _probe(self):
4247
 
        """Implement this method in concrete features.
4248
 
 
4249
 
        :return: True if the feature is available.
4250
 
        """
4251
 
        raise NotImplementedError
4252
 
 
4253
 
    def __str__(self):
4254
 
        if getattr(self, 'feature_name', None):
4255
 
            return self.feature_name()
4256
 
        return self.__class__.__name__
4257
 
 
4258
 
 
4259
 
class _SymlinkFeature(Feature):
4260
 
 
4261
 
    def _probe(self):
4262
 
        return osutils.has_symlinks()
4263
 
 
4264
 
    def feature_name(self):
4265
 
        return 'symlinks'
4266
 
 
4267
 
SymlinkFeature = _SymlinkFeature()
4268
 
 
4269
 
 
4270
 
class _HardlinkFeature(Feature):
4271
 
 
4272
 
    def _probe(self):
4273
 
        return osutils.has_hardlinks()
4274
 
 
4275
 
    def feature_name(self):
4276
 
        return 'hardlinks'
4277
 
 
4278
 
HardlinkFeature = _HardlinkFeature()
4279
 
 
4280
 
 
4281
 
class _OsFifoFeature(Feature):
4282
 
 
4283
 
    def _probe(self):
4284
 
        return getattr(os, 'mkfifo', None)
4285
 
 
4286
 
    def feature_name(self):
4287
 
        return 'filesystem fifos'
4288
 
 
4289
 
OsFifoFeature = _OsFifoFeature()
4290
 
 
4291
 
 
4292
 
class _UnicodeFilenameFeature(Feature):
4293
 
    """Does the filesystem support Unicode filenames?"""
4294
 
 
4295
 
    def _probe(self):
4296
 
        try:
4297
 
            # Check for character combinations unlikely to be covered by any
4298
 
            # single non-unicode encoding. We use the characters
4299
 
            # - greek small letter alpha (U+03B1) and
4300
 
            # - braille pattern dots-123456 (U+283F).
4301
 
            os.stat(u'\u03b1\u283f')
4302
 
        except UnicodeEncodeError:
4303
 
            return False
4304
 
        except (IOError, OSError):
4305
 
            # The filesystem allows the Unicode filename but the file doesn't
4306
 
            # exist.
4307
 
            return True
4308
 
        else:
4309
 
            # The filesystem allows the Unicode filename and the file exists,
4310
 
            # for some reason.
4311
 
            return True
4312
 
 
4313
 
UnicodeFilenameFeature = _UnicodeFilenameFeature()
4314
 
 
4315
 
 
4316
 
class _CompatabilityThunkFeature(Feature):
4317
 
    """This feature is just a thunk to another feature.
4318
 
 
4319
 
    It issues a deprecation warning if it is accessed, to let you know that you
4320
 
    should really use a different feature.
4321
 
    """
4322
 
 
4323
 
    def __init__(self, dep_version, module, name,
4324
 
                 replacement_name, replacement_module=None):
4325
 
        super(_CompatabilityThunkFeature, self).__init__()
4326
 
        self._module = module
4327
 
        if replacement_module is None:
4328
 
            replacement_module = module
4329
 
        self._replacement_module = replacement_module
4330
 
        self._name = name
4331
 
        self._replacement_name = replacement_name
4332
 
        self._dep_version = dep_version
4333
 
        self._feature = None
4334
 
 
4335
 
    def _ensure(self):
4336
 
        if self._feature is None:
4337
 
            depr_msg = self._dep_version % ('%s.%s'
4338
 
                                            % (self._module, self._name))
4339
 
            use_msg = ' Use %s.%s instead.' % (self._replacement_module,
4340
 
                                               self._replacement_name)
4341
 
            symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning)
4342
 
            # Import the new feature and use it as a replacement for the
4343
 
            # deprecated one.
4344
 
            self._feature = pyutils.get_named_object(
4345
 
                self._replacement_module, self._replacement_name)
4346
 
 
4347
 
    def _probe(self):
4348
 
        self._ensure()
4349
 
        return self._feature._probe()
4350
 
 
4351
 
 
4352
 
class ModuleAvailableFeature(Feature):
4353
 
    """This is a feature than describes a module we want to be available.
4354
 
 
4355
 
    Declare the name of the module in __init__(), and then after probing, the
4356
 
    module will be available as 'self.module'.
4357
 
 
4358
 
    :ivar module: The module if it is available, else None.
4359
 
    """
4360
 
 
4361
 
    def __init__(self, module_name):
4362
 
        super(ModuleAvailableFeature, self).__init__()
4363
 
        self.module_name = module_name
4364
 
 
4365
 
    def _probe(self):
4366
 
        try:
4367
 
            self._module = __import__(self.module_name, {}, {}, [''])
4368
 
            return True
4369
 
        except ImportError:
4370
 
            return False
4371
 
 
4372
 
    @property
4373
 
    def module(self):
4374
 
        if self.available(): # Make sure the probe has been done
4375
 
            return self._module
4376
 
        return None
4377
 
 
4378
 
    def feature_name(self):
4379
 
        return self.module_name
4380
 
 
4381
 
 
4382
4451
def probe_unicode_in_user_encoding():
4383
4452
    """Try to encode several unicode strings to use in unicode-aware tests.
4384
4453
    Return first successfull match.
4412
4481
    return None
4413
4482
 
4414
4483
 
4415
 
class _HTTPSServerFeature(Feature):
4416
 
    """Some tests want an https Server, check if one is available.
4417
 
 
4418
 
    Right now, the only way this is available is under python2.6 which provides
4419
 
    an ssl module.
4420
 
    """
4421
 
 
4422
 
    def _probe(self):
4423
 
        try:
4424
 
            import ssl
4425
 
            return True
4426
 
        except ImportError:
4427
 
            return False
4428
 
 
4429
 
    def feature_name(self):
4430
 
        return 'HTTPSServer'
4431
 
 
4432
 
 
4433
 
HTTPSServerFeature = _HTTPSServerFeature()
4434
 
 
4435
 
 
4436
 
class _UnicodeFilename(Feature):
4437
 
    """Does the filesystem support Unicode filenames?"""
4438
 
 
4439
 
    def _probe(self):
4440
 
        try:
4441
 
            os.stat(u'\u03b1')
4442
 
        except UnicodeEncodeError:
4443
 
            return False
4444
 
        except (IOError, OSError):
4445
 
            # The filesystem allows the Unicode filename but the file doesn't
4446
 
            # exist.
4447
 
            return True
4448
 
        else:
4449
 
            # The filesystem allows the Unicode filename and the file exists,
4450
 
            # for some reason.
4451
 
            return True
4452
 
 
4453
 
UnicodeFilename = _UnicodeFilename()
4454
 
 
4455
 
 
4456
 
class _ByteStringNamedFilesystem(Feature):
4457
 
    """Is the filesystem based on bytes?"""
4458
 
 
4459
 
    def _probe(self):
4460
 
        if os.name == "posix":
4461
 
            return True
4462
 
        return False
4463
 
 
4464
 
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
4465
 
 
4466
 
 
4467
 
class _UTF8Filesystem(Feature):
4468
 
    """Is the filesystem UTF-8?"""
4469
 
 
4470
 
    def _probe(self):
4471
 
        if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
4472
 
            return True
4473
 
        return False
4474
 
 
4475
 
UTF8Filesystem = _UTF8Filesystem()
4476
 
 
4477
 
 
4478
 
class _BreakinFeature(Feature):
4479
 
    """Does this platform support the breakin feature?"""
4480
 
 
4481
 
    def _probe(self):
4482
 
        from bzrlib import breakin
4483
 
        if breakin.determine_signal() is None:
4484
 
            return False
4485
 
        if sys.platform == 'win32':
4486
 
            # Windows doesn't have os.kill, and we catch the SIGBREAK signal.
4487
 
            # We trigger SIGBREAK via a Console api so we need ctypes to
4488
 
            # access the function
4489
 
            try:
4490
 
                import ctypes
4491
 
            except OSError:
4492
 
                return False
4493
 
        return True
4494
 
 
4495
 
    def feature_name(self):
4496
 
        return "SIGQUIT or SIGBREAK w/ctypes on win32"
4497
 
 
4498
 
 
4499
 
BreakinFeature = _BreakinFeature()
4500
 
 
4501
 
 
4502
 
class _CaseInsCasePresFilenameFeature(Feature):
4503
 
    """Is the file-system case insensitive, but case-preserving?"""
4504
 
 
4505
 
    def _probe(self):
4506
 
        fileno, name = tempfile.mkstemp(prefix='MixedCase')
4507
 
        try:
4508
 
            # first check truly case-preserving for created files, then check
4509
 
            # case insensitive when opening existing files.
4510
 
            name = osutils.normpath(name)
4511
 
            base, rel = osutils.split(name)
4512
 
            found_rel = osutils.canonical_relpath(base, name)
4513
 
            return (found_rel == rel
4514
 
                    and os.path.isfile(name.upper())
4515
 
                    and os.path.isfile(name.lower()))
4516
 
        finally:
4517
 
            os.close(fileno)
4518
 
            os.remove(name)
4519
 
 
4520
 
    def feature_name(self):
4521
 
        return "case-insensitive case-preserving filesystem"
4522
 
 
4523
 
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
4524
 
 
4525
 
 
4526
 
class _CaseInsensitiveFilesystemFeature(Feature):
4527
 
    """Check if underlying filesystem is case-insensitive but *not* case
4528
 
    preserving.
4529
 
    """
4530
 
    # Note that on Windows, Cygwin, MacOS etc, the file-systems are far
4531
 
    # more likely to be case preserving, so this case is rare.
4532
 
 
4533
 
    def _probe(self):
4534
 
        if CaseInsCasePresFilenameFeature.available():
4535
 
            return False
4536
 
 
4537
 
        if TestCaseWithMemoryTransport.TEST_ROOT is None:
4538
 
            root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
4539
 
            TestCaseWithMemoryTransport.TEST_ROOT = root
4540
 
        else:
4541
 
            root = TestCaseWithMemoryTransport.TEST_ROOT
4542
 
        tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
4543
 
            dir=root)
4544
 
        name_a = osutils.pathjoin(tdir, 'a')
4545
 
        name_A = osutils.pathjoin(tdir, 'A')
4546
 
        os.mkdir(name_a)
4547
 
        result = osutils.isdir(name_A)
4548
 
        _rmtree_temp_dir(tdir)
4549
 
        return result
4550
 
 
4551
 
    def feature_name(self):
4552
 
        return 'case-insensitive filesystem'
4553
 
 
4554
 
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
4555
 
 
4556
 
 
4557
 
class _CaseSensitiveFilesystemFeature(Feature):
4558
 
 
4559
 
    def _probe(self):
4560
 
        if CaseInsCasePresFilenameFeature.available():
4561
 
            return False
4562
 
        elif CaseInsensitiveFilesystemFeature.available():
4563
 
            return False
4564
 
        else:
4565
 
            return True
4566
 
 
4567
 
    def feature_name(self):
4568
 
        return 'case-sensitive filesystem'
4569
 
 
4570
 
# new coding style is for feature instances to be lowercase
4571
 
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
4572
 
 
4573
 
 
4574
4484
# Only define SubUnitBzrRunner if subunit is available.
4575
4485
try:
4576
4486
    from subunit import TestProtocolClient
4577
4487
    from subunit.test_results import AutoTimingTestResultDecorator
4578
4488
    class SubUnitBzrProtocolClient(TestProtocolClient):
4579
4489
 
 
4490
        def stopTest(self, test):
 
4491
            super(SubUnitBzrProtocolClient, self).stopTest(test)
 
4492
            _clear__type_equality_funcs(test)
 
4493
 
4580
4494
        def addSuccess(self, test, details=None):
4581
4495
            # The subunit client always includes the details in the subunit
4582
4496
            # stream, but we don't want to include it in ours.
4594
4508
except ImportError:
4595
4509
    pass
4596
4510
 
4597
 
class _PosixPermissionsFeature(Feature):
4598
 
 
4599
 
    def _probe(self):
4600
 
        def has_perms():
4601
 
            # create temporary file and check if specified perms are maintained.
4602
 
            import tempfile
4603
 
 
4604
 
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
4605
 
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
4606
 
            fd, name = f
4607
 
            os.close(fd)
4608
 
            os.chmod(name, write_perms)
4609
 
 
4610
 
            read_perms = os.stat(name).st_mode & 0777
4611
 
            os.unlink(name)
4612
 
            return (write_perms == read_perms)
4613
 
 
4614
 
        return (os.name == 'posix') and has_perms()
4615
 
 
4616
 
    def feature_name(self):
4617
 
        return 'POSIX permissions support'
4618
 
 
4619
 
posix_permissions_feature = _PosixPermissionsFeature()
 
4511
 
 
4512
# API compatibility for old plugins; see bug 892622.
 
4513
for name in [
 
4514
    'Feature',
 
4515
    'HTTPServerFeature', 
 
4516
    'ModuleAvailableFeature',
 
4517
    'HTTPSServerFeature', 'SymlinkFeature', 'HardlinkFeature',
 
4518
    'OsFifoFeature', 'UnicodeFilenameFeature',
 
4519
    'ByteStringNamedFilesystem', 'UTF8Filesystem',
 
4520
    'BreakinFeature', 'CaseInsCasePresFilenameFeature',
 
4521
    'CaseInsensitiveFilesystemFeature', 'case_sensitive_filesystem_feature',
 
4522
    'posix_permissions_feature',
 
4523
    ]:
 
4524
    globals()[name] = _CompatabilityThunkFeature(
 
4525
        symbol_versioning.deprecated_in((2, 5, 0)),
 
4526
        'bzrlib.tests', name,
 
4527
        name, 'bzrlib.tests.features')
 
4528
 
 
4529
 
 
4530
for (old_name, new_name) in [
 
4531
    ('UnicodeFilename', 'UnicodeFilenameFeature'),
 
4532
    ]:
 
4533
    globals()[name] = _CompatabilityThunkFeature(
 
4534
        symbol_versioning.deprecated_in((2, 5, 0)),
 
4535
        'bzrlib.tests', old_name,
 
4536
        new_name, 'bzrlib.tests.features')