~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-09-01 08:02:42 UTC
  • mfrom: (5390.3.3 faster-revert-593560)
  • Revision ID: pqm@pqm.ubuntu.com-20100901080242-esg62ody4frwmy66
(spiv) Avoid repeatedly calling self.target.all_file_ids() in
 InterTree.iter_changes. (Andrew Bennetts)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 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
 
39
import math
35
40
import os
36
 
import platform
37
41
import pprint
38
42
import random
39
43
import re
40
44
import shlex
41
 
import site
42
45
import stat
43
46
import subprocess
44
47
import sys
52
55
import testtools
53
56
# nb: check this before importing anything else from within it
54
57
_testtools_version = getattr(testtools, '__version__', ())
55
 
if _testtools_version < (0, 9, 5):
56
 
    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"
57
60
        % (testtools.__file__, _testtools_version))
58
61
from testtools import content
59
62
 
60
 
import bzrlib
61
63
from bzrlib import (
62
64
    branchbuilder,
63
 
    controldir,
 
65
    bzrdir,
64
66
    chk_map,
65
 
    commands as _mod_commands,
66
67
    config,
67
 
    i18n,
68
68
    debug,
69
69
    errors,
70
70
    hooks,
71
71
    lock as _mod_lock,
72
 
    lockdir,
73
72
    memorytree,
74
73
    osutils,
75
 
    plugin as _mod_plugin,
76
 
    pyutils,
 
74
    progress,
77
75
    ui,
78
76
    urlutils,
79
77
    registry,
80
 
    symbol_versioning,
81
 
    trace,
82
78
    transport as _mod_transport,
83
79
    workingtree,
84
80
    )
 
81
import bzrlib.branch
 
82
import bzrlib.commands
 
83
import bzrlib.timestamp
 
84
import bzrlib.export
 
85
import bzrlib.inventory
 
86
import bzrlib.iterablefile
 
87
import bzrlib.lockdir
85
88
try:
86
89
    import bzrlib.lsprof
87
90
except ImportError:
88
91
    # lsprof not available
89
92
    pass
90
 
from bzrlib.smart import client, request
 
93
from bzrlib.merge import merge_inner
 
94
import bzrlib.merge3
 
95
import bzrlib.plugin
 
96
from bzrlib.smart import client, request, server
 
97
import bzrlib.store
 
98
from bzrlib import symbol_versioning
 
99
from bzrlib.symbol_versioning import (
 
100
    DEPRECATED_PARAMETER,
 
101
    deprecated_function,
 
102
    deprecated_in,
 
103
    deprecated_method,
 
104
    deprecated_passed,
 
105
    )
 
106
import bzrlib.trace
91
107
from bzrlib.transport import (
92
108
    memory,
93
109
    pathfilter,
94
110
    )
95
 
from bzrlib.symbol_versioning import (
96
 
    deprecated_function,
97
 
    deprecated_in,
98
 
    )
 
111
from bzrlib.trace import mutter, note
99
112
from bzrlib.tests import (
100
 
    fixtures,
101
113
    test_server,
102
114
    TestUtil,
103
115
    treeshape,
104
116
    )
105
117
from bzrlib.ui import NullProgressView
106
118
from bzrlib.ui.text import TextUIFactory
107
 
from bzrlib.tests.features import _CompatabilityThunkFeature
 
119
import bzrlib.version_info_formats.format_custom
 
120
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
108
121
 
109
122
# Mark this python module as being part of the implementation
110
123
# of unittest: this gives us better tracebacks where the last
122
135
SUBUNIT_SEEK_SET = 0
123
136
SUBUNIT_SEEK_CUR = 1
124
137
 
125
 
# These are intentionally brought into this namespace. That way plugins, etc
126
 
# can just "from bzrlib.tests import TestCase, TestLoader, etc"
127
 
TestSuite = TestUtil.TestSuite
128
 
TestLoader = TestUtil.TestLoader
129
 
 
130
 
# Tests should run in a clean and clearly defined environment. The goal is to
131
 
# keep them isolated from the running environment as mush as possible. The test
132
 
# framework ensures the variables defined below are set (or deleted if the
133
 
# value is None) before a test is run and reset to their original value after
134
 
# the test is run. Generally if some code depends on an environment variable,
135
 
# the tests should start without this variable in the environment. There are a
136
 
# few exceptions but you shouldn't violate this rule lightly.
137
 
isolated_environ = {
138
 
    'BZR_HOME': None,
139
 
    'HOME': None,
140
 
    'XDG_CONFIG_HOME': None,
141
 
    # bzr now uses the Win32 API and doesn't rely on APPDATA, but the
142
 
    # tests do check our impls match APPDATA
143
 
    'BZR_EDITOR': None, # test_msgeditor manipulates this variable
144
 
    'VISUAL': None,
145
 
    'EDITOR': None,
146
 
    'BZR_EMAIL': None,
147
 
    'BZREMAIL': None, # may still be present in the environment
148
 
    'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
149
 
    'BZR_PROGRESS_BAR': None,
150
 
    # This should trap leaks to ~/.bzr.log. This occurs when tests use TestCase
151
 
    # as a base class instead of TestCaseInTempDir. Tests inheriting from
152
 
    # TestCase should not use disk resources, BZR_LOG is one.
153
 
    'BZR_LOG': '/you-should-use-TestCaseInTempDir-if-you-need-a-log-file',
154
 
    'BZR_PLUGIN_PATH': None,
155
 
    'BZR_DISABLE_PLUGINS': None,
156
 
    'BZR_PLUGINS_AT': None,
157
 
    'BZR_CONCURRENCY': None,
158
 
    # Make sure that any text ui tests are consistent regardless of
159
 
    # the environment the test case is run in; you may want tests that
160
 
    # test other combinations.  'dumb' is a reasonable guess for tests
161
 
    # going to a pipe or a StringIO.
162
 
    'TERM': 'dumb',
163
 
    'LINES': '25',
164
 
    'COLUMNS': '80',
165
 
    'BZR_COLUMNS': '80',
166
 
    # Disable SSH Agent
167
 
    'SSH_AUTH_SOCK': None,
168
 
    # Proxies
169
 
    'http_proxy': None,
170
 
    'HTTP_PROXY': None,
171
 
    'https_proxy': None,
172
 
    'HTTPS_PROXY': None,
173
 
    'no_proxy': None,
174
 
    'NO_PROXY': None,
175
 
    'all_proxy': None,
176
 
    'ALL_PROXY': None,
177
 
    # Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
178
 
    # least. If you do (care), please update this comment
179
 
    # -- vila 20080401
180
 
    'ftp_proxy': None,
181
 
    'FTP_PROXY': None,
182
 
    'BZR_REMOTE_PATH': None,
183
 
    # Generally speaking, we don't want apport reporting on crashes in
184
 
    # the test envirnoment unless we're specifically testing apport,
185
 
    # so that it doesn't leak into the real system environment.  We
186
 
    # use an env var so it propagates to subprocesses.
187
 
    'APPORT_DISABLE': '1',
188
 
    }
189
 
 
190
 
 
191
 
def override_os_environ(test, env=None):
192
 
    """Modify os.environ keeping a copy.
193
 
    
194
 
    :param test: A test instance
195
 
 
196
 
    :param env: A dict containing variable definitions to be installed
197
 
    """
198
 
    if env is None:
199
 
        env = isolated_environ
200
 
    test._original_os_environ = dict([(var, value)
201
 
                                      for var, value in os.environ.iteritems()])
202
 
    for var, value in env.iteritems():
203
 
        osutils.set_or_unset_env(var, value)
204
 
        if var not in test._original_os_environ:
205
 
            # The var is new, add it with a value of None, so
206
 
            # restore_os_environ will delete it
207
 
            test._original_os_environ[var] = None
208
 
 
209
 
 
210
 
def restore_os_environ(test):
211
 
    """Restore os.environ to its original state.
212
 
 
213
 
    :param test: A test instance previously passed to override_os_environ.
214
 
    """
215
 
    for var, value in test._original_os_environ.iteritems():
216
 
        # Restore the original value (or delete it if the value has been set to
217
 
        # None in override_os_environ).
218
 
        osutils.set_or_unset_env(var, value)
219
 
 
220
 
 
221
 
def _clear__type_equality_funcs(test):
222
 
    """Cleanup bound methods stored on TestCase instances
223
 
 
224
 
    Clear the dict breaking a few (mostly) harmless cycles in the affected
225
 
    unittests released with Python 2.6 and initial Python 2.7 versions.
226
 
 
227
 
    For a few revisions between Python 2.7.1 and Python 2.7.2 that annoyingly
228
 
    shipped in Oneiric, an object with no clear method was used, hence the
229
 
    extra complications, see bug 809048 for details.
230
 
    """
231
 
    type_equality_funcs = getattr(test, "_type_equality_funcs", None)
232
 
    if type_equality_funcs is not None:
233
 
        tef_clear = getattr(type_equality_funcs, "clear", None)
234
 
        if tef_clear is None:
235
 
            tef_instance_dict = getattr(type_equality_funcs, "__dict__", None)
236
 
            if tef_instance_dict is not None:
237
 
                tef_clear = tef_instance_dict.clear
238
 
        if tef_clear is not None:
239
 
            tef_clear()
240
 
 
241
138
 
242
139
class ExtendedTestResult(testtools.TextTestResult):
243
140
    """Accepts, reports and accumulates the results of running tests.
248
145
    different types of display.
249
146
 
250
147
    When a test finishes, in whatever way, it calls one of the addSuccess,
251
 
    addFailure or addError methods.  These in turn may redirect to a more
 
148
    addFailure or addError classes.  These in turn may redirect to a more
252
149
    specific case for the special test results supported by our extended
253
150
    tests.
254
151
 
293
190
        self.count = 0
294
191
        self._overall_start_time = time.time()
295
192
        self._strict = strict
296
 
        self._first_thread_leaker_id = None
297
 
        self._tests_leaking_threads_count = 0
298
 
        self._traceback_from_test = None
299
193
 
300
194
    def stopTestRun(self):
301
195
        run = self.testsRun
340
234
            ok = self.wasStrictlySuccessful()
341
235
        else:
342
236
            ok = self.wasSuccessful()
343
 
        if self._first_thread_leaker_id:
 
237
        if TestCase._first_thread_leaker_id:
344
238
            self.stream.write(
345
239
                '%s is leaking threads among %d leaking tests.\n' % (
346
 
                self._first_thread_leaker_id,
347
 
                self._tests_leaking_threads_count))
 
240
                TestCase._first_thread_leaker_id,
 
241
                TestCase._leaking_threads_tests))
348
242
            # We don't report the main thread as an active one.
349
243
            self.stream.write(
350
244
                '%d non-main threads were left active in the end.\n'
351
 
                % (len(self._active_threads) - 1))
 
245
                % (TestCase._active_threads - 1))
352
246
 
353
247
    def getDescription(self, test):
354
248
        return test.id()
361
255
 
362
256
    def _elapsedTestTimeString(self):
363
257
        """Return a time string for the overall time the current test has taken."""
364
 
        return self._formatTime(self._delta_to_float(
365
 
            self._now() - self._start_datetime))
 
258
        return self._formatTime(time.time() - self._start_time)
366
259
 
367
260
    def _testTimeString(self, testCase):
368
261
        benchmark_time = self._extractBenchmarkTime(testCase)
382
275
        what = re.sub(r'^bzrlib\.tests\.', '', what)
383
276
        return what
384
277
 
385
 
    # GZ 2010-10-04: Cloned tests may end up harmlessly calling this method
386
 
    #                multiple times in a row, because the handler is added for
387
 
    #                each test but the container list is shared between cases.
388
 
    #                See lp:498869 lp:625574 and lp:637725 for background.
389
 
    def _record_traceback_from_test(self, exc_info):
390
 
        """Store the traceback from passed exc_info tuple till"""
391
 
        self._traceback_from_test = exc_info[2]
392
 
 
393
278
    def startTest(self, test):
394
279
        super(ExtendedTestResult, self).startTest(test)
395
280
        if self.count == 0:
396
281
            self.startTests()
397
 
        self.count += 1
398
282
        self.report_test_start(test)
399
283
        test.number = self.count
400
284
        self._recordTestStartTime()
401
 
        # Make testtools cases give us the real traceback on failure
402
 
        addOnException = getattr(test, "addOnException", None)
403
 
        if addOnException is not None:
404
 
            addOnException(self._record_traceback_from_test)
405
 
        # Only check for thread leaks on bzrlib derived test cases
406
 
        if isinstance(test, TestCase):
407
 
            test.addCleanup(self._check_leaked_threads, test)
408
 
 
409
 
    def stopTest(self, test):
410
 
        super(ExtendedTestResult, self).stopTest(test)
411
 
        # Manually break cycles, means touching various private things but hey
412
 
        getDetails = getattr(test, "getDetails", None)
413
 
        if getDetails is not None:
414
 
            getDetails().clear()
415
 
        _clear__type_equality_funcs(test)
416
 
        self._traceback_from_test = None
417
285
 
418
286
    def startTests(self):
419
 
        self.report_tests_starting()
420
 
        self._active_threads = threading.enumerate()
421
 
 
422
 
    def _check_leaked_threads(self, test):
423
 
        """See if any threads have leaked since last call
424
 
 
425
 
        A sample of live threads is stored in the _active_threads attribute,
426
 
        when this method runs it compares the current live threads and any not
427
 
        in the previous sample are treated as having leaked.
428
 
        """
429
 
        now_active_threads = set(threading.enumerate())
430
 
        threads_leaked = now_active_threads.difference(self._active_threads)
431
 
        if threads_leaked:
432
 
            self._report_thread_leak(test, threads_leaked, now_active_threads)
433
 
            self._tests_leaking_threads_count += 1
434
 
            if self._first_thread_leaker_id is None:
435
 
                self._first_thread_leaker_id = test.id()
436
 
            self._active_threads = now_active_threads
 
287
        import platform
 
288
        if getattr(sys, 'frozen', None) is None:
 
289
            bzr_path = osutils.realpath(sys.argv[0])
 
290
        else:
 
291
            bzr_path = sys.executable
 
292
        self.stream.write(
 
293
            'bzr selftest: %s\n' % (bzr_path,))
 
294
        self.stream.write(
 
295
            '   %s\n' % (
 
296
                    bzrlib.__path__[0],))
 
297
        self.stream.write(
 
298
            '   bzr-%s python-%s %s\n' % (
 
299
                    bzrlib.version_string,
 
300
                    bzrlib._format_version_tuple(sys.version_info),
 
301
                    platform.platform(aliased=1),
 
302
                    ))
 
303
        self.stream.write('\n')
437
304
 
438
305
    def _recordTestStartTime(self):
439
306
        """Record that a test has started."""
440
 
        self._start_datetime = self._now()
 
307
        self._start_time = time.time()
 
308
 
 
309
    def _cleanupLogFile(self, test):
 
310
        # We can only do this if we have one of our TestCases, not if
 
311
        # we have a doctest.
 
312
        setKeepLogfile = getattr(test, 'setKeepLogfile', None)
 
313
        if setKeepLogfile is not None:
 
314
            setKeepLogfile()
441
315
 
442
316
    def addError(self, test, err):
443
317
        """Tell result that test finished with an error.
445
319
        Called from the TestCase run() method when the test
446
320
        fails with an unexpected error.
447
321
        """
448
 
        self._post_mortem(self._traceback_from_test)
 
322
        self._post_mortem()
449
323
        super(ExtendedTestResult, self).addError(test, err)
450
324
        self.error_count += 1
451
325
        self.report_error(test, err)
452
326
        if self.stop_early:
453
327
            self.stop()
 
328
        self._cleanupLogFile(test)
454
329
 
455
330
    def addFailure(self, test, err):
456
331
        """Tell result that test failed.
458
333
        Called from the TestCase run() method when the test
459
334
        fails because e.g. an assert() method failed.
460
335
        """
461
 
        self._post_mortem(self._traceback_from_test)
 
336
        self._post_mortem()
462
337
        super(ExtendedTestResult, self).addFailure(test, err)
463
338
        self.failure_count += 1
464
339
        self.report_failure(test, err)
465
340
        if self.stop_early:
466
341
            self.stop()
 
342
        self._cleanupLogFile(test)
467
343
 
468
344
    def addSuccess(self, test, details=None):
469
345
        """Tell result that test completed successfully.
477
353
                    self._formatTime(benchmark_time),
478
354
                    test.id()))
479
355
        self.report_success(test)
 
356
        self._cleanupLogFile(test)
480
357
        super(ExtendedTestResult, self).addSuccess(test)
481
358
        test._log_contents = ''
482
359
 
484
361
        self.known_failure_count += 1
485
362
        self.report_known_failure(test, err)
486
363
 
487
 
    def addUnexpectedSuccess(self, test, details=None):
488
 
        """Tell result the test unexpectedly passed, counting as a failure
489
 
 
490
 
        When the minimum version of testtools required becomes 0.9.8 this
491
 
        can be updated to use the new handling there.
492
 
        """
493
 
        super(ExtendedTestResult, self).addFailure(test, details=details)
494
 
        self.failure_count += 1
495
 
        self.report_unexpected_success(test,
496
 
            "".join(details["reason"].iter_text()))
497
 
        if self.stop_early:
498
 
            self.stop()
499
 
 
500
364
    def addNotSupported(self, test, feature):
501
365
        """The test will not be run because of a missing feature.
502
366
        """
519
383
        self.not_applicable_count += 1
520
384
        self.report_not_applicable(test, reason)
521
385
 
522
 
    def _count_stored_tests(self):
523
 
        """Count of tests instances kept alive due to not succeeding"""
524
 
        return self.error_count + self.failure_count + self.known_failure_count
525
 
 
526
 
    def _post_mortem(self, tb=None):
 
386
    def _post_mortem(self):
527
387
        """Start a PDB post mortem session."""
528
388
        if os.environ.get('BZR_TEST_PDB', None):
529
 
            import pdb
530
 
            pdb.post_mortem(tb)
 
389
            import pdb;pdb.post_mortem()
531
390
 
532
391
    def progress(self, offset, whence):
533
392
        """The test is adjusting the count of tests to run."""
538
397
        else:
539
398
            raise errors.BzrError("Unknown whence %r" % whence)
540
399
 
541
 
    def report_tests_starting(self):
542
 
        """Display information before the test run begins"""
543
 
        if getattr(sys, 'frozen', None) is None:
544
 
            bzr_path = osutils.realpath(sys.argv[0])
545
 
        else:
546
 
            bzr_path = sys.executable
547
 
        self.stream.write(
548
 
            'bzr selftest: %s\n' % (bzr_path,))
549
 
        self.stream.write(
550
 
            '   %s\n' % (
551
 
                    bzrlib.__path__[0],))
552
 
        self.stream.write(
553
 
            '   bzr-%s python-%s %s\n' % (
554
 
                    bzrlib.version_string,
555
 
                    bzrlib._format_version_tuple(sys.version_info),
556
 
                    platform.platform(aliased=1),
557
 
                    ))
558
 
        self.stream.write('\n')
559
 
 
560
 
    def report_test_start(self, test):
561
 
        """Display information on the test just about to be run"""
562
 
 
563
 
    def _report_thread_leak(self, test, leaked_threads, active_threads):
564
 
        """Display information on a test that leaked one or more threads"""
565
 
        # GZ 2010-09-09: A leak summary reported separately from the general
566
 
        #                thread debugging would be nice. Tests under subunit
567
 
        #                need something not using stream, perhaps adding a
568
 
        #                testtools details object would be fitting.
569
 
        if 'threads' in selftest_debug_flags:
570
 
            self.stream.write('%s is leaking, active is now %d\n' %
571
 
                (test.id(), len(active_threads)))
 
400
    def report_cleaning_up(self):
 
401
        pass
572
402
 
573
403
    def startTestRun(self):
574
404
        self.startTime = time.time()
611
441
        self.pb.finished()
612
442
        super(TextTestResult, self).stopTestRun()
613
443
 
614
 
    def report_tests_starting(self):
615
 
        super(TextTestResult, self).report_tests_starting()
 
444
    def startTestRun(self):
 
445
        super(TextTestResult, self).startTestRun()
616
446
        self.pb.update('[test 0/%d] Starting' % (self.num_tests))
617
447
 
 
448
    def printErrors(self):
 
449
        # clear the pb to make room for the error listing
 
450
        self.pb.clear()
 
451
        super(TextTestResult, self).printErrors()
 
452
 
618
453
    def _progress_prefix_text(self):
619
454
        # the longer this text, the less space we have to show the test
620
455
        # name...
642
477
        return a
643
478
 
644
479
    def report_test_start(self, test):
 
480
        self.count += 1
645
481
        self.pb.update(
646
482
                self._progress_prefix_text()
647
483
                + ' '
665
501
    def report_known_failure(self, test, err):
666
502
        pass
667
503
 
668
 
    def report_unexpected_success(self, test, reason):
669
 
        self.stream.write('FAIL: %s\n    %s: %s\n' % (
670
 
            self._test_description(test),
671
 
            "Unexpected success. Should have failed",
672
 
            reason,
673
 
            ))
674
 
 
675
504
    def report_skip(self, test, reason):
676
505
        pass
677
506
 
681
510
    def report_unsupported(self, test, feature):
682
511
        """test cannot be run because feature is missing."""
683
512
 
 
513
    def report_cleaning_up(self):
 
514
        self.pb.update('Cleaning up')
 
515
 
684
516
 
685
517
class VerboseTestResult(ExtendedTestResult):
686
518
    """Produce long output, with one line per test run plus times"""
693
525
            result = a_string
694
526
        return result.ljust(final_width)
695
527
 
696
 
    def report_tests_starting(self):
 
528
    def startTestRun(self):
 
529
        super(VerboseTestResult, self).startTestRun()
697
530
        self.stream.write('running %d tests...\n' % self.num_tests)
698
 
        super(VerboseTestResult, self).report_tests_starting()
699
531
 
700
532
    def report_test_start(self, test):
 
533
        self.count += 1
701
534
        name = self._shortened_test_description(test)
702
535
        width = osutils.terminal_width()
703
536
        if width is not None:
729
562
                % (self._testTimeString(test),
730
563
                   self._error_summary(err)))
731
564
 
732
 
    def report_unexpected_success(self, test, reason):
733
 
        self.stream.write(' FAIL %s\n%s: %s\n'
734
 
                % (self._testTimeString(test),
735
 
                   "Unexpected success. Should have failed",
736
 
                   reason))
737
 
 
738
565
    def report_success(self, test):
739
566
        self.stream.write('   OK %s\n' % self._testTimeString(test))
740
567
        for bench_called, stats in getattr(test, '_benchcalls', []):
787
614
            encode = codec[0]
788
615
        else:
789
616
            encode = codec.encode
790
 
        # GZ 2010-09-08: Really we don't want to be writing arbitrary bytes,
791
 
        #                so should swap to the plain codecs.StreamWriter
792
 
        stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream,
793
 
            "backslashreplace")
 
617
        stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream)
794
618
        stream.encoding = new_encoding
795
619
        self.stream = stream
796
620
        self.descriptions = descriptions
946
770
        return NullProgressView()
947
771
 
948
772
 
949
 
def isolated_doctest_setUp(test):
950
 
    override_os_environ(test)
951
 
 
952
 
 
953
 
def isolated_doctest_tearDown(test):
954
 
    restore_os_environ(test)
955
 
 
956
 
 
957
 
def IsolatedDocTestSuite(*args, **kwargs):
958
 
    """Overrides doctest.DocTestSuite to handle isolation.
959
 
 
960
 
    The method is really a factory and users are expected to use it as such.
961
 
    """
962
 
 
963
 
    kwargs['setUp'] = isolated_doctest_setUp
964
 
    kwargs['tearDown'] = isolated_doctest_tearDown
965
 
    return doctest.DocTestSuite(*args, **kwargs)
966
 
 
967
 
 
968
773
class TestCase(testtools.TestCase):
969
774
    """Base class for bzr unit tests.
970
775
 
981
786
    routine, and to build and check bzr trees.
982
787
 
983
788
    In addition to the usual method of overriding tearDown(), this class also
984
 
    allows subclasses to register cleanup functions via addCleanup, which are
 
789
    allows subclasses to register functions into the _cleanups list, which is
985
790
    run in order as the object is torn down.  It's less likely this will be
986
791
    accidentally overlooked.
987
792
    """
988
793
 
989
 
    _log_file = None
 
794
    _active_threads = None
 
795
    _leaking_threads_tests = 0
 
796
    _first_thread_leaker_id = None
 
797
    _log_file_name = None
990
798
    # record lsprof data when performing benchmark calls.
991
799
    _gather_lsprof_in_benchmarks = False
992
800
 
993
801
    def __init__(self, methodName='testMethod'):
994
802
        super(TestCase, self).__init__(methodName)
 
803
        self._cleanups = []
995
804
        self._directory_isolation = True
996
805
        self.exception_handlers.insert(0,
997
806
            (UnavailableFeature, self._do_unsupported_or_skip))
1000
809
 
1001
810
    def setUp(self):
1002
811
        super(TestCase, self).setUp()
1003
 
 
1004
 
        # At this point we're still accessing the config files in $BZR_HOME (as
1005
 
        # set by the user running selftest).
1006
 
        timeout = config.GlobalStack().get('selftest.timeout')
1007
 
        if timeout:
1008
 
            timeout_fixture = fixtures.TimeoutFixture(timeout)
1009
 
            timeout_fixture.setUp()
1010
 
            self.addCleanup(timeout_fixture.cleanUp)
1011
 
 
1012
812
        for feature in getattr(self, '_test_needs_features', []):
1013
813
            self.requireFeature(feature)
 
814
        self._log_contents = None
 
815
        self.addDetail("log", content.Content(content.ContentType("text",
 
816
            "plain", {"charset": "utf8"}),
 
817
            lambda:[self._get_log(keep_log_file=True)]))
1014
818
        self._cleanEnvironment()
1015
 
 
1016
 
        if bzrlib.global_state is not None:
1017
 
            self.overrideAttr(bzrlib.global_state, 'cmdline_overrides',
1018
 
                              config.CommandLineStore())
1019
 
 
1020
819
        self._silenceUI()
1021
820
        self._startLogFile()
1022
821
        self._benchcalls = []
1025
824
        self._track_transports()
1026
825
        self._track_locks()
1027
826
        self._clear_debug_flags()
1028
 
        # Isolate global verbosity level, to make sure it's reproducible
1029
 
        # between tests.  We should get rid of this altogether: bug 656694. --
1030
 
        # mbp 20101008
1031
 
        self.overrideAttr(bzrlib.trace, '_verbosity_level', 0)
1032
 
        self._log_files = set()
1033
 
        # Each key in the ``_counters`` dict holds a value for a different
1034
 
        # counter. When the test ends, addDetail() should be used to output the
1035
 
        # counter values. This happens in install_counter_hook().
1036
 
        self._counters = {}
1037
 
        if 'config_stats' in selftest_debug_flags:
1038
 
            self._install_config_stats_hooks()
1039
 
        # Do not use i18n for tests (unless the test reverses this)
1040
 
        i18n.disable_i18n()
 
827
        TestCase._active_threads = threading.activeCount()
 
828
        self.addCleanup(self._check_leaked_threads)
1041
829
 
1042
830
    def debug(self):
1043
831
        # debug a frame up.
1044
832
        import pdb
1045
 
        # The sys preserved stdin/stdout should allow blackbox tests debugging
1046
 
        pdb.Pdb(stdin=sys.__stdin__, stdout=sys.__stdout__
1047
 
                ).set_trace(sys._getframe().f_back)
1048
 
 
1049
 
    def discardDetail(self, name):
1050
 
        """Extend the addDetail, getDetails api so we can remove a detail.
1051
 
 
1052
 
        eg. bzr always adds the 'log' detail at startup, but we don't want to
1053
 
        include it for skipped, xfail, etc tests.
1054
 
 
1055
 
        It is safe to call this for a detail that doesn't exist, in case this
1056
 
        gets called multiple times.
1057
 
        """
1058
 
        # We cheat. details is stored in __details which means we shouldn't
1059
 
        # touch it. but getDetails() returns the dict directly, so we can
1060
 
        # mutate it.
1061
 
        details = self.getDetails()
1062
 
        if name in details:
1063
 
            del details[name]
1064
 
 
1065
 
    def install_counter_hook(self, hooks, name, counter_name=None):
1066
 
        """Install a counting hook.
1067
 
 
1068
 
        Any hook can be counted as long as it doesn't need to return a value.
1069
 
 
1070
 
        :param hooks: Where the hook should be installed.
1071
 
 
1072
 
        :param name: The hook name that will be counted.
1073
 
 
1074
 
        :param counter_name: The counter identifier in ``_counters``, defaults
1075
 
            to ``name``.
1076
 
        """
1077
 
        _counters = self._counters # Avoid closing over self
1078
 
        if counter_name is None:
1079
 
            counter_name = name
1080
 
        if _counters.has_key(counter_name):
1081
 
            raise AssertionError('%s is already used as a counter name'
1082
 
                                  % (counter_name,))
1083
 
        _counters[counter_name] = 0
1084
 
        self.addDetail(counter_name, content.Content(content.UTF8_TEXT,
1085
 
            lambda: ['%d' % (_counters[counter_name],)]))
1086
 
        def increment_counter(*args, **kwargs):
1087
 
            _counters[counter_name] += 1
1088
 
        label = 'count %s calls' % (counter_name,)
1089
 
        hooks.install_named_hook(name, increment_counter, label)
1090
 
        self.addCleanup(hooks.uninstall_named_hook, name, label)
1091
 
 
1092
 
    def _install_config_stats_hooks(self):
1093
 
        """Install config hooks to count hook calls.
1094
 
 
1095
 
        """
1096
 
        for hook_name in ('get', 'set', 'remove', 'load', 'save'):
1097
 
            self.install_counter_hook(config.ConfigHooks, hook_name,
1098
 
                                       'config.%s' % (hook_name,))
1099
 
 
1100
 
        # The OldConfigHooks are private and need special handling to protect
1101
 
        # against recursive tests (tests that run other tests), so we just do
1102
 
        # manually what registering them into _builtin_known_hooks will provide
1103
 
        # us.
1104
 
        self.overrideAttr(config, 'OldConfigHooks', config._OldConfigHooks())
1105
 
        for hook_name in ('get', 'set', 'remove', 'load', 'save'):
1106
 
            self.install_counter_hook(config.OldConfigHooks, hook_name,
1107
 
                                      'old_config.%s' % (hook_name,))
 
833
        pdb.Pdb().set_trace(sys._getframe().f_back)
 
834
 
 
835
    def _check_leaked_threads(self):
 
836
        active = threading.activeCount()
 
837
        leaked_threads = active - TestCase._active_threads
 
838
        TestCase._active_threads = active
 
839
        # If some tests make the number of threads *decrease*, we'll consider
 
840
        # that they are just observing old threads dieing, not agressively kill
 
841
        # random threads. So we don't report these tests as leaking. The risk
 
842
        # is that we have false positives that way (the test see 2 threads
 
843
        # going away but leak one) but it seems less likely than the actual
 
844
        # false positives (the test see threads going away and does not leak).
 
845
        if leaked_threads > 0:
 
846
            if 'threads' in selftest_debug_flags:
 
847
                print '%s is leaking, active is now %d' % (self.id(), active)
 
848
            TestCase._leaking_threads_tests += 1
 
849
            if TestCase._first_thread_leaker_id is None:
 
850
                TestCase._first_thread_leaker_id = self.id()
1108
851
 
1109
852
    def _clear_debug_flags(self):
1110
853
        """Prevent externally set debug flags affecting tests.
1121
864
 
1122
865
    def _clear_hooks(self):
1123
866
        # prevent hooks affecting tests
1124
 
        known_hooks = hooks.known_hooks
1125
867
        self._preserved_hooks = {}
1126
 
        for key, (parent, name) in known_hooks.iter_parent_objects():
1127
 
            current_hooks = getattr(parent, name)
 
868
        for key, factory in hooks.known_hooks.items():
 
869
            parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
 
870
            current_hooks = hooks.known_hooks_key_to_object(key)
1128
871
            self._preserved_hooks[parent] = (name, current_hooks)
1129
 
        self._preserved_lazy_hooks = hooks._lazy_hooks
1130
 
        hooks._lazy_hooks = {}
1131
872
        self.addCleanup(self._restoreHooks)
1132
 
        for key, (parent, name) in known_hooks.iter_parent_objects():
1133
 
            factory = known_hooks.get(key)
 
873
        for key, factory in hooks.known_hooks.items():
 
874
            parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
1134
875
            setattr(parent, name, factory())
1135
876
        # this hook should always be installed
1136
877
        request._install_hook()
1165
906
        # break some locks on purpose and should be taken into account by
1166
907
        # considering that breaking a lock is just a dirty way of releasing it.
1167
908
        if len(acquired_locks) != (len(released_locks) + len(broken_locks)):
1168
 
            message = (
1169
 
                'Different number of acquired and '
1170
 
                'released or broken locks.\n'
1171
 
                'acquired=%s\n'
1172
 
                'released=%s\n'
1173
 
                'broken=%s\n' %
1174
 
                (acquired_locks, released_locks, broken_locks))
 
909
            message = ('Different number of acquired and '
 
910
                       'released or broken locks. (%s, %s + %s)' %
 
911
                       (acquired_locks, released_locks, broken_locks))
1175
912
            if not self._lock_check_thorough:
1176
913
                # Rather than fail, just warn
1177
914
                print "Broken test %s: %s" % (self, message)
1205
942
 
1206
943
    def permit_dir(self, name):
1207
944
        """Permit a directory to be used by this test. See permit_url."""
1208
 
        name_transport = _mod_transport.get_transport_from_path(name)
 
945
        name_transport = _mod_transport.get_transport(name)
1209
946
        self.permit_url(name)
1210
947
        self.permit_url(name_transport.base)
1211
948
 
1234
971
            try:
1235
972
                workingtree.WorkingTree.open(path)
1236
973
            except (errors.NotBranchError, errors.NoWorkingTree):
1237
 
                raise TestSkipped('Needs a working tree of bzr sources')
 
974
                return
1238
975
        finally:
1239
976
            self.enable_directory_isolation()
1240
977
 
1290
1027
        self.addCleanup(transport_server.stop_server)
1291
1028
        # Obtain a real transport because if the server supplies a password, it
1292
1029
        # will be hidden from the base on the client side.
1293
 
        t = _mod_transport.get_transport_from_url(transport_server.get_url())
 
1030
        t = _mod_transport.get_transport(transport_server.get_url())
1294
1031
        # Some transport servers effectively chroot the backing transport;
1295
1032
        # others like SFTPServer don't - users of the transport can walk up the
1296
1033
        # transport to read the entire backing transport. This wouldn't matter
1327
1064
        # hook into bzr dir opening. This leaves a small window of error for
1328
1065
        # transport tests, but they are well known, and we can improve on this
1329
1066
        # step.
1330
 
        controldir.ControlDir.hooks.install_named_hook("pre_open",
 
1067
        bzrdir.BzrDir.hooks.install_named_hook("pre_open",
1331
1068
            self._preopen_isolate_transport, "Check bzr directories are safe.")
1332
1069
 
1333
1070
    def _ndiff_strings(self, a, b):
1352
1089
        except UnicodeError, e:
1353
1090
            # If we can't compare without getting a UnicodeError, then
1354
1091
            # obviously they are different
1355
 
            trace.mutter('UnicodeError: %s', e)
 
1092
            mutter('UnicodeError: %s', e)
1356
1093
        if message:
1357
1094
            message += '\n'
1358
1095
        raise AssertionError("%snot equal:\na = %s\nb = %s\n"
1397
1134
                         'st_mtime did not match')
1398
1135
        self.assertEqual(expected.st_ctime, actual.st_ctime,
1399
1136
                         'st_ctime did not match')
1400
 
        if sys.platform == 'win32':
 
1137
        if sys.platform != 'win32':
1401
1138
            # On Win32 both 'dev' and 'ino' cannot be trusted. In python2.4 it
1402
1139
            # is 'dev' that varies, in python 2.5 (6?) it is st_ino that is
1403
 
            # odd. We just force it to always be 0 to avoid any problems.
1404
 
            self.assertEqual(0, expected.st_dev)
1405
 
            self.assertEqual(0, actual.st_dev)
1406
 
            self.assertEqual(0, expected.st_ino)
1407
 
            self.assertEqual(0, actual.st_ino)
1408
 
        else:
 
1140
            # odd. Regardless we shouldn't actually try to assert anything
 
1141
            # about their values
1409
1142
            self.assertEqual(expected.st_dev, actual.st_dev,
1410
1143
                             'st_dev did not match')
1411
1144
            self.assertEqual(expected.st_ino, actual.st_ino,
1420
1153
                length, len(obj_with_len), obj_with_len))
1421
1154
 
1422
1155
    def assertLogsError(self, exception_class, func, *args, **kwargs):
1423
 
        """Assert that `func(*args, **kwargs)` quietly logs a specific error.
 
1156
        """Assert that func(*args, **kwargs) quietly logs a specific exception.
1424
1157
        """
 
1158
        from bzrlib import trace
1425
1159
        captured = []
1426
1160
        orig_log_exception_quietly = trace.log_exception_quietly
1427
1161
        try:
1428
1162
            def capture():
1429
1163
                orig_log_exception_quietly()
1430
 
                captured.append(sys.exc_info()[1])
 
1164
                captured.append(sys.exc_info())
1431
1165
            trace.log_exception_quietly = capture
1432
1166
            func(*args, **kwargs)
1433
1167
        finally:
1434
1168
            trace.log_exception_quietly = orig_log_exception_quietly
1435
1169
        self.assertLength(1, captured)
1436
 
        err = captured[0]
 
1170
        err = captured[0][1]
1437
1171
        self.assertIsInstance(err, exception_class)
1438
1172
        return err
1439
1173
 
1476
1210
        if haystack.find(needle) == -1:
1477
1211
            self.fail("string %r not found in '''%s'''" % (needle, haystack))
1478
1212
 
1479
 
    def assertNotContainsString(self, haystack, needle):
1480
 
        if haystack.find(needle) != -1:
1481
 
            self.fail("string %r found in '''%s'''" % (needle, haystack))
1482
 
 
1483
1213
    def assertSubset(self, sublist, superlist):
1484
1214
        """Assert that every entry in sublist is present in superlist."""
1485
1215
        missing = set(sublist) - set(superlist)
1574
1304
 
1575
1305
    def assertFileEqual(self, content, path):
1576
1306
        """Fail if path does not contain 'content'."""
1577
 
        self.assertPathExists(path)
 
1307
        self.failUnlessExists(path)
1578
1308
        f = file(path, 'rb')
1579
1309
        try:
1580
1310
            s = f.read()
1590
1320
        else:
1591
1321
            self.assertEqual(expected_docstring, obj.__doc__)
1592
1322
 
1593
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1594
1323
    def failUnlessExists(self, path):
1595
 
        return self.assertPathExists(path)
1596
 
 
1597
 
    def assertPathExists(self, path):
1598
1324
        """Fail unless path or paths, which may be abs or relative, exist."""
1599
1325
        if not isinstance(path, basestring):
1600
1326
            for p in path:
1601
 
                self.assertPathExists(p)
 
1327
                self.failUnlessExists(p)
1602
1328
        else:
1603
 
            self.assertTrue(osutils.lexists(path),
1604
 
                path + " does not exist")
 
1329
            self.failUnless(osutils.lexists(path),path+" does not exist")
1605
1330
 
1606
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1607
1331
    def failIfExists(self, path):
1608
 
        return self.assertPathDoesNotExist(path)
1609
 
 
1610
 
    def assertPathDoesNotExist(self, path):
1611
1332
        """Fail if path or paths, which may be abs or relative, exist."""
1612
1333
        if not isinstance(path, basestring):
1613
1334
            for p in path:
1614
 
                self.assertPathDoesNotExist(p)
 
1335
                self.failIfExists(p)
1615
1336
        else:
1616
 
            self.assertFalse(osutils.lexists(path),
1617
 
                path + " exists")
 
1337
            self.failIf(osutils.lexists(path),path+" exists")
1618
1338
 
1619
1339
    def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
1620
1340
        """A helper for callDeprecated and applyDeprecated.
1646
1366
        not other callers that go direct to the warning module.
1647
1367
 
1648
1368
        To test that a deprecated method raises an error, do something like
1649
 
        this (remember that both assertRaises and applyDeprecated delays *args
1650
 
        and **kwargs passing)::
 
1369
        this::
1651
1370
 
1652
1371
            self.assertRaises(errors.ReservedId,
1653
1372
                self.applyDeprecated,
1731
1450
        return result
1732
1451
 
1733
1452
    def _startLogFile(self):
1734
 
        """Setup a in-memory target for bzr and testcase log messages"""
1735
 
        pseudo_log_file = StringIO()
1736
 
        def _get_log_contents_for_weird_testtools_api():
1737
 
            return [pseudo_log_file.getvalue().decode(
1738
 
                "utf-8", "replace").encode("utf-8")]
1739
 
        self.addDetail("log", content.Content(content.ContentType("text",
1740
 
            "plain", {"charset": "utf8"}),
1741
 
            _get_log_contents_for_weird_testtools_api))
1742
 
        self._log_file = pseudo_log_file
1743
 
        self._log_memento = trace.push_log_file(self._log_file)
 
1453
        """Send bzr and test log messages to a temporary file.
 
1454
 
 
1455
        The file is removed as the test is torn down.
 
1456
        """
 
1457
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
 
1458
        self._log_file = os.fdopen(fileno, 'w+')
 
1459
        self._log_memento = bzrlib.trace.push_log_file(self._log_file)
 
1460
        self._log_file_name = name
1744
1461
        self.addCleanup(self._finishLogFile)
1745
1462
 
1746
1463
    def _finishLogFile(self):
1747
 
        """Flush and dereference the in-memory log for this testcase"""
1748
 
        if trace._trace_file:
 
1464
        """Finished with the log file.
 
1465
 
 
1466
        Close the file and delete it, unless setKeepLogfile was called.
 
1467
        """
 
1468
        if bzrlib.trace._trace_file:
1749
1469
            # flush the log file, to get all content
1750
 
            trace._trace_file.flush()
1751
 
        trace.pop_log_file(self._log_memento)
1752
 
        # The logging module now tracks references for cleanup so discard ours
1753
 
        del self._log_memento
 
1470
            bzrlib.trace._trace_file.flush()
 
1471
        bzrlib.trace.pop_log_file(self._log_memento)
 
1472
        # Cache the log result and delete the file on disk
 
1473
        self._get_log(False)
1754
1474
 
1755
1475
    def thisFailsStrictLockCheck(self):
1756
1476
        """It is known that this test would fail with -Dstrict_locks.
1765
1485
        """
1766
1486
        debug.debug_flags.discard('strict_locks')
1767
1487
 
 
1488
    def addCleanup(self, callable, *args, **kwargs):
 
1489
        """Arrange to run a callable when this case is torn down.
 
1490
 
 
1491
        Callables are run in the reverse of the order they are registered,
 
1492
        ie last-in first-out.
 
1493
        """
 
1494
        self._cleanups.append((callable, args, kwargs))
 
1495
 
1768
1496
    def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1769
1497
        """Overrides an object attribute restoring it after the test.
1770
1498
 
1771
 
        :note: This should be used with discretion; you should think about
1772
 
        whether it's better to make the code testable without monkey-patching.
1773
 
 
1774
1499
        :param obj: The object that will be mutated.
1775
1500
 
1776
1501
        :param attr_name: The attribute name we want to preserve/override in
1780
1505
 
1781
1506
        :returns: The actual attr value.
1782
1507
        """
 
1508
        value = getattr(obj, attr_name)
1783
1509
        # The actual value is captured by the call below
1784
 
        value = getattr(obj, attr_name, _unitialized_attr)
1785
 
        if value is _unitialized_attr:
1786
 
            # When the test completes, the attribute should not exist, but if
1787
 
            # we aren't setting a value, we don't need to do anything.
1788
 
            if new is not _unitialized_attr:
1789
 
                self.addCleanup(delattr, obj, attr_name)
1790
 
        else:
1791
 
            self.addCleanup(setattr, obj, attr_name, value)
 
1510
        self.addCleanup(setattr, obj, attr_name, value)
1792
1511
        if new is not _unitialized_attr:
1793
1512
            setattr(obj, attr_name, new)
1794
1513
        return value
1795
1514
 
1796
 
    def overrideEnv(self, name, new):
1797
 
        """Set an environment variable, and reset it after the test.
1798
 
 
1799
 
        :param name: The environment variable name.
1800
 
 
1801
 
        :param new: The value to set the variable to. If None, the 
1802
 
            variable is deleted from the environment.
1803
 
 
1804
 
        :returns: The actual variable value.
1805
 
        """
1806
 
        value = osutils.set_or_unset_env(name, new)
1807
 
        self.addCleanup(osutils.set_or_unset_env, name, value)
1808
 
        return value
1809
 
 
1810
 
    def recordCalls(self, obj, attr_name):
1811
 
        """Monkeypatch in a wrapper that will record calls.
1812
 
 
1813
 
        The monkeypatch is automatically removed when the test concludes.
1814
 
 
1815
 
        :param obj: The namespace holding the reference to be replaced;
1816
 
            typically a module, class, or object.
1817
 
        :param attr_name: A string for the name of the attribute to 
1818
 
            patch.
1819
 
        :returns: A list that will be extended with one item every time the
1820
 
            function is called, with a tuple of (args, kwargs).
1821
 
        """
1822
 
        calls = []
1823
 
 
1824
 
        def decorator(*args, **kwargs):
1825
 
            calls.append((args, kwargs))
1826
 
            return orig(*args, **kwargs)
1827
 
        orig = self.overrideAttr(obj, attr_name, decorator)
1828
 
        return calls
1829
 
 
1830
1515
    def _cleanEnvironment(self):
1831
 
        for name, value in isolated_environ.iteritems():
1832
 
            self.overrideEnv(name, value)
 
1516
        new_env = {
 
1517
            'BZR_HOME': None, # Don't inherit BZR_HOME to all the tests.
 
1518
            'HOME': os.getcwd(),
 
1519
            # bzr now uses the Win32 API and doesn't rely on APPDATA, but the
 
1520
            # tests do check our impls match APPDATA
 
1521
            'BZR_EDITOR': None, # test_msgeditor manipulates this variable
 
1522
            'VISUAL': None,
 
1523
            'EDITOR': None,
 
1524
            'BZR_EMAIL': None,
 
1525
            'BZREMAIL': None, # may still be present in the environment
 
1526
            'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
 
1527
            'BZR_PROGRESS_BAR': None,
 
1528
            'BZR_LOG': None,
 
1529
            'BZR_PLUGIN_PATH': None,
 
1530
            'BZR_DISABLE_PLUGINS': None,
 
1531
            'BZR_PLUGINS_AT': None,
 
1532
            'BZR_CONCURRENCY': None,
 
1533
            # Make sure that any text ui tests are consistent regardless of
 
1534
            # the environment the test case is run in; you may want tests that
 
1535
            # test other combinations.  'dumb' is a reasonable guess for tests
 
1536
            # going to a pipe or a StringIO.
 
1537
            'TERM': 'dumb',
 
1538
            'LINES': '25',
 
1539
            'COLUMNS': '80',
 
1540
            'BZR_COLUMNS': '80',
 
1541
            # SSH Agent
 
1542
            'SSH_AUTH_SOCK': None,
 
1543
            # Proxies
 
1544
            'http_proxy': None,
 
1545
            'HTTP_PROXY': None,
 
1546
            'https_proxy': None,
 
1547
            'HTTPS_PROXY': None,
 
1548
            'no_proxy': None,
 
1549
            'NO_PROXY': None,
 
1550
            'all_proxy': None,
 
1551
            'ALL_PROXY': None,
 
1552
            # Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
 
1553
            # least. If you do (care), please update this comment
 
1554
            # -- vila 20080401
 
1555
            'ftp_proxy': None,
 
1556
            'FTP_PROXY': None,
 
1557
            'BZR_REMOTE_PATH': None,
 
1558
            # Generally speaking, we don't want apport reporting on crashes in
 
1559
            # the test envirnoment unless we're specifically testing apport,
 
1560
            # so that it doesn't leak into the real system environment.  We
 
1561
            # use an env var so it propagates to subprocesses.
 
1562
            'APPORT_DISABLE': '1',
 
1563
        }
 
1564
        self._old_env = {}
 
1565
        self.addCleanup(self._restoreEnvironment)
 
1566
        for name, value in new_env.iteritems():
 
1567
            self._captureVar(name, value)
 
1568
 
 
1569
    def _captureVar(self, name, newvalue):
 
1570
        """Set an environment variable, and reset it when finished."""
 
1571
        self._old_env[name] = osutils.set_or_unset_env(name, newvalue)
 
1572
 
 
1573
    def _restoreEnvironment(self):
 
1574
        for name, value in self._old_env.iteritems():
 
1575
            osutils.set_or_unset_env(name, value)
1833
1576
 
1834
1577
    def _restoreHooks(self):
1835
1578
        for klass, (name, hooks) in self._preserved_hooks.items():
1836
1579
            setattr(klass, name, hooks)
1837
 
        self._preserved_hooks.clear()
1838
 
        bzrlib.hooks._lazy_hooks = self._preserved_lazy_hooks
1839
 
        self._preserved_lazy_hooks.clear()
1840
1580
 
1841
1581
    def knownFailure(self, reason):
1842
 
        """Declare that this test fails for a known reason
1843
 
 
1844
 
        Tests that are known to fail should generally be using expectedFailure
1845
 
        with an appropriate reverse assertion if a change could cause the test
1846
 
        to start passing. Conversely if the test has no immediate prospect of
1847
 
        succeeding then using skip is more suitable.
1848
 
 
1849
 
        When this method is called while an exception is being handled, that
1850
 
        traceback will be used, otherwise a new exception will be thrown to
1851
 
        provide one but won't be reported.
1852
 
        """
1853
 
        self._add_reason(reason)
1854
 
        try:
1855
 
            exc_info = sys.exc_info()
1856
 
            if exc_info != (None, None, None):
1857
 
                self._report_traceback(exc_info)
1858
 
            else:
1859
 
                try:
1860
 
                    raise self.failureException(reason)
1861
 
                except self.failureException:
1862
 
                    exc_info = sys.exc_info()
1863
 
            # GZ 02-08-2011: Maybe cleanup this err.exc_info attribute too?
1864
 
            raise testtools.testcase._ExpectedFailure(exc_info)
1865
 
        finally:
1866
 
            del exc_info
1867
 
 
1868
 
    def _suppress_log(self):
1869
 
        """Remove the log info from details."""
1870
 
        self.discardDetail('log')
 
1582
        """This test has failed for some known reason."""
 
1583
        raise KnownFailure(reason)
1871
1584
 
1872
1585
    def _do_skip(self, result, reason):
1873
 
        self._suppress_log()
1874
1586
        addSkip = getattr(result, 'addSkip', None)
1875
1587
        if not callable(addSkip):
1876
1588
            result.addSuccess(result)
1879
1591
 
1880
1592
    @staticmethod
1881
1593
    def _do_known_failure(self, result, e):
1882
 
        self._suppress_log()
1883
1594
        err = sys.exc_info()
1884
1595
        addExpectedFailure = getattr(result, 'addExpectedFailure', None)
1885
1596
        if addExpectedFailure is not None:
1893
1604
            reason = 'No reason given'
1894
1605
        else:
1895
1606
            reason = e.args[0]
1896
 
        self._suppress_log ()
1897
1607
        addNotApplicable = getattr(result, 'addNotApplicable', None)
1898
1608
        if addNotApplicable is not None:
1899
1609
            result.addNotApplicable(self, reason)
1901
1611
            self._do_skip(result, reason)
1902
1612
 
1903
1613
    @staticmethod
1904
 
    def _report_skip(self, result, err):
1905
 
        """Override the default _report_skip.
1906
 
 
1907
 
        We want to strip the 'log' detail. If we waint until _do_skip, it has
1908
 
        already been formatted into the 'reason' string, and we can't pull it
1909
 
        out again.
1910
 
        """
1911
 
        self._suppress_log()
1912
 
        super(TestCase, self)._report_skip(self, result, err)
1913
 
 
1914
 
    @staticmethod
1915
 
    def _report_expected_failure(self, result, err):
1916
 
        """Strip the log.
1917
 
 
1918
 
        See _report_skip for motivation.
1919
 
        """
1920
 
        self._suppress_log()
1921
 
        super(TestCase, self)._report_expected_failure(self, result, err)
1922
 
 
1923
 
    @staticmethod
1924
1614
    def _do_unsupported_or_skip(self, result, e):
1925
1615
        reason = e.args[0]
1926
 
        self._suppress_log()
1927
1616
        addNotSupported = getattr(result, 'addNotSupported', None)
1928
1617
        if addNotSupported is not None:
1929
1618
            result.addNotSupported(self, reason)
1955
1644
            self._benchtime += time.time() - start
1956
1645
 
1957
1646
    def log(self, *args):
1958
 
        trace.mutter(*args)
 
1647
        mutter(*args)
 
1648
 
 
1649
    def _get_log(self, keep_log_file=False):
 
1650
        """Internal helper to get the log from bzrlib.trace for this test.
 
1651
 
 
1652
        Please use self.getDetails, or self.get_log to access this in test case
 
1653
        code.
 
1654
 
 
1655
        :param keep_log_file: When True, if the log is still a file on disk
 
1656
            leave it as a file on disk. When False, if the log is still a file
 
1657
            on disk, the log file is deleted and the log preserved as
 
1658
            self._log_contents.
 
1659
        :return: A string containing the log.
 
1660
        """
 
1661
        if self._log_contents is not None:
 
1662
            try:
 
1663
                self._log_contents.decode('utf8')
 
1664
            except UnicodeDecodeError:
 
1665
                unicodestr = self._log_contents.decode('utf8', 'replace')
 
1666
                self._log_contents = unicodestr.encode('utf8')
 
1667
            return self._log_contents
 
1668
        import bzrlib.trace
 
1669
        if bzrlib.trace._trace_file:
 
1670
            # flush the log file, to get all content
 
1671
            bzrlib.trace._trace_file.flush()
 
1672
        if self._log_file_name is not None:
 
1673
            logfile = open(self._log_file_name)
 
1674
            try:
 
1675
                log_contents = logfile.read()
 
1676
            finally:
 
1677
                logfile.close()
 
1678
            try:
 
1679
                log_contents.decode('utf8')
 
1680
            except UnicodeDecodeError:
 
1681
                unicodestr = log_contents.decode('utf8', 'replace')
 
1682
                log_contents = unicodestr.encode('utf8')
 
1683
            if not keep_log_file:
 
1684
                close_attempts = 0
 
1685
                max_close_attempts = 100
 
1686
                first_close_error = None
 
1687
                while close_attempts < max_close_attempts:
 
1688
                    close_attempts += 1
 
1689
                    try:
 
1690
                        self._log_file.close()
 
1691
                    except IOError, ioe:
 
1692
                        if ioe.errno is None:
 
1693
                            # No errno implies 'close() called during
 
1694
                            # concurrent operation on the same file object', so
 
1695
                            # retry.  Probably a thread is trying to write to
 
1696
                            # the log file.
 
1697
                            if first_close_error is None:
 
1698
                                first_close_error = ioe
 
1699
                            continue
 
1700
                        raise
 
1701
                    else:
 
1702
                        break
 
1703
                if close_attempts > 1:
 
1704
                    sys.stderr.write(
 
1705
                        'Unable to close log file on first attempt, '
 
1706
                        'will retry: %s\n' % (first_close_error,))
 
1707
                    if close_attempts == max_close_attempts:
 
1708
                        sys.stderr.write(
 
1709
                            'Unable to close log file after %d attempts.\n'
 
1710
                            % (max_close_attempts,))
 
1711
                self._log_file = None
 
1712
                # Permit multiple calls to get_log until we clean it up in
 
1713
                # finishLogFile
 
1714
                self._log_contents = log_contents
 
1715
                try:
 
1716
                    os.remove(self._log_file_name)
 
1717
                except OSError, e:
 
1718
                    if sys.platform == 'win32' and e.errno == errno.EACCES:
 
1719
                        sys.stderr.write(('Unable to delete log file '
 
1720
                                             ' %r\n' % self._log_file_name))
 
1721
                    else:
 
1722
                        raise
 
1723
                self._log_file_name = None
 
1724
            return log_contents
 
1725
        else:
 
1726
            return "No log file content and no log file name."
1959
1727
 
1960
1728
    def get_log(self):
1961
1729
        """Get a unicode string containing the log from bzrlib.trace.
1997
1765
 
1998
1766
        self.log('run bzr: %r', args)
1999
1767
        # FIXME: don't call into logging here
2000
 
        handler = trace.EncodedStreamHandler(stderr, errors="replace",
2001
 
            level=logging.INFO)
 
1768
        handler = logging.StreamHandler(stderr)
 
1769
        handler.setLevel(logging.INFO)
2002
1770
        logger = logging.getLogger('')
2003
1771
        logger.addHandler(handler)
2004
1772
        old_ui_factory = ui.ui_factory
2011
1779
 
2012
1780
        try:
2013
1781
            try:
2014
 
                result = self.apply_redirected(
2015
 
                    ui.ui_factory.stdin,
 
1782
                result = self.apply_redirected(ui.ui_factory.stdin,
2016
1783
                    stdout, stderr,
2017
 
                    _mod_commands.run_bzr_catch_user_errors,
 
1784
                    bzrlib.commands.run_bzr_catch_user_errors,
2018
1785
                    args)
2019
1786
            except KeyboardInterrupt:
2020
1787
                # Reraise KeyboardInterrupt with contents of redirected stdout
2162
1929
    def start_bzr_subprocess(self, process_args, env_changes=None,
2163
1930
                             skip_if_plan_to_signal=False,
2164
1931
                             working_dir=None,
2165
 
                             allow_plugins=False, stderr=subprocess.PIPE):
 
1932
                             allow_plugins=False):
2166
1933
        """Start bzr in a subprocess for testing.
2167
1934
 
2168
1935
        This starts a new Python interpreter and runs bzr in there.
2177
1944
            variables. A value of None will unset the env variable.
2178
1945
            The values must be strings. The change will only occur in the
2179
1946
            child, so you don't need to fix the environment after running.
2180
 
        :param skip_if_plan_to_signal: raise TestSkipped when true and system
2181
 
            doesn't support signalling subprocesses.
 
1947
        :param skip_if_plan_to_signal: raise TestSkipped when true and os.kill
 
1948
            is not available.
2182
1949
        :param allow_plugins: If False (default) pass --no-plugins to bzr.
2183
 
        :param stderr: file to use for the subprocess's stderr.  Valid values
2184
 
            are those valid for the stderr argument of `subprocess.Popen`.
2185
 
            Default value is ``subprocess.PIPE``.
2186
1950
 
2187
1951
        :returns: Popen object for the started process.
2188
1952
        """
2189
1953
        if skip_if_plan_to_signal:
2190
 
            if os.name != "posix":
2191
 
                raise TestSkipped("Sending signals not supported")
 
1954
            if not getattr(os, 'kill', None):
 
1955
                raise TestSkipped("os.kill not available.")
2192
1956
 
2193
1957
        if env_changes is None:
2194
1958
            env_changes = {}
2195
 
        # Because $HOME is set to a tempdir for the context of a test, modules
2196
 
        # installed in the user dir will not be found unless $PYTHONUSERBASE
2197
 
        # gets set to the computed directory of this parent process.
2198
 
        if site.USER_BASE is not None:
2199
 
            env_changes["PYTHONUSERBASE"] = site.USER_BASE
2200
1959
        old_env = {}
2201
1960
 
2202
1961
        def cleanup_environment():
2219
1978
            # so we will avoid using it on all platforms, just to
2220
1979
            # make sure the code path is used, and we don't break on win32
2221
1980
            cleanup_environment()
2222
 
            # Include the subprocess's log file in the test details, in case
2223
 
            # the test fails due to an error in the subprocess.
2224
 
            self._add_subprocess_log(trace._get_bzr_log_filename())
2225
1981
            command = [sys.executable]
2226
1982
            # frozen executables don't need the path to bzr
2227
1983
            if getattr(sys, "frozen", None) is None:
2231
1987
            command.extend(process_args)
2232
1988
            process = self._popen(command, stdin=subprocess.PIPE,
2233
1989
                                  stdout=subprocess.PIPE,
2234
 
                                  stderr=stderr)
 
1990
                                  stderr=subprocess.PIPE)
2235
1991
        finally:
2236
1992
            restore_environment()
2237
1993
            if cwd is not None:
2239
1995
 
2240
1996
        return process
2241
1997
 
2242
 
    def _add_subprocess_log(self, log_file_path):
2243
 
        if len(self._log_files) == 0:
2244
 
            # Register an addCleanup func.  We do this on the first call to
2245
 
            # _add_subprocess_log rather than in TestCase.setUp so that this
2246
 
            # addCleanup is registered after any cleanups for tempdirs that
2247
 
            # subclasses might create, which will probably remove the log file
2248
 
            # we want to read.
2249
 
            self.addCleanup(self._subprocess_log_cleanup)
2250
 
        # self._log_files is a set, so if a log file is reused we won't grab it
2251
 
        # twice.
2252
 
        self._log_files.add(log_file_path)
2253
 
 
2254
 
    def _subprocess_log_cleanup(self):
2255
 
        for count, log_file_path in enumerate(self._log_files):
2256
 
            # We use buffer_now=True to avoid holding the file open beyond
2257
 
            # the life of this function, which might interfere with e.g.
2258
 
            # cleaning tempdirs on Windows.
2259
 
            # XXX: Testtools 0.9.5 doesn't have the content_from_file helper
2260
 
            #detail_content = content.content_from_file(
2261
 
            #    log_file_path, buffer_now=True)
2262
 
            with open(log_file_path, 'rb') as log_file:
2263
 
                log_file_bytes = log_file.read()
2264
 
            detail_content = content.Content(content.ContentType("text",
2265
 
                "plain", {"charset": "utf8"}), lambda: [log_file_bytes])
2266
 
            self.addDetail("start_bzr_subprocess-log-%d" % (count,),
2267
 
                detail_content)
2268
 
 
2269
1998
    def _popen(self, *args, **kwargs):
2270
1999
        """Place a call to Popen.
2271
2000
 
2308
2037
        if retcode is not None and retcode != process.returncode:
2309
2038
            if process_args is None:
2310
2039
                process_args = "(unknown args)"
2311
 
            trace.mutter('Output of bzr %s:\n%s', process_args, out)
2312
 
            trace.mutter('Error for bzr %s:\n%s', process_args, err)
 
2040
            mutter('Output of bzr %s:\n%s', process_args, out)
 
2041
            mutter('Error for bzr %s:\n%s', process_args, err)
2313
2042
            self.fail('Command bzr %s failed with retcode %s != %s'
2314
2043
                      % (process_args, retcode, process.returncode))
2315
2044
        return [out, err]
2316
2045
 
2317
 
    def check_tree_shape(self, tree, shape):
2318
 
        """Compare a tree to a list of expected names.
 
2046
    def check_inventory_shape(self, inv, shape):
 
2047
        """Compare an inventory to a list of expected names.
2319
2048
 
2320
2049
        Fail if they are not precisely equal.
2321
2050
        """
2322
2051
        extras = []
2323
2052
        shape = list(shape)             # copy
2324
 
        for path, ie in tree.iter_entries_by_dir():
 
2053
        for path, ie in inv.entries():
2325
2054
            name = path.replace('\\', '/')
2326
2055
            if ie.kind == 'directory':
2327
2056
                name = name + '/'
2328
 
            if name == "/":
2329
 
                pass # ignore root entry
2330
 
            elif name in shape:
 
2057
            if name in shape:
2331
2058
                shape.remove(name)
2332
2059
            else:
2333
2060
                extras.append(name)
2374
2101
 
2375
2102
        Tests that expect to provoke LockContention errors should call this.
2376
2103
        """
2377
 
        self.overrideAttr(lockdir, '_DEFAULT_TIMEOUT_SECONDS', 0)
 
2104
        self.overrideAttr(bzrlib.lockdir, '_DEFAULT_TIMEOUT_SECONDS', 0)
2378
2105
 
2379
2106
    def make_utf8_encoded_stringio(self, encoding_type=None):
2380
2107
        """Return a StringIOWrapper instance, that will encode Unicode
2393
2120
        from bzrlib.smart import request
2394
2121
        request_handlers = request.request_handlers
2395
2122
        orig_method = request_handlers.get(verb)
2396
 
        orig_info = request_handlers.get_info(verb)
2397
2123
        request_handlers.remove(verb)
2398
 
        self.addCleanup(request_handlers.register, verb, orig_method,
2399
 
            info=orig_info)
 
2124
        self.addCleanup(request_handlers.register, verb, orig_method)
2400
2125
 
2401
2126
 
2402
2127
class CapturedCall(object):
2425
2150
class TestCaseWithMemoryTransport(TestCase):
2426
2151
    """Common test class for tests that do not need disk resources.
2427
2152
 
2428
 
    Tests that need disk resources should derive from TestCaseInTempDir
2429
 
    orTestCaseWithTransport.
 
2153
    Tests that need disk resources should derive from TestCaseWithTransport.
2430
2154
 
2431
2155
    TestCaseWithMemoryTransport sets the TEST_ROOT variable for all bzr tests.
2432
2156
 
2433
 
    For TestCaseWithMemoryTransport the ``test_home_dir`` is set to the name of
 
2157
    For TestCaseWithMemoryTransport the test_home_dir is set to the name of
2434
2158
    a directory which does not exist. This serves to help ensure test isolation
2435
 
    is preserved. ``test_dir`` is set to the TEST_ROOT, as is cwd, because they
2436
 
    must exist. However, TestCaseWithMemoryTransport does not offer local file
2437
 
    defaults for the transport in tests, nor does it obey the command line
 
2159
    is preserved. test_dir is set to the TEST_ROOT, as is cwd, because they
 
2160
    must exist. However, TestCaseWithMemoryTransport does not offer local
 
2161
    file defaults for the transport in tests, nor does it obey the command line
2438
2162
    override, so tests that accidentally write to the common directory should
2439
2163
    be rare.
2440
2164
 
2441
 
    :cvar TEST_ROOT: Directory containing all temporary directories, plus a
2442
 
        ``.bzr`` directory that stops us ascending higher into the filesystem.
 
2165
    :cvar TEST_ROOT: Directory containing all temporary directories, plus
 
2166
    a .bzr directory that stops us ascending higher into the filesystem.
2443
2167
    """
2444
2168
 
2445
2169
    TEST_ROOT = None
2455
2179
        self.transport_readonly_server = None
2456
2180
        self.__vfs_server = None
2457
2181
 
2458
 
    def setUp(self):
2459
 
        super(TestCaseWithMemoryTransport, self).setUp()
2460
 
 
2461
 
        def _add_disconnect_cleanup(transport):
2462
 
            """Schedule disconnection of given transport at test cleanup
2463
 
 
2464
 
            This needs to happen for all connected transports or leaks occur.
2465
 
 
2466
 
            Note reconnections may mean we call disconnect multiple times per
2467
 
            transport which is suboptimal but seems harmless.
2468
 
            """
2469
 
            self.addCleanup(transport.disconnect)
2470
 
 
2471
 
        _mod_transport.Transport.hooks.install_named_hook('post_connect',
2472
 
            _add_disconnect_cleanup, None)
2473
 
 
2474
 
        self._make_test_root()
2475
 
        self.addCleanup(os.chdir, os.getcwdu())
2476
 
        self.makeAndChdirToTestDir()
2477
 
        self.overrideEnvironmentForTesting()
2478
 
        self.__readonly_server = None
2479
 
        self.__server = None
2480
 
        self.reduceLockdirTimeout()
2481
 
        # Each test may use its own config files even if the local config files
2482
 
        # don't actually exist. They'll rightly fail if they try to create them
2483
 
        # though.
2484
 
        self.overrideAttr(config, '_shared_stores', {})
2485
 
 
2486
2182
    def get_transport(self, relpath=None):
2487
2183
        """Return a writeable transport.
2488
2184
 
2491
2187
 
2492
2188
        :param relpath: a path relative to the base url.
2493
2189
        """
2494
 
        t = _mod_transport.get_transport_from_url(self.get_url(relpath))
 
2190
        t = _mod_transport.get_transport(self.get_url(relpath))
2495
2191
        self.assertFalse(t.is_readonly())
2496
2192
        return t
2497
2193
 
2503
2199
 
2504
2200
        :param relpath: a path relative to the base url.
2505
2201
        """
2506
 
        t = _mod_transport.get_transport_from_url(
2507
 
            self.get_readonly_url(relpath))
 
2202
        t = _mod_transport.get_transport(self.get_readonly_url(relpath))
2508
2203
        self.assertTrue(t.is_readonly())
2509
2204
        return t
2510
2205
 
2631
2326
        real branch.
2632
2327
        """
2633
2328
        root = TestCaseWithMemoryTransport.TEST_ROOT
2634
 
        try:
2635
 
            # Make sure we get a readable and accessible home for .bzr.log
2636
 
            # and/or config files, and not fallback to weird defaults (see
2637
 
            # http://pad.lv/825027).
2638
 
            self.assertIs(None, os.environ.get('BZR_HOME', None))
2639
 
            os.environ['BZR_HOME'] = root
2640
 
            wt = controldir.ControlDir.create_standalone_workingtree(root)
2641
 
            del os.environ['BZR_HOME']
2642
 
        except Exception, e:
2643
 
            self.fail("Fail to initialize the safety net: %r\n" % (e,))
2644
 
        # Hack for speed: remember the raw bytes of the dirstate file so that
2645
 
        # we don't need to re-open the wt to check it hasn't changed.
2646
 
        TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE = (
2647
 
            wt.control_transport.get_bytes('dirstate'))
 
2329
        bzrdir.BzrDir.create_standalone_workingtree(root)
2648
2330
 
2649
2331
    def _check_safety_net(self):
2650
2332
        """Check that the safety .bzr directory have not been touched.
2653
2335
        propagating. This method ensures than a test did not leaked.
2654
2336
        """
2655
2337
        root = TestCaseWithMemoryTransport.TEST_ROOT
2656
 
        t = _mod_transport.get_transport_from_path(root)
2657
 
        self.permit_url(t.base)
2658
 
        if (t.get_bytes('.bzr/checkout/dirstate') != 
2659
 
                TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE):
 
2338
        self.permit_url(_mod_transport.get_transport(root).base)
 
2339
        wt = workingtree.WorkingTree.open(root)
 
2340
        last_rev = wt.last_revision()
 
2341
        if last_rev != 'null:':
2660
2342
            # The current test have modified the /bzr directory, we need to
2661
2343
            # recreate a new one or all the followng tests will fail.
2662
2344
            # If you need to inspect its content uncomment the following line
2694
2376
        self.test_home_dir = self.test_dir + "/MemoryTransportMissingHomeDir"
2695
2377
        self.permit_dir(self.test_dir)
2696
2378
 
2697
 
    def make_branch(self, relpath, format=None, name=None):
 
2379
    def make_branch(self, relpath, format=None):
2698
2380
        """Create a branch on the transport at relpath."""
2699
2381
        repo = self.make_repository(relpath, format=format)
2700
 
        return repo.bzrdir.create_branch(append_revisions_only=False,
2701
 
                                         name=name)
2702
 
 
2703
 
    def get_default_format(self):
2704
 
        return 'default'
2705
 
 
2706
 
    def resolve_format(self, format):
2707
 
        """Resolve an object to a ControlDir format object.
2708
 
 
2709
 
        The initial format object can either already be
2710
 
        a ControlDirFormat, None (for the default format),
2711
 
        or a string with the name of the control dir format.
2712
 
 
2713
 
        :param format: Object to resolve
2714
 
        :return A ControlDirFormat instance
2715
 
        """
2716
 
        if format is None:
2717
 
            format = self.get_default_format()
2718
 
        if isinstance(format, basestring):
2719
 
            format = controldir.format_registry.make_bzrdir(format)
2720
 
        return format
 
2382
        return repo.bzrdir.create_branch()
2721
2383
 
2722
2384
    def make_bzrdir(self, relpath, format=None):
2723
2385
        try:
2727
2389
            t = _mod_transport.get_transport(maybe_a_url)
2728
2390
            if len(segments) > 1 and segments[-1] not in ('', '.'):
2729
2391
                t.ensure_base()
2730
 
            format = self.resolve_format(format)
 
2392
            if format is None:
 
2393
                format = 'default'
 
2394
            if isinstance(format, basestring):
 
2395
                format = bzrdir.format_registry.make_bzrdir(format)
2731
2396
            return format.initialize_on_transport(t)
2732
2397
        except errors.UninitializableFormat:
2733
2398
            raise TestSkipped("Format %s is not initializable." % format)
2734
2399
 
2735
 
    def make_repository(self, relpath, shared=None, format=None):
 
2400
    def make_repository(self, relpath, shared=False, format=None):
2736
2401
        """Create a repository on our default transport at relpath.
2737
2402
 
2738
2403
        Note that relpath must be a relative path, not a full url.
2749
2414
            backing_server = self.get_server()
2750
2415
        smart_server = test_server.SmartTCPServer_for_testing()
2751
2416
        self.start_server(smart_server, backing_server)
2752
 
        remote_transport = _mod_transport.get_transport_from_url(smart_server.get_url()
 
2417
        remote_transport = _mod_transport.get_transport(smart_server.get_url()
2753
2418
                                                   ).clone(path)
2754
2419
        return remote_transport
2755
2420
 
2766
2431
        test_home_dir = self.test_home_dir
2767
2432
        if isinstance(test_home_dir, unicode):
2768
2433
            test_home_dir = test_home_dir.encode(sys.getfilesystemencoding())
2769
 
        self.overrideEnv('HOME', test_home_dir)
2770
 
        self.overrideEnv('BZR_HOME', test_home_dir)
 
2434
        os.environ['HOME'] = test_home_dir
 
2435
        os.environ['BZR_HOME'] = test_home_dir
 
2436
 
 
2437
    def setUp(self):
 
2438
        super(TestCaseWithMemoryTransport, self).setUp()
 
2439
        # Ensure that ConnectedTransport doesn't leak sockets
 
2440
        def get_transport_with_cleanup(*args, **kwargs):
 
2441
            t = orig_get_transport(*args, **kwargs)
 
2442
            if isinstance(t, _mod_transport.ConnectedTransport):
 
2443
                self.addCleanup(t.disconnect)
 
2444
            return t
 
2445
 
 
2446
        orig_get_transport = self.overrideAttr(_mod_transport, 'get_transport',
 
2447
                                               get_transport_with_cleanup)
 
2448
        self._make_test_root()
 
2449
        self.addCleanup(os.chdir, os.getcwdu())
 
2450
        self.makeAndChdirToTestDir()
 
2451
        self.overrideEnvironmentForTesting()
 
2452
        self.__readonly_server = None
 
2453
        self.__server = None
 
2454
        self.reduceLockdirTimeout()
2771
2455
 
2772
2456
    def setup_smart_server_with_call_log(self):
2773
2457
        """Sets up a smart server as the transport server with a call log."""
2774
2458
        self.transport_server = test_server.SmartTCPServer_for_testing
2775
 
        self.hpss_connections = []
2776
2459
        self.hpss_calls = []
2777
2460
        import traceback
2778
2461
        # Skip the current stack down to the caller of
2781
2464
        def capture_hpss_call(params):
2782
2465
            self.hpss_calls.append(
2783
2466
                CapturedCall(params, prefix_length))
2784
 
        def capture_connect(transport):
2785
 
            self.hpss_connections.append(transport)
2786
2467
        client._SmartClient.hooks.install_named_hook(
2787
2468
            'call', capture_hpss_call, None)
2788
 
        _mod_transport.Transport.hooks.install_named_hook(
2789
 
            'post_connect', capture_connect, None)
2790
2469
 
2791
2470
    def reset_smart_call_log(self):
2792
2471
        self.hpss_calls = []
2793
 
        self.hpss_connections = []
2794
2472
 
2795
2473
 
2796
2474
class TestCaseInTempDir(TestCaseWithMemoryTransport):
2815
2493
 
2816
2494
    OVERRIDE_PYTHON = 'python'
2817
2495
 
2818
 
    def setUp(self):
2819
 
        super(TestCaseInTempDir, self).setUp()
2820
 
        # Remove the protection set in isolated_environ, we have a proper
2821
 
        # access to disk resources now.
2822
 
        self.overrideEnv('BZR_LOG', None)
2823
 
 
2824
2496
    def check_file_contents(self, filename, expect):
2825
2497
        self.log("check contents of file %s" % filename)
2826
2498
        f = file(filename)
2866
2538
        # stacking policy to honour; create a bzr dir with an unshared
2867
2539
        # repository (but not a branch - our code would be trying to escape
2868
2540
        # then!) to stop them, and permit it to be read.
2869
 
        # control = controldir.ControlDir.create(self.test_base_dir)
 
2541
        # control = bzrdir.BzrDir.create(self.test_base_dir)
2870
2542
        # control.create_repository()
2871
2543
        self.test_home_dir = self.test_base_dir + '/home'
2872
2544
        os.mkdir(self.test_home_dir)
2907
2579
                "a list or a tuple. Got %r instead" % (shape,))
2908
2580
        # It's OK to just create them using forward slashes on windows.
2909
2581
        if transport is None or transport.is_readonly():
2910
 
            transport = _mod_transport.get_transport_from_path(".")
 
2582
            transport = _mod_transport.get_transport(".")
2911
2583
        for name in shape:
2912
2584
            self.assertIsInstance(name, basestring)
2913
2585
            if name[-1] == '/':
2961
2633
    readwrite one must both define get_url() as resolving to os.getcwd().
2962
2634
    """
2963
2635
 
2964
 
    def setUp(self):
2965
 
        super(TestCaseWithTransport, self).setUp()
2966
 
        self.__vfs_server = None
2967
 
 
2968
2636
    def get_vfs_only_server(self):
2969
2637
        """See TestCaseWithMemoryTransport.
2970
2638
 
3002
2670
        # this obviously requires a format that supports branch references
3003
2671
        # so check for that by checking bzrdir.BzrDirFormat.get_default_format()
3004
2672
        # RBC 20060208
3005
 
        format = self.resolve_format(format=format)
3006
 
        if not format.supports_workingtrees:
3007
 
            b = self.make_branch(relpath+'.branch', format=format)
3008
 
            return b.create_checkout(relpath, lightweight=True)
3009
2673
        b = self.make_branch(relpath, format=format)
3010
2674
        try:
3011
2675
            return b.bzrdir.create_workingtree()
3016
2680
            if self.vfs_transport_factory is test_server.LocalURLServer:
3017
2681
                # the branch is colocated on disk, we cannot create a checkout.
3018
2682
                # hopefully callers will expect this.
3019
 
                local_controldir = controldir.ControlDir.open(
3020
 
                    self.get_vfs_only_url(relpath))
 
2683
                local_controldir= bzrdir.BzrDir.open(self.get_vfs_only_url(relpath))
3021
2684
                wt = local_controldir.create_workingtree()
3022
2685
                if wt.branch._format != b._format:
3023
2686
                    wt._branch = b
3053
2716
        self.assertFalse(differences.has_changed(),
3054
2717
            "Trees %r and %r are different: %r" % (left, right, differences))
3055
2718
 
 
2719
    def setUp(self):
 
2720
        super(TestCaseWithTransport, self).setUp()
 
2721
        self.__vfs_server = None
 
2722
 
3056
2723
    def disable_missing_extensions_warning(self):
3057
2724
        """Some tests expect a precise stderr content.
3058
2725
 
3059
2726
        There is no point in forcing them to duplicate the extension related
3060
2727
        warning.
3061
2728
        """
3062
 
        config.GlobalStack().set('ignore_missing_extensions', True)
 
2729
        config.GlobalConfig().set_user_option('ignore_missing_extensions', True)
3063
2730
 
3064
2731
 
3065
2732
class ChrootedTestCase(TestCaseWithTransport):
3307
2974
                            result_decorators=result_decorators,
3308
2975
                            )
3309
2976
    runner.stop_on_failure=stop_on_failure
3310
 
    if isinstance(suite, unittest.TestSuite):
3311
 
        # Empty out _tests list of passed suite and populate new TestSuite
3312
 
        suite._tests[:], suite = [], TestSuite(suite)
3313
2977
    # built in decorator factories:
3314
2978
    decorators = [
3315
2979
        random_order(random_seed, runner),
3348
3012
 
3349
3013
 
3350
3014
def fork_decorator(suite):
3351
 
    if getattr(os, "fork", None) is None:
3352
 
        raise errors.BzrCommandError("platform does not support fork,"
3353
 
            " try --parallel=subprocess instead.")
3354
3015
    concurrency = osutils.local_concurrency()
3355
3016
    if concurrency == 1:
3356
3017
        return suite
3413
3074
 
3414
3075
class TestDecorator(TestUtil.TestSuite):
3415
3076
    """A decorator for TestCase/TestSuite objects.
3416
 
 
3417
 
    Contains rather than flattening suite passed on construction
 
3077
    
 
3078
    Usually, subclasses should override __iter__(used when flattening test
 
3079
    suites), which we do to filter, reorder, parallelise and so on, run() and
 
3080
    debug().
3418
3081
    """
3419
3082
 
3420
 
    def __init__(self, suite=None):
3421
 
        super(TestDecorator, self).__init__()
3422
 
        if suite is not None:
3423
 
            self.addTest(suite)
3424
 
 
3425
 
    # Don't need subclass run method with suite emptying
3426
 
    run = unittest.TestSuite.run
 
3083
    def __init__(self, suite):
 
3084
        TestUtil.TestSuite.__init__(self)
 
3085
        self.addTest(suite)
 
3086
 
 
3087
    def countTestCases(self):
 
3088
        cases = 0
 
3089
        for test in self:
 
3090
            cases += test.countTestCases()
 
3091
        return cases
 
3092
 
 
3093
    def debug(self):
 
3094
        for test in self:
 
3095
            test.debug()
 
3096
 
 
3097
    def run(self, result):
 
3098
        # Use iteration on self, not self._tests, to allow subclasses to hook
 
3099
        # into __iter__.
 
3100
        for test in self:
 
3101
            if result.shouldStop:
 
3102
                break
 
3103
            test.run(result)
 
3104
        return result
3427
3105
 
3428
3106
 
3429
3107
class CountingDecorator(TestDecorator):
3440
3118
    """A decorator which excludes test matching an exclude pattern."""
3441
3119
 
3442
3120
    def __init__(self, suite, exclude_pattern):
3443
 
        super(ExcludeDecorator, self).__init__(
3444
 
            exclude_tests_by_re(suite, exclude_pattern))
 
3121
        TestDecorator.__init__(self, suite)
 
3122
        self.exclude_pattern = exclude_pattern
 
3123
        self.excluded = False
 
3124
 
 
3125
    def __iter__(self):
 
3126
        if self.excluded:
 
3127
            return iter(self._tests)
 
3128
        self.excluded = True
 
3129
        suite = exclude_tests_by_re(self, self.exclude_pattern)
 
3130
        del self._tests[:]
 
3131
        self.addTests(suite)
 
3132
        return iter(self._tests)
3445
3133
 
3446
3134
 
3447
3135
class FilterTestsDecorator(TestDecorator):
3448
3136
    """A decorator which filters tests to those matching a pattern."""
3449
3137
 
3450
3138
    def __init__(self, suite, pattern):
3451
 
        super(FilterTestsDecorator, self).__init__(
3452
 
            filter_suite_by_re(suite, pattern))
 
3139
        TestDecorator.__init__(self, suite)
 
3140
        self.pattern = pattern
 
3141
        self.filtered = False
 
3142
 
 
3143
    def __iter__(self):
 
3144
        if self.filtered:
 
3145
            return iter(self._tests)
 
3146
        self.filtered = True
 
3147
        suite = filter_suite_by_re(self, self.pattern)
 
3148
        del self._tests[:]
 
3149
        self.addTests(suite)
 
3150
        return iter(self._tests)
3453
3151
 
3454
3152
 
3455
3153
class RandomDecorator(TestDecorator):
3456
3154
    """A decorator which randomises the order of its tests."""
3457
3155
 
3458
3156
    def __init__(self, suite, random_seed, stream):
3459
 
        random_seed = self.actual_seed(random_seed)
3460
 
        stream.write("Randomizing test order using seed %s\n\n" %
3461
 
            (random_seed,))
 
3157
        TestDecorator.__init__(self, suite)
 
3158
        self.random_seed = random_seed
 
3159
        self.randomised = False
 
3160
        self.stream = stream
 
3161
 
 
3162
    def __iter__(self):
 
3163
        if self.randomised:
 
3164
            return iter(self._tests)
 
3165
        self.randomised = True
 
3166
        self.stream.write("Randomizing test order using seed %s\n\n" %
 
3167
            (self.actual_seed()))
3462
3168
        # Initialise the random number generator.
3463
 
        random.seed(random_seed)
3464
 
        super(RandomDecorator, self).__init__(randomize_suite(suite))
 
3169
        random.seed(self.actual_seed())
 
3170
        suite = randomize_suite(self)
 
3171
        del self._tests[:]
 
3172
        self.addTests(suite)
 
3173
        return iter(self._tests)
3465
3174
 
3466
 
    @staticmethod
3467
 
    def actual_seed(seed):
3468
 
        if seed == "now":
 
3175
    def actual_seed(self):
 
3176
        if self.random_seed == "now":
3469
3177
            # We convert the seed to a long to make it reuseable across
3470
3178
            # invocations (because the user can reenter it).
3471
 
            return long(time.time())
 
3179
            self.random_seed = long(time.time())
3472
3180
        else:
3473
3181
            # Convert the seed to a long if we can
3474
3182
            try:
3475
 
                return long(seed)
3476
 
            except (TypeError, ValueError):
 
3183
                self.random_seed = long(self.random_seed)
 
3184
            except:
3477
3185
                pass
3478
 
        return seed
 
3186
        return self.random_seed
3479
3187
 
3480
3188
 
3481
3189
class TestFirstDecorator(TestDecorator):
3482
3190
    """A decorator which moves named tests to the front."""
3483
3191
 
3484
3192
    def __init__(self, suite, pattern):
3485
 
        super(TestFirstDecorator, self).__init__()
3486
 
        self.addTests(split_suite_by_re(suite, pattern))
 
3193
        TestDecorator.__init__(self, suite)
 
3194
        self.pattern = pattern
 
3195
        self.filtered = False
 
3196
 
 
3197
    def __iter__(self):
 
3198
        if self.filtered:
 
3199
            return iter(self._tests)
 
3200
        self.filtered = True
 
3201
        suites = split_suite_by_re(self, self.pattern)
 
3202
        del self._tests[:]
 
3203
        self.addTests(suites)
 
3204
        return iter(self._tests)
3487
3205
 
3488
3206
 
3489
3207
def partition_tests(suite, count):
3521
3239
    """
3522
3240
    concurrency = osutils.local_concurrency()
3523
3241
    result = []
3524
 
    from subunit import ProtocolTestCase
 
3242
    from subunit import TestProtocolClient, ProtocolTestCase
3525
3243
    from subunit.test_results import AutoTimingTestResultDecorator
3526
3244
    class TestInOtherProcess(ProtocolTestCase):
3527
3245
        # Should be in subunit, I think. RBC.
3533
3251
            try:
3534
3252
                ProtocolTestCase.run(self, result)
3535
3253
            finally:
3536
 
                pid, status = os.waitpid(self.pid, 0)
3537
 
            # GZ 2011-10-18: If status is nonzero, should report to the result
3538
 
            #                that something went wrong.
 
3254
                os.waitpid(self.pid, 0)
3539
3255
 
3540
3256
    test_blocks = partition_tests(suite, concurrency)
3541
 
    # Clear the tests from the original suite so it doesn't keep them alive
3542
 
    suite._tests[:] = []
3543
3257
    for process_tests in test_blocks:
3544
 
        process_suite = TestUtil.TestSuite(process_tests)
3545
 
        # Also clear each split list so new suite has only reference
3546
 
        process_tests[:] = []
 
3258
        process_suite = TestUtil.TestSuite()
 
3259
        process_suite.addTests(process_tests)
3547
3260
        c2pread, c2pwrite = os.pipe()
3548
3261
        pid = os.fork()
3549
3262
        if pid == 0:
 
3263
            workaround_zealous_crypto_random()
3550
3264
            try:
3551
 
                stream = os.fdopen(c2pwrite, 'wb', 1)
3552
 
                workaround_zealous_crypto_random()
3553
3265
                os.close(c2pread)
3554
3266
                # Leave stderr and stdout open so we can see test noise
3555
3267
                # Close stdin so that the child goes away if it decides to
3556
3268
                # read from stdin (otherwise its a roulette to see what
3557
3269
                # child actually gets keystrokes for pdb etc).
3558
3270
                sys.stdin.close()
 
3271
                sys.stdin = None
 
3272
                stream = os.fdopen(c2pwrite, 'wb', 1)
3559
3273
                subunit_result = AutoTimingTestResultDecorator(
3560
 
                    SubUnitBzrProtocolClient(stream))
 
3274
                    TestProtocolClient(stream))
3561
3275
                process_suite.run(subunit_result)
3562
 
            except:
3563
 
                # Try and report traceback on stream, but exit with error even
3564
 
                # if stream couldn't be created or something else goes wrong.
3565
 
                # The traceback is formatted to a string and written in one go
3566
 
                # to avoid interleaving lines from multiple failing children.
3567
 
                try:
3568
 
                    stream.write(traceback.format_exc())
3569
 
                finally:
3570
 
                    os._exit(1)
3571
 
            os._exit(0)
 
3276
            finally:
 
3277
                os._exit(0)
3572
3278
        else:
3573
3279
            os.close(c2pwrite)
3574
3280
            stream = os.fdopen(c2pread, 'rb', 1)
3635
3341
    return result
3636
3342
 
3637
3343
 
3638
 
class ProfileResult(testtools.ExtendedToOriginalDecorator):
 
3344
class ForwardingResult(unittest.TestResult):
 
3345
 
 
3346
    def __init__(self, target):
 
3347
        unittest.TestResult.__init__(self)
 
3348
        self.result = target
 
3349
 
 
3350
    def startTest(self, test):
 
3351
        self.result.startTest(test)
 
3352
 
 
3353
    def stopTest(self, test):
 
3354
        self.result.stopTest(test)
 
3355
 
 
3356
    def startTestRun(self):
 
3357
        self.result.startTestRun()
 
3358
 
 
3359
    def stopTestRun(self):
 
3360
        self.result.stopTestRun()
 
3361
 
 
3362
    def addSkip(self, test, reason):
 
3363
        self.result.addSkip(test, reason)
 
3364
 
 
3365
    def addSuccess(self, test):
 
3366
        self.result.addSuccess(test)
 
3367
 
 
3368
    def addError(self, test, err):
 
3369
        self.result.addError(test, err)
 
3370
 
 
3371
    def addFailure(self, test, err):
 
3372
        self.result.addFailure(test, err)
 
3373
ForwardingResult = testtools.ExtendedToOriginalDecorator
 
3374
 
 
3375
 
 
3376
class ProfileResult(ForwardingResult):
3639
3377
    """Generate profiling data for all activity between start and success.
3640
3378
    
3641
3379
    The profile data is appended to the test's _benchcalls attribute and can
3653
3391
        # unavoidably fail.
3654
3392
        bzrlib.lsprof.BzrProfiler.profiler_block = 0
3655
3393
        self.profiler.start()
3656
 
        testtools.ExtendedToOriginalDecorator.startTest(self, test)
 
3394
        ForwardingResult.startTest(self, test)
3657
3395
 
3658
3396
    def addSuccess(self, test):
3659
3397
        stats = self.profiler.stop()
3663
3401
            test._benchcalls = []
3664
3402
            calls = test._benchcalls
3665
3403
        calls.append(((test.id(), "", ""), stats))
3666
 
        testtools.ExtendedToOriginalDecorator.addSuccess(self, test)
 
3404
        ForwardingResult.addSuccess(self, test)
3667
3405
 
3668
3406
    def stopTest(self, test):
3669
 
        testtools.ExtendedToOriginalDecorator.stopTest(self, test)
 
3407
        ForwardingResult.stopTest(self, test)
3670
3408
        self.profiler = None
3671
3409
 
3672
3410
 
3680
3418
#                           with proper exclusion rules.
3681
3419
#   -Ethreads               Will display thread ident at creation/join time to
3682
3420
#                           help track thread leaks
3683
 
#   -Euncollected_cases     Display the identity of any test cases that weren't
3684
 
#                           deallocated after being completed.
3685
 
#   -Econfig_stats          Will collect statistics using addDetail
3686
3421
selftest_debug_flags = set()
3687
3422
 
3688
3423
 
3792
3527
 
3793
3528
    :return: (absents, duplicates) absents is a list containing the test found
3794
3529
        in id_list but not in test_suite, duplicates is a list containing the
3795
 
        tests found multiple times in test_suite.
 
3530
        test found multiple times in test_suite.
3796
3531
 
3797
3532
    When using a prefined test id list, it may occurs that some tests do not
3798
3533
    exist anymore or that some tests use the same id. This function warns the
3882
3617
                key, obj, help=help, info=info, override_existing=False)
3883
3618
        except KeyError:
3884
3619
            actual = self.get(key)
3885
 
            trace.note(
3886
 
                'Test prefix alias %s is already used for %s, ignoring %s'
3887
 
                % (key, actual, obj))
 
3620
            note('Test prefix alias %s is already used for %s, ignoring %s'
 
3621
                 % (key, actual, obj))
3888
3622
 
3889
3623
    def resolve_alias(self, id_start):
3890
3624
        """Replace the alias by the prefix in the given string.
3922
3656
        'bzrlib.doc',
3923
3657
        'bzrlib.tests.blackbox',
3924
3658
        'bzrlib.tests.commands',
 
3659
        'bzrlib.tests.doc_generate',
3925
3660
        'bzrlib.tests.per_branch',
3926
 
        'bzrlib.tests.per_bzrdir',
3927
3661
        'bzrlib.tests.per_controldir',
3928
3662
        'bzrlib.tests.per_controldir_colo',
3929
3663
        'bzrlib.tests.per_foreign_vcs',
3939
3673
        'bzrlib.tests.per_repository',
3940
3674
        'bzrlib.tests.per_repository_chk',
3941
3675
        'bzrlib.tests.per_repository_reference',
3942
 
        'bzrlib.tests.per_repository_vf',
3943
3676
        'bzrlib.tests.per_uifactory',
3944
3677
        'bzrlib.tests.per_versionedfile',
3945
3678
        'bzrlib.tests.per_workingtree',
3979
3712
        'bzrlib.tests.test_commit_merge',
3980
3713
        'bzrlib.tests.test_config',
3981
3714
        'bzrlib.tests.test_conflicts',
3982
 
        'bzrlib.tests.test_controldir',
3983
3715
        'bzrlib.tests.test_counted_lock',
3984
3716
        'bzrlib.tests.test_crash',
3985
3717
        'bzrlib.tests.test_decorators',
3986
3718
        'bzrlib.tests.test_delta',
3987
3719
        'bzrlib.tests.test_debug',
 
3720
        'bzrlib.tests.test_deprecated_graph',
3988
3721
        'bzrlib.tests.test_diff',
3989
3722
        'bzrlib.tests.test_directory_service',
3990
3723
        'bzrlib.tests.test_dirstate',
3991
3724
        'bzrlib.tests.test_email_message',
3992
3725
        'bzrlib.tests.test_eol_filters',
3993
3726
        'bzrlib.tests.test_errors',
3994
 
        'bzrlib.tests.test_estimate_compressed_size',
3995
3727
        'bzrlib.tests.test_export',
3996
 
        'bzrlib.tests.test_export_pot',
3997
3728
        'bzrlib.tests.test_extract',
3998
 
        'bzrlib.tests.test_features',
3999
3729
        'bzrlib.tests.test_fetch',
4000
3730
        'bzrlib.tests.test_fixtures',
4001
3731
        'bzrlib.tests.test_fifo_cache',
4002
3732
        'bzrlib.tests.test_filters',
4003
 
        'bzrlib.tests.test_filter_tree',
4004
3733
        'bzrlib.tests.test_ftp_transport',
4005
3734
        'bzrlib.tests.test_foreign',
4006
3735
        'bzrlib.tests.test_generate_docs',
4015
3744
        'bzrlib.tests.test_http',
4016
3745
        'bzrlib.tests.test_http_response',
4017
3746
        'bzrlib.tests.test_https_ca_bundle',
4018
 
        'bzrlib.tests.test_https_urllib',
4019
 
        'bzrlib.tests.test_i18n',
4020
3747
        'bzrlib.tests.test_identitymap',
4021
3748
        'bzrlib.tests.test_ignores',
4022
3749
        'bzrlib.tests.test_index',
4041
3768
        'bzrlib.tests.test_merge3',
4042
3769
        'bzrlib.tests.test_merge_core',
4043
3770
        'bzrlib.tests.test_merge_directive',
4044
 
        'bzrlib.tests.test_mergetools',
4045
3771
        'bzrlib.tests.test_missing',
4046
3772
        'bzrlib.tests.test_msgeditor',
4047
3773
        'bzrlib.tests.test_multiparent',
4056
3782
        'bzrlib.tests.test_permissions',
4057
3783
        'bzrlib.tests.test_plugins',
4058
3784
        'bzrlib.tests.test_progress',
4059
 
        'bzrlib.tests.test_pyutils',
4060
3785
        'bzrlib.tests.test_read_bundle',
4061
3786
        'bzrlib.tests.test_reconcile',
4062
3787
        'bzrlib.tests.test_reconfigure',
4070
3795
        'bzrlib.tests.test_revisiontree',
4071
3796
        'bzrlib.tests.test_rio',
4072
3797
        'bzrlib.tests.test_rules',
4073
 
        'bzrlib.tests.test_url_policy_open',
4074
3798
        'bzrlib.tests.test_sampler',
4075
 
        'bzrlib.tests.test_scenarios',
4076
3799
        'bzrlib.tests.test_script',
4077
3800
        'bzrlib.tests.test_selftest',
4078
3801
        'bzrlib.tests.test_serializer',
4083
3806
        'bzrlib.tests.test_smart',
4084
3807
        'bzrlib.tests.test_smart_add',
4085
3808
        'bzrlib.tests.test_smart_request',
4086
 
        'bzrlib.tests.test_smart_signals',
4087
3809
        'bzrlib.tests.test_smart_transport',
4088
3810
        'bzrlib.tests.test_smtp_connection',
4089
3811
        'bzrlib.tests.test_source',
4099
3821
        'bzrlib.tests.test_testament',
4100
3822
        'bzrlib.tests.test_textfile',
4101
3823
        'bzrlib.tests.test_textmerge',
4102
 
        'bzrlib.tests.test_cethread',
4103
3824
        'bzrlib.tests.test_timestamp',
4104
3825
        'bzrlib.tests.test_trace',
4105
3826
        'bzrlib.tests.test_transactions',
4116
3837
        'bzrlib.tests.test_upgrade',
4117
3838
        'bzrlib.tests.test_upgrade_stacked',
4118
3839
        'bzrlib.tests.test_urlutils',
4119
 
        'bzrlib.tests.test_utextwrap',
4120
3840
        'bzrlib.tests.test_version',
4121
3841
        'bzrlib.tests.test_version_info',
4122
3842
        'bzrlib.tests.test_versionedfile',
4123
 
        'bzrlib.tests.test_vf_search',
4124
3843
        'bzrlib.tests.test_weave',
4125
3844
        'bzrlib.tests.test_whitebox',
4126
3845
        'bzrlib.tests.test_win32utils',
4140
3859
        'bzrlib',
4141
3860
        'bzrlib.branchbuilder',
4142
3861
        'bzrlib.decorators',
 
3862
        'bzrlib.export',
4143
3863
        'bzrlib.inventory',
4144
3864
        'bzrlib.iterablefile',
4145
3865
        'bzrlib.lockdir',
4146
3866
        'bzrlib.merge3',
4147
3867
        'bzrlib.option',
4148
 
        'bzrlib.pyutils',
4149
3868
        'bzrlib.symbol_versioning',
4150
3869
        'bzrlib.tests',
4151
3870
        'bzrlib.tests.fixtures',
4152
3871
        'bzrlib.timestamp',
4153
 
        'bzrlib.transport.http',
4154
3872
        'bzrlib.version_info_formats.format_custom',
4155
3873
        ]
4156
3874
 
4209
3927
        try:
4210
3928
            # note that this really does mean "report only" -- doctest
4211
3929
            # still runs the rest of the examples
4212
 
            doc_suite = IsolatedDocTestSuite(
4213
 
                mod, optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
 
3930
            doc_suite = doctest.DocTestSuite(mod,
 
3931
                optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
4214
3932
        except ValueError, e:
4215
3933
            print '**failed to get doctest for: %s\n%s' % (mod, e)
4216
3934
            raise
4219
3937
        suite.addTest(doc_suite)
4220
3938
 
4221
3939
    default_encoding = sys.getdefaultencoding()
4222
 
    for name, plugin in _mod_plugin.plugins().items():
 
3940
    for name, plugin in bzrlib.plugin.plugins().items():
4223
3941
        if not interesting_module(plugin.module.__name__):
4224
3942
            continue
4225
3943
        plugin_suite = plugin.test_suite()
4231
3949
        if plugin_suite is not None:
4232
3950
            suite.addTest(plugin_suite)
4233
3951
        if default_encoding != sys.getdefaultencoding():
4234
 
            trace.warning(
 
3952
            bzrlib.trace.warning(
4235
3953
                'Plugin "%s" tried to reset default encoding to: %s', name,
4236
3954
                sys.getdefaultencoding())
4237
3955
            reload(sys)
4252
3970
            # Some tests mentioned in the list are not in the test suite. The
4253
3971
            # list may be out of date, report to the tester.
4254
3972
            for id in not_found:
4255
 
                trace.warning('"%s" not found in the test suite', id)
 
3973
                bzrlib.trace.warning('"%s" not found in the test suite', id)
4256
3974
        for id in duplicates:
4257
 
            trace.warning('"%s" is used as an id by several tests', id)
 
3975
            bzrlib.trace.warning('"%s" is used as an id by several tests', id)
4258
3976
 
4259
3977
    return suite
4260
3978
 
4261
3979
 
4262
 
def multiply_scenarios(*scenarios):
4263
 
    """Multiply two or more iterables of scenarios.
4264
 
 
4265
 
    It is safe to pass scenario generators or iterators.
4266
 
 
4267
 
    :returns: A list of compound scenarios: the cross-product of all 
4268
 
        scenarios, with the names concatenated and the parameters
4269
 
        merged together.
4270
 
    """
4271
 
    return reduce(_multiply_two_scenarios, map(list, scenarios))
4272
 
 
4273
 
 
4274
 
def _multiply_two_scenarios(scenarios_left, scenarios_right):
 
3980
def multiply_scenarios(scenarios_left, scenarios_right):
4275
3981
    """Multiply two sets of scenarios.
4276
3982
 
4277
3983
    :returns: the cartesian product of the two sets of scenarios, that is
4341
4047
    """Copy test and apply scenario to it.
4342
4048
 
4343
4049
    :param test: A test to adapt.
4344
 
    :param scenario: A tuple describing the scenario.
 
4050
    :param scenario: A tuple describing the scenarion.
4345
4051
        The first element of the tuple is the new test id.
4346
4052
        The second element is a dict containing attributes to set on the
4347
4053
        test.
4363
4069
    """
4364
4070
    new_test = copy.copy(test)
4365
4071
    new_test.id = lambda: new_id
4366
 
    # XXX: Workaround <https://bugs.launchpad.net/testtools/+bug/637725>, which
4367
 
    # causes cloned tests to share the 'details' dict.  This makes it hard to
4368
 
    # read the test output for parameterized tests, because tracebacks will be
4369
 
    # associated with irrelevant tests.
4370
 
    try:
4371
 
        details = new_test._TestCase__details
4372
 
    except AttributeError:
4373
 
        # must be a different version of testtools than expected.  Do nothing.
4374
 
        pass
4375
 
    else:
4376
 
        # Reset the '__details' dict.
4377
 
        new_test._TestCase__details = {}
4378
4072
    return new_test
4379
4073
 
4380
4074
 
4401
4095
        the module is available.
4402
4096
    """
4403
4097
 
4404
 
    from bzrlib.tests.features import ModuleAvailableFeature
4405
 
    py_module = pyutils.get_named_object(py_module_name)
 
4098
    py_module = __import__(py_module_name, {}, {}, ['NO_SUCH_ATTRIB'])
4406
4099
    scenarios = [
4407
4100
        ('python', {'module': py_module}),
4408
4101
    ]
4448
4141
                         % (os.path.basename(dirname), printable_e))
4449
4142
 
4450
4143
 
 
4144
class Feature(object):
 
4145
    """An operating system Feature."""
 
4146
 
 
4147
    def __init__(self):
 
4148
        self._available = None
 
4149
 
 
4150
    def available(self):
 
4151
        """Is the feature available?
 
4152
 
 
4153
        :return: True if the feature is available.
 
4154
        """
 
4155
        if self._available is None:
 
4156
            self._available = self._probe()
 
4157
        return self._available
 
4158
 
 
4159
    def _probe(self):
 
4160
        """Implement this method in concrete features.
 
4161
 
 
4162
        :return: True if the feature is available.
 
4163
        """
 
4164
        raise NotImplementedError
 
4165
 
 
4166
    def __str__(self):
 
4167
        if getattr(self, 'feature_name', None):
 
4168
            return self.feature_name()
 
4169
        return self.__class__.__name__
 
4170
 
 
4171
 
 
4172
class _SymlinkFeature(Feature):
 
4173
 
 
4174
    def _probe(self):
 
4175
        return osutils.has_symlinks()
 
4176
 
 
4177
    def feature_name(self):
 
4178
        return 'symlinks'
 
4179
 
 
4180
SymlinkFeature = _SymlinkFeature()
 
4181
 
 
4182
 
 
4183
class _HardlinkFeature(Feature):
 
4184
 
 
4185
    def _probe(self):
 
4186
        return osutils.has_hardlinks()
 
4187
 
 
4188
    def feature_name(self):
 
4189
        return 'hardlinks'
 
4190
 
 
4191
HardlinkFeature = _HardlinkFeature()
 
4192
 
 
4193
 
 
4194
class _OsFifoFeature(Feature):
 
4195
 
 
4196
    def _probe(self):
 
4197
        return getattr(os, 'mkfifo', None)
 
4198
 
 
4199
    def feature_name(self):
 
4200
        return 'filesystem fifos'
 
4201
 
 
4202
OsFifoFeature = _OsFifoFeature()
 
4203
 
 
4204
 
 
4205
class _UnicodeFilenameFeature(Feature):
 
4206
    """Does the filesystem support Unicode filenames?"""
 
4207
 
 
4208
    def _probe(self):
 
4209
        try:
 
4210
            # Check for character combinations unlikely to be covered by any
 
4211
            # single non-unicode encoding. We use the characters
 
4212
            # - greek small letter alpha (U+03B1) and
 
4213
            # - braille pattern dots-123456 (U+283F).
 
4214
            os.stat(u'\u03b1\u283f')
 
4215
        except UnicodeEncodeError:
 
4216
            return False
 
4217
        except (IOError, OSError):
 
4218
            # The filesystem allows the Unicode filename but the file doesn't
 
4219
            # exist.
 
4220
            return True
 
4221
        else:
 
4222
            # The filesystem allows the Unicode filename and the file exists,
 
4223
            # for some reason.
 
4224
            return True
 
4225
 
 
4226
UnicodeFilenameFeature = _UnicodeFilenameFeature()
 
4227
 
 
4228
 
 
4229
class _CompatabilityThunkFeature(Feature):
 
4230
    """This feature is just a thunk to another feature.
 
4231
 
 
4232
    It issues a deprecation warning if it is accessed, to let you know that you
 
4233
    should really use a different feature.
 
4234
    """
 
4235
 
 
4236
    def __init__(self, dep_version, module, name,
 
4237
                 replacement_name, replacement_module=None):
 
4238
        super(_CompatabilityThunkFeature, self).__init__()
 
4239
        self._module = module
 
4240
        if replacement_module is None:
 
4241
            replacement_module = module
 
4242
        self._replacement_module = replacement_module
 
4243
        self._name = name
 
4244
        self._replacement_name = replacement_name
 
4245
        self._dep_version = dep_version
 
4246
        self._feature = None
 
4247
 
 
4248
    def _ensure(self):
 
4249
        if self._feature is None:
 
4250
            depr_msg = self._dep_version % ('%s.%s'
 
4251
                                            % (self._module, self._name))
 
4252
            use_msg = ' Use %s.%s instead.' % (self._replacement_module,
 
4253
                                               self._replacement_name)
 
4254
            symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning)
 
4255
            # Import the new feature and use it as a replacement for the
 
4256
            # deprecated one.
 
4257
            mod = __import__(self._replacement_module, {}, {},
 
4258
                             [self._replacement_name])
 
4259
            self._feature = getattr(mod, self._replacement_name)
 
4260
 
 
4261
    def _probe(self):
 
4262
        self._ensure()
 
4263
        return self._feature._probe()
 
4264
 
 
4265
 
 
4266
class ModuleAvailableFeature(Feature):
 
4267
    """This is a feature than describes a module we want to be available.
 
4268
 
 
4269
    Declare the name of the module in __init__(), and then after probing, the
 
4270
    module will be available as 'self.module'.
 
4271
 
 
4272
    :ivar module: The module if it is available, else None.
 
4273
    """
 
4274
 
 
4275
    def __init__(self, module_name):
 
4276
        super(ModuleAvailableFeature, self).__init__()
 
4277
        self.module_name = module_name
 
4278
 
 
4279
    def _probe(self):
 
4280
        try:
 
4281
            self._module = __import__(self.module_name, {}, {}, [''])
 
4282
            return True
 
4283
        except ImportError:
 
4284
            return False
 
4285
 
 
4286
    @property
 
4287
    def module(self):
 
4288
        if self.available(): # Make sure the probe has been done
 
4289
            return self._module
 
4290
        return None
 
4291
 
 
4292
    def feature_name(self):
 
4293
        return self.module_name
 
4294
 
 
4295
 
 
4296
# This is kept here for compatibility, it is recommended to use
 
4297
# 'bzrlib.tests.feature.paramiko' instead
 
4298
ParamikoFeature = _CompatabilityThunkFeature(
 
4299
    deprecated_in((2,1,0)),
 
4300
    'bzrlib.tests.features', 'ParamikoFeature', 'paramiko')
 
4301
 
 
4302
 
4451
4303
def probe_unicode_in_user_encoding():
4452
4304
    """Try to encode several unicode strings to use in unicode-aware tests.
4453
4305
    Return first successfull match.
4481
4333
    return None
4482
4334
 
4483
4335
 
 
4336
class _HTTPSServerFeature(Feature):
 
4337
    """Some tests want an https Server, check if one is available.
 
4338
 
 
4339
    Right now, the only way this is available is under python2.6 which provides
 
4340
    an ssl module.
 
4341
    """
 
4342
 
 
4343
    def _probe(self):
 
4344
        try:
 
4345
            import ssl
 
4346
            return True
 
4347
        except ImportError:
 
4348
            return False
 
4349
 
 
4350
    def feature_name(self):
 
4351
        return 'HTTPSServer'
 
4352
 
 
4353
 
 
4354
HTTPSServerFeature = _HTTPSServerFeature()
 
4355
 
 
4356
 
 
4357
class _UnicodeFilename(Feature):
 
4358
    """Does the filesystem support Unicode filenames?"""
 
4359
 
 
4360
    def _probe(self):
 
4361
        try:
 
4362
            os.stat(u'\u03b1')
 
4363
        except UnicodeEncodeError:
 
4364
            return False
 
4365
        except (IOError, OSError):
 
4366
            # The filesystem allows the Unicode filename but the file doesn't
 
4367
            # exist.
 
4368
            return True
 
4369
        else:
 
4370
            # The filesystem allows the Unicode filename and the file exists,
 
4371
            # for some reason.
 
4372
            return True
 
4373
 
 
4374
UnicodeFilename = _UnicodeFilename()
 
4375
 
 
4376
 
 
4377
class _ByteStringNamedFilesystem(Feature):
 
4378
    """Is the filesystem based on bytes?"""
 
4379
 
 
4380
    def _probe(self):
 
4381
        if os.name == "posix":
 
4382
            return True
 
4383
        return False
 
4384
 
 
4385
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
 
4386
 
 
4387
 
 
4388
class _UTF8Filesystem(Feature):
 
4389
    """Is the filesystem UTF-8?"""
 
4390
 
 
4391
    def _probe(self):
 
4392
        if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
 
4393
            return True
 
4394
        return False
 
4395
 
 
4396
UTF8Filesystem = _UTF8Filesystem()
 
4397
 
 
4398
 
 
4399
class _BreakinFeature(Feature):
 
4400
    """Does this platform support the breakin feature?"""
 
4401
 
 
4402
    def _probe(self):
 
4403
        from bzrlib import breakin
 
4404
        if breakin.determine_signal() is None:
 
4405
            return False
 
4406
        if sys.platform == 'win32':
 
4407
            # Windows doesn't have os.kill, and we catch the SIGBREAK signal.
 
4408
            # We trigger SIGBREAK via a Console api so we need ctypes to
 
4409
            # access the function
 
4410
            try:
 
4411
                import ctypes
 
4412
            except OSError:
 
4413
                return False
 
4414
        return True
 
4415
 
 
4416
    def feature_name(self):
 
4417
        return "SIGQUIT or SIGBREAK w/ctypes on win32"
 
4418
 
 
4419
 
 
4420
BreakinFeature = _BreakinFeature()
 
4421
 
 
4422
 
 
4423
class _CaseInsCasePresFilenameFeature(Feature):
 
4424
    """Is the file-system case insensitive, but case-preserving?"""
 
4425
 
 
4426
    def _probe(self):
 
4427
        fileno, name = tempfile.mkstemp(prefix='MixedCase')
 
4428
        try:
 
4429
            # first check truly case-preserving for created files, then check
 
4430
            # case insensitive when opening existing files.
 
4431
            name = osutils.normpath(name)
 
4432
            base, rel = osutils.split(name)
 
4433
            found_rel = osutils.canonical_relpath(base, name)
 
4434
            return (found_rel == rel
 
4435
                    and os.path.isfile(name.upper())
 
4436
                    and os.path.isfile(name.lower()))
 
4437
        finally:
 
4438
            os.close(fileno)
 
4439
            os.remove(name)
 
4440
 
 
4441
    def feature_name(self):
 
4442
        return "case-insensitive case-preserving filesystem"
 
4443
 
 
4444
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
 
4445
 
 
4446
 
 
4447
class _CaseInsensitiveFilesystemFeature(Feature):
 
4448
    """Check if underlying filesystem is case-insensitive but *not* case
 
4449
    preserving.
 
4450
    """
 
4451
    # Note that on Windows, Cygwin, MacOS etc, the file-systems are far
 
4452
    # more likely to be case preserving, so this case is rare.
 
4453
 
 
4454
    def _probe(self):
 
4455
        if CaseInsCasePresFilenameFeature.available():
 
4456
            return False
 
4457
 
 
4458
        if TestCaseWithMemoryTransport.TEST_ROOT is None:
 
4459
            root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
 
4460
            TestCaseWithMemoryTransport.TEST_ROOT = root
 
4461
        else:
 
4462
            root = TestCaseWithMemoryTransport.TEST_ROOT
 
4463
        tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
 
4464
            dir=root)
 
4465
        name_a = osutils.pathjoin(tdir, 'a')
 
4466
        name_A = osutils.pathjoin(tdir, 'A')
 
4467
        os.mkdir(name_a)
 
4468
        result = osutils.isdir(name_A)
 
4469
        _rmtree_temp_dir(tdir)
 
4470
        return result
 
4471
 
 
4472
    def feature_name(self):
 
4473
        return 'case-insensitive filesystem'
 
4474
 
 
4475
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
 
4476
 
 
4477
 
 
4478
class _CaseSensitiveFilesystemFeature(Feature):
 
4479
 
 
4480
    def _probe(self):
 
4481
        if CaseInsCasePresFilenameFeature.available():
 
4482
            return False
 
4483
        elif CaseInsensitiveFilesystemFeature.available():
 
4484
            return False
 
4485
        else:
 
4486
            return True
 
4487
 
 
4488
    def feature_name(self):
 
4489
        return 'case-sensitive filesystem'
 
4490
 
 
4491
# new coding style is for feature instances to be lowercase
 
4492
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
 
4493
 
 
4494
 
 
4495
# Kept for compatibility, use bzrlib.tests.features.subunit instead
 
4496
SubUnitFeature = _CompatabilityThunkFeature(
 
4497
    deprecated_in((2,1,0)),
 
4498
    'bzrlib.tests.features', 'SubUnitFeature', 'subunit')
4484
4499
# Only define SubUnitBzrRunner if subunit is available.
4485
4500
try:
4486
4501
    from subunit import TestProtocolClient
4487
4502
    from subunit.test_results import AutoTimingTestResultDecorator
4488
 
    class SubUnitBzrProtocolClient(TestProtocolClient):
4489
 
 
4490
 
        def stopTest(self, test):
4491
 
            super(SubUnitBzrProtocolClient, self).stopTest(test)
4492
 
            _clear__type_equality_funcs(test)
4493
 
 
4494
 
        def addSuccess(self, test, details=None):
4495
 
            # The subunit client always includes the details in the subunit
4496
 
            # stream, but we don't want to include it in ours.
4497
 
            if details is not None and 'log' in details:
4498
 
                del details['log']
4499
 
            return super(SubUnitBzrProtocolClient, self).addSuccess(
4500
 
                test, details)
4501
 
 
4502
4503
    class SubUnitBzrRunner(TextTestRunner):
4503
4504
        def run(self, test):
4504
4505
            result = AutoTimingTestResultDecorator(
4505
 
                SubUnitBzrProtocolClient(self.stream))
 
4506
                TestProtocolClient(self.stream))
4506
4507
            test.run(result)
4507
4508
            return result
4508
4509
except ImportError:
4509
4510
    pass
4510
4511
 
4511
 
 
4512
 
# API compatibility for old plugins; see bug 892622.
4513
 
for name in [
4514
 
    'Feature',
4515
 
    'HTTPServerFeature', 
4516
 
    'ModuleAvailableFeature',
4517
 
    'HTTPSServerFeature', 'SymlinkFeature', 'HardlinkFeature',
4518
 
    'OsFifoFeature', 'UnicodeFilenameFeature',
4519
 
    'ByteStringNamedFilesystem', 'UTF8Filesystem',
4520
 
    'BreakinFeature', 'CaseInsCasePresFilenameFeature',
4521
 
    'CaseInsensitiveFilesystemFeature', 'case_sensitive_filesystem_feature',
4522
 
    'posix_permissions_feature',
4523
 
    ]:
4524
 
    globals()[name] = _CompatabilityThunkFeature(
4525
 
        symbol_versioning.deprecated_in((2, 5, 0)),
4526
 
        'bzrlib.tests', name,
4527
 
        name, 'bzrlib.tests.features')
4528
 
 
4529
 
 
4530
 
for (old_name, new_name) in [
4531
 
    ('UnicodeFilename', 'UnicodeFilenameFeature'),
4532
 
    ]:
4533
 
    globals()[name] = _CompatabilityThunkFeature(
4534
 
        symbol_versioning.deprecated_in((2, 5, 0)),
4535
 
        'bzrlib.tests', old_name,
4536
 
        new_name, 'bzrlib.tests.features')
 
4512
class _PosixPermissionsFeature(Feature):
 
4513
 
 
4514
    def _probe(self):
 
4515
        def has_perms():
 
4516
            # create temporary file and check if specified perms are maintained.
 
4517
            import tempfile
 
4518
 
 
4519
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
 
4520
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
 
4521
            fd, name = f
 
4522
            os.close(fd)
 
4523
            os.chmod(name, write_perms)
 
4524
 
 
4525
            read_perms = os.stat(name).st_mode & 0777
 
4526
            os.unlink(name)
 
4527
            return (write_perms == read_perms)
 
4528
 
 
4529
        return (os.name == 'posix') and has_perms()
 
4530
 
 
4531
    def feature_name(self):
 
4532
        return 'POSIX permissions support'
 
4533
 
 
4534
posix_permissions_feature = _PosixPermissionsFeature()