~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Andrew Bennetts
  • Date: 2010-10-08 08:15:14 UTC
  • mto: This revision was merged to the branch mainline in revision 5498.
  • Revision ID: andrew.bennetts@canonical.com-20101008081514-dviqzrdfwyzsqbz2
Split NEWS into per-release doc/en/release-notes/bzr-*.txt

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2013, 2015, 2016 Canonical Ltd
 
1
# Copyright (C) 2005-2010 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
 
from __future__ import absolute_import
 
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)
20
24
 
21
25
# NOTE: Some classes in here use camelCaseNaming() rather than
22
26
# underscore_naming().  That's for consistency with unittest; it's not the
32
36
import errno
33
37
import itertools
34
38
import logging
35
 
import math
36
39
import os
37
40
import platform
38
41
import pprint
39
42
import random
40
43
import re
41
44
import shlex
42
 
import site
43
45
import stat
44
46
import subprocess
45
47
import sys
53
55
import testtools
54
56
# nb: check this before importing anything else from within it
55
57
_testtools_version = getattr(testtools, '__version__', ())
56
 
if _testtools_version < (0, 9, 5):
57
 
    raise ImportError("need at least testtools 0.9.5: %s is %r"
 
58
if _testtools_version < (0, 9, 2):
 
59
    raise ImportError("need at least testtools 0.9.2: %s is %r"
58
60
        % (testtools.__file__, _testtools_version))
59
61
from testtools import content
60
62
 
61
 
import bzrlib
62
63
from bzrlib import (
63
64
    branchbuilder,
64
 
    controldir,
 
65
    bzrdir,
65
66
    chk_map,
66
 
    commands as _mod_commands,
67
67
    config,
68
 
    i18n,
69
68
    debug,
70
69
    errors,
71
70
    hooks,
72
71
    lock as _mod_lock,
73
 
    lockdir,
74
72
    memorytree,
75
73
    osutils,
76
 
    plugin as _mod_plugin,
77
 
    pyutils,
78
74
    ui,
79
75
    urlutils,
80
76
    registry,
81
 
    symbol_versioning,
82
 
    trace,
83
77
    transport as _mod_transport,
84
78
    workingtree,
85
79
    )
 
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
86
87
try:
87
88
    import bzrlib.lsprof
88
89
except ImportError:
89
90
    # lsprof not available
90
91
    pass
91
 
from bzrlib.smart import client, request
 
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
 
98
from bzrlib.symbol_versioning import (
 
99
    DEPRECATED_PARAMETER,
 
100
    deprecated_function,
 
101
    deprecated_in,
 
102
    deprecated_method,
 
103
    deprecated_passed,
 
104
    )
 
105
import bzrlib.trace
92
106
from bzrlib.transport import (
93
107
    memory,
94
108
    pathfilter,
95
109
    )
96
 
from bzrlib.symbol_versioning import (
97
 
    deprecated_function,
98
 
    deprecated_in,
99
 
    )
 
110
from bzrlib.trace import mutter, note
100
111
from bzrlib.tests import (
101
 
    fixtures,
102
112
    test_server,
103
113
    TestUtil,
104
114
    treeshape,
105
115
    )
106
116
from bzrlib.ui import NullProgressView
107
117
from bzrlib.ui.text import TextUIFactory
108
 
from bzrlib.tests.features import _CompatabilityThunkFeature
 
118
import bzrlib.version_info_formats.format_custom
 
119
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
109
120
 
110
121
# Mark this python module as being part of the implementation
111
122
# of unittest: this gives us better tracebacks where the last
128
139
TestSuite = TestUtil.TestSuite
129
140
TestLoader = TestUtil.TestLoader
130
141
 
131
 
# Tests should run in a clean and clearly defined environment. The goal is to
132
 
# keep them isolated from the running environment as mush as possible. The test
133
 
# framework ensures the variables defined below are set (or deleted if the
134
 
# value is None) before a test is run and reset to their original value after
135
 
# the test is run. Generally if some code depends on an environment variable,
136
 
# the tests should start without this variable in the environment. There are a
137
 
# few exceptions but you shouldn't violate this rule lightly.
138
 
isolated_environ = {
139
 
    'BZR_HOME': None,
140
 
    'HOME': None,
141
 
    'XDG_CONFIG_HOME': None,
142
 
    # bzr now uses the Win32 API and doesn't rely on APPDATA, but the
143
 
    # tests do check our impls match APPDATA
144
 
    'BZR_EDITOR': None, # test_msgeditor manipulates this variable
145
 
    'VISUAL': None,
146
 
    'EDITOR': None,
147
 
    'BZR_EMAIL': None,
148
 
    'BZREMAIL': None, # may still be present in the environment
149
 
    'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
150
 
    'BZR_PROGRESS_BAR': None,
151
 
    # This should trap leaks to ~/.bzr.log. This occurs when tests use TestCase
152
 
    # as a base class instead of TestCaseInTempDir. Tests inheriting from
153
 
    # TestCase should not use disk resources, BZR_LOG is one.
154
 
    'BZR_LOG': '/you-should-use-TestCaseInTempDir-if-you-need-a-log-file',
155
 
    'BZR_PLUGIN_PATH': None,
156
 
    'BZR_DISABLE_PLUGINS': None,
157
 
    'BZR_PLUGINS_AT': None,
158
 
    'BZR_CONCURRENCY': None,
159
 
    # Make sure that any text ui tests are consistent regardless of
160
 
    # the environment the test case is run in; you may want tests that
161
 
    # test other combinations.  'dumb' is a reasonable guess for tests
162
 
    # going to a pipe or a StringIO.
163
 
    'TERM': 'dumb',
164
 
    'LINES': '25',
165
 
    'COLUMNS': '80',
166
 
    'BZR_COLUMNS': '80',
167
 
    # Disable SSH Agent
168
 
    'SSH_AUTH_SOCK': None,
169
 
    # Proxies
170
 
    'http_proxy': None,
171
 
    'HTTP_PROXY': None,
172
 
    'https_proxy': None,
173
 
    'HTTPS_PROXY': None,
174
 
    'no_proxy': None,
175
 
    'NO_PROXY': None,
176
 
    'all_proxy': None,
177
 
    'ALL_PROXY': None,
178
 
    # Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
179
 
    # least. If you do (care), please update this comment
180
 
    # -- vila 20080401
181
 
    'ftp_proxy': None,
182
 
    'FTP_PROXY': None,
183
 
    'BZR_REMOTE_PATH': None,
184
 
    # Generally speaking, we don't want apport reporting on crashes in
185
 
    # the test envirnoment unless we're specifically testing apport,
186
 
    # so that it doesn't leak into the real system environment.  We
187
 
    # use an env var so it propagates to subprocesses.
188
 
    'APPORT_DISABLE': '1',
189
 
    }
190
 
 
191
 
 
192
 
def override_os_environ(test, env=None):
193
 
    """Modify os.environ keeping a copy.
194
 
    
195
 
    :param test: A test instance
196
 
 
197
 
    :param env: A dict containing variable definitions to be installed
198
 
    """
199
 
    if env is None:
200
 
        env = isolated_environ
201
 
    test._original_os_environ = dict([(var, value)
202
 
                                      for var, value in os.environ.iteritems()])
203
 
    for var, value in env.iteritems():
204
 
        osutils.set_or_unset_env(var, value)
205
 
        if var not in test._original_os_environ:
206
 
            # The var is new, add it with a value of None, so
207
 
            # restore_os_environ will delete it
208
 
            test._original_os_environ[var] = None
209
 
 
210
 
 
211
 
def restore_os_environ(test):
212
 
    """Restore os.environ to its original state.
213
 
 
214
 
    :param test: A test instance previously passed to override_os_environ.
215
 
    """
216
 
    for var, value in test._original_os_environ.iteritems():
217
 
        # Restore the original value (or delete it if the value has been set to
218
 
        # None in override_os_environ).
219
 
        osutils.set_or_unset_env(var, value)
220
 
 
221
 
 
222
 
def _clear__type_equality_funcs(test):
223
 
    """Cleanup bound methods stored on TestCase instances
224
 
 
225
 
    Clear the dict breaking a few (mostly) harmless cycles in the affected
226
 
    unittests released with Python 2.6 and initial Python 2.7 versions.
227
 
 
228
 
    For a few revisions between Python 2.7.1 and Python 2.7.2 that annoyingly
229
 
    shipped in Oneiric, an object with no clear method was used, hence the
230
 
    extra complications, see bug 809048 for details.
231
 
    """
232
 
    type_equality_funcs = getattr(test, "_type_equality_funcs", None)
233
 
    if type_equality_funcs is not None:
234
 
        tef_clear = getattr(type_equality_funcs, "clear", None)
235
 
        if tef_clear is None:
236
 
            tef_instance_dict = getattr(type_equality_funcs, "__dict__", None)
237
 
            if tef_instance_dict is not None:
238
 
                tef_clear = tef_instance_dict.clear
239
 
        if tef_clear is not None:
240
 
            tef_clear()
241
 
 
242
 
 
243
142
class ExtendedTestResult(testtools.TextTestResult):
244
143
    """Accepts, reports and accumulates the results of running tests.
245
144
 
249
148
    different types of display.
250
149
 
251
150
    When a test finishes, in whatever way, it calls one of the addSuccess,
252
 
    addFailure or addError methods.  These in turn may redirect to a more
 
151
    addFailure or addError classes.  These in turn may redirect to a more
253
152
    specific case for the special test results supported by our extended
254
153
    tests.
255
154
 
296
195
        self._strict = strict
297
196
        self._first_thread_leaker_id = None
298
197
        self._tests_leaking_threads_count = 0
299
 
        self._traceback_from_test = None
300
198
 
301
199
    def stopTestRun(self):
302
200
        run = self.testsRun
360
258
            return float(''.join(details['benchtime'].iter_bytes()))
361
259
        return getattr(testCase, "_benchtime", None)
362
260
 
363
 
    def _delta_to_float(self, a_timedelta, precision):
364
 
        # This calls ceiling to ensure that the most pessimistic view of time
365
 
        # taken is shown (rather than leaving it to the Python %f operator
366
 
        # to decide whether to round/floor/ceiling. This was added when we
367
 
        # had pyp3 test failures that suggest a floor was happening.
368
 
        shift = 10 ** precision
369
 
        return math.ceil((a_timedelta.days * 86400.0 + a_timedelta.seconds +
370
 
            a_timedelta.microseconds / 1000000.0) * shift) / shift
371
 
 
372
261
    def _elapsedTestTimeString(self):
373
262
        """Return a time string for the overall time the current test has taken."""
374
 
        return self._formatTime(self._delta_to_float(
375
 
            self._now() - self._start_datetime, 3))
 
263
        return self._formatTime(time.time() - self._start_time)
376
264
 
377
265
    def _testTimeString(self, testCase):
378
266
        benchmark_time = self._extractBenchmarkTime(testCase)
392
280
        what = re.sub(r'^bzrlib\.tests\.', '', what)
393
281
        return what
394
282
 
395
 
    # GZ 2010-10-04: Cloned tests may end up harmlessly calling this method
396
 
    #                multiple times in a row, because the handler is added for
397
 
    #                each test but the container list is shared between cases.
398
 
    #                See lp:498869 lp:625574 and lp:637725 for background.
399
 
    def _record_traceback_from_test(self, exc_info):
400
 
        """Store the traceback from passed exc_info tuple till"""
401
 
        self._traceback_from_test = exc_info[2]
402
 
 
403
283
    def startTest(self, test):
404
284
        super(ExtendedTestResult, self).startTest(test)
405
285
        if self.count == 0:
408
288
        self.report_test_start(test)
409
289
        test.number = self.count
410
290
        self._recordTestStartTime()
411
 
        # Make testtools cases give us the real traceback on failure
412
 
        addOnException = getattr(test, "addOnException", None)
413
 
        if addOnException is not None:
414
 
            addOnException(self._record_traceback_from_test)
415
 
        # Only check for thread leaks on bzrlib derived test cases
416
 
        if isinstance(test, TestCase):
417
 
            test.addCleanup(self._check_leaked_threads, test)
418
 
 
419
 
    def stopTest(self, test):
420
 
        super(ExtendedTestResult, self).stopTest(test)
421
 
        # Manually break cycles, means touching various private things but hey
422
 
        getDetails = getattr(test, "getDetails", None)
423
 
        if getDetails is not None:
424
 
            getDetails().clear()
425
 
        _clear__type_equality_funcs(test)
426
 
        self._traceback_from_test = None
 
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)
427
295
 
428
296
    def startTests(self):
429
297
        self.report_tests_starting()
447
315
 
448
316
    def _recordTestStartTime(self):
449
317
        """Record that a test has started."""
450
 
        self._start_datetime = self._now()
 
318
        self._start_time = time.time()
451
319
 
452
320
    def addError(self, test, err):
453
321
        """Tell result that test finished with an error.
455
323
        Called from the TestCase run() method when the test
456
324
        fails with an unexpected error.
457
325
        """
458
 
        self._post_mortem(self._traceback_from_test)
 
326
        self._post_mortem()
459
327
        super(ExtendedTestResult, self).addError(test, err)
460
328
        self.error_count += 1
461
329
        self.report_error(test, err)
468
336
        Called from the TestCase run() method when the test
469
337
        fails because e.g. an assert() method failed.
470
338
        """
471
 
        self._post_mortem(self._traceback_from_test)
 
339
        self._post_mortem()
472
340
        super(ExtendedTestResult, self).addFailure(test, err)
473
341
        self.failure_count += 1
474
342
        self.report_failure(test, err)
494
362
        self.known_failure_count += 1
495
363
        self.report_known_failure(test, err)
496
364
 
497
 
    def addUnexpectedSuccess(self, test, details=None):
498
 
        """Tell result the test unexpectedly passed, counting as a failure
499
 
 
500
 
        When the minimum version of testtools required becomes 0.9.8 this
501
 
        can be updated to use the new handling there.
502
 
        """
503
 
        super(ExtendedTestResult, self).addFailure(test, details=details)
504
 
        self.failure_count += 1
505
 
        self.report_unexpected_success(test,
506
 
            "".join(details["reason"].iter_text()))
507
 
        if self.stop_early:
508
 
            self.stop()
509
 
 
510
365
    def addNotSupported(self, test, feature):
511
366
        """The test will not be run because of a missing feature.
512
367
        """
529
384
        self.not_applicable_count += 1
530
385
        self.report_not_applicable(test, reason)
531
386
 
532
 
    def _count_stored_tests(self):
533
 
        """Count of tests instances kept alive due to not succeeding"""
534
 
        return self.error_count + self.failure_count + self.known_failure_count
535
 
 
536
 
    def _post_mortem(self, tb=None):
 
387
    def _post_mortem(self):
537
388
        """Start a PDB post mortem session."""
538
389
        if os.environ.get('BZR_TEST_PDB', None):
539
 
            import pdb
540
 
            pdb.post_mortem(tb)
 
390
            import pdb;pdb.post_mortem()
541
391
 
542
392
    def progress(self, offset, whence):
543
393
        """The test is adjusting the count of tests to run."""
675
525
    def report_known_failure(self, test, err):
676
526
        pass
677
527
 
678
 
    def report_unexpected_success(self, test, reason):
679
 
        self.stream.write('FAIL: %s\n    %s: %s\n' % (
680
 
            self._test_description(test),
681
 
            "Unexpected success. Should have failed",
682
 
            reason,
683
 
            ))
684
 
 
685
528
    def report_skip(self, test, reason):
686
529
        pass
687
530
 
739
582
                % (self._testTimeString(test),
740
583
                   self._error_summary(err)))
741
584
 
742
 
    def report_unexpected_success(self, test, reason):
743
 
        self.stream.write(' FAIL %s\n%s: %s\n'
744
 
                % (self._testTimeString(test),
745
 
                   "Unexpected success. Should have failed",
746
 
                   reason))
747
 
 
748
585
    def report_success(self, test):
749
586
        self.stream.write('   OK %s\n' % self._testTimeString(test))
750
587
        for bench_called, stats in getattr(test, '_benchcalls', []):
797
634
            encode = codec[0]
798
635
        else:
799
636
            encode = codec.encode
800
 
        # GZ 2010-09-08: Really we don't want to be writing arbitrary bytes,
801
 
        #                so should swap to the plain codecs.StreamWriter
802
 
        stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream,
803
 
            "backslashreplace")
 
637
        stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream)
804
638
        stream.encoding = new_encoding
805
639
        self.stream = stream
806
640
        self.descriptions = descriptions
956
790
        return NullProgressView()
957
791
 
958
792
 
959
 
def isolated_doctest_setUp(test):
960
 
    override_os_environ(test)
961
 
 
962
 
 
963
 
def isolated_doctest_tearDown(test):
964
 
    restore_os_environ(test)
965
 
 
966
 
 
967
 
def IsolatedDocTestSuite(*args, **kwargs):
968
 
    """Overrides doctest.DocTestSuite to handle isolation.
969
 
 
970
 
    The method is really a factory and users are expected to use it as such.
971
 
    """
972
 
 
973
 
    kwargs['setUp'] = isolated_doctest_setUp
974
 
    kwargs['tearDown'] = isolated_doctest_tearDown
975
 
    return doctest.DocTestSuite(*args, **kwargs)
976
 
 
977
 
 
978
793
class TestCase(testtools.TestCase):
979
794
    """Base class for bzr unit tests.
980
795
 
1010
825
 
1011
826
    def setUp(self):
1012
827
        super(TestCase, self).setUp()
1013
 
 
1014
 
        # At this point we're still accessing the config files in $BZR_HOME (as
1015
 
        # set by the user running selftest).
1016
 
        timeout = config.GlobalStack().get('selftest.timeout')
1017
 
        if timeout:
1018
 
            timeout_fixture = fixtures.TimeoutFixture(timeout)
1019
 
            timeout_fixture.setUp()
1020
 
            self.addCleanup(timeout_fixture.cleanUp)
1021
 
 
1022
828
        for feature in getattr(self, '_test_needs_features', []):
1023
829
            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)]))
1024
834
        self._cleanEnvironment()
1025
 
 
1026
 
        if bzrlib.global_state is not None:
1027
 
            self.overrideAttr(bzrlib.global_state, 'cmdline_overrides',
1028
 
                              config.CommandLineStore())
1029
 
 
1030
835
        self._silenceUI()
1031
836
        self._startLogFile()
1032
837
        self._benchcalls = []
1035
840
        self._track_transports()
1036
841
        self._track_locks()
1037
842
        self._clear_debug_flags()
1038
 
        # Isolate global verbosity level, to make sure it's reproducible
1039
 
        # between tests.  We should get rid of this altogether: bug 656694. --
1040
 
        # mbp 20101008
1041
 
        self.overrideAttr(bzrlib.trace, '_verbosity_level', 0)
1042
 
        self._log_files = set()
1043
 
        # Each key in the ``_counters`` dict holds a value for a different
1044
 
        # counter. When the test ends, addDetail() should be used to output the
1045
 
        # counter values. This happens in install_counter_hook().
1046
 
        self._counters = {}
1047
 
        if 'config_stats' in selftest_debug_flags:
1048
 
            self._install_config_stats_hooks()
1049
 
        # Do not use i18n for tests (unless the test reverses this)
1050
 
        i18n.disable_i18n()
1051
843
 
1052
844
    def debug(self):
1053
845
        # debug a frame up.
1054
846
        import pdb
1055
 
        # The sys preserved stdin/stdout should allow blackbox tests debugging
1056
 
        pdb.Pdb(stdin=sys.__stdin__, stdout=sys.__stdout__
1057
 
                ).set_trace(sys._getframe().f_back)
 
847
        pdb.Pdb().set_trace(sys._getframe().f_back)
1058
848
 
1059
849
    def discardDetail(self, name):
1060
850
        """Extend the addDetail, getDetails api so we can remove a detail.
1072
862
        if name in details:
1073
863
            del details[name]
1074
864
 
1075
 
    def install_counter_hook(self, hooks, name, counter_name=None):
1076
 
        """Install a counting hook.
1077
 
 
1078
 
        Any hook can be counted as long as it doesn't need to return a value.
1079
 
 
1080
 
        :param hooks: Where the hook should be installed.
1081
 
 
1082
 
        :param name: The hook name that will be counted.
1083
 
 
1084
 
        :param counter_name: The counter identifier in ``_counters``, defaults
1085
 
            to ``name``.
1086
 
        """
1087
 
        _counters = self._counters # Avoid closing over self
1088
 
        if counter_name is None:
1089
 
            counter_name = name
1090
 
        if _counters.has_key(counter_name):
1091
 
            raise AssertionError('%s is already used as a counter name'
1092
 
                                  % (counter_name,))
1093
 
        _counters[counter_name] = 0
1094
 
        self.addDetail(counter_name, content.Content(content.UTF8_TEXT,
1095
 
            lambda: ['%d' % (_counters[counter_name],)]))
1096
 
        def increment_counter(*args, **kwargs):
1097
 
            _counters[counter_name] += 1
1098
 
        label = 'count %s calls' % (counter_name,)
1099
 
        hooks.install_named_hook(name, increment_counter, label)
1100
 
        self.addCleanup(hooks.uninstall_named_hook, name, label)
1101
 
 
1102
 
    def _install_config_stats_hooks(self):
1103
 
        """Install config hooks to count hook calls.
1104
 
 
1105
 
        """
1106
 
        for hook_name in ('get', 'set', 'remove', 'load', 'save'):
1107
 
            self.install_counter_hook(config.ConfigHooks, hook_name,
1108
 
                                       'config.%s' % (hook_name,))
1109
 
 
1110
 
        # The OldConfigHooks are private and need special handling to protect
1111
 
        # against recursive tests (tests that run other tests), so we just do
1112
 
        # manually what registering them into _builtin_known_hooks will provide
1113
 
        # us.
1114
 
        self.overrideAttr(config, 'OldConfigHooks', config._OldConfigHooks())
1115
 
        for hook_name in ('get', 'set', 'remove', 'load', 'save'):
1116
 
            self.install_counter_hook(config.OldConfigHooks, hook_name,
1117
 
                                      'old_config.%s' % (hook_name,))
1118
 
 
1119
865
    def _clear_debug_flags(self):
1120
866
        """Prevent externally set debug flags affecting tests.
1121
867
 
1131
877
 
1132
878
    def _clear_hooks(self):
1133
879
        # prevent hooks affecting tests
1134
 
        known_hooks = hooks.known_hooks
1135
880
        self._preserved_hooks = {}
1136
 
        for key, (parent, name) in known_hooks.iter_parent_objects():
1137
 
            current_hooks = getattr(parent, name)
 
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)
1138
884
            self._preserved_hooks[parent] = (name, current_hooks)
1139
 
        self._preserved_lazy_hooks = hooks._lazy_hooks
1140
 
        hooks._lazy_hooks = {}
1141
885
        self.addCleanup(self._restoreHooks)
1142
 
        for key, (parent, name) in known_hooks.iter_parent_objects():
1143
 
            factory = known_hooks.get(key)
 
886
        for key, factory in hooks.known_hooks.items():
 
887
            parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
1144
888
            setattr(parent, name, factory())
1145
889
        # this hook should always be installed
1146
890
        request._install_hook()
1175
919
        # break some locks on purpose and should be taken into account by
1176
920
        # considering that breaking a lock is just a dirty way of releasing it.
1177
921
        if len(acquired_locks) != (len(released_locks) + len(broken_locks)):
1178
 
            message = (
1179
 
                'Different number of acquired and '
1180
 
                'released or broken locks.\n'
1181
 
                'acquired=%s\n'
1182
 
                'released=%s\n'
1183
 
                'broken=%s\n' %
1184
 
                (acquired_locks, released_locks, broken_locks))
 
922
            message = ('Different number of acquired and '
 
923
                       'released or broken locks. (%s, %s + %s)' %
 
924
                       (acquired_locks, released_locks, broken_locks))
1185
925
            if not self._lock_check_thorough:
1186
926
                # Rather than fail, just warn
1187
927
                print "Broken test %s: %s" % (self, message)
1215
955
 
1216
956
    def permit_dir(self, name):
1217
957
        """Permit a directory to be used by this test. See permit_url."""
1218
 
        name_transport = _mod_transport.get_transport_from_path(name)
 
958
        name_transport = _mod_transport.get_transport(name)
1219
959
        self.permit_url(name)
1220
960
        self.permit_url(name_transport.base)
1221
961
 
1300
1040
        self.addCleanup(transport_server.stop_server)
1301
1041
        # Obtain a real transport because if the server supplies a password, it
1302
1042
        # will be hidden from the base on the client side.
1303
 
        t = _mod_transport.get_transport_from_url(transport_server.get_url())
 
1043
        t = _mod_transport.get_transport(transport_server.get_url())
1304
1044
        # Some transport servers effectively chroot the backing transport;
1305
1045
        # others like SFTPServer don't - users of the transport can walk up the
1306
1046
        # transport to read the entire backing transport. This wouldn't matter
1337
1077
        # hook into bzr dir opening. This leaves a small window of error for
1338
1078
        # transport tests, but they are well known, and we can improve on this
1339
1079
        # step.
1340
 
        controldir.ControlDir.hooks.install_named_hook("pre_open",
 
1080
        bzrdir.BzrDir.hooks.install_named_hook("pre_open",
1341
1081
            self._preopen_isolate_transport, "Check bzr directories are safe.")
1342
1082
 
1343
1083
    def _ndiff_strings(self, a, b):
1362
1102
        except UnicodeError, e:
1363
1103
            # If we can't compare without getting a UnicodeError, then
1364
1104
            # obviously they are different
1365
 
            trace.mutter('UnicodeError: %s', e)
 
1105
            mutter('UnicodeError: %s', e)
1366
1106
        if message:
1367
1107
            message += '\n'
1368
1108
        raise AssertionError("%snot equal:\na = %s\nb = %s\n"
1369
1109
            % (message,
1370
1110
               pprint.pformat(a), pprint.pformat(b)))
1371
1111
 
1372
 
    # FIXME: This is deprecated in unittest2 but plugins may still use it so we
1373
 
    # need a deprecation period for them -- vila 2016-02-01
1374
1112
    assertEquals = assertEqual
1375
1113
 
1376
1114
    def assertEqualDiff(self, a, b, message=None):
1379
1117
        This is intended for use with multi-line strings where it can
1380
1118
        be hard to find the differences by eye.
1381
1119
        """
1382
 
        # TODO: perhaps override assertEqual to call this for strings?
 
1120
        # TODO: perhaps override assertEquals to call this for strings?
1383
1121
        if a == b:
1384
1122
            return
1385
1123
        if message is None:
1409
1147
                         'st_mtime did not match')
1410
1148
        self.assertEqual(expected.st_ctime, actual.st_ctime,
1411
1149
                         'st_ctime did not match')
1412
 
        if sys.platform == 'win32':
 
1150
        if sys.platform != 'win32':
1413
1151
            # On Win32 both 'dev' and 'ino' cannot be trusted. In python2.4 it
1414
1152
            # is 'dev' that varies, in python 2.5 (6?) it is st_ino that is
1415
 
            # odd. We just force it to always be 0 to avoid any problems.
1416
 
            self.assertEqual(0, expected.st_dev)
1417
 
            self.assertEqual(0, actual.st_dev)
1418
 
            self.assertEqual(0, expected.st_ino)
1419
 
            self.assertEqual(0, actual.st_ino)
1420
 
        else:
 
1153
            # odd. Regardless we shouldn't actually try to assert anything
 
1154
            # about their values
1421
1155
            self.assertEqual(expected.st_dev, actual.st_dev,
1422
1156
                             'st_dev did not match')
1423
1157
            self.assertEqual(expected.st_ino, actual.st_ino,
1432
1166
                length, len(obj_with_len), obj_with_len))
1433
1167
 
1434
1168
    def assertLogsError(self, exception_class, func, *args, **kwargs):
1435
 
        """Assert that `func(*args, **kwargs)` quietly logs a specific error.
 
1169
        """Assert that func(*args, **kwargs) quietly logs a specific exception.
1436
1170
        """
 
1171
        from bzrlib import trace
1437
1172
        captured = []
1438
1173
        orig_log_exception_quietly = trace.log_exception_quietly
1439
1174
        try:
1440
1175
            def capture():
1441
1176
                orig_log_exception_quietly()
1442
 
                captured.append(sys.exc_info()[1])
 
1177
                captured.append(sys.exc_info())
1443
1178
            trace.log_exception_quietly = capture
1444
1179
            func(*args, **kwargs)
1445
1180
        finally:
1446
1181
            trace.log_exception_quietly = orig_log_exception_quietly
1447
1182
        self.assertLength(1, captured)
1448
 
        err = captured[0]
 
1183
        err = captured[0][1]
1449
1184
        self.assertIsInstance(err, exception_class)
1450
1185
        return err
1451
1186
 
1488
1223
        if haystack.find(needle) == -1:
1489
1224
            self.fail("string %r not found in '''%s'''" % (needle, haystack))
1490
1225
 
1491
 
    def assertNotContainsString(self, haystack, needle):
1492
 
        if haystack.find(needle) != -1:
1493
 
            self.fail("string %r found in '''%s'''" % (needle, haystack))
1494
 
 
1495
1226
    def assertSubset(self, sublist, superlist):
1496
1227
        """Assert that every entry in sublist is present in superlist."""
1497
1228
        missing = set(sublist) - set(superlist)
1586
1317
 
1587
1318
    def assertFileEqual(self, content, path):
1588
1319
        """Fail if path does not contain 'content'."""
1589
 
        self.assertPathExists(path)
 
1320
        self.failUnlessExists(path)
1590
1321
        f = file(path, 'rb')
1591
1322
        try:
1592
1323
            s = f.read()
1602
1333
        else:
1603
1334
            self.assertEqual(expected_docstring, obj.__doc__)
1604
1335
 
1605
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1606
1336
    def failUnlessExists(self, path):
1607
 
        return self.assertPathExists(path)
1608
 
 
1609
 
    def assertPathExists(self, path):
1610
1337
        """Fail unless path or paths, which may be abs or relative, exist."""
1611
1338
        if not isinstance(path, basestring):
1612
1339
            for p in path:
1613
 
                self.assertPathExists(p)
 
1340
                self.failUnlessExists(p)
1614
1341
        else:
1615
 
            self.assertTrue(osutils.lexists(path),
1616
 
                path + " does not exist")
 
1342
            self.failUnless(osutils.lexists(path),path+" does not exist")
1617
1343
 
1618
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1619
1344
    def failIfExists(self, path):
1620
 
        return self.assertPathDoesNotExist(path)
1621
 
 
1622
 
    def assertPathDoesNotExist(self, path):
1623
1345
        """Fail if path or paths, which may be abs or relative, exist."""
1624
1346
        if not isinstance(path, basestring):
1625
1347
            for p in path:
1626
 
                self.assertPathDoesNotExist(p)
 
1348
                self.failIfExists(p)
1627
1349
        else:
1628
 
            self.assertFalse(osutils.lexists(path),
1629
 
                path + " exists")
 
1350
            self.failIf(osutils.lexists(path),path+" exists")
1630
1351
 
1631
1352
    def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
1632
1353
        """A helper for callDeprecated and applyDeprecated.
1658
1379
        not other callers that go direct to the warning module.
1659
1380
 
1660
1381
        To test that a deprecated method raises an error, do something like
1661
 
        this (remember that both assertRaises and applyDeprecated delays *args
1662
 
        and **kwargs passing)::
 
1382
        this::
1663
1383
 
1664
1384
            self.assertRaises(errors.ReservedId,
1665
1385
                self.applyDeprecated,
1743
1463
        return result
1744
1464
 
1745
1465
    def _startLogFile(self):
1746
 
        """Setup a in-memory target for bzr and testcase log messages"""
1747
 
        pseudo_log_file = StringIO()
1748
 
        def _get_log_contents_for_weird_testtools_api():
1749
 
            return [pseudo_log_file.getvalue().decode(
1750
 
                "utf-8", "replace").encode("utf-8")]
1751
 
        self.addDetail("log", content.Content(content.ContentType("text",
1752
 
            "plain", {"charset": "utf8"}),
1753
 
            _get_log_contents_for_weird_testtools_api))
1754
 
        self._log_file = pseudo_log_file
1755
 
        self._log_memento = trace.push_log_file(self._log_file)
 
1466
        """Send bzr and test log messages to a temporary file.
 
1467
 
 
1468
        The file is removed as the test is torn down.
 
1469
        """
 
1470
        self._log_file = StringIO()
 
1471
        self._log_memento = bzrlib.trace.push_log_file(self._log_file)
1756
1472
        self.addCleanup(self._finishLogFile)
1757
1473
 
1758
1474
    def _finishLogFile(self):
1759
 
        """Flush and dereference the in-memory log for this testcase"""
1760
 
        if trace._trace_file:
 
1475
        """Finished with the log file.
 
1476
 
 
1477
        Close the file and delete it, unless setKeepLogfile was called.
 
1478
        """
 
1479
        if bzrlib.trace._trace_file:
1761
1480
            # flush the log file, to get all content
1762
 
            trace._trace_file.flush()
1763
 
        trace.pop_log_file(self._log_memento)
1764
 
        # The logging module now tracks references for cleanup so discard ours
1765
 
        del self._log_memento
 
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)
1766
1485
 
1767
1486
    def thisFailsStrictLockCheck(self):
1768
1487
        """It is known that this test would fail with -Dstrict_locks.
1780
1499
    def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1781
1500
        """Overrides an object attribute restoring it after the test.
1782
1501
 
1783
 
        :note: This should be used with discretion; you should think about
1784
 
        whether it's better to make the code testable without monkey-patching.
1785
 
 
1786
1502
        :param obj: The object that will be mutated.
1787
1503
 
1788
1504
        :param attr_name: The attribute name we want to preserve/override in
1792
1508
 
1793
1509
        :returns: The actual attr value.
1794
1510
        """
 
1511
        value = getattr(obj, attr_name)
1795
1512
        # The actual value is captured by the call below
1796
 
        value = getattr(obj, attr_name, _unitialized_attr)
1797
 
        if value is _unitialized_attr:
1798
 
            # When the test completes, the attribute should not exist, but if
1799
 
            # we aren't setting a value, we don't need to do anything.
1800
 
            if new is not _unitialized_attr:
1801
 
                self.addCleanup(delattr, obj, attr_name)
1802
 
        else:
1803
 
            self.addCleanup(setattr, obj, attr_name, value)
 
1513
        self.addCleanup(setattr, obj, attr_name, value)
1804
1514
        if new is not _unitialized_attr:
1805
1515
            setattr(obj, attr_name, new)
1806
1516
        return value
1807
1517
 
1808
 
    def overrideEnv(self, name, new):
1809
 
        """Set an environment variable, and reset it after the test.
1810
 
 
1811
 
        :param name: The environment variable name.
1812
 
 
1813
 
        :param new: The value to set the variable to. If None, the 
1814
 
            variable is deleted from the environment.
1815
 
 
1816
 
        :returns: The actual variable value.
1817
 
        """
1818
 
        value = osutils.set_or_unset_env(name, new)
1819
 
        self.addCleanup(osutils.set_or_unset_env, name, value)
1820
 
        return value
1821
 
 
1822
 
    def recordCalls(self, obj, attr_name):
1823
 
        """Monkeypatch in a wrapper that will record calls.
1824
 
 
1825
 
        The monkeypatch is automatically removed when the test concludes.
1826
 
 
1827
 
        :param obj: The namespace holding the reference to be replaced;
1828
 
            typically a module, class, or object.
1829
 
        :param attr_name: A string for the name of the attribute to 
1830
 
            patch.
1831
 
        :returns: A list that will be extended with one item every time the
1832
 
            function is called, with a tuple of (args, kwargs).
1833
 
        """
1834
 
        calls = []
1835
 
 
1836
 
        def decorator(*args, **kwargs):
1837
 
            calls.append((args, kwargs))
1838
 
            return orig(*args, **kwargs)
1839
 
        orig = self.overrideAttr(obj, attr_name, decorator)
1840
 
        return calls
1841
 
 
1842
1518
    def _cleanEnvironment(self):
1843
 
        for name, value in isolated_environ.iteritems():
1844
 
            self.overrideEnv(name, value)
 
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)
1845
1579
 
1846
1580
    def _restoreHooks(self):
1847
1581
        for klass, (name, hooks) in self._preserved_hooks.items():
1848
1582
            setattr(klass, name, hooks)
1849
 
        self._preserved_hooks.clear()
1850
 
        bzrlib.hooks._lazy_hooks = self._preserved_lazy_hooks
1851
 
        self._preserved_lazy_hooks.clear()
1852
1583
 
1853
1584
    def knownFailure(self, reason):
1854
 
        """Declare that this test fails for a known reason
1855
 
 
1856
 
        Tests that are known to fail should generally be using expectedFailure
1857
 
        with an appropriate reverse assertion if a change could cause the test
1858
 
        to start passing. Conversely if the test has no immediate prospect of
1859
 
        succeeding then using skip is more suitable.
1860
 
 
1861
 
        When this method is called while an exception is being handled, that
1862
 
        traceback will be used, otherwise a new exception will be thrown to
1863
 
        provide one but won't be reported.
1864
 
        """
1865
 
        self._add_reason(reason)
1866
 
        try:
1867
 
            exc_info = sys.exc_info()
1868
 
            if exc_info != (None, None, None):
1869
 
                self._report_traceback(exc_info)
1870
 
            else:
1871
 
                try:
1872
 
                    raise self.failureException(reason)
1873
 
                except self.failureException:
1874
 
                    exc_info = sys.exc_info()
1875
 
            # GZ 02-08-2011: Maybe cleanup this err.exc_info attribute too?
1876
 
            raise testtools.testcase._ExpectedFailure(exc_info)
1877
 
        finally:
1878
 
            del exc_info
 
1585
        """This test has failed for some known reason."""
 
1586
        raise KnownFailure(reason)
1879
1587
 
1880
1588
    def _suppress_log(self):
1881
1589
        """Remove the log info from details."""
1967
1675
            self._benchtime += time.time() - start
1968
1676
 
1969
1677
    def log(self, *args):
1970
 
        trace.mutter(*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."
1971
1714
 
1972
1715
    def get_log(self):
1973
1716
        """Get a unicode string containing the log from bzrlib.trace.
2009
1752
 
2010
1753
        self.log('run bzr: %r', args)
2011
1754
        # FIXME: don't call into logging here
2012
 
        handler = trace.EncodedStreamHandler(stderr, errors="replace",
2013
 
            level=logging.INFO)
 
1755
        handler = logging.StreamHandler(stderr)
 
1756
        handler.setLevel(logging.INFO)
2014
1757
        logger = logging.getLogger('')
2015
1758
        logger.addHandler(handler)
2016
1759
        old_ui_factory = ui.ui_factory
2023
1766
 
2024
1767
        try:
2025
1768
            try:
2026
 
                result = self.apply_redirected(
2027
 
                    ui.ui_factory.stdin,
 
1769
                result = self.apply_redirected(ui.ui_factory.stdin,
2028
1770
                    stdout, stderr,
2029
 
                    _mod_commands.run_bzr_catch_user_errors,
 
1771
                    bzrlib.commands.run_bzr_catch_user_errors,
2030
1772
                    args)
2031
1773
            except KeyboardInterrupt:
2032
1774
                # Reraise KeyboardInterrupt with contents of redirected stdout
2052
1794
        if err:
2053
1795
            self.log('errors:\n%r', err)
2054
1796
        if retcode is not None:
2055
 
            self.assertEqual(retcode, result,
 
1797
            self.assertEquals(retcode, result,
2056
1798
                              message='Unexpected return code')
2057
1799
        return result, out, err
2058
1800
 
2174
1916
    def start_bzr_subprocess(self, process_args, env_changes=None,
2175
1917
                             skip_if_plan_to_signal=False,
2176
1918
                             working_dir=None,
2177
 
                             allow_plugins=False, stderr=subprocess.PIPE):
 
1919
                             allow_plugins=False):
2178
1920
        """Start bzr in a subprocess for testing.
2179
1921
 
2180
1922
        This starts a new Python interpreter and runs bzr in there.
2192
1934
        :param skip_if_plan_to_signal: raise TestSkipped when true and system
2193
1935
            doesn't support signalling subprocesses.
2194
1936
        :param allow_plugins: If False (default) pass --no-plugins to bzr.
2195
 
        :param stderr: file to use for the subprocess's stderr.  Valid values
2196
 
            are those valid for the stderr argument of `subprocess.Popen`.
2197
 
            Default value is ``subprocess.PIPE``.
2198
1937
 
2199
1938
        :returns: Popen object for the started process.
2200
1939
        """
2204
1943
 
2205
1944
        if env_changes is None:
2206
1945
            env_changes = {}
2207
 
        # Because $HOME is set to a tempdir for the context of a test, modules
2208
 
        # installed in the user dir will not be found unless $PYTHONUSERBASE
2209
 
        # gets set to the computed directory of this parent process.
2210
 
        if site.USER_BASE is not None:
2211
 
            env_changes["PYTHONUSERBASE"] = site.USER_BASE
2212
1946
        old_env = {}
2213
1947
 
2214
1948
        def cleanup_environment():
2231
1965
            # so we will avoid using it on all platforms, just to
2232
1966
            # make sure the code path is used, and we don't break on win32
2233
1967
            cleanup_environment()
2234
 
            # Include the subprocess's log file in the test details, in case
2235
 
            # the test fails due to an error in the subprocess.
2236
 
            self._add_subprocess_log(trace._get_bzr_log_filename())
2237
1968
            command = [sys.executable]
2238
1969
            # frozen executables don't need the path to bzr
2239
1970
            if getattr(sys, "frozen", None) is None:
2243
1974
            command.extend(process_args)
2244
1975
            process = self._popen(command, stdin=subprocess.PIPE,
2245
1976
                                  stdout=subprocess.PIPE,
2246
 
                                  stderr=stderr)
 
1977
                                  stderr=subprocess.PIPE)
2247
1978
        finally:
2248
1979
            restore_environment()
2249
1980
            if cwd is not None:
2251
1982
 
2252
1983
        return process
2253
1984
 
2254
 
    def _add_subprocess_log(self, log_file_path):
2255
 
        if len(self._log_files) == 0:
2256
 
            # Register an addCleanup func.  We do this on the first call to
2257
 
            # _add_subprocess_log rather than in TestCase.setUp so that this
2258
 
            # addCleanup is registered after any cleanups for tempdirs that
2259
 
            # subclasses might create, which will probably remove the log file
2260
 
            # we want to read.
2261
 
            self.addCleanup(self._subprocess_log_cleanup)
2262
 
        # self._log_files is a set, so if a log file is reused we won't grab it
2263
 
        # twice.
2264
 
        self._log_files.add(log_file_path)
2265
 
 
2266
 
    def _subprocess_log_cleanup(self):
2267
 
        for count, log_file_path in enumerate(self._log_files):
2268
 
            # We use buffer_now=True to avoid holding the file open beyond
2269
 
            # the life of this function, which might interfere with e.g.
2270
 
            # cleaning tempdirs on Windows.
2271
 
            # XXX: Testtools 0.9.5 doesn't have the content_from_file helper
2272
 
            #detail_content = content.content_from_file(
2273
 
            #    log_file_path, buffer_now=True)
2274
 
            with open(log_file_path, 'rb') as log_file:
2275
 
                log_file_bytes = log_file.read()
2276
 
            detail_content = content.Content(content.ContentType("text",
2277
 
                "plain", {"charset": "utf8"}), lambda: [log_file_bytes])
2278
 
            self.addDetail("start_bzr_subprocess-log-%d" % (count,),
2279
 
                detail_content)
2280
 
 
2281
1985
    def _popen(self, *args, **kwargs):
2282
1986
        """Place a call to Popen.
2283
1987
 
2320
2024
        if retcode is not None and retcode != process.returncode:
2321
2025
            if process_args is None:
2322
2026
                process_args = "(unknown args)"
2323
 
            trace.mutter('Output of bzr %s:\n%s', process_args, out)
2324
 
            trace.mutter('Error for bzr %s:\n%s', process_args, err)
 
2027
            mutter('Output of bzr %s:\n%s', process_args, out)
 
2028
            mutter('Error for bzr %s:\n%s', process_args, err)
2325
2029
            self.fail('Command bzr %s failed with retcode %s != %s'
2326
2030
                      % (process_args, retcode, process.returncode))
2327
2031
        return [out, err]
2328
2032
 
2329
 
    def check_tree_shape(self, tree, shape):
2330
 
        """Compare a tree to a list of expected names.
 
2033
    def check_inventory_shape(self, inv, shape):
 
2034
        """Compare an inventory to a list of expected names.
2331
2035
 
2332
2036
        Fail if they are not precisely equal.
2333
2037
        """
2334
2038
        extras = []
2335
2039
        shape = list(shape)             # copy
2336
 
        for path, ie in tree.iter_entries_by_dir():
 
2040
        for path, ie in inv.entries():
2337
2041
            name = path.replace('\\', '/')
2338
2042
            if ie.kind == 'directory':
2339
2043
                name = name + '/'
2340
 
            if name == "/":
2341
 
                pass # ignore root entry
2342
 
            elif name in shape:
 
2044
            if name in shape:
2343
2045
                shape.remove(name)
2344
2046
            else:
2345
2047
                extras.append(name)
2386
2088
 
2387
2089
        Tests that expect to provoke LockContention errors should call this.
2388
2090
        """
2389
 
        self.overrideAttr(lockdir, '_DEFAULT_TIMEOUT_SECONDS', 0)
 
2091
        self.overrideAttr(bzrlib.lockdir, '_DEFAULT_TIMEOUT_SECONDS', 0)
2390
2092
 
2391
2093
    def make_utf8_encoded_stringio(self, encoding_type=None):
2392
2094
        """Return a StringIOWrapper instance, that will encode Unicode
2405
2107
        from bzrlib.smart import request
2406
2108
        request_handlers = request.request_handlers
2407
2109
        orig_method = request_handlers.get(verb)
2408
 
        orig_info = request_handlers.get_info(verb)
2409
2110
        request_handlers.remove(verb)
2410
 
        self.addCleanup(request_handlers.register, verb, orig_method,
2411
 
            info=orig_info)
 
2111
        self.addCleanup(request_handlers.register, verb, orig_method)
2412
2112
 
2413
2113
 
2414
2114
class CapturedCall(object):
2437
2137
class TestCaseWithMemoryTransport(TestCase):
2438
2138
    """Common test class for tests that do not need disk resources.
2439
2139
 
2440
 
    Tests that need disk resources should derive from TestCaseInTempDir
2441
 
    orTestCaseWithTransport.
 
2140
    Tests that need disk resources should derive from TestCaseWithTransport.
2442
2141
 
2443
2142
    TestCaseWithMemoryTransport sets the TEST_ROOT variable for all bzr tests.
2444
2143
 
2445
 
    For TestCaseWithMemoryTransport the ``test_home_dir`` is set to the name of
 
2144
    For TestCaseWithMemoryTransport the test_home_dir is set to the name of
2446
2145
    a directory which does not exist. This serves to help ensure test isolation
2447
 
    is preserved. ``test_dir`` is set to the TEST_ROOT, as is cwd, because they
2448
 
    must exist. However, TestCaseWithMemoryTransport does not offer local file
2449
 
    defaults for the transport in tests, nor does it obey the command line
 
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
2450
2149
    override, so tests that accidentally write to the common directory should
2451
2150
    be rare.
2452
2151
 
2453
 
    :cvar TEST_ROOT: Directory containing all temporary directories, plus a
2454
 
        ``.bzr`` directory that stops us ascending higher into the filesystem.
 
2152
    :cvar TEST_ROOT: Directory containing all temporary directories, plus
 
2153
    a .bzr directory that stops us ascending higher into the filesystem.
2455
2154
    """
2456
2155
 
2457
2156
    TEST_ROOT = None
2467
2166
        self.transport_readonly_server = None
2468
2167
        self.__vfs_server = None
2469
2168
 
2470
 
    def setUp(self):
2471
 
        super(TestCaseWithMemoryTransport, self).setUp()
2472
 
 
2473
 
        def _add_disconnect_cleanup(transport):
2474
 
            """Schedule disconnection of given transport at test cleanup
2475
 
 
2476
 
            This needs to happen for all connected transports or leaks occur.
2477
 
 
2478
 
            Note reconnections may mean we call disconnect multiple times per
2479
 
            transport which is suboptimal but seems harmless.
2480
 
            """
2481
 
            self.addCleanup(transport.disconnect)
2482
 
 
2483
 
        _mod_transport.Transport.hooks.install_named_hook('post_connect',
2484
 
            _add_disconnect_cleanup, None)
2485
 
 
2486
 
        self._make_test_root()
2487
 
        self.addCleanup(os.chdir, os.getcwdu())
2488
 
        self.makeAndChdirToTestDir()
2489
 
        self.overrideEnvironmentForTesting()
2490
 
        self.__readonly_server = None
2491
 
        self.__server = None
2492
 
        self.reduceLockdirTimeout()
2493
 
        # Each test may use its own config files even if the local config files
2494
 
        # don't actually exist. They'll rightly fail if they try to create them
2495
 
        # though.
2496
 
        self.overrideAttr(config, '_shared_stores', {})
2497
 
 
2498
2169
    def get_transport(self, relpath=None):
2499
2170
        """Return a writeable transport.
2500
2171
 
2503
2174
 
2504
2175
        :param relpath: a path relative to the base url.
2505
2176
        """
2506
 
        t = _mod_transport.get_transport_from_url(self.get_url(relpath))
 
2177
        t = _mod_transport.get_transport(self.get_url(relpath))
2507
2178
        self.assertFalse(t.is_readonly())
2508
2179
        return t
2509
2180
 
2515
2186
 
2516
2187
        :param relpath: a path relative to the base url.
2517
2188
        """
2518
 
        t = _mod_transport.get_transport_from_url(
2519
 
            self.get_readonly_url(relpath))
 
2189
        t = _mod_transport.get_transport(self.get_readonly_url(relpath))
2520
2190
        self.assertTrue(t.is_readonly())
2521
2191
        return t
2522
2192
 
2643
2313
        real branch.
2644
2314
        """
2645
2315
        root = TestCaseWithMemoryTransport.TEST_ROOT
2646
 
        try:
2647
 
            # Make sure we get a readable and accessible home for .bzr.log
2648
 
            # and/or config files, and not fallback to weird defaults (see
2649
 
            # http://pad.lv/825027).
2650
 
            self.assertIs(None, os.environ.get('BZR_HOME', None))
2651
 
            os.environ['BZR_HOME'] = root
2652
 
            wt = controldir.ControlDir.create_standalone_workingtree(root)
2653
 
            del os.environ['BZR_HOME']
2654
 
        except Exception, e:
2655
 
            self.fail("Fail to initialize the safety net: %r\n" % (e,))
2656
 
        # Hack for speed: remember the raw bytes of the dirstate file so that
2657
 
        # we don't need to re-open the wt to check it hasn't changed.
2658
 
        TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE = (
2659
 
            wt.control_transport.get_bytes('dirstate'))
 
2316
        bzrdir.BzrDir.create_standalone_workingtree(root)
2660
2317
 
2661
2318
    def _check_safety_net(self):
2662
2319
        """Check that the safety .bzr directory have not been touched.
2665
2322
        propagating. This method ensures than a test did not leaked.
2666
2323
        """
2667
2324
        root = TestCaseWithMemoryTransport.TEST_ROOT
2668
 
        t = _mod_transport.get_transport_from_path(root)
2669
 
        self.permit_url(t.base)
2670
 
        if (t.get_bytes('.bzr/checkout/dirstate') != 
2671
 
                TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE):
 
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:':
2672
2329
            # The current test have modified the /bzr directory, we need to
2673
2330
            # recreate a new one or all the followng tests will fail.
2674
2331
            # If you need to inspect its content uncomment the following line
2706
2363
        self.test_home_dir = self.test_dir + "/MemoryTransportMissingHomeDir"
2707
2364
        self.permit_dir(self.test_dir)
2708
2365
 
2709
 
    def make_branch(self, relpath, format=None, name=None):
 
2366
    def make_branch(self, relpath, format=None):
2710
2367
        """Create a branch on the transport at relpath."""
2711
2368
        repo = self.make_repository(relpath, format=format)
2712
 
        return repo.bzrdir.create_branch(append_revisions_only=False,
2713
 
                                         name=name)
2714
 
 
2715
 
    def get_default_format(self):
2716
 
        return 'default'
2717
 
 
2718
 
    def resolve_format(self, format):
2719
 
        """Resolve an object to a ControlDir format object.
2720
 
 
2721
 
        The initial format object can either already be
2722
 
        a ControlDirFormat, None (for the default format),
2723
 
        or a string with the name of the control dir format.
2724
 
 
2725
 
        :param format: Object to resolve
2726
 
        :return A ControlDirFormat instance
2727
 
        """
2728
 
        if format is None:
2729
 
            format = self.get_default_format()
2730
 
        if isinstance(format, basestring):
2731
 
            format = controldir.format_registry.make_bzrdir(format)
2732
 
        return format
 
2369
        return repo.bzrdir.create_branch()
2733
2370
 
2734
2371
    def make_bzrdir(self, relpath, format=None):
2735
2372
        try:
2739
2376
            t = _mod_transport.get_transport(maybe_a_url)
2740
2377
            if len(segments) > 1 and segments[-1] not in ('', '.'):
2741
2378
                t.ensure_base()
2742
 
            format = self.resolve_format(format)
 
2379
            if format is None:
 
2380
                format = 'default'
 
2381
            if isinstance(format, basestring):
 
2382
                format = bzrdir.format_registry.make_bzrdir(format)
2743
2383
            return format.initialize_on_transport(t)
2744
2384
        except errors.UninitializableFormat:
2745
2385
            raise TestSkipped("Format %s is not initializable." % format)
2746
2386
 
2747
 
    def make_repository(self, relpath, shared=None, format=None):
 
2387
    def make_repository(self, relpath, shared=False, format=None):
2748
2388
        """Create a repository on our default transport at relpath.
2749
2389
 
2750
2390
        Note that relpath must be a relative path, not a full url.
2761
2401
            backing_server = self.get_server()
2762
2402
        smart_server = test_server.SmartTCPServer_for_testing()
2763
2403
        self.start_server(smart_server, backing_server)
2764
 
        remote_transport = _mod_transport.get_transport_from_url(smart_server.get_url()
 
2404
        remote_transport = _mod_transport.get_transport(smart_server.get_url()
2765
2405
                                                   ).clone(path)
2766
2406
        return remote_transport
2767
2407
 
2778
2418
        test_home_dir = self.test_home_dir
2779
2419
        if isinstance(test_home_dir, unicode):
2780
2420
            test_home_dir = test_home_dir.encode(sys.getfilesystemencoding())
2781
 
        self.overrideEnv('HOME', test_home_dir)
2782
 
        self.overrideEnv('BZR_HOME', test_home_dir)
 
2421
        os.environ['HOME'] = test_home_dir
 
2422
        os.environ['BZR_HOME'] = test_home_dir
 
2423
 
 
2424
    def setUp(self):
 
2425
        super(TestCaseWithMemoryTransport, self).setUp()
 
2426
        # Ensure that ConnectedTransport doesn't leak sockets
 
2427
        def get_transport_with_cleanup(*args, **kwargs):
 
2428
            t = orig_get_transport(*args, **kwargs)
 
2429
            if isinstance(t, _mod_transport.ConnectedTransport):
 
2430
                self.addCleanup(t.disconnect)
 
2431
            return t
 
2432
 
 
2433
        orig_get_transport = self.overrideAttr(_mod_transport, 'get_transport',
 
2434
                                               get_transport_with_cleanup)
 
2435
        self._make_test_root()
 
2436
        self.addCleanup(os.chdir, os.getcwdu())
 
2437
        self.makeAndChdirToTestDir()
 
2438
        self.overrideEnvironmentForTesting()
 
2439
        self.__readonly_server = None
 
2440
        self.__server = None
 
2441
        self.reduceLockdirTimeout()
2783
2442
 
2784
2443
    def setup_smart_server_with_call_log(self):
2785
2444
        """Sets up a smart server as the transport server with a call log."""
2786
2445
        self.transport_server = test_server.SmartTCPServer_for_testing
2787
 
        self.hpss_connections = []
2788
2446
        self.hpss_calls = []
2789
2447
        import traceback
2790
2448
        # Skip the current stack down to the caller of
2793
2451
        def capture_hpss_call(params):
2794
2452
            self.hpss_calls.append(
2795
2453
                CapturedCall(params, prefix_length))
2796
 
        def capture_connect(transport):
2797
 
            self.hpss_connections.append(transport)
2798
2454
        client._SmartClient.hooks.install_named_hook(
2799
2455
            'call', capture_hpss_call, None)
2800
 
        _mod_transport.Transport.hooks.install_named_hook(
2801
 
            'post_connect', capture_connect, None)
2802
2456
 
2803
2457
    def reset_smart_call_log(self):
2804
2458
        self.hpss_calls = []
2805
 
        self.hpss_connections = []
2806
2459
 
2807
2460
 
2808
2461
class TestCaseInTempDir(TestCaseWithMemoryTransport):
2827
2480
 
2828
2481
    OVERRIDE_PYTHON = 'python'
2829
2482
 
2830
 
    def setUp(self):
2831
 
        super(TestCaseInTempDir, self).setUp()
2832
 
        # Remove the protection set in isolated_environ, we have a proper
2833
 
        # access to disk resources now.
2834
 
        self.overrideEnv('BZR_LOG', None)
2835
 
 
2836
2483
    def check_file_contents(self, filename, expect):
2837
2484
        self.log("check contents of file %s" % filename)
2838
2485
        f = file(filename)
2878
2525
        # stacking policy to honour; create a bzr dir with an unshared
2879
2526
        # repository (but not a branch - our code would be trying to escape
2880
2527
        # then!) to stop them, and permit it to be read.
2881
 
        # control = controldir.ControlDir.create(self.test_base_dir)
 
2528
        # control = bzrdir.BzrDir.create(self.test_base_dir)
2882
2529
        # control.create_repository()
2883
2530
        self.test_home_dir = self.test_base_dir + '/home'
2884
2531
        os.mkdir(self.test_home_dir)
2919
2566
                "a list or a tuple. Got %r instead" % (shape,))
2920
2567
        # It's OK to just create them using forward slashes on windows.
2921
2568
        if transport is None or transport.is_readonly():
2922
 
            transport = _mod_transport.get_transport_from_path(".")
 
2569
            transport = _mod_transport.get_transport(".")
2923
2570
        for name in shape:
2924
2571
            self.assertIsInstance(name, basestring)
2925
2572
            if name[-1] == '/':
2973
2620
    readwrite one must both define get_url() as resolving to os.getcwd().
2974
2621
    """
2975
2622
 
2976
 
    def setUp(self):
2977
 
        super(TestCaseWithTransport, self).setUp()
2978
 
        self.__vfs_server = None
2979
 
 
2980
2623
    def get_vfs_only_server(self):
2981
2624
        """See TestCaseWithMemoryTransport.
2982
2625
 
3014
2657
        # this obviously requires a format that supports branch references
3015
2658
        # so check for that by checking bzrdir.BzrDirFormat.get_default_format()
3016
2659
        # RBC 20060208
3017
 
        format = self.resolve_format(format=format)
3018
 
        if not format.supports_workingtrees:
3019
 
            b = self.make_branch(relpath+'.branch', format=format)
3020
 
            return b.create_checkout(relpath, lightweight=True)
3021
2660
        b = self.make_branch(relpath, format=format)
3022
2661
        try:
3023
2662
            return b.bzrdir.create_workingtree()
3028
2667
            if self.vfs_transport_factory is test_server.LocalURLServer:
3029
2668
                # the branch is colocated on disk, we cannot create a checkout.
3030
2669
                # hopefully callers will expect this.
3031
 
                local_controldir = controldir.ControlDir.open(
3032
 
                    self.get_vfs_only_url(relpath))
 
2670
                local_controldir= bzrdir.BzrDir.open(self.get_vfs_only_url(relpath))
3033
2671
                wt = local_controldir.create_workingtree()
3034
2672
                if wt.branch._format != b._format:
3035
2673
                    wt._branch = b
3065
2703
        self.assertFalse(differences.has_changed(),
3066
2704
            "Trees %r and %r are different: %r" % (left, right, differences))
3067
2705
 
 
2706
    def setUp(self):
 
2707
        super(TestCaseWithTransport, self).setUp()
 
2708
        self.__vfs_server = None
 
2709
 
3068
2710
    def disable_missing_extensions_warning(self):
3069
2711
        """Some tests expect a precise stderr content.
3070
2712
 
3071
2713
        There is no point in forcing them to duplicate the extension related
3072
2714
        warning.
3073
2715
        """
3074
 
        config.GlobalStack().set('ignore_missing_extensions', True)
 
2716
        config.GlobalConfig().set_user_option('ignore_missing_extensions', True)
3075
2717
 
3076
2718
 
3077
2719
class ChrootedTestCase(TestCaseWithTransport):
3319
2961
                            result_decorators=result_decorators,
3320
2962
                            )
3321
2963
    runner.stop_on_failure=stop_on_failure
3322
 
    if isinstance(suite, unittest.TestSuite):
3323
 
        # Empty out _tests list of passed suite and populate new TestSuite
3324
 
        suite._tests[:], suite = [], TestSuite(suite)
3325
2964
    # built in decorator factories:
3326
2965
    decorators = [
3327
2966
        random_order(random_seed, runner),
3425
3064
 
3426
3065
class TestDecorator(TestUtil.TestSuite):
3427
3066
    """A decorator for TestCase/TestSuite objects.
3428
 
 
3429
 
    Contains rather than flattening suite passed on construction
 
3067
    
 
3068
    Usually, subclasses should override __iter__(used when flattening test
 
3069
    suites), which we do to filter, reorder, parallelise and so on, run() and
 
3070
    debug().
3430
3071
    """
3431
3072
 
3432
 
    def __init__(self, suite=None):
3433
 
        super(TestDecorator, self).__init__()
3434
 
        if suite is not None:
3435
 
            self.addTest(suite)
3436
 
 
3437
 
    # Don't need subclass run method with suite emptying
3438
 
    run = unittest.TestSuite.run
 
3073
    def __init__(self, suite):
 
3074
        TestUtil.TestSuite.__init__(self)
 
3075
        self.addTest(suite)
 
3076
 
 
3077
    def countTestCases(self):
 
3078
        cases = 0
 
3079
        for test in self:
 
3080
            cases += test.countTestCases()
 
3081
        return cases
 
3082
 
 
3083
    def debug(self):
 
3084
        for test in self:
 
3085
            test.debug()
 
3086
 
 
3087
    def run(self, result):
 
3088
        # Use iteration on self, not self._tests, to allow subclasses to hook
 
3089
        # into __iter__.
 
3090
        for test in self:
 
3091
            if result.shouldStop:
 
3092
                break
 
3093
            test.run(result)
 
3094
        return result
3439
3095
 
3440
3096
 
3441
3097
class CountingDecorator(TestDecorator):
3452
3108
    """A decorator which excludes test matching an exclude pattern."""
3453
3109
 
3454
3110
    def __init__(self, suite, exclude_pattern):
3455
 
        super(ExcludeDecorator, self).__init__(
3456
 
            exclude_tests_by_re(suite, exclude_pattern))
 
3111
        TestDecorator.__init__(self, suite)
 
3112
        self.exclude_pattern = exclude_pattern
 
3113
        self.excluded = False
 
3114
 
 
3115
    def __iter__(self):
 
3116
        if self.excluded:
 
3117
            return iter(self._tests)
 
3118
        self.excluded = True
 
3119
        suite = exclude_tests_by_re(self, self.exclude_pattern)
 
3120
        del self._tests[:]
 
3121
        self.addTests(suite)
 
3122
        return iter(self._tests)
3457
3123
 
3458
3124
 
3459
3125
class FilterTestsDecorator(TestDecorator):
3460
3126
    """A decorator which filters tests to those matching a pattern."""
3461
3127
 
3462
3128
    def __init__(self, suite, pattern):
3463
 
        super(FilterTestsDecorator, self).__init__(
3464
 
            filter_suite_by_re(suite, pattern))
 
3129
        TestDecorator.__init__(self, suite)
 
3130
        self.pattern = pattern
 
3131
        self.filtered = False
 
3132
 
 
3133
    def __iter__(self):
 
3134
        if self.filtered:
 
3135
            return iter(self._tests)
 
3136
        self.filtered = True
 
3137
        suite = filter_suite_by_re(self, self.pattern)
 
3138
        del self._tests[:]
 
3139
        self.addTests(suite)
 
3140
        return iter(self._tests)
3465
3141
 
3466
3142
 
3467
3143
class RandomDecorator(TestDecorator):
3468
3144
    """A decorator which randomises the order of its tests."""
3469
3145
 
3470
3146
    def __init__(self, suite, random_seed, stream):
3471
 
        random_seed = self.actual_seed(random_seed)
3472
 
        stream.write("Randomizing test order using seed %s\n\n" %
3473
 
            (random_seed,))
 
3147
        TestDecorator.__init__(self, suite)
 
3148
        self.random_seed = random_seed
 
3149
        self.randomised = False
 
3150
        self.stream = stream
 
3151
 
 
3152
    def __iter__(self):
 
3153
        if self.randomised:
 
3154
            return iter(self._tests)
 
3155
        self.randomised = True
 
3156
        self.stream.write("Randomizing test order using seed %s\n\n" %
 
3157
            (self.actual_seed()))
3474
3158
        # Initialise the random number generator.
3475
 
        random.seed(random_seed)
3476
 
        super(RandomDecorator, self).__init__(randomize_suite(suite))
 
3159
        random.seed(self.actual_seed())
 
3160
        suite = randomize_suite(self)
 
3161
        del self._tests[:]
 
3162
        self.addTests(suite)
 
3163
        return iter(self._tests)
3477
3164
 
3478
 
    @staticmethod
3479
 
    def actual_seed(seed):
3480
 
        if seed == "now":
 
3165
    def actual_seed(self):
 
3166
        if self.random_seed == "now":
3481
3167
            # We convert the seed to a long to make it reuseable across
3482
3168
            # invocations (because the user can reenter it).
3483
 
            return long(time.time())
 
3169
            self.random_seed = long(time.time())
3484
3170
        else:
3485
3171
            # Convert the seed to a long if we can
3486
3172
            try:
3487
 
                return long(seed)
3488
 
            except (TypeError, ValueError):
 
3173
                self.random_seed = long(self.random_seed)
 
3174
            except:
3489
3175
                pass
3490
 
        return seed
 
3176
        return self.random_seed
3491
3177
 
3492
3178
 
3493
3179
class TestFirstDecorator(TestDecorator):
3494
3180
    """A decorator which moves named tests to the front."""
3495
3181
 
3496
3182
    def __init__(self, suite, pattern):
3497
 
        super(TestFirstDecorator, self).__init__()
3498
 
        self.addTests(split_suite_by_re(suite, pattern))
 
3183
        TestDecorator.__init__(self, suite)
 
3184
        self.pattern = pattern
 
3185
        self.filtered = False
 
3186
 
 
3187
    def __iter__(self):
 
3188
        if self.filtered:
 
3189
            return iter(self._tests)
 
3190
        self.filtered = True
 
3191
        suites = split_suite_by_re(self, self.pattern)
 
3192
        del self._tests[:]
 
3193
        self.addTests(suites)
 
3194
        return iter(self._tests)
3499
3195
 
3500
3196
 
3501
3197
def partition_tests(suite, count):
3533
3229
    """
3534
3230
    concurrency = osutils.local_concurrency()
3535
3231
    result = []
3536
 
    from subunit import ProtocolTestCase
 
3232
    from subunit import TestProtocolClient, ProtocolTestCase
3537
3233
    from subunit.test_results import AutoTimingTestResultDecorator
3538
3234
    class TestInOtherProcess(ProtocolTestCase):
3539
3235
        # Should be in subunit, I think. RBC.
3545
3241
            try:
3546
3242
                ProtocolTestCase.run(self, result)
3547
3243
            finally:
3548
 
                pid, status = os.waitpid(self.pid, 0)
3549
 
            # GZ 2011-10-18: If status is nonzero, should report to the result
3550
 
            #                that something went wrong.
 
3244
                os.waitpid(self.pid, 0)
3551
3245
 
3552
3246
    test_blocks = partition_tests(suite, concurrency)
3553
 
    # Clear the tests from the original suite so it doesn't keep them alive
3554
 
    suite._tests[:] = []
3555
3247
    for process_tests in test_blocks:
3556
 
        process_suite = TestUtil.TestSuite(process_tests)
3557
 
        # Also clear each split list so new suite has only reference
3558
 
        process_tests[:] = []
 
3248
        process_suite = TestUtil.TestSuite()
 
3249
        process_suite.addTests(process_tests)
3559
3250
        c2pread, c2pwrite = os.pipe()
3560
3251
        pid = os.fork()
3561
3252
        if pid == 0:
 
3253
            workaround_zealous_crypto_random()
3562
3254
            try:
3563
 
                stream = os.fdopen(c2pwrite, 'wb', 1)
3564
 
                workaround_zealous_crypto_random()
3565
3255
                os.close(c2pread)
3566
3256
                # Leave stderr and stdout open so we can see test noise
3567
3257
                # Close stdin so that the child goes away if it decides to
3568
3258
                # read from stdin (otherwise its a roulette to see what
3569
3259
                # child actually gets keystrokes for pdb etc).
3570
3260
                sys.stdin.close()
 
3261
                sys.stdin = None
 
3262
                stream = os.fdopen(c2pwrite, 'wb', 1)
3571
3263
                subunit_result = AutoTimingTestResultDecorator(
3572
 
                    SubUnitBzrProtocolClient(stream))
 
3264
                    TestProtocolClient(stream))
3573
3265
                process_suite.run(subunit_result)
3574
 
            except:
3575
 
                # Try and report traceback on stream, but exit with error even
3576
 
                # if stream couldn't be created or something else goes wrong.
3577
 
                # The traceback is formatted to a string and written in one go
3578
 
                # to avoid interleaving lines from multiple failing children.
3579
 
                try:
3580
 
                    stream.write(traceback.format_exc())
3581
 
                finally:
3582
 
                    os._exit(1)
3583
 
            os._exit(0)
 
3266
            finally:
 
3267
                os._exit(0)
3584
3268
        else:
3585
3269
            os.close(c2pwrite)
3586
3270
            stream = os.fdopen(c2pread, 'rb', 1)
3647
3331
    return result
3648
3332
 
3649
3333
 
3650
 
class ProfileResult(testtools.ExtendedToOriginalDecorator):
 
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):
3651
3367
    """Generate profiling data for all activity between start and success.
3652
3368
    
3653
3369
    The profile data is appended to the test's _benchcalls attribute and can
3665
3381
        # unavoidably fail.
3666
3382
        bzrlib.lsprof.BzrProfiler.profiler_block = 0
3667
3383
        self.profiler.start()
3668
 
        testtools.ExtendedToOriginalDecorator.startTest(self, test)
 
3384
        ForwardingResult.startTest(self, test)
3669
3385
 
3670
3386
    def addSuccess(self, test):
3671
3387
        stats = self.profiler.stop()
3675
3391
            test._benchcalls = []
3676
3392
            calls = test._benchcalls
3677
3393
        calls.append(((test.id(), "", ""), stats))
3678
 
        testtools.ExtendedToOriginalDecorator.addSuccess(self, test)
 
3394
        ForwardingResult.addSuccess(self, test)
3679
3395
 
3680
3396
    def stopTest(self, test):
3681
 
        testtools.ExtendedToOriginalDecorator.stopTest(self, test)
 
3397
        ForwardingResult.stopTest(self, test)
3682
3398
        self.profiler = None
3683
3399
 
3684
3400
 
3692
3408
#                           with proper exclusion rules.
3693
3409
#   -Ethreads               Will display thread ident at creation/join time to
3694
3410
#                           help track thread leaks
3695
 
#   -Euncollected_cases     Display the identity of any test cases that weren't
3696
 
#                           deallocated after being completed.
3697
 
#   -Econfig_stats          Will collect statistics using addDetail
3698
3411
selftest_debug_flags = set()
3699
3412
 
3700
3413
 
3804
3517
 
3805
3518
    :return: (absents, duplicates) absents is a list containing the test found
3806
3519
        in id_list but not in test_suite, duplicates is a list containing the
3807
 
        tests found multiple times in test_suite.
 
3520
        test found multiple times in test_suite.
3808
3521
 
3809
3522
    When using a prefined test id list, it may occurs that some tests do not
3810
3523
    exist anymore or that some tests use the same id. This function warns the
3894
3607
                key, obj, help=help, info=info, override_existing=False)
3895
3608
        except KeyError:
3896
3609
            actual = self.get(key)
3897
 
            trace.note(
3898
 
                'Test prefix alias %s is already used for %s, ignoring %s'
3899
 
                % (key, actual, obj))
 
3610
            note('Test prefix alias %s is already used for %s, ignoring %s'
 
3611
                 % (key, actual, obj))
3900
3612
 
3901
3613
    def resolve_alias(self, id_start):
3902
3614
        """Replace the alias by the prefix in the given string.
3934
3646
        'bzrlib.doc',
3935
3647
        'bzrlib.tests.blackbox',
3936
3648
        'bzrlib.tests.commands',
 
3649
        'bzrlib.tests.doc_generate',
3937
3650
        'bzrlib.tests.per_branch',
3938
3651
        'bzrlib.tests.per_bzrdir',
3939
3652
        'bzrlib.tests.per_controldir',
3951
3664
        'bzrlib.tests.per_repository',
3952
3665
        'bzrlib.tests.per_repository_chk',
3953
3666
        'bzrlib.tests.per_repository_reference',
3954
 
        'bzrlib.tests.per_repository_vf',
3955
3667
        'bzrlib.tests.per_uifactory',
3956
3668
        'bzrlib.tests.per_versionedfile',
3957
3669
        'bzrlib.tests.per_workingtree',
3991
3703
        'bzrlib.tests.test_commit_merge',
3992
3704
        'bzrlib.tests.test_config',
3993
3705
        'bzrlib.tests.test_conflicts',
3994
 
        'bzrlib.tests.test_controldir',
3995
3706
        'bzrlib.tests.test_counted_lock',
3996
3707
        'bzrlib.tests.test_crash',
3997
3708
        'bzrlib.tests.test_decorators',
3998
3709
        'bzrlib.tests.test_delta',
3999
3710
        'bzrlib.tests.test_debug',
 
3711
        'bzrlib.tests.test_deprecated_graph',
4000
3712
        'bzrlib.tests.test_diff',
4001
3713
        'bzrlib.tests.test_directory_service',
4002
3714
        'bzrlib.tests.test_dirstate',
4003
3715
        'bzrlib.tests.test_email_message',
4004
3716
        'bzrlib.tests.test_eol_filters',
4005
3717
        'bzrlib.tests.test_errors',
4006
 
        'bzrlib.tests.test_estimate_compressed_size',
4007
3718
        'bzrlib.tests.test_export',
4008
 
        'bzrlib.tests.test_export_pot',
4009
3719
        'bzrlib.tests.test_extract',
4010
 
        'bzrlib.tests.test_features',
4011
3720
        'bzrlib.tests.test_fetch',
4012
3721
        'bzrlib.tests.test_fixtures',
4013
3722
        'bzrlib.tests.test_fifo_cache',
4014
3723
        'bzrlib.tests.test_filters',
4015
 
        'bzrlib.tests.test_filter_tree',
4016
3724
        'bzrlib.tests.test_ftp_transport',
4017
3725
        'bzrlib.tests.test_foreign',
4018
3726
        'bzrlib.tests.test_generate_docs',
4027
3735
        'bzrlib.tests.test_http',
4028
3736
        'bzrlib.tests.test_http_response',
4029
3737
        'bzrlib.tests.test_https_ca_bundle',
4030
 
        'bzrlib.tests.test_https_urllib',
4031
 
        'bzrlib.tests.test_i18n',
4032
3738
        'bzrlib.tests.test_identitymap',
4033
3739
        'bzrlib.tests.test_ignores',
4034
3740
        'bzrlib.tests.test_index',
4053
3759
        'bzrlib.tests.test_merge3',
4054
3760
        'bzrlib.tests.test_merge_core',
4055
3761
        'bzrlib.tests.test_merge_directive',
4056
 
        'bzrlib.tests.test_mergetools',
4057
3762
        'bzrlib.tests.test_missing',
4058
3763
        'bzrlib.tests.test_msgeditor',
4059
3764
        'bzrlib.tests.test_multiparent',
4068
3773
        'bzrlib.tests.test_permissions',
4069
3774
        'bzrlib.tests.test_plugins',
4070
3775
        'bzrlib.tests.test_progress',
4071
 
        'bzrlib.tests.test_pyutils',
4072
3776
        'bzrlib.tests.test_read_bundle',
4073
3777
        'bzrlib.tests.test_reconcile',
4074
3778
        'bzrlib.tests.test_reconfigure',
4082
3786
        'bzrlib.tests.test_revisiontree',
4083
3787
        'bzrlib.tests.test_rio',
4084
3788
        'bzrlib.tests.test_rules',
4085
 
        'bzrlib.tests.test_url_policy_open',
4086
3789
        'bzrlib.tests.test_sampler',
4087
 
        'bzrlib.tests.test_scenarios',
4088
3790
        'bzrlib.tests.test_script',
4089
3791
        'bzrlib.tests.test_selftest',
4090
3792
        'bzrlib.tests.test_serializer',
4095
3797
        'bzrlib.tests.test_smart',
4096
3798
        'bzrlib.tests.test_smart_add',
4097
3799
        'bzrlib.tests.test_smart_request',
4098
 
        'bzrlib.tests.test_smart_signals',
4099
3800
        'bzrlib.tests.test_smart_transport',
4100
3801
        'bzrlib.tests.test_smtp_connection',
4101
3802
        'bzrlib.tests.test_source',
4111
3812
        'bzrlib.tests.test_testament',
4112
3813
        'bzrlib.tests.test_textfile',
4113
3814
        'bzrlib.tests.test_textmerge',
4114
 
        'bzrlib.tests.test_cethread',
4115
3815
        'bzrlib.tests.test_timestamp',
4116
3816
        'bzrlib.tests.test_trace',
4117
3817
        'bzrlib.tests.test_transactions',
4128
3828
        'bzrlib.tests.test_upgrade',
4129
3829
        'bzrlib.tests.test_upgrade_stacked',
4130
3830
        'bzrlib.tests.test_urlutils',
4131
 
        'bzrlib.tests.test_utextwrap',
4132
3831
        'bzrlib.tests.test_version',
4133
3832
        'bzrlib.tests.test_version_info',
4134
3833
        'bzrlib.tests.test_versionedfile',
4135
 
        'bzrlib.tests.test_vf_search',
4136
3834
        'bzrlib.tests.test_weave',
4137
3835
        'bzrlib.tests.test_whitebox',
4138
3836
        'bzrlib.tests.test_win32utils',
4152
3850
        'bzrlib',
4153
3851
        'bzrlib.branchbuilder',
4154
3852
        'bzrlib.decorators',
 
3853
        'bzrlib.export',
4155
3854
        'bzrlib.inventory',
4156
3855
        'bzrlib.iterablefile',
4157
3856
        'bzrlib.lockdir',
4158
3857
        'bzrlib.merge3',
4159
3858
        'bzrlib.option',
4160
 
        'bzrlib.pyutils',
4161
3859
        'bzrlib.symbol_versioning',
4162
3860
        'bzrlib.tests',
4163
3861
        'bzrlib.tests.fixtures',
4164
3862
        'bzrlib.timestamp',
4165
 
        'bzrlib.transport.http',
4166
3863
        'bzrlib.version_info_formats.format_custom',
4167
3864
        ]
4168
3865
 
4221
3918
        try:
4222
3919
            # note that this really does mean "report only" -- doctest
4223
3920
            # still runs the rest of the examples
4224
 
            doc_suite = IsolatedDocTestSuite(
4225
 
                mod, optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
 
3921
            doc_suite = doctest.DocTestSuite(mod,
 
3922
                optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
4226
3923
        except ValueError, e:
4227
3924
            print '**failed to get doctest for: %s\n%s' % (mod, e)
4228
3925
            raise
4231
3928
        suite.addTest(doc_suite)
4232
3929
 
4233
3930
    default_encoding = sys.getdefaultencoding()
4234
 
    for name, plugin in _mod_plugin.plugins().items():
 
3931
    for name, plugin in bzrlib.plugin.plugins().items():
4235
3932
        if not interesting_module(plugin.module.__name__):
4236
3933
            continue
4237
3934
        plugin_suite = plugin.test_suite()
4243
3940
        if plugin_suite is not None:
4244
3941
            suite.addTest(plugin_suite)
4245
3942
        if default_encoding != sys.getdefaultencoding():
4246
 
            trace.warning(
 
3943
            bzrlib.trace.warning(
4247
3944
                'Plugin "%s" tried to reset default encoding to: %s', name,
4248
3945
                sys.getdefaultencoding())
4249
3946
            reload(sys)
4264
3961
            # Some tests mentioned in the list are not in the test suite. The
4265
3962
            # list may be out of date, report to the tester.
4266
3963
            for id in not_found:
4267
 
                trace.warning('"%s" not found in the test suite', id)
 
3964
                bzrlib.trace.warning('"%s" not found in the test suite', id)
4268
3965
        for id in duplicates:
4269
 
            trace.warning('"%s" is used as an id by several tests', id)
 
3966
            bzrlib.trace.warning('"%s" is used as an id by several tests', id)
4270
3967
 
4271
3968
    return suite
4272
3969
 
4273
3970
 
4274
 
def multiply_scenarios(*scenarios):
4275
 
    """Multiply two or more iterables of scenarios.
4276
 
 
4277
 
    It is safe to pass scenario generators or iterators.
4278
 
 
4279
 
    :returns: A list of compound scenarios: the cross-product of all 
4280
 
        scenarios, with the names concatenated and the parameters
4281
 
        merged together.
4282
 
    """
4283
 
    return reduce(_multiply_two_scenarios, map(list, scenarios))
4284
 
 
4285
 
 
4286
 
def _multiply_two_scenarios(scenarios_left, scenarios_right):
 
3971
def multiply_scenarios(scenarios_left, scenarios_right):
4287
3972
    """Multiply two sets of scenarios.
4288
3973
 
4289
3974
    :returns: the cartesian product of the two sets of scenarios, that is
4353
4038
    """Copy test and apply scenario to it.
4354
4039
 
4355
4040
    :param test: A test to adapt.
4356
 
    :param scenario: A tuple describing the scenario.
 
4041
    :param scenario: A tuple describing the scenarion.
4357
4042
        The first element of the tuple is the new test id.
4358
4043
        The second element is a dict containing attributes to set on the
4359
4044
        test.
4413
4098
        the module is available.
4414
4099
    """
4415
4100
 
4416
 
    from bzrlib.tests.features import ModuleAvailableFeature
4417
 
    py_module = pyutils.get_named_object(py_module_name)
 
4101
    py_module = __import__(py_module_name, {}, {}, ['NO_SUCH_ATTRIB'])
4418
4102
    scenarios = [
4419
4103
        ('python', {'module': py_module}),
4420
4104
    ]
4460
4144
                         % (os.path.basename(dirname), printable_e))
4461
4145
 
4462
4146
 
 
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
 
4463
4306
def probe_unicode_in_user_encoding():
4464
4307
    """Try to encode several unicode strings to use in unicode-aware tests.
4465
4308
    Return first successfull match.
4493
4336
    return None
4494
4337
 
4495
4338
 
 
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')
4496
4502
# Only define SubUnitBzrRunner if subunit is available.
4497
4503
try:
4498
4504
    from subunit import TestProtocolClient
4499
4505
    from subunit.test_results import AutoTimingTestResultDecorator
4500
4506
    class SubUnitBzrProtocolClient(TestProtocolClient):
4501
4507
 
4502
 
        def stopTest(self, test):
4503
 
            super(SubUnitBzrProtocolClient, self).stopTest(test)
4504
 
            _clear__type_equality_funcs(test)
4505
 
 
4506
4508
        def addSuccess(self, test, details=None):
4507
4509
            # The subunit client always includes the details in the subunit
4508
4510
            # stream, but we don't want to include it in ours.
4520
4522
except ImportError:
4521
4523
    pass
4522
4524
 
4523
 
 
4524
 
# API compatibility for old plugins; see bug 892622.
4525
 
for name in [
4526
 
    'Feature',
4527
 
    'HTTPServerFeature', 
4528
 
    'ModuleAvailableFeature',
4529
 
    'HTTPSServerFeature', 'SymlinkFeature', 'HardlinkFeature',
4530
 
    'OsFifoFeature', 'UnicodeFilenameFeature',
4531
 
    'ByteStringNamedFilesystem', 'UTF8Filesystem',
4532
 
    'BreakinFeature', 'CaseInsCasePresFilenameFeature',
4533
 
    'CaseInsensitiveFilesystemFeature', 'case_sensitive_filesystem_feature',
4534
 
    'posix_permissions_feature',
4535
 
    ]:
4536
 
    globals()[name] = _CompatabilityThunkFeature(
4537
 
        symbol_versioning.deprecated_in((2, 5, 0)),
4538
 
        'bzrlib.tests', name,
4539
 
        name, 'bzrlib.tests.features')
4540
 
 
4541
 
 
4542
 
for (old_name, new_name) in [
4543
 
    ('UnicodeFilename', 'UnicodeFilenameFeature'),
4544
 
    ]:
4545
 
    globals()[name] = _CompatabilityThunkFeature(
4546
 
        symbol_versioning.deprecated_in((2, 5, 0)),
4547
 
        'bzrlib.tests', old_name,
4548
 
        new_name, 'bzrlib.tests.features')
 
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()