~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/__init__.py

  • Committer: Martin
  • Date: 2010-05-03 20:57:39 UTC
  • mto: This revision was merged to the branch mainline in revision 5204.
  • Revision ID: gzlist@googlemail.com-20100503205739-n326zdvevv0rmruh
Retain original stack and error message when translating to ValueError in bencode

Show diffs side-by-side

added added

removed removed

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