~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Patch Queue Manager
  • Date: 2011-09-22 14:12:18 UTC
  • mfrom: (6155.3.1 jam)
  • Revision ID: pqm@pqm.ubuntu.com-20110922141218-86s4uu6nqvourw4f
(jameinel) Cleanup comments bzrlib/smart/__init__.py (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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)
24
 
 
25
19
# NOTE: Some classes in here use camelCaseNaming() rather than
26
20
# underscore_naming().  That's for consistency with unittest; it's not the
27
21
# general style of bzrlib.  Please continue that consistency when adding e.g.
55
49
import testtools
56
50
# nb: check this before importing anything else from within it
57
51
_testtools_version = getattr(testtools, '__version__', ())
58
 
if _testtools_version < (0, 9, 2):
59
 
    raise ImportError("need at least testtools 0.9.2: %s is %r"
 
52
if _testtools_version < (0, 9, 5):
 
53
    raise ImportError("need at least testtools 0.9.5: %s is %r"
60
54
        % (testtools.__file__, _testtools_version))
61
55
from testtools import content
62
56
 
 
57
import bzrlib
63
58
from bzrlib import (
64
59
    branchbuilder,
65
60
    bzrdir,
66
61
    chk_map,
 
62
    commands as _mod_commands,
67
63
    config,
 
64
    i18n,
68
65
    debug,
69
66
    errors,
70
67
    hooks,
71
68
    lock as _mod_lock,
 
69
    lockdir,
72
70
    memorytree,
73
71
    osutils,
 
72
    plugin as _mod_plugin,
 
73
    pyutils,
74
74
    ui,
75
75
    urlutils,
76
76
    registry,
 
77
    symbol_versioning,
 
78
    trace,
77
79
    transport as _mod_transport,
78
80
    workingtree,
79
81
    )
80
 
import bzrlib.branch
81
 
import bzrlib.commands
82
 
import bzrlib.timestamp
83
 
import bzrlib.export
84
 
import bzrlib.inventory
85
 
import bzrlib.iterablefile
86
 
import bzrlib.lockdir
87
82
try:
88
83
    import bzrlib.lsprof
89
84
except ImportError:
90
85
    # lsprof not available
91
86
    pass
92
 
from bzrlib.merge import merge_inner
93
 
import bzrlib.merge3
94
 
import bzrlib.plugin
95
 
from bzrlib.smart import client, request, server
96
 
import bzrlib.store
97
 
from bzrlib import symbol_versioning
 
87
from bzrlib.smart import client, request
 
88
from bzrlib.transport import (
 
89
    memory,
 
90
    pathfilter,
 
91
    )
98
92
from bzrlib.symbol_versioning import (
99
 
    DEPRECATED_PARAMETER,
100
93
    deprecated_function,
101
94
    deprecated_in,
102
 
    deprecated_method,
103
 
    deprecated_passed,
104
 
    )
105
 
import bzrlib.trace
106
 
from bzrlib.transport import (
107
 
    memory,
108
 
    pathfilter,
109
 
    )
110
 
from bzrlib.trace import mutter, note
 
95
    )
111
96
from bzrlib.tests import (
112
97
    test_server,
113
98
    TestUtil,
115
100
    )
116
101
from bzrlib.ui import NullProgressView
117
102
from bzrlib.ui.text import TextUIFactory
118
 
import bzrlib.version_info_formats.format_custom
119
 
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
120
103
 
121
104
# Mark this python module as being part of the implementation
122
105
# of unittest: this gives us better tracebacks where the last
139
122
TestSuite = TestUtil.TestSuite
140
123
TestLoader = TestUtil.TestLoader
141
124
 
 
125
# Tests should run in a clean and clearly defined environment. The goal is to
 
126
# keep them isolated from the running environment as mush as possible. The test
 
127
# framework ensures the variables defined below are set (or deleted if the
 
128
# value is None) before a test is run and reset to their original value after
 
129
# the test is run. Generally if some code depends on an environment variable,
 
130
# the tests should start without this variable in the environment. There are a
 
131
# few exceptions but you shouldn't violate this rule lightly.
 
132
isolated_environ = {
 
133
    'BZR_HOME': None,
 
134
    'HOME': None,
 
135
    # bzr now uses the Win32 API and doesn't rely on APPDATA, but the
 
136
    # tests do check our impls match APPDATA
 
137
    'BZR_EDITOR': None, # test_msgeditor manipulates this variable
 
138
    'VISUAL': None,
 
139
    'EDITOR': None,
 
140
    'BZR_EMAIL': None,
 
141
    'BZREMAIL': None, # may still be present in the environment
 
142
    'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
 
143
    'BZR_PROGRESS_BAR': None,
 
144
    # This should trap leaks to ~/.bzr.log. This occurs when tests use TestCase
 
145
    # as a base class instead of TestCaseInTempDir. Tests inheriting from
 
146
    # TestCase should not use disk resources, BZR_LOG is one.
 
147
    'BZR_LOG': '/you-should-use-TestCaseInTempDir-if-you-need-a-log-file',
 
148
    'BZR_PLUGIN_PATH': None,
 
149
    'BZR_DISABLE_PLUGINS': None,
 
150
    'BZR_PLUGINS_AT': None,
 
151
    'BZR_CONCURRENCY': None,
 
152
    # Make sure that any text ui tests are consistent regardless of
 
153
    # the environment the test case is run in; you may want tests that
 
154
    # test other combinations.  'dumb' is a reasonable guess for tests
 
155
    # going to a pipe or a StringIO.
 
156
    'TERM': 'dumb',
 
157
    'LINES': '25',
 
158
    'COLUMNS': '80',
 
159
    'BZR_COLUMNS': '80',
 
160
    # Disable SSH Agent
 
161
    'SSH_AUTH_SOCK': None,
 
162
    # Proxies
 
163
    'http_proxy': None,
 
164
    'HTTP_PROXY': None,
 
165
    'https_proxy': None,
 
166
    'HTTPS_PROXY': None,
 
167
    'no_proxy': None,
 
168
    'NO_PROXY': None,
 
169
    'all_proxy': None,
 
170
    'ALL_PROXY': None,
 
171
    # Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
 
172
    # least. If you do (care), please update this comment
 
173
    # -- vila 20080401
 
174
    'ftp_proxy': None,
 
175
    'FTP_PROXY': None,
 
176
    'BZR_REMOTE_PATH': None,
 
177
    # Generally speaking, we don't want apport reporting on crashes in
 
178
    # the test envirnoment unless we're specifically testing apport,
 
179
    # so that it doesn't leak into the real system environment.  We
 
180
    # use an env var so it propagates to subprocesses.
 
181
    'APPORT_DISABLE': '1',
 
182
    }
 
183
 
 
184
 
 
185
def override_os_environ(test, env=None):
 
186
    """Modify os.environ keeping a copy.
 
187
    
 
188
    :param test: A test instance
 
189
 
 
190
    :param env: A dict containing variable definitions to be installed
 
191
    """
 
192
    if env is None:
 
193
        env = isolated_environ
 
194
    test._original_os_environ = dict([(var, value)
 
195
                                      for var, value in os.environ.iteritems()])
 
196
    for var, value in env.iteritems():
 
197
        osutils.set_or_unset_env(var, value)
 
198
        if var not in test._original_os_environ:
 
199
            # The var is new, add it with a value of None, so
 
200
            # restore_os_environ will delete it
 
201
            test._original_os_environ[var] = None
 
202
 
 
203
 
 
204
def restore_os_environ(test):
 
205
    """Restore os.environ to its original state.
 
206
 
 
207
    :param test: A test instance previously passed to override_os_environ.
 
208
    """
 
209
    for var, value in test._original_os_environ.iteritems():
 
210
        # Restore the original value (or delete it if the value has been set to
 
211
        # None in override_os_environ).
 
212
        osutils.set_or_unset_env(var, value)
 
213
 
 
214
 
142
215
class ExtendedTestResult(testtools.TextTestResult):
143
216
    """Accepts, reports and accumulates the results of running tests.
144
217
 
195
268
        self._strict = strict
196
269
        self._first_thread_leaker_id = None
197
270
        self._tests_leaking_threads_count = 0
 
271
        self._traceback_from_test = None
198
272
 
199
273
    def stopTestRun(self):
200
274
        run = self.testsRun
260
334
 
261
335
    def _elapsedTestTimeString(self):
262
336
        """Return a time string for the overall time the current test has taken."""
263
 
        return self._formatTime(time.time() - self._start_time)
 
337
        return self._formatTime(self._delta_to_float(
 
338
            self._now() - self._start_datetime))
264
339
 
265
340
    def _testTimeString(self, testCase):
266
341
        benchmark_time = self._extractBenchmarkTime(testCase)
280
355
        what = re.sub(r'^bzrlib\.tests\.', '', what)
281
356
        return what
282
357
 
 
358
    # GZ 2010-10-04: Cloned tests may end up harmlessly calling this method
 
359
    #                multiple times in a row, because the handler is added for
 
360
    #                each test but the container list is shared between cases.
 
361
    #                See lp:498869 lp:625574 and lp:637725 for background.
 
362
    def _record_traceback_from_test(self, exc_info):
 
363
        """Store the traceback from passed exc_info tuple till"""
 
364
        self._traceback_from_test = exc_info[2]
 
365
 
283
366
    def startTest(self, test):
284
367
        super(ExtendedTestResult, self).startTest(test)
285
368
        if self.count == 0:
288
371
        self.report_test_start(test)
289
372
        test.number = self.count
290
373
        self._recordTestStartTime()
291
 
        # Only check for thread leaks if the test case supports cleanups
292
 
        addCleanup = getattr(test, "addCleanup", None)
293
 
        if addCleanup is not None:
294
 
            addCleanup(self._check_leaked_threads, test)
 
374
        # Make testtools cases give us the real traceback on failure
 
375
        addOnException = getattr(test, "addOnException", None)
 
376
        if addOnException is not None:
 
377
            addOnException(self._record_traceback_from_test)
 
378
        # Only check for thread leaks on bzrlib derived test cases
 
379
        if isinstance(test, TestCase):
 
380
            test.addCleanup(self._check_leaked_threads, test)
 
381
 
 
382
    def stopTest(self, test):
 
383
        super(ExtendedTestResult, self).stopTest(test)
 
384
        # Manually break cycles, means touching various private things but hey
 
385
        getDetails = getattr(test, "getDetails", None)
 
386
        if getDetails is not None:
 
387
            getDetails().clear()
 
388
        # Clear _type_equality_funcs to try to stop TestCase instances
 
389
        # from wasting memory. 'clear' is not available in all Python
 
390
        # versions (bug 809048)
 
391
        type_equality_funcs = getattr(test, "_type_equality_funcs", None)
 
392
        if type_equality_funcs is not None:
 
393
            tef_clear = getattr(type_equality_funcs, "clear", None)
 
394
            if tef_clear is None:
 
395
                tef_instance_dict = getattr(type_equality_funcs, "__dict__", None)
 
396
                if tef_instance_dict is not None:
 
397
                    tef_clear = tef_instance_dict.clear
 
398
            if tef_clear is not None:
 
399
                tef_clear()
 
400
        self._traceback_from_test = None
295
401
 
296
402
    def startTests(self):
297
403
        self.report_tests_starting()
315
421
 
316
422
    def _recordTestStartTime(self):
317
423
        """Record that a test has started."""
318
 
        self._start_time = time.time()
 
424
        self._start_datetime = self._now()
319
425
 
320
426
    def addError(self, test, err):
321
427
        """Tell result that test finished with an error.
323
429
        Called from the TestCase run() method when the test
324
430
        fails with an unexpected error.
325
431
        """
326
 
        self._post_mortem()
 
432
        self._post_mortem(self._traceback_from_test)
327
433
        super(ExtendedTestResult, self).addError(test, err)
328
434
        self.error_count += 1
329
435
        self.report_error(test, err)
336
442
        Called from the TestCase run() method when the test
337
443
        fails because e.g. an assert() method failed.
338
444
        """
339
 
        self._post_mortem()
 
445
        self._post_mortem(self._traceback_from_test)
340
446
        super(ExtendedTestResult, self).addFailure(test, err)
341
447
        self.failure_count += 1
342
448
        self.report_failure(test, err)
362
468
        self.known_failure_count += 1
363
469
        self.report_known_failure(test, err)
364
470
 
 
471
    def addUnexpectedSuccess(self, test, details=None):
 
472
        """Tell result the test unexpectedly passed, counting as a failure
 
473
 
 
474
        When the minimum version of testtools required becomes 0.9.8 this
 
475
        can be updated to use the new handling there.
 
476
        """
 
477
        super(ExtendedTestResult, self).addFailure(test, details=details)
 
478
        self.failure_count += 1
 
479
        self.report_unexpected_success(test,
 
480
            "".join(details["reason"].iter_text()))
 
481
        if self.stop_early:
 
482
            self.stop()
 
483
 
365
484
    def addNotSupported(self, test, feature):
366
485
        """The test will not be run because of a missing feature.
367
486
        """
384
503
        self.not_applicable_count += 1
385
504
        self.report_not_applicable(test, reason)
386
505
 
387
 
    def _post_mortem(self):
 
506
    def _post_mortem(self, tb=None):
388
507
        """Start a PDB post mortem session."""
389
508
        if os.environ.get('BZR_TEST_PDB', None):
390
 
            import pdb;pdb.post_mortem()
 
509
            import pdb
 
510
            pdb.post_mortem(tb)
391
511
 
392
512
    def progress(self, offset, whence):
393
513
        """The test is adjusting the count of tests to run."""
525
645
    def report_known_failure(self, test, err):
526
646
        pass
527
647
 
 
648
    def report_unexpected_success(self, test, reason):
 
649
        self.stream.write('FAIL: %s\n    %s: %s\n' % (
 
650
            self._test_description(test),
 
651
            "Unexpected success. Should have failed",
 
652
            reason,
 
653
            ))
 
654
 
528
655
    def report_skip(self, test, reason):
529
656
        pass
530
657
 
582
709
                % (self._testTimeString(test),
583
710
                   self._error_summary(err)))
584
711
 
 
712
    def report_unexpected_success(self, test, reason):
 
713
        self.stream.write(' FAIL %s\n%s: %s\n'
 
714
                % (self._testTimeString(test),
 
715
                   "Unexpected success. Should have failed",
 
716
                   reason))
 
717
 
585
718
    def report_success(self, test):
586
719
        self.stream.write('   OK %s\n' % self._testTimeString(test))
587
720
        for bench_called, stats in getattr(test, '_benchcalls', []):
634
767
            encode = codec[0]
635
768
        else:
636
769
            encode = codec.encode
637
 
        stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream)
 
770
        # GZ 2010-09-08: Really we don't want to be writing arbitrary bytes,
 
771
        #                so should swap to the plain codecs.StreamWriter
 
772
        stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream,
 
773
            "backslashreplace")
638
774
        stream.encoding = new_encoding
639
775
        self.stream = stream
640
776
        self.descriptions = descriptions
790
926
        return NullProgressView()
791
927
 
792
928
 
 
929
def isolated_doctest_setUp(test):
 
930
    override_os_environ(test)
 
931
 
 
932
 
 
933
def isolated_doctest_tearDown(test):
 
934
    restore_os_environ(test)
 
935
 
 
936
 
 
937
def IsolatedDocTestSuite(*args, **kwargs):
 
938
    """Overrides doctest.DocTestSuite to handle isolation.
 
939
 
 
940
    The method is really a factory and users are expected to use it as such.
 
941
    """
 
942
 
 
943
    kwargs['setUp'] = isolated_doctest_setUp
 
944
    kwargs['tearDown'] = isolated_doctest_tearDown
 
945
    return doctest.DocTestSuite(*args, **kwargs)
 
946
 
 
947
 
793
948
class TestCase(testtools.TestCase):
794
949
    """Base class for bzr unit tests.
795
950
 
827
982
        super(TestCase, self).setUp()
828
983
        for feature in getattr(self, '_test_needs_features', []):
829
984
            self.requireFeature(feature)
830
 
        self._log_contents = None
831
 
        self.addDetail("log", content.Content(content.ContentType("text",
832
 
            "plain", {"charset": "utf8"}),
833
 
            lambda:[self._get_log(keep_log_file=True)]))
834
985
        self._cleanEnvironment()
835
986
        self._silenceUI()
836
987
        self._startLogFile()
840
991
        self._track_transports()
841
992
        self._track_locks()
842
993
        self._clear_debug_flags()
 
994
        # Isolate global verbosity level, to make sure it's reproducible
 
995
        # between tests.  We should get rid of this altogether: bug 656694. --
 
996
        # mbp 20101008
 
997
        self.overrideAttr(bzrlib.trace, '_verbosity_level', 0)
 
998
        # Isolate config option expansion until its default value for bzrlib is
 
999
        # settled on or a the FIXME associated with _get_expand_default_value
 
1000
        # is addressed -- vila 20110219
 
1001
        self.overrideAttr(config, '_expand_default_value', None)
 
1002
        self._log_files = set()
 
1003
        # Each key in the ``_counters`` dict holds a value for a different
 
1004
        # counter. When the test ends, addDetail() should be used to output the
 
1005
        # counter values. This happens in install_counter_hook().
 
1006
        self._counters = {}
 
1007
        if 'config_stats' in selftest_debug_flags:
 
1008
            self._install_config_stats_hooks()
 
1009
        # Do not use i18n for tests (unless the test reverses this)
 
1010
        i18n.disable_i18n()
843
1011
 
844
1012
    def debug(self):
845
1013
        # debug a frame up.
846
1014
        import pdb
847
 
        pdb.Pdb().set_trace(sys._getframe().f_back)
 
1015
        # The sys preserved stdin/stdout should allow blackbox tests debugging
 
1016
        pdb.Pdb(stdin=sys.__stdin__, stdout=sys.__stdout__
 
1017
                ).set_trace(sys._getframe().f_back)
848
1018
 
849
1019
    def discardDetail(self, name):
850
1020
        """Extend the addDetail, getDetails api so we can remove a detail.
862
1032
        if name in details:
863
1033
            del details[name]
864
1034
 
 
1035
    def install_counter_hook(self, hooks, name, counter_name=None):
 
1036
        """Install a counting hook.
 
1037
 
 
1038
        Any hook can be counted as long as it doesn't need to return a value.
 
1039
 
 
1040
        :param hooks: Where the hook should be installed.
 
1041
 
 
1042
        :param name: The hook name that will be counted.
 
1043
 
 
1044
        :param counter_name: The counter identifier in ``_counters``, defaults
 
1045
            to ``name``.
 
1046
        """
 
1047
        _counters = self._counters # Avoid closing over self
 
1048
        if counter_name is None:
 
1049
            counter_name = name
 
1050
        if _counters.has_key(counter_name):
 
1051
            raise AssertionError('%s is already used as a counter name'
 
1052
                                  % (counter_name,))
 
1053
        _counters[counter_name] = 0
 
1054
        self.addDetail(counter_name, content.Content(content.UTF8_TEXT,
 
1055
            lambda: ['%d' % (_counters[counter_name],)]))
 
1056
        def increment_counter(*args, **kwargs):
 
1057
            _counters[counter_name] += 1
 
1058
        label = 'count %s calls' % (counter_name,)
 
1059
        hooks.install_named_hook(name, increment_counter, label)
 
1060
        self.addCleanup(hooks.uninstall_named_hook, name, label)
 
1061
 
 
1062
    def _install_config_stats_hooks(self):
 
1063
        """Install config hooks to count hook calls.
 
1064
 
 
1065
        """
 
1066
        for hook_name in ('get', 'set', 'remove', 'load', 'save'):
 
1067
            self.install_counter_hook(config.ConfigHooks, hook_name,
 
1068
                                       'config.%s' % (hook_name,))
 
1069
 
 
1070
        # The OldConfigHooks are private and need special handling to protect
 
1071
        # against recursive tests (tests that run other tests), so we just do
 
1072
        # manually what registering them into _builtin_known_hooks will provide
 
1073
        # us.
 
1074
        self.overrideAttr(config, 'OldConfigHooks', config._OldConfigHooks())
 
1075
        for hook_name in ('get', 'set', 'remove', 'load', 'save'):
 
1076
            self.install_counter_hook(config.OldConfigHooks, hook_name,
 
1077
                                      'old_config.%s' % (hook_name,))
 
1078
 
865
1079
    def _clear_debug_flags(self):
866
1080
        """Prevent externally set debug flags affecting tests.
867
1081
 
877
1091
 
878
1092
    def _clear_hooks(self):
879
1093
        # prevent hooks affecting tests
 
1094
        known_hooks = hooks.known_hooks
880
1095
        self._preserved_hooks = {}
881
 
        for key, factory in hooks.known_hooks.items():
882
 
            parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
883
 
            current_hooks = hooks.known_hooks_key_to_object(key)
 
1096
        for key, (parent, name) in known_hooks.iter_parent_objects():
 
1097
            current_hooks = getattr(parent, name)
884
1098
            self._preserved_hooks[parent] = (name, current_hooks)
 
1099
        self._preserved_lazy_hooks = hooks._lazy_hooks
 
1100
        hooks._lazy_hooks = {}
885
1101
        self.addCleanup(self._restoreHooks)
886
 
        for key, factory in hooks.known_hooks.items():
887
 
            parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
 
1102
        for key, (parent, name) in known_hooks.iter_parent_objects():
 
1103
            factory = known_hooks.get(key)
888
1104
            setattr(parent, name, factory())
889
1105
        # this hook should always be installed
890
1106
        request._install_hook()
919
1135
        # break some locks on purpose and should be taken into account by
920
1136
        # considering that breaking a lock is just a dirty way of releasing it.
921
1137
        if len(acquired_locks) != (len(released_locks) + len(broken_locks)):
922
 
            message = ('Different number of acquired and '
923
 
                       'released or broken locks. (%s, %s + %s)' %
924
 
                       (acquired_locks, released_locks, broken_locks))
 
1138
            message = (
 
1139
                'Different number of acquired and '
 
1140
                'released or broken locks.\n'
 
1141
                'acquired=%s\n'
 
1142
                'released=%s\n'
 
1143
                'broken=%s\n' %
 
1144
                (acquired_locks, released_locks, broken_locks))
925
1145
            if not self._lock_check_thorough:
926
1146
                # Rather than fail, just warn
927
1147
                print "Broken test %s: %s" % (self, message)
955
1175
 
956
1176
    def permit_dir(self, name):
957
1177
        """Permit a directory to be used by this test. See permit_url."""
958
 
        name_transport = _mod_transport.get_transport(name)
 
1178
        name_transport = _mod_transport.get_transport_from_path(name)
959
1179
        self.permit_url(name)
960
1180
        self.permit_url(name_transport.base)
961
1181
 
1040
1260
        self.addCleanup(transport_server.stop_server)
1041
1261
        # Obtain a real transport because if the server supplies a password, it
1042
1262
        # will be hidden from the base on the client side.
1043
 
        t = _mod_transport.get_transport(transport_server.get_url())
 
1263
        t = _mod_transport.get_transport_from_url(transport_server.get_url())
1044
1264
        # Some transport servers effectively chroot the backing transport;
1045
1265
        # others like SFTPServer don't - users of the transport can walk up the
1046
1266
        # transport to read the entire backing transport. This wouldn't matter
1102
1322
        except UnicodeError, e:
1103
1323
            # If we can't compare without getting a UnicodeError, then
1104
1324
            # obviously they are different
1105
 
            mutter('UnicodeError: %s', e)
 
1325
            trace.mutter('UnicodeError: %s', e)
1106
1326
        if message:
1107
1327
            message += '\n'
1108
1328
        raise AssertionError("%snot equal:\na = %s\nb = %s\n"
1147
1367
                         'st_mtime did not match')
1148
1368
        self.assertEqual(expected.st_ctime, actual.st_ctime,
1149
1369
                         'st_ctime did not match')
1150
 
        if sys.platform != 'win32':
 
1370
        if sys.platform == 'win32':
1151
1371
            # On Win32 both 'dev' and 'ino' cannot be trusted. In python2.4 it
1152
1372
            # is 'dev' that varies, in python 2.5 (6?) it is st_ino that is
1153
 
            # odd. Regardless we shouldn't actually try to assert anything
1154
 
            # about their values
 
1373
            # odd. We just force it to always be 0 to avoid any problems.
 
1374
            self.assertEqual(0, expected.st_dev)
 
1375
            self.assertEqual(0, actual.st_dev)
 
1376
            self.assertEqual(0, expected.st_ino)
 
1377
            self.assertEqual(0, actual.st_ino)
 
1378
        else:
1155
1379
            self.assertEqual(expected.st_dev, actual.st_dev,
1156
1380
                             'st_dev did not match')
1157
1381
            self.assertEqual(expected.st_ino, actual.st_ino,
1166
1390
                length, len(obj_with_len), obj_with_len))
1167
1391
 
1168
1392
    def assertLogsError(self, exception_class, func, *args, **kwargs):
1169
 
        """Assert that func(*args, **kwargs) quietly logs a specific exception.
 
1393
        """Assert that `func(*args, **kwargs)` quietly logs a specific error.
1170
1394
        """
1171
 
        from bzrlib import trace
1172
1395
        captured = []
1173
1396
        orig_log_exception_quietly = trace.log_exception_quietly
1174
1397
        try:
1175
1398
            def capture():
1176
1399
                orig_log_exception_quietly()
1177
 
                captured.append(sys.exc_info())
 
1400
                captured.append(sys.exc_info()[1])
1178
1401
            trace.log_exception_quietly = capture
1179
1402
            func(*args, **kwargs)
1180
1403
        finally:
1181
1404
            trace.log_exception_quietly = orig_log_exception_quietly
1182
1405
        self.assertLength(1, captured)
1183
 
        err = captured[0][1]
 
1406
        err = captured[0]
1184
1407
        self.assertIsInstance(err, exception_class)
1185
1408
        return err
1186
1409
 
1223
1446
        if haystack.find(needle) == -1:
1224
1447
            self.fail("string %r not found in '''%s'''" % (needle, haystack))
1225
1448
 
 
1449
    def assertNotContainsString(self, haystack, needle):
 
1450
        if haystack.find(needle) != -1:
 
1451
            self.fail("string %r found in '''%s'''" % (needle, haystack))
 
1452
 
1226
1453
    def assertSubset(self, sublist, superlist):
1227
1454
        """Assert that every entry in sublist is present in superlist."""
1228
1455
        missing = set(sublist) - set(superlist)
1317
1544
 
1318
1545
    def assertFileEqual(self, content, path):
1319
1546
        """Fail if path does not contain 'content'."""
1320
 
        self.failUnlessExists(path)
 
1547
        self.assertPathExists(path)
1321
1548
        f = file(path, 'rb')
1322
1549
        try:
1323
1550
            s = f.read()
1333
1560
        else:
1334
1561
            self.assertEqual(expected_docstring, obj.__doc__)
1335
1562
 
 
1563
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1336
1564
    def failUnlessExists(self, path):
 
1565
        return self.assertPathExists(path)
 
1566
 
 
1567
    def assertPathExists(self, path):
1337
1568
        """Fail unless path or paths, which may be abs or relative, exist."""
1338
1569
        if not isinstance(path, basestring):
1339
1570
            for p in path:
1340
 
                self.failUnlessExists(p)
 
1571
                self.assertPathExists(p)
1341
1572
        else:
1342
 
            self.failUnless(osutils.lexists(path),path+" does not exist")
 
1573
            self.assertTrue(osutils.lexists(path),
 
1574
                path + " does not exist")
1343
1575
 
 
1576
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1344
1577
    def failIfExists(self, path):
 
1578
        return self.assertPathDoesNotExist(path)
 
1579
 
 
1580
    def assertPathDoesNotExist(self, path):
1345
1581
        """Fail if path or paths, which may be abs or relative, exist."""
1346
1582
        if not isinstance(path, basestring):
1347
1583
            for p in path:
1348
 
                self.failIfExists(p)
 
1584
                self.assertPathDoesNotExist(p)
1349
1585
        else:
1350
 
            self.failIf(osutils.lexists(path),path+" exists")
 
1586
            self.assertFalse(osutils.lexists(path),
 
1587
                path + " exists")
1351
1588
 
1352
1589
    def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
1353
1590
        """A helper for callDeprecated and applyDeprecated.
1379
1616
        not other callers that go direct to the warning module.
1380
1617
 
1381
1618
        To test that a deprecated method raises an error, do something like
1382
 
        this::
 
1619
        this (remember that both assertRaises and applyDeprecated delays *args
 
1620
        and **kwargs passing)::
1383
1621
 
1384
1622
            self.assertRaises(errors.ReservedId,
1385
1623
                self.applyDeprecated,
1467
1705
 
1468
1706
        The file is removed as the test is torn down.
1469
1707
        """
1470
 
        self._log_file = StringIO()
1471
 
        self._log_memento = bzrlib.trace.push_log_file(self._log_file)
 
1708
        pseudo_log_file = StringIO()
 
1709
        def _get_log_contents_for_weird_testtools_api():
 
1710
            return [pseudo_log_file.getvalue().decode(
 
1711
                "utf-8", "replace").encode("utf-8")]
 
1712
        self.addDetail("log", content.Content(content.ContentType("text",
 
1713
            "plain", {"charset": "utf8"}),
 
1714
            _get_log_contents_for_weird_testtools_api))
 
1715
        self._log_file = pseudo_log_file
 
1716
        self._log_memento = trace.push_log_file(self._log_file)
1472
1717
        self.addCleanup(self._finishLogFile)
1473
1718
 
1474
1719
    def _finishLogFile(self):
1475
1720
        """Finished with the log file.
1476
1721
 
1477
 
        Close the file and delete it, unless setKeepLogfile was called.
 
1722
        Close the file and delete it.
1478
1723
        """
1479
 
        if bzrlib.trace._trace_file:
 
1724
        if trace._trace_file:
1480
1725
            # flush the log file, to get all content
1481
 
            bzrlib.trace._trace_file.flush()
1482
 
        bzrlib.trace.pop_log_file(self._log_memento)
1483
 
        # Cache the log result and delete the file on disk
1484
 
        self._get_log(False)
 
1726
            trace._trace_file.flush()
 
1727
        trace.pop_log_file(self._log_memento)
1485
1728
 
1486
1729
    def thisFailsStrictLockCheck(self):
1487
1730
        """It is known that this test would fail with -Dstrict_locks.
1499
1742
    def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1500
1743
        """Overrides an object attribute restoring it after the test.
1501
1744
 
 
1745
        :note: This should be used with discretion; you should think about
 
1746
        whether it's better to make the code testable without monkey-patching.
 
1747
 
1502
1748
        :param obj: The object that will be mutated.
1503
1749
 
1504
1750
        :param attr_name: The attribute name we want to preserve/override in
1515
1761
            setattr(obj, attr_name, new)
1516
1762
        return value
1517
1763
 
 
1764
    def overrideEnv(self, name, new):
 
1765
        """Set an environment variable, and reset it after the test.
 
1766
 
 
1767
        :param name: The environment variable name.
 
1768
 
 
1769
        :param new: The value to set the variable to. If None, the 
 
1770
            variable is deleted from the environment.
 
1771
 
 
1772
        :returns: The actual variable value.
 
1773
        """
 
1774
        value = osutils.set_or_unset_env(name, new)
 
1775
        self.addCleanup(osutils.set_or_unset_env, name, value)
 
1776
        return value
 
1777
 
 
1778
    def recordCalls(self, obj, attr_name):
 
1779
        """Monkeypatch in a wrapper that will record calls.
 
1780
 
 
1781
        The monkeypatch is automatically removed when the test concludes.
 
1782
 
 
1783
        :param obj: The namespace holding the reference to be replaced;
 
1784
            typically a module, class, or object.
 
1785
        :param attr_name: A string for the name of the attribute to 
 
1786
            patch.
 
1787
        :returns: A list that will be extended with one item every time the
 
1788
            function is called, with a tuple of (args, kwargs).
 
1789
        """
 
1790
        calls = []
 
1791
 
 
1792
        def decorator(*args, **kwargs):
 
1793
            calls.append((args, kwargs))
 
1794
            return orig(*args, **kwargs)
 
1795
        orig = self.overrideAttr(obj, attr_name, decorator)
 
1796
        return calls
 
1797
 
1518
1798
    def _cleanEnvironment(self):
1519
 
        new_env = {
1520
 
            'BZR_HOME': None, # Don't inherit BZR_HOME to all the tests.
1521
 
            'HOME': os.getcwd(),
1522
 
            # bzr now uses the Win32 API and doesn't rely on APPDATA, but the
1523
 
            # tests do check our impls match APPDATA
1524
 
            'BZR_EDITOR': None, # test_msgeditor manipulates this variable
1525
 
            'VISUAL': None,
1526
 
            'EDITOR': None,
1527
 
            'BZR_EMAIL': None,
1528
 
            'BZREMAIL': None, # may still be present in the environment
1529
 
            'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
1530
 
            'BZR_PROGRESS_BAR': None,
1531
 
            'BZR_LOG': None,
1532
 
            'BZR_PLUGIN_PATH': None,
1533
 
            'BZR_DISABLE_PLUGINS': None,
1534
 
            'BZR_PLUGINS_AT': None,
1535
 
            'BZR_CONCURRENCY': None,
1536
 
            # Make sure that any text ui tests are consistent regardless of
1537
 
            # the environment the test case is run in; you may want tests that
1538
 
            # test other combinations.  'dumb' is a reasonable guess for tests
1539
 
            # going to a pipe or a StringIO.
1540
 
            'TERM': 'dumb',
1541
 
            'LINES': '25',
1542
 
            'COLUMNS': '80',
1543
 
            'BZR_COLUMNS': '80',
1544
 
            # SSH Agent
1545
 
            'SSH_AUTH_SOCK': None,
1546
 
            # Proxies
1547
 
            'http_proxy': None,
1548
 
            'HTTP_PROXY': None,
1549
 
            'https_proxy': None,
1550
 
            'HTTPS_PROXY': None,
1551
 
            'no_proxy': None,
1552
 
            'NO_PROXY': None,
1553
 
            'all_proxy': None,
1554
 
            'ALL_PROXY': None,
1555
 
            # Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
1556
 
            # least. If you do (care), please update this comment
1557
 
            # -- vila 20080401
1558
 
            'ftp_proxy': None,
1559
 
            'FTP_PROXY': None,
1560
 
            'BZR_REMOTE_PATH': None,
1561
 
            # Generally speaking, we don't want apport reporting on crashes in
1562
 
            # the test envirnoment unless we're specifically testing apport,
1563
 
            # so that it doesn't leak into the real system environment.  We
1564
 
            # use an env var so it propagates to subprocesses.
1565
 
            'APPORT_DISABLE': '1',
1566
 
        }
1567
 
        self._old_env = {}
1568
 
        self.addCleanup(self._restoreEnvironment)
1569
 
        for name, value in new_env.iteritems():
1570
 
            self._captureVar(name, value)
1571
 
 
1572
 
    def _captureVar(self, name, newvalue):
1573
 
        """Set an environment variable, and reset it when finished."""
1574
 
        self._old_env[name] = osutils.set_or_unset_env(name, newvalue)
1575
 
 
1576
 
    def _restoreEnvironment(self):
1577
 
        for name, value in self._old_env.iteritems():
1578
 
            osutils.set_or_unset_env(name, value)
 
1799
        for name, value in isolated_environ.iteritems():
 
1800
            self.overrideEnv(name, value)
1579
1801
 
1580
1802
    def _restoreHooks(self):
1581
1803
        for klass, (name, hooks) in self._preserved_hooks.items():
1582
1804
            setattr(klass, name, hooks)
 
1805
        self._preserved_hooks.clear()
 
1806
        bzrlib.hooks._lazy_hooks = self._preserved_lazy_hooks
 
1807
        self._preserved_lazy_hooks.clear()
1583
1808
 
1584
1809
    def knownFailure(self, reason):
1585
 
        """This test has failed for some known reason."""
1586
 
        raise KnownFailure(reason)
 
1810
        """Declare that this test fails for a known reason
 
1811
 
 
1812
        Tests that are known to fail should generally be using expectedFailure
 
1813
        with an appropriate reverse assertion if a change could cause the test
 
1814
        to start passing. Conversely if the test has no immediate prospect of
 
1815
        succeeding then using skip is more suitable.
 
1816
 
 
1817
        When this method is called while an exception is being handled, that
 
1818
        traceback will be used, otherwise a new exception will be thrown to
 
1819
        provide one but won't be reported.
 
1820
        """
 
1821
        self._add_reason(reason)
 
1822
        try:
 
1823
            exc_info = sys.exc_info()
 
1824
            if exc_info != (None, None, None):
 
1825
                self._report_traceback(exc_info)
 
1826
            else:
 
1827
                try:
 
1828
                    raise self.failureException(reason)
 
1829
                except self.failureException:
 
1830
                    exc_info = sys.exc_info()
 
1831
            # GZ 02-08-2011: Maybe cleanup this err.exc_info attribute too?
 
1832
            raise testtools.testcase._ExpectedFailure(exc_info)
 
1833
        finally:
 
1834
            del exc_info
1587
1835
 
1588
1836
    def _suppress_log(self):
1589
1837
        """Remove the log info from details."""
1675
1923
            self._benchtime += time.time() - start
1676
1924
 
1677
1925
    def log(self, *args):
1678
 
        mutter(*args)
1679
 
 
1680
 
    def _get_log(self, keep_log_file=False):
1681
 
        """Internal helper to get the log from bzrlib.trace for this test.
1682
 
 
1683
 
        Please use self.getDetails, or self.get_log to access this in test case
1684
 
        code.
1685
 
 
1686
 
        :param keep_log_file: When True, if the log is still a file on disk
1687
 
            leave it as a file on disk. When False, if the log is still a file
1688
 
            on disk, the log file is deleted and the log preserved as
1689
 
            self._log_contents.
1690
 
        :return: A string containing the log.
1691
 
        """
1692
 
        if self._log_contents is not None:
1693
 
            try:
1694
 
                self._log_contents.decode('utf8')
1695
 
            except UnicodeDecodeError:
1696
 
                unicodestr = self._log_contents.decode('utf8', 'replace')
1697
 
                self._log_contents = unicodestr.encode('utf8')
1698
 
            return self._log_contents
1699
 
        if self._log_file is not None:
1700
 
            log_contents = self._log_file.getvalue()
1701
 
            try:
1702
 
                log_contents.decode('utf8')
1703
 
            except UnicodeDecodeError:
1704
 
                unicodestr = log_contents.decode('utf8', 'replace')
1705
 
                log_contents = unicodestr.encode('utf8')
1706
 
            if not keep_log_file:
1707
 
                self._log_file = None
1708
 
                # Permit multiple calls to get_log until we clean it up in
1709
 
                # finishLogFile
1710
 
                self._log_contents = log_contents
1711
 
            return log_contents
1712
 
        else:
1713
 
            return "No log file content."
 
1926
        trace.mutter(*args)
1714
1927
 
1715
1928
    def get_log(self):
1716
1929
        """Get a unicode string containing the log from bzrlib.trace.
1766
1979
 
1767
1980
        try:
1768
1981
            try:
1769
 
                result = self.apply_redirected(ui.ui_factory.stdin,
 
1982
                result = self.apply_redirected(
 
1983
                    ui.ui_factory.stdin,
1770
1984
                    stdout, stderr,
1771
 
                    bzrlib.commands.run_bzr_catch_user_errors,
 
1985
                    _mod_commands.run_bzr_catch_user_errors,
1772
1986
                    args)
1773
1987
            except KeyboardInterrupt:
1774
1988
                # Reraise KeyboardInterrupt with contents of redirected stdout
1916
2130
    def start_bzr_subprocess(self, process_args, env_changes=None,
1917
2131
                             skip_if_plan_to_signal=False,
1918
2132
                             working_dir=None,
1919
 
                             allow_plugins=False):
 
2133
                             allow_plugins=False, stderr=subprocess.PIPE):
1920
2134
        """Start bzr in a subprocess for testing.
1921
2135
 
1922
2136
        This starts a new Python interpreter and runs bzr in there.
1934
2148
        :param skip_if_plan_to_signal: raise TestSkipped when true and system
1935
2149
            doesn't support signalling subprocesses.
1936
2150
        :param allow_plugins: If False (default) pass --no-plugins to bzr.
 
2151
        :param stderr: file to use for the subprocess's stderr.  Valid values
 
2152
            are those valid for the stderr argument of `subprocess.Popen`.
 
2153
            Default value is ``subprocess.PIPE``.
1937
2154
 
1938
2155
        :returns: Popen object for the started process.
1939
2156
        """
1965
2182
            # so we will avoid using it on all platforms, just to
1966
2183
            # make sure the code path is used, and we don't break on win32
1967
2184
            cleanup_environment()
 
2185
            # Include the subprocess's log file in the test details, in case
 
2186
            # the test fails due to an error in the subprocess.
 
2187
            self._add_subprocess_log(trace._get_bzr_log_filename())
1968
2188
            command = [sys.executable]
1969
2189
            # frozen executables don't need the path to bzr
1970
2190
            if getattr(sys, "frozen", None) is None:
1974
2194
            command.extend(process_args)
1975
2195
            process = self._popen(command, stdin=subprocess.PIPE,
1976
2196
                                  stdout=subprocess.PIPE,
1977
 
                                  stderr=subprocess.PIPE)
 
2197
                                  stderr=stderr)
1978
2198
        finally:
1979
2199
            restore_environment()
1980
2200
            if cwd is not None:
1982
2202
 
1983
2203
        return process
1984
2204
 
 
2205
    def _add_subprocess_log(self, log_file_path):
 
2206
        if len(self._log_files) == 0:
 
2207
            # Register an addCleanup func.  We do this on the first call to
 
2208
            # _add_subprocess_log rather than in TestCase.setUp so that this
 
2209
            # addCleanup is registered after any cleanups for tempdirs that
 
2210
            # subclasses might create, which will probably remove the log file
 
2211
            # we want to read.
 
2212
            self.addCleanup(self._subprocess_log_cleanup)
 
2213
        # self._log_files is a set, so if a log file is reused we won't grab it
 
2214
        # twice.
 
2215
        self._log_files.add(log_file_path)
 
2216
 
 
2217
    def _subprocess_log_cleanup(self):
 
2218
        for count, log_file_path in enumerate(self._log_files):
 
2219
            # We use buffer_now=True to avoid holding the file open beyond
 
2220
            # the life of this function, which might interfere with e.g.
 
2221
            # cleaning tempdirs on Windows.
 
2222
            # XXX: Testtools 0.9.5 doesn't have the content_from_file helper
 
2223
            #detail_content = content.content_from_file(
 
2224
            #    log_file_path, buffer_now=True)
 
2225
            with open(log_file_path, 'rb') as log_file:
 
2226
                log_file_bytes = log_file.read()
 
2227
            detail_content = content.Content(content.ContentType("text",
 
2228
                "plain", {"charset": "utf8"}), lambda: [log_file_bytes])
 
2229
            self.addDetail("start_bzr_subprocess-log-%d" % (count,),
 
2230
                detail_content)
 
2231
 
1985
2232
    def _popen(self, *args, **kwargs):
1986
2233
        """Place a call to Popen.
1987
2234
 
2024
2271
        if retcode is not None and retcode != process.returncode:
2025
2272
            if process_args is None:
2026
2273
                process_args = "(unknown args)"
2027
 
            mutter('Output of bzr %s:\n%s', process_args, out)
2028
 
            mutter('Error for bzr %s:\n%s', process_args, err)
 
2274
            trace.mutter('Output of bzr %s:\n%s', process_args, out)
 
2275
            trace.mutter('Error for bzr %s:\n%s', process_args, err)
2029
2276
            self.fail('Command bzr %s failed with retcode %s != %s'
2030
2277
                      % (process_args, retcode, process.returncode))
2031
2278
        return [out, err]
2032
2279
 
2033
 
    def check_inventory_shape(self, inv, shape):
2034
 
        """Compare an inventory to a list of expected names.
 
2280
    def check_tree_shape(self, tree, shape):
 
2281
        """Compare a tree to a list of expected names.
2035
2282
 
2036
2283
        Fail if they are not precisely equal.
2037
2284
        """
2038
2285
        extras = []
2039
2286
        shape = list(shape)             # copy
2040
 
        for path, ie in inv.entries():
 
2287
        for path, ie in tree.iter_entries_by_dir():
2041
2288
            name = path.replace('\\', '/')
2042
2289
            if ie.kind == 'directory':
2043
2290
                name = name + '/'
2044
 
            if name in shape:
 
2291
            if name == "/":
 
2292
                pass # ignore root entry
 
2293
            elif name in shape:
2045
2294
                shape.remove(name)
2046
2295
            else:
2047
2296
                extras.append(name)
2088
2337
 
2089
2338
        Tests that expect to provoke LockContention errors should call this.
2090
2339
        """
2091
 
        self.overrideAttr(bzrlib.lockdir, '_DEFAULT_TIMEOUT_SECONDS', 0)
 
2340
        self.overrideAttr(lockdir, '_DEFAULT_TIMEOUT_SECONDS', 0)
2092
2341
 
2093
2342
    def make_utf8_encoded_stringio(self, encoding_type=None):
2094
2343
        """Return a StringIOWrapper instance, that will encode Unicode
2137
2386
class TestCaseWithMemoryTransport(TestCase):
2138
2387
    """Common test class for tests that do not need disk resources.
2139
2388
 
2140
 
    Tests that need disk resources should derive from TestCaseWithTransport.
 
2389
    Tests that need disk resources should derive from TestCaseInTempDir
 
2390
    orTestCaseWithTransport.
2141
2391
 
2142
2392
    TestCaseWithMemoryTransport sets the TEST_ROOT variable for all bzr tests.
2143
2393
 
2144
 
    For TestCaseWithMemoryTransport the test_home_dir is set to the name of
 
2394
    For TestCaseWithMemoryTransport the ``test_home_dir`` is set to the name of
2145
2395
    a directory which does not exist. This serves to help ensure test isolation
2146
 
    is preserved. test_dir is set to the TEST_ROOT, as is cwd, because they
2147
 
    must exist. However, TestCaseWithMemoryTransport does not offer local
2148
 
    file defaults for the transport in tests, nor does it obey the command line
 
2396
    is preserved. ``test_dir`` is set to the TEST_ROOT, as is cwd, because they
 
2397
    must exist. However, TestCaseWithMemoryTransport does not offer local file
 
2398
    defaults for the transport in tests, nor does it obey the command line
2149
2399
    override, so tests that accidentally write to the common directory should
2150
2400
    be rare.
2151
2401
 
2152
 
    :cvar TEST_ROOT: Directory containing all temporary directories, plus
2153
 
    a .bzr directory that stops us ascending higher into the filesystem.
 
2402
    :cvar TEST_ROOT: Directory containing all temporary directories, plus a
 
2403
        ``.bzr`` directory that stops us ascending higher into the filesystem.
2154
2404
    """
2155
2405
 
2156
2406
    TEST_ROOT = None
2174
2424
 
2175
2425
        :param relpath: a path relative to the base url.
2176
2426
        """
2177
 
        t = _mod_transport.get_transport(self.get_url(relpath))
 
2427
        t = _mod_transport.get_transport_from_url(self.get_url(relpath))
2178
2428
        self.assertFalse(t.is_readonly())
2179
2429
        return t
2180
2430
 
2186
2436
 
2187
2437
        :param relpath: a path relative to the base url.
2188
2438
        """
2189
 
        t = _mod_transport.get_transport(self.get_readonly_url(relpath))
 
2439
        t = _mod_transport.get_transport_from_url(
 
2440
            self.get_readonly_url(relpath))
2190
2441
        self.assertTrue(t.is_readonly())
2191
2442
        return t
2192
2443
 
2313
2564
        real branch.
2314
2565
        """
2315
2566
        root = TestCaseWithMemoryTransport.TEST_ROOT
2316
 
        bzrdir.BzrDir.create_standalone_workingtree(root)
 
2567
        try:
 
2568
            # Make sure we get a readable and accessible home for .bzr.log
 
2569
            # and/or config files, and not fallback to weird defaults (see
 
2570
            # http://pad.lv/825027).
 
2571
            self.assertIs(None, os.environ.get('BZR_HOME', None))
 
2572
            os.environ['BZR_HOME'] = root
 
2573
            wt = bzrdir.BzrDir.create_standalone_workingtree(root)
 
2574
            del os.environ['BZR_HOME']
 
2575
        except Exception, e:
 
2576
            self.fail("Fail to initialize the safety net: %r\nExiting\n" % (e,))
 
2577
        # Hack for speed: remember the raw bytes of the dirstate file so that
 
2578
        # we don't need to re-open the wt to check it hasn't changed.
 
2579
        TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE = (
 
2580
            wt.control_transport.get_bytes('dirstate'))
2317
2581
 
2318
2582
    def _check_safety_net(self):
2319
2583
        """Check that the safety .bzr directory have not been touched.
2322
2586
        propagating. This method ensures than a test did not leaked.
2323
2587
        """
2324
2588
        root = TestCaseWithMemoryTransport.TEST_ROOT
2325
 
        self.permit_url(_mod_transport.get_transport(root).base)
2326
 
        wt = workingtree.WorkingTree.open(root)
2327
 
        last_rev = wt.last_revision()
2328
 
        if last_rev != 'null:':
 
2589
        t = _mod_transport.get_transport_from_path(root)
 
2590
        self.permit_url(t.base)
 
2591
        if (t.get_bytes('.bzr/checkout/dirstate') != 
 
2592
                TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE):
2329
2593
            # The current test have modified the /bzr directory, we need to
2330
2594
            # recreate a new one or all the followng tests will fail.
2331
2595
            # If you need to inspect its content uncomment the following line
2366
2630
    def make_branch(self, relpath, format=None):
2367
2631
        """Create a branch on the transport at relpath."""
2368
2632
        repo = self.make_repository(relpath, format=format)
2369
 
        return repo.bzrdir.create_branch()
 
2633
        return repo.bzrdir.create_branch(append_revisions_only=False)
 
2634
 
 
2635
    def resolve_format(self, format):
 
2636
        """Resolve an object to a ControlDir format object.
 
2637
 
 
2638
        The initial format object can either already be
 
2639
        a ControlDirFormat, None (for the default format),
 
2640
        or a string with the name of the control dir format.
 
2641
 
 
2642
        :param format: Object to resolve
 
2643
        :return A ControlDirFormat instance
 
2644
        """
 
2645
        if format is None:
 
2646
            format = 'default'
 
2647
        if isinstance(format, basestring):
 
2648
            format = bzrdir.format_registry.make_bzrdir(format)
 
2649
        return format
 
2650
 
 
2651
    def resolve_format(self, format):
 
2652
        """Resolve an object to a ControlDir format object.
 
2653
 
 
2654
        The initial format object can either already be
 
2655
        a ControlDirFormat, None (for the default format),
 
2656
        or a string with the name of the control dir format.
 
2657
 
 
2658
        :param format: Object to resolve
 
2659
        :return A ControlDirFormat instance
 
2660
        """
 
2661
        if format is None:
 
2662
            format = 'default'
 
2663
        if isinstance(format, basestring):
 
2664
            format = bzrdir.format_registry.make_bzrdir(format)
 
2665
        return format
2370
2666
 
2371
2667
    def make_bzrdir(self, relpath, format=None):
2372
2668
        try:
2376
2672
            t = _mod_transport.get_transport(maybe_a_url)
2377
2673
            if len(segments) > 1 and segments[-1] not in ('', '.'):
2378
2674
                t.ensure_base()
2379
 
            if format is None:
2380
 
                format = 'default'
2381
 
            if isinstance(format, basestring):
2382
 
                format = bzrdir.format_registry.make_bzrdir(format)
 
2675
            format = self.resolve_format(format)
2383
2676
            return format.initialize_on_transport(t)
2384
2677
        except errors.UninitializableFormat:
2385
2678
            raise TestSkipped("Format %s is not initializable." % format)
2386
2679
 
2387
 
    def make_repository(self, relpath, shared=False, format=None):
 
2680
    def make_repository(self, relpath, shared=None, format=None):
2388
2681
        """Create a repository on our default transport at relpath.
2389
2682
 
2390
2683
        Note that relpath must be a relative path, not a full url.
2401
2694
            backing_server = self.get_server()
2402
2695
        smart_server = test_server.SmartTCPServer_for_testing()
2403
2696
        self.start_server(smart_server, backing_server)
2404
 
        remote_transport = _mod_transport.get_transport(smart_server.get_url()
 
2697
        remote_transport = _mod_transport.get_transport_from_url(smart_server.get_url()
2405
2698
                                                   ).clone(path)
2406
2699
        return remote_transport
2407
2700
 
2418
2711
        test_home_dir = self.test_home_dir
2419
2712
        if isinstance(test_home_dir, unicode):
2420
2713
            test_home_dir = test_home_dir.encode(sys.getfilesystemencoding())
2421
 
        os.environ['HOME'] = test_home_dir
2422
 
        os.environ['BZR_HOME'] = test_home_dir
 
2714
        self.overrideEnv('HOME', test_home_dir)
 
2715
        self.overrideEnv('BZR_HOME', test_home_dir)
2423
2716
 
2424
2717
    def setUp(self):
2425
2718
        super(TestCaseWithMemoryTransport, self).setUp()
2426
2719
        # Ensure that ConnectedTransport doesn't leak sockets
2427
 
        def get_transport_with_cleanup(*args, **kwargs):
2428
 
            t = orig_get_transport(*args, **kwargs)
 
2720
        def get_transport_from_url_with_cleanup(*args, **kwargs):
 
2721
            t = orig_get_transport_from_url(*args, **kwargs)
2429
2722
            if isinstance(t, _mod_transport.ConnectedTransport):
2430
2723
                self.addCleanup(t.disconnect)
2431
2724
            return t
2432
2725
 
2433
 
        orig_get_transport = self.overrideAttr(_mod_transport, 'get_transport',
2434
 
                                               get_transport_with_cleanup)
 
2726
        orig_get_transport_from_url = self.overrideAttr(
 
2727
            _mod_transport, 'get_transport_from_url',
 
2728
            get_transport_from_url_with_cleanup)
2435
2729
        self._make_test_root()
2436
2730
        self.addCleanup(os.chdir, os.getcwdu())
2437
2731
        self.makeAndChdirToTestDir()
2480
2774
 
2481
2775
    OVERRIDE_PYTHON = 'python'
2482
2776
 
 
2777
    def setUp(self):
 
2778
        super(TestCaseInTempDir, self).setUp()
 
2779
        # Remove the protection set in isolated_environ, we have a proper
 
2780
        # access to disk resources now.
 
2781
        self.overrideEnv('BZR_LOG', None)
 
2782
 
2483
2783
    def check_file_contents(self, filename, expect):
2484
2784
        self.log("check contents of file %s" % filename)
2485
2785
        f = file(filename)
2566
2866
                "a list or a tuple. Got %r instead" % (shape,))
2567
2867
        # It's OK to just create them using forward slashes on windows.
2568
2868
        if transport is None or transport.is_readonly():
2569
 
            transport = _mod_transport.get_transport(".")
 
2869
            transport = _mod_transport.get_transport_from_path(".")
2570
2870
        for name in shape:
2571
2871
            self.assertIsInstance(name, basestring)
2572
2872
            if name[-1] == '/':
3331
3631
    return result
3332
3632
 
3333
3633
 
3334
 
class ForwardingResult(unittest.TestResult):
3335
 
 
3336
 
    def __init__(self, target):
3337
 
        unittest.TestResult.__init__(self)
3338
 
        self.result = target
3339
 
 
3340
 
    def startTest(self, test):
3341
 
        self.result.startTest(test)
3342
 
 
3343
 
    def stopTest(self, test):
3344
 
        self.result.stopTest(test)
3345
 
 
3346
 
    def startTestRun(self):
3347
 
        self.result.startTestRun()
3348
 
 
3349
 
    def stopTestRun(self):
3350
 
        self.result.stopTestRun()
3351
 
 
3352
 
    def addSkip(self, test, reason):
3353
 
        self.result.addSkip(test, reason)
3354
 
 
3355
 
    def addSuccess(self, test):
3356
 
        self.result.addSuccess(test)
3357
 
 
3358
 
    def addError(self, test, err):
3359
 
        self.result.addError(test, err)
3360
 
 
3361
 
    def addFailure(self, test, err):
3362
 
        self.result.addFailure(test, err)
3363
 
ForwardingResult = testtools.ExtendedToOriginalDecorator
3364
 
 
3365
 
 
3366
 
class ProfileResult(ForwardingResult):
 
3634
class ProfileResult(testtools.ExtendedToOriginalDecorator):
3367
3635
    """Generate profiling data for all activity between start and success.
3368
3636
    
3369
3637
    The profile data is appended to the test's _benchcalls attribute and can
3381
3649
        # unavoidably fail.
3382
3650
        bzrlib.lsprof.BzrProfiler.profiler_block = 0
3383
3651
        self.profiler.start()
3384
 
        ForwardingResult.startTest(self, test)
 
3652
        testtools.ExtendedToOriginalDecorator.startTest(self, test)
3385
3653
 
3386
3654
    def addSuccess(self, test):
3387
3655
        stats = self.profiler.stop()
3391
3659
            test._benchcalls = []
3392
3660
            calls = test._benchcalls
3393
3661
        calls.append(((test.id(), "", ""), stats))
3394
 
        ForwardingResult.addSuccess(self, test)
 
3662
        testtools.ExtendedToOriginalDecorator.addSuccess(self, test)
3395
3663
 
3396
3664
    def stopTest(self, test):
3397
 
        ForwardingResult.stopTest(self, test)
 
3665
        testtools.ExtendedToOriginalDecorator.stopTest(self, test)
3398
3666
        self.profiler = None
3399
3667
 
3400
3668
 
3408
3676
#                           with proper exclusion rules.
3409
3677
#   -Ethreads               Will display thread ident at creation/join time to
3410
3678
#                           help track thread leaks
 
3679
 
 
3680
#   -Econfig_stats          Will collect statistics using addDetail
3411
3681
selftest_debug_flags = set()
3412
3682
 
3413
3683
 
3607
3877
                key, obj, help=help, info=info, override_existing=False)
3608
3878
        except KeyError:
3609
3879
            actual = self.get(key)
3610
 
            note('Test prefix alias %s is already used for %s, ignoring %s'
3611
 
                 % (key, actual, obj))
 
3880
            trace.note(
 
3881
                'Test prefix alias %s is already used for %s, ignoring %s'
 
3882
                % (key, actual, obj))
3612
3883
 
3613
3884
    def resolve_alias(self, id_start):
3614
3885
        """Replace the alias by the prefix in the given string.
3664
3935
        'bzrlib.tests.per_repository',
3665
3936
        'bzrlib.tests.per_repository_chk',
3666
3937
        'bzrlib.tests.per_repository_reference',
 
3938
        'bzrlib.tests.per_repository_vf',
3667
3939
        'bzrlib.tests.per_uifactory',
3668
3940
        'bzrlib.tests.per_versionedfile',
3669
3941
        'bzrlib.tests.per_workingtree',
3703
3975
        'bzrlib.tests.test_commit_merge',
3704
3976
        'bzrlib.tests.test_config',
3705
3977
        'bzrlib.tests.test_conflicts',
 
3978
        'bzrlib.tests.test_controldir',
3706
3979
        'bzrlib.tests.test_counted_lock',
3707
3980
        'bzrlib.tests.test_crash',
3708
3981
        'bzrlib.tests.test_decorators',
3709
3982
        'bzrlib.tests.test_delta',
3710
3983
        'bzrlib.tests.test_debug',
3711
 
        'bzrlib.tests.test_deprecated_graph',
3712
3984
        'bzrlib.tests.test_diff',
3713
3985
        'bzrlib.tests.test_directory_service',
3714
3986
        'bzrlib.tests.test_dirstate',
3715
3987
        'bzrlib.tests.test_email_message',
3716
3988
        'bzrlib.tests.test_eol_filters',
3717
3989
        'bzrlib.tests.test_errors',
 
3990
        'bzrlib.tests.test_estimate_compressed_size',
3718
3991
        'bzrlib.tests.test_export',
 
3992
        'bzrlib.tests.test_export_pot',
3719
3993
        'bzrlib.tests.test_extract',
 
3994
        'bzrlib.tests.test_features',
3720
3995
        'bzrlib.tests.test_fetch',
3721
3996
        'bzrlib.tests.test_fixtures',
3722
3997
        'bzrlib.tests.test_fifo_cache',
3723
3998
        'bzrlib.tests.test_filters',
 
3999
        'bzrlib.tests.test_filter_tree',
3724
4000
        'bzrlib.tests.test_ftp_transport',
3725
4001
        'bzrlib.tests.test_foreign',
3726
4002
        'bzrlib.tests.test_generate_docs',
3735
4011
        'bzrlib.tests.test_http',
3736
4012
        'bzrlib.tests.test_http_response',
3737
4013
        'bzrlib.tests.test_https_ca_bundle',
 
4014
        'bzrlib.tests.test_i18n',
3738
4015
        'bzrlib.tests.test_identitymap',
3739
4016
        'bzrlib.tests.test_ignores',
3740
4017
        'bzrlib.tests.test_index',
3759
4036
        'bzrlib.tests.test_merge3',
3760
4037
        'bzrlib.tests.test_merge_core',
3761
4038
        'bzrlib.tests.test_merge_directive',
 
4039
        'bzrlib.tests.test_mergetools',
3762
4040
        'bzrlib.tests.test_missing',
3763
4041
        'bzrlib.tests.test_msgeditor',
3764
4042
        'bzrlib.tests.test_multiparent',
3773
4051
        'bzrlib.tests.test_permissions',
3774
4052
        'bzrlib.tests.test_plugins',
3775
4053
        'bzrlib.tests.test_progress',
 
4054
        'bzrlib.tests.test_pyutils',
3776
4055
        'bzrlib.tests.test_read_bundle',
3777
4056
        'bzrlib.tests.test_reconcile',
3778
4057
        'bzrlib.tests.test_reconfigure',
3787
4066
        'bzrlib.tests.test_rio',
3788
4067
        'bzrlib.tests.test_rules',
3789
4068
        'bzrlib.tests.test_sampler',
 
4069
        'bzrlib.tests.test_scenarios',
3790
4070
        'bzrlib.tests.test_script',
3791
4071
        'bzrlib.tests.test_selftest',
3792
4072
        'bzrlib.tests.test_serializer',
3812
4092
        'bzrlib.tests.test_testament',
3813
4093
        'bzrlib.tests.test_textfile',
3814
4094
        'bzrlib.tests.test_textmerge',
 
4095
        'bzrlib.tests.test_cethread',
3815
4096
        'bzrlib.tests.test_timestamp',
3816
4097
        'bzrlib.tests.test_trace',
3817
4098
        'bzrlib.tests.test_transactions',
3828
4109
        'bzrlib.tests.test_upgrade',
3829
4110
        'bzrlib.tests.test_upgrade_stacked',
3830
4111
        'bzrlib.tests.test_urlutils',
 
4112
        'bzrlib.tests.test_utextwrap',
3831
4113
        'bzrlib.tests.test_version',
3832
4114
        'bzrlib.tests.test_version_info',
3833
4115
        'bzrlib.tests.test_versionedfile',
3850
4132
        'bzrlib',
3851
4133
        'bzrlib.branchbuilder',
3852
4134
        'bzrlib.decorators',
3853
 
        'bzrlib.export',
3854
4135
        'bzrlib.inventory',
3855
4136
        'bzrlib.iterablefile',
3856
4137
        'bzrlib.lockdir',
3857
4138
        'bzrlib.merge3',
3858
4139
        'bzrlib.option',
 
4140
        'bzrlib.pyutils',
3859
4141
        'bzrlib.symbol_versioning',
3860
4142
        'bzrlib.tests',
3861
4143
        'bzrlib.tests.fixtures',
3862
4144
        'bzrlib.timestamp',
 
4145
        'bzrlib.transport.http',
3863
4146
        'bzrlib.version_info_formats.format_custom',
3864
4147
        ]
3865
4148
 
3918
4201
        try:
3919
4202
            # note that this really does mean "report only" -- doctest
3920
4203
            # still runs the rest of the examples
3921
 
            doc_suite = doctest.DocTestSuite(mod,
3922
 
                optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
 
4204
            doc_suite = IsolatedDocTestSuite(
 
4205
                mod, optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
3923
4206
        except ValueError, e:
3924
4207
            print '**failed to get doctest for: %s\n%s' % (mod, e)
3925
4208
            raise
3928
4211
        suite.addTest(doc_suite)
3929
4212
 
3930
4213
    default_encoding = sys.getdefaultencoding()
3931
 
    for name, plugin in bzrlib.plugin.plugins().items():
 
4214
    for name, plugin in _mod_plugin.plugins().items():
3932
4215
        if not interesting_module(plugin.module.__name__):
3933
4216
            continue
3934
4217
        plugin_suite = plugin.test_suite()
3940
4223
        if plugin_suite is not None:
3941
4224
            suite.addTest(plugin_suite)
3942
4225
        if default_encoding != sys.getdefaultencoding():
3943
 
            bzrlib.trace.warning(
 
4226
            trace.warning(
3944
4227
                'Plugin "%s" tried to reset default encoding to: %s', name,
3945
4228
                sys.getdefaultencoding())
3946
4229
            reload(sys)
3961
4244
            # Some tests mentioned in the list are not in the test suite. The
3962
4245
            # list may be out of date, report to the tester.
3963
4246
            for id in not_found:
3964
 
                bzrlib.trace.warning('"%s" not found in the test suite', id)
 
4247
                trace.warning('"%s" not found in the test suite', id)
3965
4248
        for id in duplicates:
3966
 
            bzrlib.trace.warning('"%s" is used as an id by several tests', id)
 
4249
            trace.warning('"%s" is used as an id by several tests', id)
3967
4250
 
3968
4251
    return suite
3969
4252
 
3970
4253
 
3971
 
def multiply_scenarios(scenarios_left, scenarios_right):
 
4254
def multiply_scenarios(*scenarios):
 
4255
    """Multiply two or more iterables of scenarios.
 
4256
 
 
4257
    It is safe to pass scenario generators or iterators.
 
4258
 
 
4259
    :returns: A list of compound scenarios: the cross-product of all 
 
4260
        scenarios, with the names concatenated and the parameters
 
4261
        merged together.
 
4262
    """
 
4263
    return reduce(_multiply_two_scenarios, map(list, scenarios))
 
4264
 
 
4265
 
 
4266
def _multiply_two_scenarios(scenarios_left, scenarios_right):
3972
4267
    """Multiply two sets of scenarios.
3973
4268
 
3974
4269
    :returns: the cartesian product of the two sets of scenarios, that is
4098
4393
        the module is available.
4099
4394
    """
4100
4395
 
4101
 
    py_module = __import__(py_module_name, {}, {}, ['NO_SUCH_ATTRIB'])
 
4396
    from bzrlib.tests.features import ModuleAvailableFeature
 
4397
    py_module = pyutils.get_named_object(py_module_name)
4102
4398
    scenarios = [
4103
4399
        ('python', {'module': py_module}),
4104
4400
    ]
4144
4440
                         % (os.path.basename(dirname), printable_e))
4145
4441
 
4146
4442
 
4147
 
class Feature(object):
4148
 
    """An operating system Feature."""
4149
 
 
4150
 
    def __init__(self):
4151
 
        self._available = None
4152
 
 
4153
 
    def available(self):
4154
 
        """Is the feature available?
4155
 
 
4156
 
        :return: True if the feature is available.
4157
 
        """
4158
 
        if self._available is None:
4159
 
            self._available = self._probe()
4160
 
        return self._available
4161
 
 
4162
 
    def _probe(self):
4163
 
        """Implement this method in concrete features.
4164
 
 
4165
 
        :return: True if the feature is available.
4166
 
        """
4167
 
        raise NotImplementedError
4168
 
 
4169
 
    def __str__(self):
4170
 
        if getattr(self, 'feature_name', None):
4171
 
            return self.feature_name()
4172
 
        return self.__class__.__name__
4173
 
 
4174
 
 
4175
 
class _SymlinkFeature(Feature):
4176
 
 
4177
 
    def _probe(self):
4178
 
        return osutils.has_symlinks()
4179
 
 
4180
 
    def feature_name(self):
4181
 
        return 'symlinks'
4182
 
 
4183
 
SymlinkFeature = _SymlinkFeature()
4184
 
 
4185
 
 
4186
 
class _HardlinkFeature(Feature):
4187
 
 
4188
 
    def _probe(self):
4189
 
        return osutils.has_hardlinks()
4190
 
 
4191
 
    def feature_name(self):
4192
 
        return 'hardlinks'
4193
 
 
4194
 
HardlinkFeature = _HardlinkFeature()
4195
 
 
4196
 
 
4197
 
class _OsFifoFeature(Feature):
4198
 
 
4199
 
    def _probe(self):
4200
 
        return getattr(os, 'mkfifo', None)
4201
 
 
4202
 
    def feature_name(self):
4203
 
        return 'filesystem fifos'
4204
 
 
4205
 
OsFifoFeature = _OsFifoFeature()
4206
 
 
4207
 
 
4208
 
class _UnicodeFilenameFeature(Feature):
4209
 
    """Does the filesystem support Unicode filenames?"""
4210
 
 
4211
 
    def _probe(self):
4212
 
        try:
4213
 
            # Check for character combinations unlikely to be covered by any
4214
 
            # single non-unicode encoding. We use the characters
4215
 
            # - greek small letter alpha (U+03B1) and
4216
 
            # - braille pattern dots-123456 (U+283F).
4217
 
            os.stat(u'\u03b1\u283f')
4218
 
        except UnicodeEncodeError:
4219
 
            return False
4220
 
        except (IOError, OSError):
4221
 
            # The filesystem allows the Unicode filename but the file doesn't
4222
 
            # exist.
4223
 
            return True
4224
 
        else:
4225
 
            # The filesystem allows the Unicode filename and the file exists,
4226
 
            # for some reason.
4227
 
            return True
4228
 
 
4229
 
UnicodeFilenameFeature = _UnicodeFilenameFeature()
4230
 
 
4231
 
 
4232
 
class _CompatabilityThunkFeature(Feature):
4233
 
    """This feature is just a thunk to another feature.
4234
 
 
4235
 
    It issues a deprecation warning if it is accessed, to let you know that you
4236
 
    should really use a different feature.
4237
 
    """
4238
 
 
4239
 
    def __init__(self, dep_version, module, name,
4240
 
                 replacement_name, replacement_module=None):
4241
 
        super(_CompatabilityThunkFeature, self).__init__()
4242
 
        self._module = module
4243
 
        if replacement_module is None:
4244
 
            replacement_module = module
4245
 
        self._replacement_module = replacement_module
4246
 
        self._name = name
4247
 
        self._replacement_name = replacement_name
4248
 
        self._dep_version = dep_version
4249
 
        self._feature = None
4250
 
 
4251
 
    def _ensure(self):
4252
 
        if self._feature is None:
4253
 
            depr_msg = self._dep_version % ('%s.%s'
4254
 
                                            % (self._module, self._name))
4255
 
            use_msg = ' Use %s.%s instead.' % (self._replacement_module,
4256
 
                                               self._replacement_name)
4257
 
            symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning)
4258
 
            # Import the new feature and use it as a replacement for the
4259
 
            # deprecated one.
4260
 
            mod = __import__(self._replacement_module, {}, {},
4261
 
                             [self._replacement_name])
4262
 
            self._feature = getattr(mod, self._replacement_name)
4263
 
 
4264
 
    def _probe(self):
4265
 
        self._ensure()
4266
 
        return self._feature._probe()
4267
 
 
4268
 
 
4269
 
class ModuleAvailableFeature(Feature):
4270
 
    """This is a feature than describes a module we want to be available.
4271
 
 
4272
 
    Declare the name of the module in __init__(), and then after probing, the
4273
 
    module will be available as 'self.module'.
4274
 
 
4275
 
    :ivar module: The module if it is available, else None.
4276
 
    """
4277
 
 
4278
 
    def __init__(self, module_name):
4279
 
        super(ModuleAvailableFeature, self).__init__()
4280
 
        self.module_name = module_name
4281
 
 
4282
 
    def _probe(self):
4283
 
        try:
4284
 
            self._module = __import__(self.module_name, {}, {}, [''])
4285
 
            return True
4286
 
        except ImportError:
4287
 
            return False
4288
 
 
4289
 
    @property
4290
 
    def module(self):
4291
 
        if self.available(): # Make sure the probe has been done
4292
 
            return self._module
4293
 
        return None
4294
 
 
4295
 
    def feature_name(self):
4296
 
        return self.module_name
4297
 
 
4298
 
 
4299
 
# This is kept here for compatibility, it is recommended to use
4300
 
# 'bzrlib.tests.feature.paramiko' instead
4301
 
ParamikoFeature = _CompatabilityThunkFeature(
4302
 
    deprecated_in((2,1,0)),
4303
 
    'bzrlib.tests.features', 'ParamikoFeature', 'paramiko')
4304
 
 
4305
 
 
4306
4443
def probe_unicode_in_user_encoding():
4307
4444
    """Try to encode several unicode strings to use in unicode-aware tests.
4308
4445
    Return first successfull match.
4336
4473
    return None
4337
4474
 
4338
4475
 
4339
 
class _HTTPSServerFeature(Feature):
4340
 
    """Some tests want an https Server, check if one is available.
4341
 
 
4342
 
    Right now, the only way this is available is under python2.6 which provides
4343
 
    an ssl module.
4344
 
    """
4345
 
 
4346
 
    def _probe(self):
4347
 
        try:
4348
 
            import ssl
4349
 
            return True
4350
 
        except ImportError:
4351
 
            return False
4352
 
 
4353
 
    def feature_name(self):
4354
 
        return 'HTTPSServer'
4355
 
 
4356
 
 
4357
 
HTTPSServerFeature = _HTTPSServerFeature()
4358
 
 
4359
 
 
4360
 
class _UnicodeFilename(Feature):
4361
 
    """Does the filesystem support Unicode filenames?"""
4362
 
 
4363
 
    def _probe(self):
4364
 
        try:
4365
 
            os.stat(u'\u03b1')
4366
 
        except UnicodeEncodeError:
4367
 
            return False
4368
 
        except (IOError, OSError):
4369
 
            # The filesystem allows the Unicode filename but the file doesn't
4370
 
            # exist.
4371
 
            return True
4372
 
        else:
4373
 
            # The filesystem allows the Unicode filename and the file exists,
4374
 
            # for some reason.
4375
 
            return True
4376
 
 
4377
 
UnicodeFilename = _UnicodeFilename()
4378
 
 
4379
 
 
4380
 
class _ByteStringNamedFilesystem(Feature):
4381
 
    """Is the filesystem based on bytes?"""
4382
 
 
4383
 
    def _probe(self):
4384
 
        if os.name == "posix":
4385
 
            return True
4386
 
        return False
4387
 
 
4388
 
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
4389
 
 
4390
 
 
4391
 
class _UTF8Filesystem(Feature):
4392
 
    """Is the filesystem UTF-8?"""
4393
 
 
4394
 
    def _probe(self):
4395
 
        if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
4396
 
            return True
4397
 
        return False
4398
 
 
4399
 
UTF8Filesystem = _UTF8Filesystem()
4400
 
 
4401
 
 
4402
 
class _BreakinFeature(Feature):
4403
 
    """Does this platform support the breakin feature?"""
4404
 
 
4405
 
    def _probe(self):
4406
 
        from bzrlib import breakin
4407
 
        if breakin.determine_signal() is None:
4408
 
            return False
4409
 
        if sys.platform == 'win32':
4410
 
            # Windows doesn't have os.kill, and we catch the SIGBREAK signal.
4411
 
            # We trigger SIGBREAK via a Console api so we need ctypes to
4412
 
            # access the function
4413
 
            try:
4414
 
                import ctypes
4415
 
            except OSError:
4416
 
                return False
4417
 
        return True
4418
 
 
4419
 
    def feature_name(self):
4420
 
        return "SIGQUIT or SIGBREAK w/ctypes on win32"
4421
 
 
4422
 
 
4423
 
BreakinFeature = _BreakinFeature()
4424
 
 
4425
 
 
4426
 
class _CaseInsCasePresFilenameFeature(Feature):
4427
 
    """Is the file-system case insensitive, but case-preserving?"""
4428
 
 
4429
 
    def _probe(self):
4430
 
        fileno, name = tempfile.mkstemp(prefix='MixedCase')
4431
 
        try:
4432
 
            # first check truly case-preserving for created files, then check
4433
 
            # case insensitive when opening existing files.
4434
 
            name = osutils.normpath(name)
4435
 
            base, rel = osutils.split(name)
4436
 
            found_rel = osutils.canonical_relpath(base, name)
4437
 
            return (found_rel == rel
4438
 
                    and os.path.isfile(name.upper())
4439
 
                    and os.path.isfile(name.lower()))
4440
 
        finally:
4441
 
            os.close(fileno)
4442
 
            os.remove(name)
4443
 
 
4444
 
    def feature_name(self):
4445
 
        return "case-insensitive case-preserving filesystem"
4446
 
 
4447
 
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
4448
 
 
4449
 
 
4450
 
class _CaseInsensitiveFilesystemFeature(Feature):
4451
 
    """Check if underlying filesystem is case-insensitive but *not* case
4452
 
    preserving.
4453
 
    """
4454
 
    # Note that on Windows, Cygwin, MacOS etc, the file-systems are far
4455
 
    # more likely to be case preserving, so this case is rare.
4456
 
 
4457
 
    def _probe(self):
4458
 
        if CaseInsCasePresFilenameFeature.available():
4459
 
            return False
4460
 
 
4461
 
        if TestCaseWithMemoryTransport.TEST_ROOT is None:
4462
 
            root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
4463
 
            TestCaseWithMemoryTransport.TEST_ROOT = root
4464
 
        else:
4465
 
            root = TestCaseWithMemoryTransport.TEST_ROOT
4466
 
        tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
4467
 
            dir=root)
4468
 
        name_a = osutils.pathjoin(tdir, 'a')
4469
 
        name_A = osutils.pathjoin(tdir, 'A')
4470
 
        os.mkdir(name_a)
4471
 
        result = osutils.isdir(name_A)
4472
 
        _rmtree_temp_dir(tdir)
4473
 
        return result
4474
 
 
4475
 
    def feature_name(self):
4476
 
        return 'case-insensitive filesystem'
4477
 
 
4478
 
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
4479
 
 
4480
 
 
4481
 
class _CaseSensitiveFilesystemFeature(Feature):
4482
 
 
4483
 
    def _probe(self):
4484
 
        if CaseInsCasePresFilenameFeature.available():
4485
 
            return False
4486
 
        elif CaseInsensitiveFilesystemFeature.available():
4487
 
            return False
4488
 
        else:
4489
 
            return True
4490
 
 
4491
 
    def feature_name(self):
4492
 
        return 'case-sensitive filesystem'
4493
 
 
4494
 
# new coding style is for feature instances to be lowercase
4495
 
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
4496
 
 
4497
 
 
4498
 
# Kept for compatibility, use bzrlib.tests.features.subunit instead
4499
 
SubUnitFeature = _CompatabilityThunkFeature(
4500
 
    deprecated_in((2,1,0)),
4501
 
    'bzrlib.tests.features', 'SubUnitFeature', 'subunit')
4502
4476
# Only define SubUnitBzrRunner if subunit is available.
4503
4477
try:
4504
4478
    from subunit import TestProtocolClient
4522
4496
except ImportError:
4523
4497
    pass
4524
4498
 
4525
 
class _PosixPermissionsFeature(Feature):
4526
 
 
4527
 
    def _probe(self):
4528
 
        def has_perms():
4529
 
            # create temporary file and check if specified perms are maintained.
4530
 
            import tempfile
4531
 
 
4532
 
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
4533
 
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
4534
 
            fd, name = f
4535
 
            os.close(fd)
4536
 
            os.chmod(name, write_perms)
4537
 
 
4538
 
            read_perms = os.stat(name).st_mode & 0777
4539
 
            os.unlink(name)
4540
 
            return (write_perms == read_perms)
4541
 
 
4542
 
        return (os.name == 'posix') and has_perms()
4543
 
 
4544
 
    def feature_name(self):
4545
 
        return 'POSIX permissions support'
4546
 
 
4547
 
posix_permissions_feature = _PosixPermissionsFeature()
 
4499
 
 
4500
@deprecated_function(deprecated_in((2, 5, 0)))
 
4501
def ModuleAvailableFeature(name):
 
4502
    from bzrlib.tests import features
 
4503
    return features.ModuleAvailableFeature(name)
 
4504