~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-02-16 00:53:04 UTC
  • mfrom: (4997.1.3 find-branches-local)
  • Revision ID: pqm@pqm.ubuntu.com-20100216005304-1p8xafkhiizh6ugi
(Jelmer) Add BzrDir.list_branches().

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
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
"""Testing framework extensions"""
18
17
 
19
18
# TODO: Perhaps there should be an API to find out if bzr running under the
20
19
# test suite -- some plugins might want to avoid making intrusive changes if
29
28
 
30
29
import atexit
31
30
import codecs
32
 
import copy
 
31
from copy import copy
33
32
from cStringIO import StringIO
34
33
import difflib
35
34
import doctest
36
35
import errno
37
 
import itertools
38
36
import logging
 
37
import math
39
38
import os
40
 
import platform
41
 
import pprint
 
39
from pprint import pformat
42
40
import random
43
41
import re
44
42
import shlex
45
43
import stat
46
 
import subprocess
 
44
from subprocess import Popen, PIPE, STDOUT
47
45
import sys
48
46
import tempfile
49
47
import threading
55
53
import testtools
56
54
# nb: check this before importing anything else from within it
57
55
_testtools_version = getattr(testtools, '__version__', ())
58
 
if _testtools_version < (0, 9, 5):
59
 
    raise ImportError("need at least testtools 0.9.5: %s is %r"
 
56
if _testtools_version < (0, 9, 2):
 
57
    raise ImportError("need at least testtools 0.9.2: %s is %r"
60
58
        % (testtools.__file__, _testtools_version))
61
59
from testtools import content
62
60
 
63
 
import bzrlib
64
61
from bzrlib import (
65
62
    branchbuilder,
66
63
    bzrdir,
67
64
    chk_map,
68
 
    commands as _mod_commands,
69
65
    config,
70
66
    debug,
71
67
    errors,
72
68
    hooks,
73
69
    lock as _mod_lock,
74
 
    lockdir,
75
70
    memorytree,
76
71
    osutils,
77
 
    plugin as _mod_plugin,
78
 
    pyutils,
 
72
    progress,
79
73
    ui,
80
74
    urlutils,
81
75
    registry,
82
 
    symbol_versioning,
83
 
    trace,
84
 
    transport as _mod_transport,
85
76
    workingtree,
86
77
    )
 
78
import bzrlib.branch
 
79
import bzrlib.commands
 
80
import bzrlib.timestamp
 
81
import bzrlib.export
 
82
import bzrlib.inventory
 
83
import bzrlib.iterablefile
 
84
import bzrlib.lockdir
87
85
try:
88
86
    import bzrlib.lsprof
89
87
except ImportError:
90
88
    # lsprof not available
91
89
    pass
92
 
from bzrlib.smart import client, request
 
90
from bzrlib.merge import merge_inner
 
91
import bzrlib.merge3
 
92
import bzrlib.plugin
 
93
from bzrlib.smart import client, request, server
 
94
import bzrlib.store
 
95
from bzrlib import symbol_versioning
 
96
from bzrlib.symbol_versioning import (
 
97
    DEPRECATED_PARAMETER,
 
98
    deprecated_function,
 
99
    deprecated_in,
 
100
    deprecated_method,
 
101
    deprecated_passed,
 
102
    )
 
103
import bzrlib.trace
93
104
from bzrlib.transport import (
 
105
    get_transport,
94
106
    memory,
95
107
    pathfilter,
96
108
    )
 
109
import bzrlib.transport
 
110
from bzrlib.trace import mutter, note
97
111
from bzrlib.tests import (
98
112
    test_server,
99
113
    TestUtil,
100
 
    treeshape,
101
114
    )
 
115
from bzrlib.tests.http_server import HttpServer
 
116
from bzrlib.tests.TestUtil import (
 
117
                          TestSuite,
 
118
                          TestLoader,
 
119
                          )
 
120
from bzrlib.tests.treeshape import build_tree_contents
102
121
from bzrlib.ui import NullProgressView
103
122
from bzrlib.ui.text import TextUIFactory
 
123
import bzrlib.version_info_formats.format_custom
 
124
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
104
125
 
105
126
# Mark this python module as being part of the implementation
106
127
# of unittest: this gives us better tracebacks where the last
118
139
SUBUNIT_SEEK_SET = 0
119
140
SUBUNIT_SEEK_CUR = 1
120
141
 
121
 
# These are intentionally brought into this namespace. That way plugins, etc
122
 
# can just "from bzrlib.tests import TestCase, TestLoader, etc"
123
 
TestSuite = TestUtil.TestSuite
124
 
TestLoader = TestUtil.TestLoader
125
 
 
126
 
# Tests should run in a clean and clearly defined environment. The goal is to
127
 
# keep them isolated from the running environment as mush as possible. The test
128
 
# framework ensures the variables defined below are set (or deleted if the
129
 
# value is None) before a test is run and reset to their original value after
130
 
# the test is run. Generally if some code depends on an environment variable,
131
 
# the tests should start without this variable in the environment. There are a
132
 
# few exceptions but you shouldn't violate this rule lightly.
133
 
isolated_environ = {
134
 
    'BZR_HOME': None,
135
 
    'HOME': None,
136
 
    # bzr now uses the Win32 API and doesn't rely on APPDATA, but the
137
 
    # tests do check our impls match APPDATA
138
 
    'BZR_EDITOR': None, # test_msgeditor manipulates this variable
139
 
    'VISUAL': None,
140
 
    'EDITOR': None,
141
 
    'BZR_EMAIL': None,
142
 
    'BZREMAIL': None, # may still be present in the environment
143
 
    'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
144
 
    'BZR_PROGRESS_BAR': None,
145
 
    # This should trap leaks to ~/.bzr.log. This occurs when tests use TestCase
146
 
    # as a base class instead of TestCaseInTempDir. Tests inheriting from
147
 
    # TestCase should not use disk resources, BZR_LOG is one.
148
 
    'BZR_LOG': '/you-should-use-TestCaseInTempDir-if-you-need-a-log-file',
149
 
    'BZR_PLUGIN_PATH': None,
150
 
    'BZR_DISABLE_PLUGINS': None,
151
 
    'BZR_PLUGINS_AT': None,
152
 
    'BZR_CONCURRENCY': None,
153
 
    # Make sure that any text ui tests are consistent regardless of
154
 
    # the environment the test case is run in; you may want tests that
155
 
    # test other combinations.  'dumb' is a reasonable guess for tests
156
 
    # going to a pipe or a StringIO.
157
 
    'TERM': 'dumb',
158
 
    'LINES': '25',
159
 
    'COLUMNS': '80',
160
 
    'BZR_COLUMNS': '80',
161
 
    # Disable SSH Agent
162
 
    'SSH_AUTH_SOCK': None,
163
 
    # Proxies
164
 
    'http_proxy': None,
165
 
    'HTTP_PROXY': None,
166
 
    'https_proxy': None,
167
 
    'HTTPS_PROXY': None,
168
 
    'no_proxy': None,
169
 
    'NO_PROXY': None,
170
 
    'all_proxy': None,
171
 
    'ALL_PROXY': None,
172
 
    # Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
173
 
    # least. If you do (care), please update this comment
174
 
    # -- vila 20080401
175
 
    'ftp_proxy': None,
176
 
    'FTP_PROXY': None,
177
 
    'BZR_REMOTE_PATH': None,
178
 
    # Generally speaking, we don't want apport reporting on crashes in
179
 
    # the test envirnoment unless we're specifically testing apport,
180
 
    # so that it doesn't leak into the real system environment.  We
181
 
    # use an env var so it propagates to subprocesses.
182
 
    'APPORT_DISABLE': '1',
183
 
    }
184
 
 
185
 
 
186
 
def override_os_environ(test, env=None):
187
 
    """Modify os.environ keeping a copy.
188
 
    
189
 
    :param test: A test instance
190
 
 
191
 
    :param env: A dict containing variable definitions to be installed
192
 
    """
193
 
    if env is None:
194
 
        env = isolated_environ
195
 
    test._original_os_environ = dict([(var, value)
196
 
                                      for var, value in os.environ.iteritems()])
197
 
    for var, value in env.iteritems():
198
 
        osutils.set_or_unset_env(var, value)
199
 
        if var not in test._original_os_environ:
200
 
            # The var is new, add it with a value of None, so
201
 
            # restore_os_environ will delete it
202
 
            test._original_os_environ[var] = None
203
 
 
204
 
 
205
 
def restore_os_environ(test):
206
 
    """Restore os.environ to its original state.
207
 
 
208
 
    :param test: A test instance previously passed to override_os_environ.
209
 
    """
210
 
    for var, value in test._original_os_environ.iteritems():
211
 
        # Restore the original value (or delete it if the value has been set to
212
 
        # None in override_os_environ).
213
 
        osutils.set_or_unset_env(var, value)
214
 
 
215
 
 
216
 
class ExtendedTestResult(testtools.TextTestResult):
 
142
 
 
143
class ExtendedTestResult(unittest._TextTestResult):
217
144
    """Accepts, reports and accumulates the results of running tests.
218
145
 
219
146
    Compared to the unittest version this class adds support for
240
167
        :param bench_history: Optionally, a writable file object to accumulate
241
168
            benchmark results.
242
169
        """
243
 
        testtools.TextTestResult.__init__(self, stream)
 
170
        unittest._TextTestResult.__init__(self, stream, descriptions, verbosity)
244
171
        if bench_history is not None:
245
172
            from bzrlib.version import _get_bzr_source_tree
246
173
            src_tree = _get_bzr_source_tree()
267
194
        self.count = 0
268
195
        self._overall_start_time = time.time()
269
196
        self._strict = strict
270
 
        self._first_thread_leaker_id = None
271
 
        self._tests_leaking_threads_count = 0
272
 
        self._traceback_from_test = None
273
197
 
274
198
    def stopTestRun(self):
275
199
        run = self.testsRun
276
200
        actionTaken = "Ran"
277
201
        stopTime = time.time()
278
202
        timeTaken = stopTime - self.startTime
279
 
        # GZ 2010-07-19: Seems testtools has no printErrors method, and though
280
 
        #                the parent class method is similar have to duplicate
281
 
        self._show_list('ERROR', self.errors)
282
 
        self._show_list('FAIL', self.failures)
283
 
        self.stream.write(self.sep2)
284
 
        self.stream.write("%s %d test%s in %.3fs\n\n" % (actionTaken,
 
203
        self.printErrors()
 
204
        self.stream.writeln(self.separator2)
 
205
        self.stream.writeln("%s %d test%s in %.3fs" % (actionTaken,
285
206
                            run, run != 1 and "s" or "", timeTaken))
 
207
        self.stream.writeln()
286
208
        if not self.wasSuccessful():
287
209
            self.stream.write("FAILED (")
288
210
            failed, errored = map(len, (self.failures, self.errors))
295
217
                if failed or errored: self.stream.write(", ")
296
218
                self.stream.write("known_failure_count=%d" %
297
219
                    self.known_failure_count)
298
 
            self.stream.write(")\n")
 
220
            self.stream.writeln(")")
299
221
        else:
300
222
            if self.known_failure_count:
301
 
                self.stream.write("OK (known_failures=%d)\n" %
 
223
                self.stream.writeln("OK (known_failures=%d)" %
302
224
                    self.known_failure_count)
303
225
            else:
304
 
                self.stream.write("OK\n")
 
226
                self.stream.writeln("OK")
305
227
        if self.skip_count > 0:
306
228
            skipped = self.skip_count
307
 
            self.stream.write('%d test%s skipped\n' %
 
229
            self.stream.writeln('%d test%s skipped' %
308
230
                                (skipped, skipped != 1 and "s" or ""))
309
231
        if self.unsupported:
310
232
            for feature, count in sorted(self.unsupported.items()):
311
 
                self.stream.write("Missing feature '%s' skipped %d tests.\n" %
 
233
                self.stream.writeln("Missing feature '%s' skipped %d tests." %
312
234
                    (feature, count))
313
235
        if self._strict:
314
236
            ok = self.wasStrictlySuccessful()
315
237
        else:
316
238
            ok = self.wasSuccessful()
317
 
        if self._first_thread_leaker_id:
 
239
        if TestCase._first_thread_leaker_id:
318
240
            self.stream.write(
319
241
                '%s is leaking threads among %d leaking tests.\n' % (
320
 
                self._first_thread_leaker_id,
321
 
                self._tests_leaking_threads_count))
 
242
                TestCase._first_thread_leaker_id,
 
243
                TestCase._leaking_threads_tests))
322
244
            # We don't report the main thread as an active one.
323
245
            self.stream.write(
324
246
                '%d non-main threads were left active in the end.\n'
325
 
                % (len(self._active_threads) - 1))
 
247
                % (TestCase._active_threads - 1))
326
248
 
327
249
    def getDescription(self, test):
328
250
        return test.id()
335
257
 
336
258
    def _elapsedTestTimeString(self):
337
259
        """Return a time string for the overall time the current test has taken."""
338
 
        return self._formatTime(self._delta_to_float(
339
 
            self._now() - self._start_datetime))
 
260
        return self._formatTime(time.time() - self._start_time)
340
261
 
341
262
    def _testTimeString(self, testCase):
342
263
        benchmark_time = self._extractBenchmarkTime(testCase)
353
274
 
354
275
    def _shortened_test_description(self, test):
355
276
        what = test.id()
356
 
        what = re.sub(r'^bzrlib\.tests\.', '', what)
 
277
        what = re.sub(r'^bzrlib\.(tests|benchmarks)\.', '', what)
357
278
        return what
358
279
 
359
 
    # GZ 2010-10-04: Cloned tests may end up harmlessly calling this method
360
 
    #                multiple times in a row, because the handler is added for
361
 
    #                each test but the container list is shared between cases.
362
 
    #                See lp:498869 lp:625574 and lp:637725 for background.
363
 
    def _record_traceback_from_test(self, exc_info):
364
 
        """Store the traceback from passed exc_info tuple till"""
365
 
        self._traceback_from_test = exc_info[2]
366
 
 
367
280
    def startTest(self, test):
368
 
        super(ExtendedTestResult, self).startTest(test)
 
281
        unittest.TestResult.startTest(self, test)
369
282
        if self.count == 0:
370
283
            self.startTests()
371
 
        self.count += 1
372
284
        self.report_test_start(test)
373
285
        test.number = self.count
374
286
        self._recordTestStartTime()
375
 
        # Make testtools cases give us the real traceback on failure
376
 
        addOnException = getattr(test, "addOnException", None)
377
 
        if addOnException is not None:
378
 
            addOnException(self._record_traceback_from_test)
379
 
        # Only check for thread leaks on bzrlib derived test cases
380
 
        if isinstance(test, TestCase):
381
 
            test.addCleanup(self._check_leaked_threads, test)
382
 
 
383
 
    def stopTest(self, test):
384
 
        super(ExtendedTestResult, self).stopTest(test)
385
 
        # Manually break cycles, means touching various private things but hey
386
 
        getDetails = getattr(test, "getDetails", None)
387
 
        if getDetails is not None:
388
 
            getDetails().clear()
389
 
        type_equality_funcs = getattr(test, "_type_equality_funcs", None)
390
 
        if type_equality_funcs is not None:
391
 
            type_equality_funcs.clear()
392
 
        self._traceback_from_test = None
393
287
 
394
288
    def startTests(self):
395
 
        self.report_tests_starting()
396
 
        self._active_threads = threading.enumerate()
397
 
 
398
 
    def _check_leaked_threads(self, test):
399
 
        """See if any threads have leaked since last call
400
 
 
401
 
        A sample of live threads is stored in the _active_threads attribute,
402
 
        when this method runs it compares the current live threads and any not
403
 
        in the previous sample are treated as having leaked.
404
 
        """
405
 
        now_active_threads = set(threading.enumerate())
406
 
        threads_leaked = now_active_threads.difference(self._active_threads)
407
 
        if threads_leaked:
408
 
            self._report_thread_leak(test, threads_leaked, now_active_threads)
409
 
            self._tests_leaking_threads_count += 1
410
 
            if self._first_thread_leaker_id is None:
411
 
                self._first_thread_leaker_id = test.id()
412
 
            self._active_threads = now_active_threads
 
289
        import platform
 
290
        if getattr(sys, 'frozen', None) is None:
 
291
            bzr_path = osutils.realpath(sys.argv[0])
 
292
        else:
 
293
            bzr_path = sys.executable
 
294
        self.stream.write(
 
295
            'bzr selftest: %s\n' % (bzr_path,))
 
296
        self.stream.write(
 
297
            '   %s\n' % (
 
298
                    bzrlib.__path__[0],))
 
299
        self.stream.write(
 
300
            '   bzr-%s python-%s %s\n' % (
 
301
                    bzrlib.version_string,
 
302
                    bzrlib._format_version_tuple(sys.version_info),
 
303
                    platform.platform(aliased=1),
 
304
                    ))
 
305
        self.stream.write('\n')
413
306
 
414
307
    def _recordTestStartTime(self):
415
308
        """Record that a test has started."""
416
 
        self._start_datetime = self._now()
 
309
        self._start_time = time.time()
 
310
 
 
311
    def _cleanupLogFile(self, test):
 
312
        # We can only do this if we have one of our TestCases, not if
 
313
        # we have a doctest.
 
314
        setKeepLogfile = getattr(test, 'setKeepLogfile', None)
 
315
        if setKeepLogfile is not None:
 
316
            setKeepLogfile()
417
317
 
418
318
    def addError(self, test, err):
419
319
        """Tell result that test finished with an error.
421
321
        Called from the TestCase run() method when the test
422
322
        fails with an unexpected error.
423
323
        """
424
 
        self._post_mortem(self._traceback_from_test)
425
 
        super(ExtendedTestResult, self).addError(test, err)
 
324
        self._post_mortem()
 
325
        unittest.TestResult.addError(self, test, err)
426
326
        self.error_count += 1
427
327
        self.report_error(test, err)
428
328
        if self.stop_early:
429
329
            self.stop()
 
330
        self._cleanupLogFile(test)
430
331
 
431
332
    def addFailure(self, test, err):
432
333
        """Tell result that test failed.
434
335
        Called from the TestCase run() method when the test
435
336
        fails because e.g. an assert() method failed.
436
337
        """
437
 
        self._post_mortem(self._traceback_from_test)
438
 
        super(ExtendedTestResult, self).addFailure(test, err)
 
338
        self._post_mortem()
 
339
        unittest.TestResult.addFailure(self, test, err)
439
340
        self.failure_count += 1
440
341
        self.report_failure(test, err)
441
342
        if self.stop_early:
442
343
            self.stop()
 
344
        self._cleanupLogFile(test)
443
345
 
444
346
    def addSuccess(self, test, details=None):
445
347
        """Tell result that test completed successfully.
453
355
                    self._formatTime(benchmark_time),
454
356
                    test.id()))
455
357
        self.report_success(test)
456
 
        super(ExtendedTestResult, self).addSuccess(test)
 
358
        self._cleanupLogFile(test)
 
359
        unittest.TestResult.addSuccess(self, test)
457
360
        test._log_contents = ''
458
361
 
459
362
    def addExpectedFailure(self, test, err):
460
363
        self.known_failure_count += 1
461
364
        self.report_known_failure(test, err)
462
365
 
463
 
    def addUnexpectedSuccess(self, test, details=None):
464
 
        """Tell result the test unexpectedly passed, counting as a failure
465
 
 
466
 
        When the minimum version of testtools required becomes 0.9.8 this
467
 
        can be updated to use the new handling there.
468
 
        """
469
 
        super(ExtendedTestResult, self).addFailure(test, details=details)
470
 
        self.failure_count += 1
471
 
        self.report_unexpected_success(test,
472
 
            "".join(details["reason"].iter_text()))
473
 
        if self.stop_early:
474
 
            self.stop()
475
 
 
476
366
    def addNotSupported(self, test, feature):
477
367
        """The test will not be run because of a missing feature.
478
368
        """
495
385
        self.not_applicable_count += 1
496
386
        self.report_not_applicable(test, reason)
497
387
 
498
 
    def _post_mortem(self, tb=None):
 
388
    def _post_mortem(self):
499
389
        """Start a PDB post mortem session."""
500
390
        if os.environ.get('BZR_TEST_PDB', None):
501
 
            import pdb
502
 
            pdb.post_mortem(tb)
 
391
            import pdb;pdb.post_mortem()
503
392
 
504
393
    def progress(self, offset, whence):
505
394
        """The test is adjusting the count of tests to run."""
510
399
        else:
511
400
            raise errors.BzrError("Unknown whence %r" % whence)
512
401
 
513
 
    def report_tests_starting(self):
514
 
        """Display information before the test run begins"""
515
 
        if getattr(sys, 'frozen', None) is None:
516
 
            bzr_path = osutils.realpath(sys.argv[0])
517
 
        else:
518
 
            bzr_path = sys.executable
519
 
        self.stream.write(
520
 
            'bzr selftest: %s\n' % (bzr_path,))
521
 
        self.stream.write(
522
 
            '   %s\n' % (
523
 
                    bzrlib.__path__[0],))
524
 
        self.stream.write(
525
 
            '   bzr-%s python-%s %s\n' % (
526
 
                    bzrlib.version_string,
527
 
                    bzrlib._format_version_tuple(sys.version_info),
528
 
                    platform.platform(aliased=1),
529
 
                    ))
530
 
        self.stream.write('\n')
531
 
 
532
 
    def report_test_start(self, test):
533
 
        """Display information on the test just about to be run"""
534
 
 
535
 
    def _report_thread_leak(self, test, leaked_threads, active_threads):
536
 
        """Display information on a test that leaked one or more threads"""
537
 
        # GZ 2010-09-09: A leak summary reported separately from the general
538
 
        #                thread debugging would be nice. Tests under subunit
539
 
        #                need something not using stream, perhaps adding a
540
 
        #                testtools details object would be fitting.
541
 
        if 'threads' in selftest_debug_flags:
542
 
            self.stream.write('%s is leaking, active is now %d\n' %
543
 
                (test.id(), len(active_threads)))
 
402
    def report_cleaning_up(self):
 
403
        pass
544
404
 
545
405
    def startTestRun(self):
546
406
        self.startTime = time.time()
583
443
        self.pb.finished()
584
444
        super(TextTestResult, self).stopTestRun()
585
445
 
586
 
    def report_tests_starting(self):
587
 
        super(TextTestResult, self).report_tests_starting()
 
446
    def startTestRun(self):
 
447
        super(TextTestResult, self).startTestRun()
588
448
        self.pb.update('[test 0/%d] Starting' % (self.num_tests))
589
449
 
 
450
    def printErrors(self):
 
451
        # clear the pb to make room for the error listing
 
452
        self.pb.clear()
 
453
        super(TextTestResult, self).printErrors()
 
454
 
590
455
    def _progress_prefix_text(self):
591
456
        # the longer this text, the less space we have to show the test
592
457
        # name...
614
479
        return a
615
480
 
616
481
    def report_test_start(self, test):
 
482
        self.count += 1
617
483
        self.pb.update(
618
484
                self._progress_prefix_text()
619
485
                + ' '
623
489
        return self._shortened_test_description(test)
624
490
 
625
491
    def report_error(self, test, err):
626
 
        self.stream.write('ERROR: %s\n    %s\n' % (
 
492
        self.ui.note('ERROR: %s\n    %s\n' % (
627
493
            self._test_description(test),
628
494
            err[1],
629
495
            ))
630
496
 
631
497
    def report_failure(self, test, err):
632
 
        self.stream.write('FAIL: %s\n    %s\n' % (
 
498
        self.ui.note('FAIL: %s\n    %s\n' % (
633
499
            self._test_description(test),
634
500
            err[1],
635
501
            ))
637
503
    def report_known_failure(self, test, err):
638
504
        pass
639
505
 
640
 
    def report_unexpected_success(self, test, reason):
641
 
        self.stream.write('FAIL: %s\n    %s: %s\n' % (
642
 
            self._test_description(test),
643
 
            "Unexpected success. Should have failed",
644
 
            reason,
645
 
            ))
646
 
 
647
506
    def report_skip(self, test, reason):
648
507
        pass
649
508
 
653
512
    def report_unsupported(self, test, feature):
654
513
        """test cannot be run because feature is missing."""
655
514
 
 
515
    def report_cleaning_up(self):
 
516
        self.pb.update('Cleaning up')
 
517
 
656
518
 
657
519
class VerboseTestResult(ExtendedTestResult):
658
520
    """Produce long output, with one line per test run plus times"""
665
527
            result = a_string
666
528
        return result.ljust(final_width)
667
529
 
668
 
    def report_tests_starting(self):
 
530
    def startTestRun(self):
 
531
        super(VerboseTestResult, self).startTestRun()
669
532
        self.stream.write('running %d tests...\n' % self.num_tests)
670
 
        super(VerboseTestResult, self).report_tests_starting()
671
533
 
672
534
    def report_test_start(self, test):
 
535
        self.count += 1
673
536
        name = self._shortened_test_description(test)
674
537
        width = osutils.terminal_width()
675
538
        if width is not None:
687
550
        return '%s%s' % (indent, err[1])
688
551
 
689
552
    def report_error(self, test, err):
690
 
        self.stream.write('ERROR %s\n%s\n'
 
553
        self.stream.writeln('ERROR %s\n%s'
691
554
                % (self._testTimeString(test),
692
555
                   self._error_summary(err)))
693
556
 
694
557
    def report_failure(self, test, err):
695
 
        self.stream.write(' FAIL %s\n%s\n'
 
558
        self.stream.writeln(' FAIL %s\n%s'
696
559
                % (self._testTimeString(test),
697
560
                   self._error_summary(err)))
698
561
 
699
562
    def report_known_failure(self, test, err):
700
 
        self.stream.write('XFAIL %s\n%s\n'
 
563
        self.stream.writeln('XFAIL %s\n%s'
701
564
                % (self._testTimeString(test),
702
565
                   self._error_summary(err)))
703
566
 
704
 
    def report_unexpected_success(self, test, reason):
705
 
        self.stream.write(' FAIL %s\n%s: %s\n'
706
 
                % (self._testTimeString(test),
707
 
                   "Unexpected success. Should have failed",
708
 
                   reason))
709
 
 
710
567
    def report_success(self, test):
711
 
        self.stream.write('   OK %s\n' % self._testTimeString(test))
 
568
        self.stream.writeln('   OK %s' % self._testTimeString(test))
712
569
        for bench_called, stats in getattr(test, '_benchcalls', []):
713
 
            self.stream.write('LSProf output for %s(%s, %s)\n' % bench_called)
 
570
            self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
714
571
            stats.pprint(file=self.stream)
715
572
        # flush the stream so that we get smooth output. This verbose mode is
716
573
        # used to show the output in PQM.
717
574
        self.stream.flush()
718
575
 
719
576
    def report_skip(self, test, reason):
720
 
        self.stream.write(' SKIP %s\n%s\n'
 
577
        self.stream.writeln(' SKIP %s\n%s'
721
578
                % (self._testTimeString(test), reason))
722
579
 
723
580
    def report_not_applicable(self, test, reason):
724
 
        self.stream.write('  N/A %s\n    %s\n'
 
581
        self.stream.writeln('  N/A %s\n    %s'
725
582
                % (self._testTimeString(test), reason))
726
583
 
727
584
    def report_unsupported(self, test, feature):
728
585
        """test cannot be run because feature is missing."""
729
 
        self.stream.write("NODEP %s\n    The feature '%s' is not available.\n"
 
586
        self.stream.writeln("NODEP %s\n    The feature '%s' is not available."
730
587
                %(self._testTimeString(test), feature))
731
588
 
732
589
 
759
616
            encode = codec[0]
760
617
        else:
761
618
            encode = codec.encode
762
 
        # GZ 2010-09-08: Really we don't want to be writing arbitrary bytes,
763
 
        #                so should swap to the plain codecs.StreamWriter
764
 
        stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream,
765
 
            "backslashreplace")
 
619
        stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream)
766
620
        stream.encoding = new_encoding
767
 
        self.stream = stream
 
621
        self.stream = unittest._WritelnDecorator(stream)
768
622
        self.descriptions = descriptions
769
623
        self.verbosity = verbosity
770
624
        self._bench_history = bench_history
894
748
    # XXX: Should probably unify more with CannedInputUIFactory or a
895
749
    # particular configuration of TextUIFactory, or otherwise have a clearer
896
750
    # idea of how they're supposed to be different.
897
 
    # See https://bugs.launchpad.net/bzr/+bug/408213
 
751
    # See https://bugs.edge.launchpad.net/bzr/+bug/408213
898
752
 
899
753
    def __init__(self, stdout=None, stderr=None, stdin=None):
900
754
        if stdin is not None:
918
772
        return NullProgressView()
919
773
 
920
774
 
921
 
def isolated_doctest_setUp(test):
922
 
    override_os_environ(test)
923
 
 
924
 
 
925
 
def isolated_doctest_tearDown(test):
926
 
    restore_os_environ(test)
927
 
 
928
 
 
929
 
def IsolatedDocTestSuite(*args, **kwargs):
930
 
    """Overrides doctest.DocTestSuite to handle isolation.
931
 
 
932
 
    The method is really a factory and users are expected to use it as such.
933
 
    """
934
 
 
935
 
    kwargs['setUp'] = isolated_doctest_setUp
936
 
    kwargs['tearDown'] = isolated_doctest_tearDown
937
 
    return doctest.DocTestSuite(*args, **kwargs)
938
 
 
939
 
 
940
775
class TestCase(testtools.TestCase):
941
776
    """Base class for bzr unit tests.
942
777
 
953
788
    routine, and to build and check bzr trees.
954
789
 
955
790
    In addition to the usual method of overriding tearDown(), this class also
956
 
    allows subclasses to register cleanup functions via addCleanup, which are
 
791
    allows subclasses to register functions into the _cleanups list, which is
957
792
    run in order as the object is torn down.  It's less likely this will be
958
793
    accidentally overlooked.
959
794
    """
960
795
 
961
 
    _log_file = None
 
796
    _active_threads = None
 
797
    _leaking_threads_tests = 0
 
798
    _first_thread_leaker_id = None
 
799
    _log_file_name = None
962
800
    # record lsprof data when performing benchmark calls.
963
801
    _gather_lsprof_in_benchmarks = False
964
802
 
965
803
    def __init__(self, methodName='testMethod'):
966
804
        super(TestCase, self).__init__(methodName)
 
805
        self._cleanups = []
967
806
        self._directory_isolation = True
968
807
        self.exception_handlers.insert(0,
969
808
            (UnavailableFeature, self._do_unsupported_or_skip))
974
813
        super(TestCase, self).setUp()
975
814
        for feature in getattr(self, '_test_needs_features', []):
976
815
            self.requireFeature(feature)
 
816
        self._log_contents = None
 
817
        self.addDetail("log", content.Content(content.ContentType("text",
 
818
            "plain", {"charset": "utf8"}),
 
819
            lambda:[self._get_log(keep_log_file=True)]))
977
820
        self._cleanEnvironment()
978
821
        self._silenceUI()
979
822
        self._startLogFile()
983
826
        self._track_transports()
984
827
        self._track_locks()
985
828
        self._clear_debug_flags()
986
 
        # Isolate global verbosity level, to make sure it's reproducible
987
 
        # between tests.  We should get rid of this altogether: bug 656694. --
988
 
        # mbp 20101008
989
 
        self.overrideAttr(bzrlib.trace, '_verbosity_level', 0)
990
 
        # Isolate config option expansion until its default value for bzrlib is
991
 
        # settled on or a the FIXME associated with _get_expand_default_value
992
 
        # is addressed -- vila 20110219
993
 
        self.overrideAttr(config, '_expand_default_value', None)
994
 
        self._log_files = set()
995
 
        # Each key in the ``_counters`` dict holds a value for a different
996
 
        # counter. When the test ends, addDetail() should be used to output the
997
 
        # counter values. This happens in install_counter_hook().
998
 
        self._counters = {}
999
 
        if 'config_stats' in selftest_debug_flags:
1000
 
            self._install_config_stats_hooks()
 
829
        TestCase._active_threads = threading.activeCount()
 
830
        self.addCleanup(self._check_leaked_threads)
1001
831
 
1002
832
    def debug(self):
1003
833
        # debug a frame up.
1004
834
        import pdb
1005
835
        pdb.Pdb().set_trace(sys._getframe().f_back)
1006
836
 
1007
 
    def discardDetail(self, name):
1008
 
        """Extend the addDetail, getDetails api so we can remove a detail.
1009
 
 
1010
 
        eg. bzr always adds the 'log' detail at startup, but we don't want to
1011
 
        include it for skipped, xfail, etc tests.
1012
 
 
1013
 
        It is safe to call this for a detail that doesn't exist, in case this
1014
 
        gets called multiple times.
1015
 
        """
1016
 
        # We cheat. details is stored in __details which means we shouldn't
1017
 
        # touch it. but getDetails() returns the dict directly, so we can
1018
 
        # mutate it.
1019
 
        details = self.getDetails()
1020
 
        if name in details:
1021
 
            del details[name]
1022
 
 
1023
 
    def install_counter_hook(self, hooks, name, counter_name=None):
1024
 
        """Install a counting hook.
1025
 
 
1026
 
        Any hook can be counted as long as it doesn't need to return a value.
1027
 
 
1028
 
        :param hooks: Where the hook should be installed.
1029
 
 
1030
 
        :param name: The hook name that will be counted.
1031
 
 
1032
 
        :param counter_name: The counter identifier in ``_counters``, defaults
1033
 
            to ``name``.
1034
 
        """
1035
 
        _counters = self._counters # Avoid closing over self
1036
 
        if counter_name is None:
1037
 
            counter_name = name
1038
 
        if _counters.has_key(counter_name):
1039
 
            raise AssertionError('%s is already used as a counter name'
1040
 
                                  % (counter_name,))
1041
 
        _counters[counter_name] = 0
1042
 
        self.addDetail(counter_name, content.Content(content.UTF8_TEXT,
1043
 
            lambda: ['%d' % (_counters[counter_name],)]))
1044
 
        def increment_counter(*args, **kwargs):
1045
 
            _counters[counter_name] += 1
1046
 
        label = 'count %s calls' % (counter_name,)
1047
 
        hooks.install_named_hook(name, increment_counter, label)
1048
 
        self.addCleanup(hooks.uninstall_named_hook, name, label)
1049
 
 
1050
 
    def _install_config_stats_hooks(self):
1051
 
        """Install config hooks to count hook calls.
1052
 
 
1053
 
        """
1054
 
        for hook_name in ('get', 'set', 'remove', 'load', 'save'):
1055
 
            self.install_counter_hook(config.ConfigHooks, hook_name,
1056
 
                                       'config.%s' % (hook_name,))
1057
 
 
1058
 
        # The OldConfigHooks are private and need special handling to protect
1059
 
        # against recursive tests (tests that run other tests), so we just do
1060
 
        # manually what registering them into _builtin_known_hooks will provide
1061
 
        # us.
1062
 
        self.overrideAttr(config, 'OldConfigHooks', config._OldConfigHooks())
1063
 
        for hook_name in ('get', 'set', 'remove', 'load', 'save'):
1064
 
            self.install_counter_hook(config.OldConfigHooks, hook_name,
1065
 
                                      'old_config.%s' % (hook_name,))
 
837
    def _check_leaked_threads(self):
 
838
        active = threading.activeCount()
 
839
        leaked_threads = active - TestCase._active_threads
 
840
        TestCase._active_threads = active
 
841
        # If some tests make the number of threads *decrease*, we'll consider
 
842
        # that they are just observing old threads dieing, not agressively kill
 
843
        # random threads. So we don't report these tests as leaking. The risk
 
844
        # is that we have false positives that way (the test see 2 threads
 
845
        # going away but leak one) but it seems less likely than the actual
 
846
        # false positives (the test see threads going away and does not leak).
 
847
        if leaked_threads > 0:
 
848
            TestCase._leaking_threads_tests += 1
 
849
            if TestCase._first_thread_leaker_id is None:
 
850
                TestCase._first_thread_leaker_id = self.id()
1066
851
 
1067
852
    def _clear_debug_flags(self):
1068
853
        """Prevent externally set debug flags affecting tests.
1079
864
 
1080
865
    def _clear_hooks(self):
1081
866
        # prevent hooks affecting tests
1082
 
        known_hooks = hooks.known_hooks
1083
867
        self._preserved_hooks = {}
1084
 
        for key, (parent, name) in known_hooks.iter_parent_objects():
1085
 
            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)
1086
871
            self._preserved_hooks[parent] = (name, current_hooks)
1087
 
        self._preserved_lazy_hooks = hooks._lazy_hooks
1088
 
        hooks._lazy_hooks = {}
1089
872
        self.addCleanup(self._restoreHooks)
1090
 
        for key, (parent, name) in known_hooks.iter_parent_objects():
1091
 
            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)
1092
875
            setattr(parent, name, factory())
1093
876
        # this hook should always be installed
1094
877
        request._install_hook()
1123
906
        # break some locks on purpose and should be taken into account by
1124
907
        # considering that breaking a lock is just a dirty way of releasing it.
1125
908
        if len(acquired_locks) != (len(released_locks) + len(broken_locks)):
1126
 
            message = (
1127
 
                'Different number of acquired and '
1128
 
                'released or broken locks.\n'
1129
 
                'acquired=%s\n'
1130
 
                'released=%s\n'
1131
 
                'broken=%s\n' %
1132
 
                (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))
1133
912
            if not self._lock_check_thorough:
1134
913
                # Rather than fail, just warn
1135
914
                print "Broken test %s: %s" % (self, message)
1163
942
 
1164
943
    def permit_dir(self, name):
1165
944
        """Permit a directory to be used by this test. See permit_url."""
1166
 
        name_transport = _mod_transport.get_transport(name)
 
945
        name_transport = get_transport(name)
1167
946
        self.permit_url(name)
1168
947
        self.permit_url(name_transport.base)
1169
948
 
1192
971
            try:
1193
972
                workingtree.WorkingTree.open(path)
1194
973
            except (errors.NotBranchError, errors.NoWorkingTree):
1195
 
                raise TestSkipped('Needs a working tree of bzr sources')
 
974
                return
1196
975
        finally:
1197
976
            self.enable_directory_isolation()
1198
977
 
1248
1027
        self.addCleanup(transport_server.stop_server)
1249
1028
        # Obtain a real transport because if the server supplies a password, it
1250
1029
        # will be hidden from the base on the client side.
1251
 
        t = _mod_transport.get_transport(transport_server.get_url())
 
1030
        t = get_transport(transport_server.get_url())
1252
1031
        # Some transport servers effectively chroot the backing transport;
1253
1032
        # others like SFTPServer don't - users of the transport can walk up the
1254
1033
        # transport to read the entire backing transport. This wouldn't matter
1310
1089
        except UnicodeError, e:
1311
1090
            # If we can't compare without getting a UnicodeError, then
1312
1091
            # obviously they are different
1313
 
            trace.mutter('UnicodeError: %s', e)
 
1092
            mutter('UnicodeError: %s', e)
1314
1093
        if message:
1315
1094
            message += '\n'
1316
1095
        raise AssertionError("%snot equal:\na = %s\nb = %s\n"
1317
1096
            % (message,
1318
 
               pprint.pformat(a), pprint.pformat(b)))
 
1097
               pformat(a), pformat(b)))
1319
1098
 
1320
1099
    assertEquals = assertEqual
1321
1100
 
1355
1134
                         'st_mtime did not match')
1356
1135
        self.assertEqual(expected.st_ctime, actual.st_ctime,
1357
1136
                         'st_ctime did not match')
1358
 
        if sys.platform == 'win32':
 
1137
        if sys.platform != 'win32':
1359
1138
            # On Win32 both 'dev' and 'ino' cannot be trusted. In python2.4 it
1360
1139
            # is 'dev' that varies, in python 2.5 (6?) it is st_ino that is
1361
 
            # odd. We just force it to always be 0 to avoid any problems.
1362
 
            self.assertEqual(0, expected.st_dev)
1363
 
            self.assertEqual(0, actual.st_dev)
1364
 
            self.assertEqual(0, expected.st_ino)
1365
 
            self.assertEqual(0, actual.st_ino)
1366
 
        else:
 
1140
            # odd. Regardless we shouldn't actually try to assert anything
 
1141
            # about their values
1367
1142
            self.assertEqual(expected.st_dev, actual.st_dev,
1368
1143
                             'st_dev did not match')
1369
1144
            self.assertEqual(expected.st_ino, actual.st_ino,
1378
1153
                length, len(obj_with_len), obj_with_len))
1379
1154
 
1380
1155
    def assertLogsError(self, exception_class, func, *args, **kwargs):
1381
 
        """Assert that `func(*args, **kwargs)` quietly logs a specific error.
 
1156
        """Assert that func(*args, **kwargs) quietly logs a specific exception.
1382
1157
        """
 
1158
        from bzrlib import trace
1383
1159
        captured = []
1384
1160
        orig_log_exception_quietly = trace.log_exception_quietly
1385
1161
        try:
1386
1162
            def capture():
1387
1163
                orig_log_exception_quietly()
1388
 
                captured.append(sys.exc_info()[1])
 
1164
                captured.append(sys.exc_info())
1389
1165
            trace.log_exception_quietly = capture
1390
1166
            func(*args, **kwargs)
1391
1167
        finally:
1392
1168
            trace.log_exception_quietly = orig_log_exception_quietly
1393
1169
        self.assertLength(1, captured)
1394
 
        err = captured[0]
 
1170
        err = captured[0][1]
1395
1171
        self.assertIsInstance(err, exception_class)
1396
1172
        return err
1397
1173
 
1434
1210
        if haystack.find(needle) == -1:
1435
1211
            self.fail("string %r not found in '''%s'''" % (needle, haystack))
1436
1212
 
1437
 
    def assertNotContainsString(self, haystack, needle):
1438
 
        if haystack.find(needle) != -1:
1439
 
            self.fail("string %r found in '''%s'''" % (needle, haystack))
1440
 
 
1441
1213
    def assertSubset(self, sublist, superlist):
1442
1214
        """Assert that every entry in sublist is present in superlist."""
1443
1215
        missing = set(sublist) - set(superlist)
1532
1304
 
1533
1305
    def assertFileEqual(self, content, path):
1534
1306
        """Fail if path does not contain 'content'."""
1535
 
        self.assertPathExists(path)
 
1307
        self.failUnlessExists(path)
1536
1308
        f = file(path, 'rb')
1537
1309
        try:
1538
1310
            s = f.read()
1540
1312
            f.close()
1541
1313
        self.assertEqualDiff(content, s)
1542
1314
 
1543
 
    def assertDocstring(self, expected_docstring, obj):
1544
 
        """Fail if obj does not have expected_docstring"""
1545
 
        if __doc__ is None:
1546
 
            # With -OO the docstring should be None instead
1547
 
            self.assertIs(obj.__doc__, None)
1548
 
        else:
1549
 
            self.assertEqual(expected_docstring, obj.__doc__)
1550
 
 
1551
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1552
1315
    def failUnlessExists(self, path):
1553
 
        return self.assertPathExists(path)
1554
 
 
1555
 
    def assertPathExists(self, path):
1556
1316
        """Fail unless path or paths, which may be abs or relative, exist."""
1557
1317
        if not isinstance(path, basestring):
1558
1318
            for p in path:
1559
 
                self.assertPathExists(p)
 
1319
                self.failUnlessExists(p)
1560
1320
        else:
1561
 
            self.assertTrue(osutils.lexists(path),
1562
 
                path + " does not exist")
 
1321
            self.failUnless(osutils.lexists(path),path+" does not exist")
1563
1322
 
1564
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1565
1323
    def failIfExists(self, path):
1566
 
        return self.assertPathDoesNotExist(path)
1567
 
 
1568
 
    def assertPathDoesNotExist(self, path):
1569
1324
        """Fail if path or paths, which may be abs or relative, exist."""
1570
1325
        if not isinstance(path, basestring):
1571
1326
            for p in path:
1572
 
                self.assertPathDoesNotExist(p)
 
1327
                self.failIfExists(p)
1573
1328
        else:
1574
 
            self.assertFalse(osutils.lexists(path),
1575
 
                path + " exists")
 
1329
            self.failIf(osutils.lexists(path),path+" exists")
1576
1330
 
1577
1331
    def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
1578
1332
        """A helper for callDeprecated and applyDeprecated.
1604
1358
        not other callers that go direct to the warning module.
1605
1359
 
1606
1360
        To test that a deprecated method raises an error, do something like
1607
 
        this (remember that both assertRaises and applyDeprecated delays *args
1608
 
        and **kwargs passing)::
 
1361
        this::
1609
1362
 
1610
1363
            self.assertRaises(errors.ReservedId,
1611
1364
                self.applyDeprecated,
1693
1446
 
1694
1447
        The file is removed as the test is torn down.
1695
1448
        """
1696
 
        pseudo_log_file = StringIO()
1697
 
        def _get_log_contents_for_weird_testtools_api():
1698
 
            return [pseudo_log_file.getvalue().decode(
1699
 
                "utf-8", "replace").encode("utf-8")]
1700
 
        self.addDetail("log", content.Content(content.ContentType("text",
1701
 
            "plain", {"charset": "utf8"}),
1702
 
            _get_log_contents_for_weird_testtools_api))
1703
 
        self._log_file = pseudo_log_file
1704
 
        self._log_memento = trace.push_log_file(self._log_file)
 
1449
        fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
 
1450
        self._log_file = os.fdopen(fileno, 'w+')
 
1451
        self._log_memento = bzrlib.trace.push_log_file(self._log_file)
 
1452
        self._log_file_name = name
1705
1453
        self.addCleanup(self._finishLogFile)
1706
1454
 
1707
1455
    def _finishLogFile(self):
1708
1456
        """Finished with the log file.
1709
1457
 
1710
 
        Close the file and delete it.
 
1458
        Close the file and delete it, unless setKeepLogfile was called.
1711
1459
        """
1712
 
        if trace._trace_file:
 
1460
        if bzrlib.trace._trace_file:
1713
1461
            # flush the log file, to get all content
1714
 
            trace._trace_file.flush()
1715
 
        trace.pop_log_file(self._log_memento)
 
1462
            bzrlib.trace._trace_file.flush()
 
1463
        bzrlib.trace.pop_log_file(self._log_memento)
 
1464
        # Cache the log result and delete the file on disk
 
1465
        self._get_log(False)
1716
1466
 
1717
1467
    def thisFailsStrictLockCheck(self):
1718
1468
        """It is known that this test would fail with -Dstrict_locks.
1727
1477
        """
1728
1478
        debug.debug_flags.discard('strict_locks')
1729
1479
 
 
1480
    def addCleanup(self, callable, *args, **kwargs):
 
1481
        """Arrange to run a callable when this case is torn down.
 
1482
 
 
1483
        Callables are run in the reverse of the order they are registered,
 
1484
        ie last-in first-out.
 
1485
        """
 
1486
        self._cleanups.append((callable, args, kwargs))
 
1487
 
1730
1488
    def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1731
1489
        """Overrides an object attribute restoring it after the test.
1732
1490
 
1746
1504
            setattr(obj, attr_name, new)
1747
1505
        return value
1748
1506
 
1749
 
    def overrideEnv(self, name, new):
1750
 
        """Set an environment variable, and reset it after the test.
1751
 
 
1752
 
        :param name: The environment variable name.
1753
 
 
1754
 
        :param new: The value to set the variable to. If None, the 
1755
 
            variable is deleted from the environment.
1756
 
 
1757
 
        :returns: The actual variable value.
1758
 
        """
1759
 
        value = osutils.set_or_unset_env(name, new)
1760
 
        self.addCleanup(osutils.set_or_unset_env, name, value)
1761
 
        return value
1762
 
 
1763
1507
    def _cleanEnvironment(self):
1764
 
        for name, value in isolated_environ.iteritems():
1765
 
            self.overrideEnv(name, value)
 
1508
        new_env = {
 
1509
            'BZR_HOME': None, # Don't inherit BZR_HOME to all the tests.
 
1510
            'HOME': os.getcwd(),
 
1511
            # bzr now uses the Win32 API and doesn't rely on APPDATA, but the
 
1512
            # tests do check our impls match APPDATA
 
1513
            'BZR_EDITOR': None, # test_msgeditor manipulates this variable
 
1514
            'VISUAL': None,
 
1515
            'EDITOR': None,
 
1516
            'BZR_EMAIL': None,
 
1517
            'BZREMAIL': None, # may still be present in the environment
 
1518
            'EMAIL': None,
 
1519
            'BZR_PROGRESS_BAR': None,
 
1520
            'BZR_LOG': None,
 
1521
            'BZR_PLUGIN_PATH': None,
 
1522
            'BZR_CONCURRENCY': None,
 
1523
            # Make sure that any text ui tests are consistent regardless of
 
1524
            # the environment the test case is run in; you may want tests that
 
1525
            # test other combinations.  'dumb' is a reasonable guess for tests
 
1526
            # going to a pipe or a StringIO.
 
1527
            'TERM': 'dumb',
 
1528
            'LINES': '25',
 
1529
            'COLUMNS': '80',
 
1530
            'BZR_COLUMNS': '80',
 
1531
            # SSH Agent
 
1532
            'SSH_AUTH_SOCK': None,
 
1533
            # Proxies
 
1534
            'http_proxy': None,
 
1535
            'HTTP_PROXY': None,
 
1536
            'https_proxy': None,
 
1537
            'HTTPS_PROXY': None,
 
1538
            'no_proxy': None,
 
1539
            'NO_PROXY': None,
 
1540
            'all_proxy': None,
 
1541
            'ALL_PROXY': None,
 
1542
            # Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
 
1543
            # least. If you do (care), please update this comment
 
1544
            # -- vila 20080401
 
1545
            'ftp_proxy': None,
 
1546
            'FTP_PROXY': None,
 
1547
            'BZR_REMOTE_PATH': None,
 
1548
            # Generally speaking, we don't want apport reporting on crashes in
 
1549
            # the test envirnoment unless we're specifically testing apport,
 
1550
            # so that it doesn't leak into the real system environment.  We
 
1551
            # use an env var so it propagates to subprocesses.
 
1552
            'APPORT_DISABLE': '1',
 
1553
        }
 
1554
        self._old_env = {}
 
1555
        self.addCleanup(self._restoreEnvironment)
 
1556
        for name, value in new_env.iteritems():
 
1557
            self._captureVar(name, value)
 
1558
 
 
1559
    def _captureVar(self, name, newvalue):
 
1560
        """Set an environment variable, and reset it when finished."""
 
1561
        self._old_env[name] = osutils.set_or_unset_env(name, newvalue)
 
1562
 
 
1563
    def _restoreEnvironment(self):
 
1564
        for name, value in self._old_env.iteritems():
 
1565
            osutils.set_or_unset_env(name, value)
1766
1566
 
1767
1567
    def _restoreHooks(self):
1768
1568
        for klass, (name, hooks) in self._preserved_hooks.items():
1769
1569
            setattr(klass, name, hooks)
1770
 
        self._preserved_hooks.clear()
1771
 
        bzrlib.hooks._lazy_hooks = self._preserved_lazy_hooks
1772
 
        self._preserved_lazy_hooks.clear()
1773
1570
 
1774
1571
    def knownFailure(self, reason):
1775
1572
        """This test has failed for some known reason."""
1776
1573
        raise KnownFailure(reason)
1777
1574
 
1778
 
    def _suppress_log(self):
1779
 
        """Remove the log info from details."""
1780
 
        self.discardDetail('log')
1781
 
 
1782
1575
    def _do_skip(self, result, reason):
1783
 
        self._suppress_log()
1784
1576
        addSkip = getattr(result, 'addSkip', None)
1785
1577
        if not callable(addSkip):
1786
1578
            result.addSuccess(result)
1789
1581
 
1790
1582
    @staticmethod
1791
1583
    def _do_known_failure(self, result, e):
1792
 
        self._suppress_log()
1793
1584
        err = sys.exc_info()
1794
1585
        addExpectedFailure = getattr(result, 'addExpectedFailure', None)
1795
1586
        if addExpectedFailure is not None:
1803
1594
            reason = 'No reason given'
1804
1595
        else:
1805
1596
            reason = e.args[0]
1806
 
        self._suppress_log ()
1807
1597
        addNotApplicable = getattr(result, 'addNotApplicable', None)
1808
1598
        if addNotApplicable is not None:
1809
1599
            result.addNotApplicable(self, reason)
1811
1601
            self._do_skip(result, reason)
1812
1602
 
1813
1603
    @staticmethod
1814
 
    def _report_skip(self, result, err):
1815
 
        """Override the default _report_skip.
1816
 
 
1817
 
        We want to strip the 'log' detail. If we waint until _do_skip, it has
1818
 
        already been formatted into the 'reason' string, and we can't pull it
1819
 
        out again.
1820
 
        """
1821
 
        self._suppress_log()
1822
 
        super(TestCase, self)._report_skip(self, result, err)
1823
 
 
1824
 
    @staticmethod
1825
 
    def _report_expected_failure(self, result, err):
1826
 
        """Strip the log.
1827
 
 
1828
 
        See _report_skip for motivation.
1829
 
        """
1830
 
        self._suppress_log()
1831
 
        super(TestCase, self)._report_expected_failure(self, result, err)
1832
 
 
1833
 
    @staticmethod
1834
1604
    def _do_unsupported_or_skip(self, result, e):
1835
1605
        reason = e.args[0]
1836
 
        self._suppress_log()
1837
1606
        addNotSupported = getattr(result, 'addNotSupported', None)
1838
1607
        if addNotSupported is not None:
1839
1608
            result.addNotSupported(self, reason)
1865
1634
            self._benchtime += time.time() - start
1866
1635
 
1867
1636
    def log(self, *args):
1868
 
        trace.mutter(*args)
 
1637
        mutter(*args)
 
1638
 
 
1639
    def _get_log(self, keep_log_file=False):
 
1640
        """Internal helper to get the log from bzrlib.trace for this test.
 
1641
 
 
1642
        Please use self.getDetails, or self.get_log to access this in test case
 
1643
        code.
 
1644
 
 
1645
        :param keep_log_file: When True, if the log is still a file on disk
 
1646
            leave it as a file on disk. When False, if the log is still a file
 
1647
            on disk, the log file is deleted and the log preserved as
 
1648
            self._log_contents.
 
1649
        :return: A string containing the log.
 
1650
        """
 
1651
        if self._log_contents is not None:
 
1652
            try:
 
1653
                self._log_contents.decode('utf8')
 
1654
            except UnicodeDecodeError:
 
1655
                unicodestr = self._log_contents.decode('utf8', 'replace')
 
1656
                self._log_contents = unicodestr.encode('utf8')
 
1657
            return self._log_contents
 
1658
        import bzrlib.trace
 
1659
        if bzrlib.trace._trace_file:
 
1660
            # flush the log file, to get all content
 
1661
            bzrlib.trace._trace_file.flush()
 
1662
        if self._log_file_name is not None:
 
1663
            logfile = open(self._log_file_name)
 
1664
            try:
 
1665
                log_contents = logfile.read()
 
1666
            finally:
 
1667
                logfile.close()
 
1668
            try:
 
1669
                log_contents.decode('utf8')
 
1670
            except UnicodeDecodeError:
 
1671
                unicodestr = log_contents.decode('utf8', 'replace')
 
1672
                log_contents = unicodestr.encode('utf8')
 
1673
            if not keep_log_file:
 
1674
                self._log_file.close()
 
1675
                self._log_file = None
 
1676
                # Permit multiple calls to get_log until we clean it up in
 
1677
                # finishLogFile
 
1678
                self._log_contents = log_contents
 
1679
                try:
 
1680
                    os.remove(self._log_file_name)
 
1681
                except OSError, e:
 
1682
                    if sys.platform == 'win32' and e.errno == errno.EACCES:
 
1683
                        sys.stderr.write(('Unable to delete log file '
 
1684
                                             ' %r\n' % self._log_file_name))
 
1685
                    else:
 
1686
                        raise
 
1687
                self._log_file_name = None
 
1688
            return log_contents
 
1689
        else:
 
1690
            return "No log file content and no log file name."
1869
1691
 
1870
1692
    def get_log(self):
1871
1693
        """Get a unicode string containing the log from bzrlib.trace.
1921
1743
 
1922
1744
        try:
1923
1745
            try:
1924
 
                result = self.apply_redirected(
1925
 
                    ui.ui_factory.stdin,
 
1746
                result = self.apply_redirected(ui.ui_factory.stdin,
1926
1747
                    stdout, stderr,
1927
 
                    _mod_commands.run_bzr_catch_user_errors,
 
1748
                    bzrlib.commands.run_bzr_catch_user_errors,
1928
1749
                    args)
1929
1750
            except KeyboardInterrupt:
1930
1751
                # Reraise KeyboardInterrupt with contents of redirected stdout
2072
1893
    def start_bzr_subprocess(self, process_args, env_changes=None,
2073
1894
                             skip_if_plan_to_signal=False,
2074
1895
                             working_dir=None,
2075
 
                             allow_plugins=False, stderr=subprocess.PIPE):
 
1896
                             allow_plugins=False):
2076
1897
        """Start bzr in a subprocess for testing.
2077
1898
 
2078
1899
        This starts a new Python interpreter and runs bzr in there.
2087
1908
            variables. A value of None will unset the env variable.
2088
1909
            The values must be strings. The change will only occur in the
2089
1910
            child, so you don't need to fix the environment after running.
2090
 
        :param skip_if_plan_to_signal: raise TestSkipped when true and system
2091
 
            doesn't support signalling subprocesses.
 
1911
        :param skip_if_plan_to_signal: raise TestSkipped when true and os.kill
 
1912
            is not available.
2092
1913
        :param allow_plugins: If False (default) pass --no-plugins to bzr.
2093
 
        :param stderr: file to use for the subprocess's stderr.  Valid values
2094
 
            are those valid for the stderr argument of `subprocess.Popen`.
2095
 
            Default value is ``subprocess.PIPE``.
2096
1914
 
2097
1915
        :returns: Popen object for the started process.
2098
1916
        """
2099
1917
        if skip_if_plan_to_signal:
2100
 
            if os.name != "posix":
2101
 
                raise TestSkipped("Sending signals not supported")
 
1918
            if not getattr(os, 'kill', None):
 
1919
                raise TestSkipped("os.kill not available.")
2102
1920
 
2103
1921
        if env_changes is None:
2104
1922
            env_changes = {}
2124
1942
            # so we will avoid using it on all platforms, just to
2125
1943
            # make sure the code path is used, and we don't break on win32
2126
1944
            cleanup_environment()
2127
 
            # Include the subprocess's log file in the test details, in case
2128
 
            # the test fails due to an error in the subprocess.
2129
 
            self._add_subprocess_log(trace._get_bzr_log_filename())
2130
1945
            command = [sys.executable]
2131
1946
            # frozen executables don't need the path to bzr
2132
1947
            if getattr(sys, "frozen", None) is None:
2134
1949
            if not allow_plugins:
2135
1950
                command.append('--no-plugins')
2136
1951
            command.extend(process_args)
2137
 
            process = self._popen(command, stdin=subprocess.PIPE,
2138
 
                                  stdout=subprocess.PIPE,
2139
 
                                  stderr=stderr)
 
1952
            process = self._popen(command, stdin=PIPE, stdout=PIPE, stderr=PIPE)
2140
1953
        finally:
2141
1954
            restore_environment()
2142
1955
            if cwd is not None:
2144
1957
 
2145
1958
        return process
2146
1959
 
2147
 
    def _add_subprocess_log(self, log_file_path):
2148
 
        if len(self._log_files) == 0:
2149
 
            # Register an addCleanup func.  We do this on the first call to
2150
 
            # _add_subprocess_log rather than in TestCase.setUp so that this
2151
 
            # addCleanup is registered after any cleanups for tempdirs that
2152
 
            # subclasses might create, which will probably remove the log file
2153
 
            # we want to read.
2154
 
            self.addCleanup(self._subprocess_log_cleanup)
2155
 
        # self._log_files is a set, so if a log file is reused we won't grab it
2156
 
        # twice.
2157
 
        self._log_files.add(log_file_path)
2158
 
 
2159
 
    def _subprocess_log_cleanup(self):
2160
 
        for count, log_file_path in enumerate(self._log_files):
2161
 
            # We use buffer_now=True to avoid holding the file open beyond
2162
 
            # the life of this function, which might interfere with e.g.
2163
 
            # cleaning tempdirs on Windows.
2164
 
            # XXX: Testtools 0.9.5 doesn't have the content_from_file helper
2165
 
            #detail_content = content.content_from_file(
2166
 
            #    log_file_path, buffer_now=True)
2167
 
            with open(log_file_path, 'rb') as log_file:
2168
 
                log_file_bytes = log_file.read()
2169
 
            detail_content = content.Content(content.ContentType("text",
2170
 
                "plain", {"charset": "utf8"}), lambda: [log_file_bytes])
2171
 
            self.addDetail("start_bzr_subprocess-log-%d" % (count,),
2172
 
                detail_content)
2173
 
 
2174
1960
    def _popen(self, *args, **kwargs):
2175
1961
        """Place a call to Popen.
2176
1962
 
2177
1963
        Allows tests to override this method to intercept the calls made to
2178
1964
        Popen for introspection.
2179
1965
        """
2180
 
        return subprocess.Popen(*args, **kwargs)
 
1966
        return Popen(*args, **kwargs)
2181
1967
 
2182
1968
    def get_source_path(self):
2183
1969
        """Return the path of the directory containing bzrlib."""
2185
1971
 
2186
1972
    def get_bzr_path(self):
2187
1973
        """Return the path of the 'bzr' executable for this test suite."""
2188
 
        bzr_path = os.path.join(self.get_source_path(), "bzr")
 
1974
        bzr_path = self.get_source_path()+'/bzr'
2189
1975
        if not os.path.isfile(bzr_path):
2190
1976
            # We are probably installed. Assume sys.argv is the right file
2191
1977
            bzr_path = sys.argv[0]
2213
1999
        if retcode is not None and retcode != process.returncode:
2214
2000
            if process_args is None:
2215
2001
                process_args = "(unknown args)"
2216
 
            trace.mutter('Output of bzr %s:\n%s', process_args, out)
2217
 
            trace.mutter('Error for bzr %s:\n%s', process_args, err)
 
2002
            mutter('Output of bzr %s:\n%s', process_args, out)
 
2003
            mutter('Error for bzr %s:\n%s', process_args, err)
2218
2004
            self.fail('Command bzr %s failed with retcode %s != %s'
2219
2005
                      % (process_args, retcode, process.returncode))
2220
2006
        return [out, err]
2221
2007
 
2222
 
    def check_tree_shape(self, tree, shape):
2223
 
        """Compare a tree to a list of expected names.
 
2008
    def check_inventory_shape(self, inv, shape):
 
2009
        """Compare an inventory to a list of expected names.
2224
2010
 
2225
2011
        Fail if they are not precisely equal.
2226
2012
        """
2227
2013
        extras = []
2228
2014
        shape = list(shape)             # copy
2229
 
        for path, ie in tree.iter_entries_by_dir():
 
2015
        for path, ie in inv.entries():
2230
2016
            name = path.replace('\\', '/')
2231
2017
            if ie.kind == 'directory':
2232
2018
                name = name + '/'
2233
 
            if name == "/":
2234
 
                pass # ignore root entry
2235
 
            elif name in shape:
 
2019
            if name in shape:
2236
2020
                shape.remove(name)
2237
2021
            else:
2238
2022
                extras.append(name)
2279
2063
 
2280
2064
        Tests that expect to provoke LockContention errors should call this.
2281
2065
        """
2282
 
        self.overrideAttr(lockdir, '_DEFAULT_TIMEOUT_SECONDS', 0)
 
2066
        self.overrideAttr(bzrlib.lockdir, '_DEFAULT_TIMEOUT_SECONDS', 0)
2283
2067
 
2284
2068
    def make_utf8_encoded_stringio(self, encoding_type=None):
2285
2069
        """Return a StringIOWrapper instance, that will encode Unicode
2328
2112
class TestCaseWithMemoryTransport(TestCase):
2329
2113
    """Common test class for tests that do not need disk resources.
2330
2114
 
2331
 
    Tests that need disk resources should derive from TestCaseInTempDir
2332
 
    orTestCaseWithTransport.
 
2115
    Tests that need disk resources should derive from TestCaseWithTransport.
2333
2116
 
2334
2117
    TestCaseWithMemoryTransport sets the TEST_ROOT variable for all bzr tests.
2335
2118
 
2336
 
    For TestCaseWithMemoryTransport the ``test_home_dir`` is set to the name of
 
2119
    For TestCaseWithMemoryTransport the test_home_dir is set to the name of
2337
2120
    a directory which does not exist. This serves to help ensure test isolation
2338
 
    is preserved. ``test_dir`` is set to the TEST_ROOT, as is cwd, because they
2339
 
    must exist. However, TestCaseWithMemoryTransport does not offer local file
2340
 
    defaults for the transport in tests, nor does it obey the command line
 
2121
    is preserved. test_dir is set to the TEST_ROOT, as is cwd, because they
 
2122
    must exist. However, TestCaseWithMemoryTransport does not offer local
 
2123
    file defaults for the transport in tests, nor does it obey the command line
2341
2124
    override, so tests that accidentally write to the common directory should
2342
2125
    be rare.
2343
2126
 
2344
 
    :cvar TEST_ROOT: Directory containing all temporary directories, plus a
2345
 
        ``.bzr`` directory that stops us ascending higher into the filesystem.
 
2127
    :cvar TEST_ROOT: Directory containing all temporary directories, plus
 
2128
    a .bzr directory that stops us ascending higher into the filesystem.
2346
2129
    """
2347
2130
 
2348
2131
    TEST_ROOT = None
2366
2149
 
2367
2150
        :param relpath: a path relative to the base url.
2368
2151
        """
2369
 
        t = _mod_transport.get_transport(self.get_url(relpath))
 
2152
        t = get_transport(self.get_url(relpath))
2370
2153
        self.assertFalse(t.is_readonly())
2371
2154
        return t
2372
2155
 
2378
2161
 
2379
2162
        :param relpath: a path relative to the base url.
2380
2163
        """
2381
 
        t = _mod_transport.get_transport(self.get_readonly_url(relpath))
 
2164
        t = get_transport(self.get_readonly_url(relpath))
2382
2165
        self.assertTrue(t.is_readonly())
2383
2166
        return t
2384
2167
 
2514
2297
        propagating. This method ensures than a test did not leaked.
2515
2298
        """
2516
2299
        root = TestCaseWithMemoryTransport.TEST_ROOT
2517
 
        self.permit_url(_mod_transport.get_transport(root).base)
 
2300
        self.permit_url(get_transport(root).base)
2518
2301
        wt = workingtree.WorkingTree.open(root)
2519
2302
        last_rev = wt.last_revision()
2520
2303
        if last_rev != 'null:':
2565
2348
            # might be a relative or absolute path
2566
2349
            maybe_a_url = self.get_url(relpath)
2567
2350
            segments = maybe_a_url.rsplit('/', 1)
2568
 
            t = _mod_transport.get_transport(maybe_a_url)
 
2351
            t = get_transport(maybe_a_url)
2569
2352
            if len(segments) > 1 and segments[-1] not in ('', '.'):
2570
2353
                t.ensure_base()
2571
2354
            if format is None:
2588
2371
        made_control = self.make_bzrdir(relpath, format=format)
2589
2372
        return made_control.create_repository(shared=shared)
2590
2373
 
2591
 
    def make_smart_server(self, path, backing_server=None):
2592
 
        if backing_server is None:
2593
 
            backing_server = self.get_server()
 
2374
    def make_smart_server(self, path):
2594
2375
        smart_server = test_server.SmartTCPServer_for_testing()
2595
 
        self.start_server(smart_server, backing_server)
2596
 
        remote_transport = _mod_transport.get_transport(smart_server.get_url()
2597
 
                                                   ).clone(path)
 
2376
        self.start_server(smart_server, self.get_server())
 
2377
        remote_transport = get_transport(smart_server.get_url()).clone(path)
2598
2378
        return remote_transport
2599
2379
 
2600
2380
    def make_branch_and_memory_tree(self, relpath, format=None):
2610
2390
        test_home_dir = self.test_home_dir
2611
2391
        if isinstance(test_home_dir, unicode):
2612
2392
            test_home_dir = test_home_dir.encode(sys.getfilesystemencoding())
2613
 
        self.overrideEnv('HOME', test_home_dir)
2614
 
        self.overrideEnv('BZR_HOME', test_home_dir)
 
2393
        os.environ['HOME'] = test_home_dir
 
2394
        os.environ['BZR_HOME'] = test_home_dir
2615
2395
 
2616
2396
    def setUp(self):
2617
2397
        super(TestCaseWithMemoryTransport, self).setUp()
2618
 
        # Ensure that ConnectedTransport doesn't leak sockets
2619
 
        def get_transport_with_cleanup(*args, **kwargs):
2620
 
            t = orig_get_transport(*args, **kwargs)
2621
 
            if isinstance(t, _mod_transport.ConnectedTransport):
2622
 
                self.addCleanup(t.disconnect)
2623
 
            return t
2624
 
 
2625
 
        orig_get_transport = self.overrideAttr(_mod_transport, 'get_transport',
2626
 
                                               get_transport_with_cleanup)
2627
2398
        self._make_test_root()
2628
2399
        self.addCleanup(os.chdir, os.getcwdu())
2629
2400
        self.makeAndChdirToTestDir()
2672
2443
 
2673
2444
    OVERRIDE_PYTHON = 'python'
2674
2445
 
2675
 
    def setUp(self):
2676
 
        super(TestCaseInTempDir, self).setUp()
2677
 
        # Remove the protection set in isolated_environ, we have a proper
2678
 
        # access to disk resources now.
2679
 
        self.overrideEnv('BZR_LOG', None)
2680
 
 
2681
2446
    def check_file_contents(self, filename, expect):
2682
2447
        self.log("check contents of file %s" % filename)
2683
 
        f = file(filename)
2684
 
        try:
2685
 
            contents = f.read()
2686
 
        finally:
2687
 
            f.close()
 
2448
        contents = file(filename, 'r').read()
2688
2449
        if contents != expect:
2689
2450
            self.log("expected: %r" % expect)
2690
2451
            self.log("actually: %r" % contents)
2764
2525
                "a list or a tuple. Got %r instead" % (shape,))
2765
2526
        # It's OK to just create them using forward slashes on windows.
2766
2527
        if transport is None or transport.is_readonly():
2767
 
            transport = _mod_transport.get_transport(".")
 
2528
            transport = get_transport(".")
2768
2529
        for name in shape:
2769
2530
            self.assertIsInstance(name, basestring)
2770
2531
            if name[-1] == '/':
2780
2541
                content = "contents of %s%s" % (name.encode('utf-8'), end)
2781
2542
                transport.put_bytes_non_atomic(urlutils.escape(name), content)
2782
2543
 
2783
 
    build_tree_contents = staticmethod(treeshape.build_tree_contents)
 
2544
    def build_tree_contents(self, shape):
 
2545
        build_tree_contents(shape)
2784
2546
 
2785
2547
    def assertInWorkingTree(self, path, root_path='.', tree=None):
2786
2548
        """Assert whether path or paths are in the WorkingTree"""
2927
2689
    """
2928
2690
 
2929
2691
    def setUp(self):
2930
 
        from bzrlib.tests import http_server
2931
2692
        super(ChrootedTestCase, self).setUp()
2932
2693
        if not self.vfs_transport_factory == memory.MemoryServer:
2933
 
            self.transport_readonly_server = http_server.HttpServer
 
2694
            self.transport_readonly_server = HttpServer
2934
2695
 
2935
2696
 
2936
2697
def condition_id_re(pattern):
2939
2700
    :param pattern: A regular expression string.
2940
2701
    :return: A callable that returns True if the re matches.
2941
2702
    """
2942
 
    filter_re = re.compile(pattern, 0)
 
2703
    filter_re = osutils.re_compile_checked(pattern, 0,
 
2704
        'test filter')
2943
2705
    def condition(test):
2944
2706
        test_id = test.id()
2945
2707
        return filter_re.search(test_id)
3197
2959
 
3198
2960
 
3199
2961
def fork_decorator(suite):
3200
 
    if getattr(os, "fork", None) is None:
3201
 
        raise errors.BzrCommandError("platform does not support fork,"
3202
 
            " try --parallel=subprocess instead.")
3203
2962
    concurrency = osutils.local_concurrency()
3204
2963
    if concurrency == 1:
3205
2964
        return suite
3260
3019
    return suite
3261
3020
 
3262
3021
 
3263
 
class TestDecorator(TestUtil.TestSuite):
 
3022
class TestDecorator(TestSuite):
3264
3023
    """A decorator for TestCase/TestSuite objects.
3265
3024
    
3266
3025
    Usually, subclasses should override __iter__(used when flattening test
3269
3028
    """
3270
3029
 
3271
3030
    def __init__(self, suite):
3272
 
        TestUtil.TestSuite.__init__(self)
 
3031
        TestSuite.__init__(self)
3273
3032
        self.addTest(suite)
3274
3033
 
3275
3034
    def countTestCases(self):
3394
3153
 
3395
3154
def partition_tests(suite, count):
3396
3155
    """Partition suite into count lists of tests."""
3397
 
    # This just assigns tests in a round-robin fashion.  On one hand this
3398
 
    # splits up blocks of related tests that might run faster if they shared
3399
 
    # resources, but on the other it avoids assigning blocks of slow tests to
3400
 
    # just one partition.  So the slowest partition shouldn't be much slower
3401
 
    # than the fastest.
3402
 
    partitions = [list() for i in range(count)]
3403
 
    tests = iter_suite_tests(suite)
3404
 
    for partition, test in itertools.izip(itertools.cycle(partitions), tests):
3405
 
        partition.append(test)
3406
 
    return partitions
3407
 
 
3408
 
 
3409
 
def workaround_zealous_crypto_random():
3410
 
    """Crypto.Random want to help us being secure, but we don't care here.
3411
 
 
3412
 
    This workaround some test failure related to the sftp server. Once paramiko
3413
 
    stop using the controversial API in Crypto.Random, we may get rid of it.
3414
 
    """
3415
 
    try:
3416
 
        from Crypto.Random import atfork
3417
 
        atfork()
3418
 
    except ImportError:
3419
 
        pass
 
3156
    result = []
 
3157
    tests = list(iter_suite_tests(suite))
 
3158
    tests_per_process = int(math.ceil(float(len(tests)) / count))
 
3159
    for block in range(count):
 
3160
        low_test = block * tests_per_process
 
3161
        high_test = low_test + tests_per_process
 
3162
        process_tests = tests[low_test:high_test]
 
3163
        result.append(process_tests)
 
3164
    return result
3420
3165
 
3421
3166
 
3422
3167
def fork_for_tests(suite):
3439
3184
            try:
3440
3185
                ProtocolTestCase.run(self, result)
3441
3186
            finally:
3442
 
                os.waitpid(self.pid, 0)
 
3187
                os.waitpid(self.pid, os.WNOHANG)
3443
3188
 
3444
3189
    test_blocks = partition_tests(suite, concurrency)
3445
3190
    for process_tests in test_blocks:
3446
 
        process_suite = TestUtil.TestSuite()
 
3191
        process_suite = TestSuite()
3447
3192
        process_suite.addTests(process_tests)
3448
3193
        c2pread, c2pwrite = os.pipe()
3449
3194
        pid = os.fork()
3450
3195
        if pid == 0:
3451
 
            workaround_zealous_crypto_random()
3452
3196
            try:
3453
3197
                os.close(c2pread)
3454
3198
                # Leave stderr and stdout open so we can see test noise
3515
3259
                '--subunit']
3516
3260
            if '--no-plugins' in sys.argv:
3517
3261
                argv.append('--no-plugins')
3518
 
            # stderr=subprocess.STDOUT would be ideal, but until we prevent
3519
 
            # noise on stderr it can interrupt the subunit protocol.
3520
 
            process = subprocess.Popen(argv, stdin=subprocess.PIPE,
3521
 
                                      stdout=subprocess.PIPE,
3522
 
                                      stderr=subprocess.PIPE,
3523
 
                                      bufsize=1)
 
3262
            # stderr=STDOUT would be ideal, but until we prevent noise on
 
3263
            # stderr it can interrupt the subunit protocol.
 
3264
            process = Popen(argv, stdin=PIPE, stdout=PIPE, stderr=PIPE,
 
3265
                bufsize=1)
3524
3266
            test = TestInSubprocess(process, test_list_file_name)
3525
3267
            result.append(test)
3526
3268
        except:
3529
3271
    return result
3530
3272
 
3531
3273
 
3532
 
class ProfileResult(testtools.ExtendedToOriginalDecorator):
 
3274
class ForwardingResult(unittest.TestResult):
 
3275
 
 
3276
    def __init__(self, target):
 
3277
        unittest.TestResult.__init__(self)
 
3278
        self.result = target
 
3279
 
 
3280
    def startTest(self, test):
 
3281
        self.result.startTest(test)
 
3282
 
 
3283
    def stopTest(self, test):
 
3284
        self.result.stopTest(test)
 
3285
 
 
3286
    def startTestRun(self):
 
3287
        self.result.startTestRun()
 
3288
 
 
3289
    def stopTestRun(self):
 
3290
        self.result.stopTestRun()
 
3291
 
 
3292
    def addSkip(self, test, reason):
 
3293
        self.result.addSkip(test, reason)
 
3294
 
 
3295
    def addSuccess(self, test):
 
3296
        self.result.addSuccess(test)
 
3297
 
 
3298
    def addError(self, test, err):
 
3299
        self.result.addError(test, err)
 
3300
 
 
3301
    def addFailure(self, test, err):
 
3302
        self.result.addFailure(test, err)
 
3303
ForwardingResult = testtools.ExtendedToOriginalDecorator
 
3304
 
 
3305
 
 
3306
class ProfileResult(ForwardingResult):
3533
3307
    """Generate profiling data for all activity between start and success.
3534
3308
    
3535
3309
    The profile data is appended to the test's _benchcalls attribute and can
3543
3317
 
3544
3318
    def startTest(self, test):
3545
3319
        self.profiler = bzrlib.lsprof.BzrProfiler()
3546
 
        # Prevent deadlocks in tests that use lsprof: those tests will
3547
 
        # unavoidably fail.
3548
 
        bzrlib.lsprof.BzrProfiler.profiler_block = 0
3549
3320
        self.profiler.start()
3550
 
        testtools.ExtendedToOriginalDecorator.startTest(self, test)
 
3321
        ForwardingResult.startTest(self, test)
3551
3322
 
3552
3323
    def addSuccess(self, test):
3553
3324
        stats = self.profiler.stop()
3557
3328
            test._benchcalls = []
3558
3329
            calls = test._benchcalls
3559
3330
        calls.append(((test.id(), "", ""), stats))
3560
 
        testtools.ExtendedToOriginalDecorator.addSuccess(self, test)
 
3331
        ForwardingResult.addSuccess(self, test)
3561
3332
 
3562
3333
    def stopTest(self, test):
3563
 
        testtools.ExtendedToOriginalDecorator.stopTest(self, test)
 
3334
        ForwardingResult.stopTest(self, test)
3564
3335
        self.profiler = None
3565
3336
 
3566
3337
 
3572
3343
#                           rather than failing tests. And no longer raise
3573
3344
#                           LockContention when fctnl locks are not being used
3574
3345
#                           with proper exclusion rules.
3575
 
#   -Ethreads               Will display thread ident at creation/join time to
3576
 
#                           help track thread leaks
3577
 
 
3578
 
#   -Econfig_stats          Will collect statistics using addDetail
3579
3346
selftest_debug_flags = set()
3580
3347
 
3581
3348
 
3775
3542
                key, obj, help=help, info=info, override_existing=False)
3776
3543
        except KeyError:
3777
3544
            actual = self.get(key)
3778
 
            trace.note(
3779
 
                'Test prefix alias %s is already used for %s, ignoring %s'
3780
 
                % (key, actual, obj))
 
3545
            note('Test prefix alias %s is already used for %s, ignoring %s'
 
3546
                 % (key, actual, obj))
3781
3547
 
3782
3548
    def resolve_alias(self, id_start):
3783
3549
        """Replace the alias by the prefix in the given string.
3815
3581
        'bzrlib.doc',
3816
3582
        'bzrlib.tests.blackbox',
3817
3583
        'bzrlib.tests.commands',
3818
 
        'bzrlib.tests.doc_generate',
3819
3584
        'bzrlib.tests.per_branch',
3820
3585
        'bzrlib.tests.per_bzrdir',
3821
 
        'bzrlib.tests.per_controldir',
3822
 
        'bzrlib.tests.per_controldir_colo',
3823
3586
        'bzrlib.tests.per_foreign_vcs',
3824
3587
        'bzrlib.tests.per_interrepository',
3825
3588
        'bzrlib.tests.per_intertree',
3833
3596
        'bzrlib.tests.per_repository',
3834
3597
        'bzrlib.tests.per_repository_chk',
3835
3598
        'bzrlib.tests.per_repository_reference',
3836
 
        'bzrlib.tests.per_repository_vf',
3837
3599
        'bzrlib.tests.per_uifactory',
3838
3600
        'bzrlib.tests.per_versionedfile',
3839
3601
        'bzrlib.tests.per_workingtree',
3840
3602
        'bzrlib.tests.test__annotator',
3841
3603
        'bzrlib.tests.test__bencode',
3842
 
        'bzrlib.tests.test__btree_serializer',
3843
3604
        'bzrlib.tests.test__chk_map',
3844
3605
        'bzrlib.tests.test__dirstate_helpers',
3845
3606
        'bzrlib.tests.test__groupcompress',
3867
3628
        'bzrlib.tests.test_chunk_writer',
3868
3629
        'bzrlib.tests.test_clean_tree',
3869
3630
        'bzrlib.tests.test_cleanup',
3870
 
        'bzrlib.tests.test_cmdline',
3871
3631
        'bzrlib.tests.test_commands',
3872
3632
        'bzrlib.tests.test_commit',
3873
3633
        'bzrlib.tests.test_commit_merge',
3874
3634
        'bzrlib.tests.test_config',
3875
3635
        'bzrlib.tests.test_conflicts',
3876
 
        'bzrlib.tests.test_controldir',
3877
3636
        'bzrlib.tests.test_counted_lock',
3878
3637
        'bzrlib.tests.test_crash',
3879
3638
        'bzrlib.tests.test_decorators',
3880
3639
        'bzrlib.tests.test_delta',
3881
3640
        'bzrlib.tests.test_debug',
 
3641
        'bzrlib.tests.test_deprecated_graph',
3882
3642
        'bzrlib.tests.test_diff',
3883
3643
        'bzrlib.tests.test_directory_service',
3884
3644
        'bzrlib.tests.test_dirstate',
3886
3646
        'bzrlib.tests.test_eol_filters',
3887
3647
        'bzrlib.tests.test_errors',
3888
3648
        'bzrlib.tests.test_export',
3889
 
        'bzrlib.tests.test_export_pot',
3890
3649
        'bzrlib.tests.test_extract',
3891
3650
        'bzrlib.tests.test_fetch',
3892
 
        'bzrlib.tests.test_fixtures',
3893
3651
        'bzrlib.tests.test_fifo_cache',
3894
3652
        'bzrlib.tests.test_filters',
3895
3653
        'bzrlib.tests.test_ftp_transport',
3906
3664
        'bzrlib.tests.test_http',
3907
3665
        'bzrlib.tests.test_http_response',
3908
3666
        'bzrlib.tests.test_https_ca_bundle',
3909
 
        'bzrlib.tests.test_i18n',
3910
3667
        'bzrlib.tests.test_identitymap',
3911
3668
        'bzrlib.tests.test_ignores',
3912
3669
        'bzrlib.tests.test_index',
3917
3674
        'bzrlib.tests.test_knit',
3918
3675
        'bzrlib.tests.test_lazy_import',
3919
3676
        'bzrlib.tests.test_lazy_regex',
3920
 
        'bzrlib.tests.test_library_state',
3921
3677
        'bzrlib.tests.test_lock',
3922
3678
        'bzrlib.tests.test_lockable_files',
3923
3679
        'bzrlib.tests.test_lockdir',
3925
3681
        'bzrlib.tests.test_lru_cache',
3926
3682
        'bzrlib.tests.test_lsprof',
3927
3683
        'bzrlib.tests.test_mail_client',
3928
 
        'bzrlib.tests.test_matchers',
3929
3684
        'bzrlib.tests.test_memorytree',
3930
3685
        'bzrlib.tests.test_merge',
3931
3686
        'bzrlib.tests.test_merge3',
3932
3687
        'bzrlib.tests.test_merge_core',
3933
3688
        'bzrlib.tests.test_merge_directive',
3934
 
        'bzrlib.tests.test_mergetools',
3935
3689
        'bzrlib.tests.test_missing',
3936
3690
        'bzrlib.tests.test_msgeditor',
3937
3691
        'bzrlib.tests.test_multiparent',
3946
3700
        'bzrlib.tests.test_permissions',
3947
3701
        'bzrlib.tests.test_plugins',
3948
3702
        'bzrlib.tests.test_progress',
3949
 
        'bzrlib.tests.test_pyutils',
3950
3703
        'bzrlib.tests.test_read_bundle',
3951
3704
        'bzrlib.tests.test_reconcile',
3952
3705
        'bzrlib.tests.test_reconfigure',
3961
3714
        'bzrlib.tests.test_rio',
3962
3715
        'bzrlib.tests.test_rules',
3963
3716
        'bzrlib.tests.test_sampler',
3964
 
        'bzrlib.tests.test_scenarios',
3965
3717
        'bzrlib.tests.test_script',
3966
3718
        'bzrlib.tests.test_selftest',
3967
3719
        'bzrlib.tests.test_serializer',
3983
3735
        'bzrlib.tests.test_switch',
3984
3736
        'bzrlib.tests.test_symbol_versioning',
3985
3737
        'bzrlib.tests.test_tag',
3986
 
        'bzrlib.tests.test_test_server',
3987
3738
        'bzrlib.tests.test_testament',
3988
3739
        'bzrlib.tests.test_textfile',
3989
3740
        'bzrlib.tests.test_textmerge',
3990
 
        'bzrlib.tests.test_cethread',
3991
3741
        'bzrlib.tests.test_timestamp',
3992
3742
        'bzrlib.tests.test_trace',
3993
3743
        'bzrlib.tests.test_transactions',
3996
3746
        'bzrlib.tests.test_transport_log',
3997
3747
        'bzrlib.tests.test_tree',
3998
3748
        'bzrlib.tests.test_treebuilder',
3999
 
        'bzrlib.tests.test_treeshape',
4000
3749
        'bzrlib.tests.test_tsort',
4001
3750
        'bzrlib.tests.test_tuned_gzip',
4002
3751
        'bzrlib.tests.test_ui',
4004
3753
        'bzrlib.tests.test_upgrade',
4005
3754
        'bzrlib.tests.test_upgrade_stacked',
4006
3755
        'bzrlib.tests.test_urlutils',
4007
 
        'bzrlib.tests.test_utextwrap',
4008
3756
        'bzrlib.tests.test_version',
4009
3757
        'bzrlib.tests.test_version_info',
4010
 
        'bzrlib.tests.test_versionedfile',
4011
3758
        'bzrlib.tests.test_weave',
4012
3759
        'bzrlib.tests.test_whitebox',
4013
3760
        'bzrlib.tests.test_win32utils',
4019
3766
 
4020
3767
 
4021
3768
def _test_suite_modules_to_doctest():
4022
 
    """Return the list of modules to doctest."""
4023
 
    if __doc__ is None:
4024
 
        # GZ 2009-03-31: No docstrings with -OO so there's nothing to doctest
4025
 
        return []
 
3769
    """Return the list of modules to doctest."""   
4026
3770
    return [
4027
3771
        'bzrlib',
4028
3772
        'bzrlib.branchbuilder',
4029
3773
        'bzrlib.decorators',
 
3774
        'bzrlib.export',
4030
3775
        'bzrlib.inventory',
4031
3776
        'bzrlib.iterablefile',
4032
3777
        'bzrlib.lockdir',
4033
3778
        'bzrlib.merge3',
4034
3779
        'bzrlib.option',
4035
 
        'bzrlib.pyutils',
4036
3780
        'bzrlib.symbol_versioning',
4037
3781
        'bzrlib.tests',
4038
 
        'bzrlib.tests.fixtures',
4039
3782
        'bzrlib.timestamp',
4040
 
        'bzrlib.transport.http',
4041
3783
        'bzrlib.version_info_formats.format_custom',
4042
3784
        ]
4043
3785
 
4096
3838
        try:
4097
3839
            # note that this really does mean "report only" -- doctest
4098
3840
            # still runs the rest of the examples
4099
 
            doc_suite = IsolatedDocTestSuite(
4100
 
                mod, optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
 
3841
            doc_suite = doctest.DocTestSuite(mod,
 
3842
                optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
4101
3843
        except ValueError, e:
4102
3844
            print '**failed to get doctest for: %s\n%s' % (mod, e)
4103
3845
            raise
4106
3848
        suite.addTest(doc_suite)
4107
3849
 
4108
3850
    default_encoding = sys.getdefaultencoding()
4109
 
    for name, plugin in _mod_plugin.plugins().items():
 
3851
    for name, plugin in bzrlib.plugin.plugins().items():
4110
3852
        if not interesting_module(plugin.module.__name__):
4111
3853
            continue
4112
3854
        plugin_suite = plugin.test_suite()
4118
3860
        if plugin_suite is not None:
4119
3861
            suite.addTest(plugin_suite)
4120
3862
        if default_encoding != sys.getdefaultencoding():
4121
 
            trace.warning(
 
3863
            bzrlib.trace.warning(
4122
3864
                'Plugin "%s" tried to reset default encoding to: %s', name,
4123
3865
                sys.getdefaultencoding())
4124
3866
            reload(sys)
4139
3881
            # Some tests mentioned in the list are not in the test suite. The
4140
3882
            # list may be out of date, report to the tester.
4141
3883
            for id in not_found:
4142
 
                trace.warning('"%s" not found in the test suite', id)
 
3884
                bzrlib.trace.warning('"%s" not found in the test suite', id)
4143
3885
        for id in duplicates:
4144
 
            trace.warning('"%s" is used as an id by several tests', id)
 
3886
            bzrlib.trace.warning('"%s" is used as an id by several tests', id)
4145
3887
 
4146
3888
    return suite
4147
3889
 
4148
3890
 
4149
 
def multiply_scenarios(*scenarios):
4150
 
    """Multiply two or more iterables of scenarios.
4151
 
 
4152
 
    It is safe to pass scenario generators or iterators.
4153
 
 
4154
 
    :returns: A list of compound scenarios: the cross-product of all 
4155
 
        scenarios, with the names concatenated and the parameters
4156
 
        merged together.
4157
 
    """
4158
 
    return reduce(_multiply_two_scenarios, map(list, scenarios))
4159
 
 
4160
 
 
4161
 
def _multiply_two_scenarios(scenarios_left, scenarios_right):
 
3891
def multiply_scenarios(scenarios_left, scenarios_right):
4162
3892
    """Multiply two sets of scenarios.
4163
3893
 
4164
3894
    :returns: the cartesian product of the two sets of scenarios, that is
4195
3925
    ...     bzrlib.tests.test_sampler.DemoTest('test_nothing'),
4196
3926
    ...     [('one', dict(param=1)),
4197
3927
    ...      ('two', dict(param=2))],
4198
 
    ...     TestUtil.TestSuite())
 
3928
    ...     TestSuite())
4199
3929
    >>> tests = list(iter_suite_tests(r))
4200
3930
    >>> len(tests)
4201
3931
    2
4248
3978
    :param new_id: The id to assign to it.
4249
3979
    :return: The new test.
4250
3980
    """
4251
 
    new_test = copy.copy(test)
 
3981
    new_test = copy(test)
4252
3982
    new_test.id = lambda: new_id
4253
 
    # XXX: Workaround <https://bugs.launchpad.net/testtools/+bug/637725>, which
4254
 
    # causes cloned tests to share the 'details' dict.  This makes it hard to
4255
 
    # read the test output for parameterized tests, because tracebacks will be
4256
 
    # associated with irrelevant tests.
4257
 
    try:
4258
 
        details = new_test._TestCase__details
4259
 
    except AttributeError:
4260
 
        # must be a different version of testtools than expected.  Do nothing.
4261
 
        pass
4262
 
    else:
4263
 
        # Reset the '__details' dict.
4264
 
        new_test._TestCase__details = {}
4265
3983
    return new_test
4266
3984
 
4267
3985
 
4288
4006
        the module is available.
4289
4007
    """
4290
4008
 
4291
 
    py_module = pyutils.get_named_object(py_module_name)
 
4009
    py_module = __import__(py_module_name, {}, {}, ['NO_SUCH_ATTRIB'])
4292
4010
    scenarios = [
4293
4011
        ('python', {'module': py_module}),
4294
4012
    ]
4327
4045
        if test_id != None:
4328
4046
            ui.ui_factory.clear_term()
4329
4047
            sys.stderr.write('\nWhile running: %s\n' % (test_id,))
4330
 
        # Ugly, but the last thing we want here is fail, so bear with it.
4331
 
        printable_e = str(e).decode(osutils.get_user_encoding(), 'replace'
4332
 
                                    ).encode('ascii', 'replace')
4333
4048
        sys.stderr.write('Unable to remove testing dir %s\n%s'
4334
 
                         % (os.path.basename(dirname), printable_e))
 
4049
                         % (os.path.basename(dirname), e))
4335
4050
 
4336
4051
 
4337
4052
class Feature(object):
4447
4162
            symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning)
4448
4163
            # Import the new feature and use it as a replacement for the
4449
4164
            # deprecated one.
4450
 
            self._feature = pyutils.get_named_object(
4451
 
                self._replacement_module, self._replacement_name)
 
4165
            mod = __import__(self._replacement_module, {}, {},
 
4166
                             [self._replacement_name])
 
4167
            self._feature = getattr(mod, self._replacement_name)
4452
4168
 
4453
4169
    def _probe(self):
4454
4170
        self._ensure()
4485
4201
        return self.module_name
4486
4202
 
4487
4203
 
 
4204
# This is kept here for compatibility, it is recommended to use
 
4205
# 'bzrlib.tests.feature.paramiko' instead
 
4206
ParamikoFeature = _CompatabilityThunkFeature(
 
4207
    deprecated_in((2,1,0)),
 
4208
    'bzrlib.tests.features', 'ParamikoFeature', 'paramiko')
 
4209
 
 
4210
 
4488
4211
def probe_unicode_in_user_encoding():
4489
4212
    """Try to encode several unicode strings to use in unicode-aware tests.
4490
4213
    Return first successfull match.
4559
4282
UnicodeFilename = _UnicodeFilename()
4560
4283
 
4561
4284
 
4562
 
class _ByteStringNamedFilesystem(Feature):
4563
 
    """Is the filesystem based on bytes?"""
4564
 
 
4565
 
    def _probe(self):
4566
 
        if os.name == "posix":
4567
 
            return True
4568
 
        return False
4569
 
 
4570
 
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
4571
 
 
4572
 
 
4573
4285
class _UTF8Filesystem(Feature):
4574
4286
    """Is the filesystem UTF-8?"""
4575
4287
 
4677
4389
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
4678
4390
 
4679
4391
 
 
4392
# Kept for compatibility, use bzrlib.tests.features.subunit instead
 
4393
SubUnitFeature = _CompatabilityThunkFeature(
 
4394
    deprecated_in((2,1,0)),
 
4395
    'bzrlib.tests.features', 'SubUnitFeature', 'subunit')
4680
4396
# Only define SubUnitBzrRunner if subunit is available.
4681
4397
try:
4682
4398
    from subunit import TestProtocolClient
4683
4399
    from subunit.test_results import AutoTimingTestResultDecorator
4684
 
    class SubUnitBzrProtocolClient(TestProtocolClient):
4685
 
 
4686
 
        def addSuccess(self, test, details=None):
4687
 
            # The subunit client always includes the details in the subunit
4688
 
            # stream, but we don't want to include it in ours.
4689
 
            if details is not None and 'log' in details:
4690
 
                del details['log']
4691
 
            return super(SubUnitBzrProtocolClient, self).addSuccess(
4692
 
                test, details)
4693
 
 
4694
4400
    class SubUnitBzrRunner(TextTestRunner):
4695
4401
        def run(self, test):
4696
4402
            result = AutoTimingTestResultDecorator(
4697
 
                SubUnitBzrProtocolClient(self.stream))
 
4403
                TestProtocolClient(self.stream))
4698
4404
            test.run(result)
4699
4405
            return result
4700
4406
except ImportError:
4701
4407
    pass
4702
 
 
4703
 
class _PosixPermissionsFeature(Feature):
4704
 
 
4705
 
    def _probe(self):
4706
 
        def has_perms():
4707
 
            # create temporary file and check if specified perms are maintained.
4708
 
            import tempfile
4709
 
 
4710
 
            write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
4711
 
            f = tempfile.mkstemp(prefix='bzr_perms_chk_')
4712
 
            fd, name = f
4713
 
            os.close(fd)
4714
 
            os.chmod(name, write_perms)
4715
 
 
4716
 
            read_perms = os.stat(name).st_mode & 0777
4717
 
            os.unlink(name)
4718
 
            return (write_perms == read_perms)
4719
 
 
4720
 
        return (os.name == 'posix') and has_perms()
4721
 
 
4722
 
    def feature_name(self):
4723
 
        return 'POSIX permissions support'
4724
 
 
4725
 
posix_permissions_feature = _PosixPermissionsFeature()