32
29
from cStringIO import StringIO
39
from pprint import pformat
44
from subprocess import Popen, PIPE, STDOUT
53
# nb: check this before importing anything else from within it
54
_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
% (testtools.__file__, _testtools_version))
58
from testtools import content
53
61
from bzrlib import (
65
commands as _mod_commands,
75
plugin as _mod_plugin,
82
transport as _mod_transport,
71
import bzrlib.commands
72
import bzrlib.timestamp
74
import bzrlib.inventory
75
import bzrlib.iterablefile
78
86
import bzrlib.lsprof
79
87
except ImportError:
80
88
# lsprof not available
82
from bzrlib.merge import merge_inner
85
from bzrlib.smart import client, request, server
87
from bzrlib import symbol_versioning
90
from bzrlib.smart import client, request
91
from bzrlib.transport import (
88
95
from bzrlib.symbol_versioning import (
90
96
deprecated_function,
95
from bzrlib.transport import get_transport, pathfilter
96
import bzrlib.transport
97
from bzrlib.transport.local import LocalURLServer
98
from bzrlib.transport.memory import MemoryServer
99
from bzrlib.transport.readonly import ReadonlyServer
100
from bzrlib.trace import mutter, note
101
from bzrlib.tests import TestUtil
102
from bzrlib.tests.http_server import HttpServer
103
from bzrlib.tests.TestUtil import (
107
from bzrlib.tests.treeshape import build_tree_contents
99
from bzrlib.tests import (
108
105
from bzrlib.ui import NullProgressView
109
106
from bzrlib.ui.text import TextUIFactory
110
import bzrlib.version_info_formats.format_custom
111
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
107
from bzrlib.tests.features import _CompatabilityThunkFeature
113
109
# Mark this python module as being part of the implementation
114
110
# of unittest: this gives us better tracebacks where the last
115
111
# shown frame is the test code, not our assertXYZ.
118
default_transport = LocalURLServer
114
default_transport = test_server.LocalURLServer
117
_unitialized_attr = object()
118
"""A sentinel needed to act as a default value in a method signature."""
120
121
# Subunit result codes, defined here to prevent a hard dependency on subunit.
121
122
SUBUNIT_SEEK_SET = 0
122
123
SUBUNIT_SEEK_CUR = 1
125
class ExtendedTestResult(unittest._TextTestResult):
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
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.
140
# bzr now uses the Win32 API and doesn't rely on APPDATA, but the
141
# tests do check our impls match APPDATA
142
'BZR_EDITOR': None, # test_msgeditor manipulates this variable
146
'BZREMAIL': None, # may still be present in the environment
147
'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
148
'BZR_PROGRESS_BAR': None,
149
# This should trap leaks to ~/.bzr.log. This occurs when tests use TestCase
150
# as a base class instead of TestCaseInTempDir. Tests inheriting from
151
# TestCase should not use disk resources, BZR_LOG is one.
152
'BZR_LOG': '/you-should-use-TestCaseInTempDir-if-you-need-a-log-file',
153
'BZR_PLUGIN_PATH': None,
154
'BZR_DISABLE_PLUGINS': None,
155
'BZR_PLUGINS_AT': None,
156
'BZR_CONCURRENCY': None,
157
# Make sure that any text ui tests are consistent regardless of
158
# the environment the test case is run in; you may want tests that
159
# test other combinations. 'dumb' is a reasonable guess for tests
160
# going to a pipe or a StringIO.
166
'SSH_AUTH_SOCK': None,
176
# Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
177
# least. If you do (care), please update this comment
181
'BZR_REMOTE_PATH': None,
182
# Generally speaking, we don't want apport reporting on crashes in
183
# the test envirnoment unless we're specifically testing apport,
184
# so that it doesn't leak into the real system environment. We
185
# use an env var so it propagates to subprocesses.
186
'APPORT_DISABLE': '1',
190
def override_os_environ(test, env=None):
191
"""Modify os.environ keeping a copy.
193
:param test: A test instance
195
:param env: A dict containing variable definitions to be installed
198
env = isolated_environ
199
test._original_os_environ = dict([(var, value)
200
for var, value in os.environ.iteritems()])
201
for var, value in env.iteritems():
202
osutils.set_or_unset_env(var, value)
203
if var not in test._original_os_environ:
204
# The var is new, add it with a value of None, so
205
# restore_os_environ will delete it
206
test._original_os_environ[var] = None
209
def restore_os_environ(test):
210
"""Restore os.environ to its original state.
212
:param test: A test instance previously passed to override_os_environ.
214
for var, value in test._original_os_environ.iteritems():
215
# Restore the original value (or delete it if the value has been set to
216
# None in override_os_environ).
217
osutils.set_or_unset_env(var, value)
220
def _clear__type_equality_funcs(test):
221
"""Cleanup bound methods stored on TestCase instances
223
Clear the dict breaking a few (mostly) harmless cycles in the affected
224
unittests released with Python 2.6 and initial Python 2.7 versions.
226
For a few revisions between Python 2.7.1 and Python 2.7.2 that annoyingly
227
shipped in Oneiric, an object with no clear method was used, hence the
228
extra complications, see bug 809048 for details.
230
type_equality_funcs = getattr(test, "_type_equality_funcs", None)
231
if type_equality_funcs is not None:
232
tef_clear = getattr(type_equality_funcs, "clear", None)
233
if tef_clear is None:
234
tef_instance_dict = getattr(type_equality_funcs, "__dict__", None)
235
if tef_instance_dict is not None:
236
tef_clear = tef_instance_dict.clear
237
if tef_clear is not None:
241
class ExtendedTestResult(testtools.TextTestResult):
126
242
"""Accepts, reports and accumulates the results of running tests.
128
244
Compared to the unittest version this class adds support for
199
320
if failed or errored: self.stream.write(", ")
200
321
self.stream.write("known_failure_count=%d" %
201
322
self.known_failure_count)
202
self.stream.writeln(")")
323
self.stream.write(")\n")
204
325
if self.known_failure_count:
205
self.stream.writeln("OK (known_failures=%d)" %
326
self.stream.write("OK (known_failures=%d)\n" %
206
327
self.known_failure_count)
208
self.stream.writeln("OK")
329
self.stream.write("OK\n")
209
330
if self.skip_count > 0:
210
331
skipped = self.skip_count
211
self.stream.writeln('%d test%s skipped' %
332
self.stream.write('%d test%s skipped\n' %
212
333
(skipped, skipped != 1 and "s" or ""))
213
334
if self.unsupported:
214
335
for feature, count in sorted(self.unsupported.items()):
215
self.stream.writeln("Missing feature '%s' skipped %d tests." %
336
self.stream.write("Missing feature '%s' skipped %d tests.\n" %
216
337
(feature, count))
218
339
ok = self.wasStrictlySuccessful()
220
341
ok = self.wasSuccessful()
221
if TestCase._first_thread_leaker_id:
342
if self._first_thread_leaker_id:
222
343
self.stream.write(
223
344
'%s is leaking threads among %d leaking tests.\n' % (
224
TestCase._first_thread_leaker_id,
225
TestCase._leaking_threads_tests))
345
self._first_thread_leaker_id,
346
self._tests_leaking_threads_count))
226
347
# We don't report the main thread as an active one.
227
348
self.stream.write(
228
349
'%d non-main threads were left active in the end.\n'
229
% (TestCase._active_threads - 1))
231
def _extractBenchmarkTime(self, testCase):
350
% (len(self._active_threads) - 1))
352
def getDescription(self, test):
355
def _extractBenchmarkTime(self, testCase, details=None):
232
356
"""Add a benchmark time for the current test case."""
357
if details and 'benchtime' in details:
358
return float(''.join(details['benchtime'].iter_bytes()))
233
359
return getattr(testCase, "_benchtime", None)
235
361
def _elapsedTestTimeString(self):
236
362
"""Return a time string for the overall time the current test has taken."""
237
return self._formatTime(time.time() - self._start_time)
363
return self._formatTime(self._delta_to_float(
364
self._now() - self._start_datetime))
239
366
def _testTimeString(self, testCase):
240
367
benchmark_time = self._extractBenchmarkTime(testCase)
252
379
def _shortened_test_description(self, test):
254
what = re.sub(r'^bzrlib\.(tests|benchmarks)\.', '', what)
381
what = re.sub(r'^bzrlib\.tests\.', '', what)
384
# GZ 2010-10-04: Cloned tests may end up harmlessly calling this method
385
# multiple times in a row, because the handler is added for
386
# each test but the container list is shared between cases.
387
# See lp:498869 lp:625574 and lp:637725 for background.
388
def _record_traceback_from_test(self, exc_info):
389
"""Store the traceback from passed exc_info tuple till"""
390
self._traceback_from_test = exc_info[2]
257
392
def startTest(self, test):
258
unittest.TestResult.startTest(self, test)
393
super(ExtendedTestResult, self).startTest(test)
259
394
if self.count == 0:
260
395
self.startTests()
261
397
self.report_test_start(test)
262
398
test.number = self.count
263
399
self._recordTestStartTime()
400
# Make testtools cases give us the real traceback on failure
401
addOnException = getattr(test, "addOnException", None)
402
if addOnException is not None:
403
addOnException(self._record_traceback_from_test)
404
# Only check for thread leaks on bzrlib derived test cases
405
if isinstance(test, TestCase):
406
test.addCleanup(self._check_leaked_threads, test)
408
def stopTest(self, test):
409
super(ExtendedTestResult, self).stopTest(test)
410
# Manually break cycles, means touching various private things but hey
411
getDetails = getattr(test, "getDetails", None)
412
if getDetails is not None:
414
_clear__type_equality_funcs(test)
415
self._traceback_from_test = None
265
417
def startTests(self):
267
if getattr(sys, 'frozen', None) is None:
268
bzr_path = osutils.realpath(sys.argv[0])
270
bzr_path = sys.executable
272
'testing: %s\n' % (bzr_path,))
275
bzrlib.__path__[0],))
277
' bzr-%s python-%s %s\n' % (
278
bzrlib.version_string,
279
bzrlib._format_version_tuple(sys.version_info),
280
platform.platform(aliased=1),
282
self.stream.write('\n')
418
self.report_tests_starting()
419
self._active_threads = threading.enumerate()
421
def _check_leaked_threads(self, test):
422
"""See if any threads have leaked since last call
424
A sample of live threads is stored in the _active_threads attribute,
425
when this method runs it compares the current live threads and any not
426
in the previous sample are treated as having leaked.
428
now_active_threads = set(threading.enumerate())
429
threads_leaked = now_active_threads.difference(self._active_threads)
431
self._report_thread_leak(test, threads_leaked, now_active_threads)
432
self._tests_leaking_threads_count += 1
433
if self._first_thread_leaker_id is None:
434
self._first_thread_leaker_id = test.id()
435
self._active_threads = now_active_threads
284
437
def _recordTestStartTime(self):
285
438
"""Record that a test has started."""
286
self._start_time = time.time()
288
def _cleanupLogFile(self, test):
289
# We can only do this if we have one of our TestCases, not if
291
setKeepLogfile = getattr(test, 'setKeepLogfile', None)
292
if setKeepLogfile is not None:
439
self._start_datetime = self._now()
295
441
def addError(self, test, err):
296
442
"""Tell result that test finished with an error.
312
457
Called from the TestCase run() method when the test
313
458
fails because e.g. an assert() method failed.
316
unittest.TestResult.addFailure(self, test, err)
460
self._post_mortem(self._traceback_from_test)
461
super(ExtendedTestResult, self).addFailure(test, err)
317
462
self.failure_count += 1
318
463
self.report_failure(test, err)
319
464
if self.stop_early:
321
self._cleanupLogFile(test)
323
def addSuccess(self, test):
467
def addSuccess(self, test, details=None):
324
468
"""Tell result that test completed successfully.
326
470
Called from the TestCase run()
328
472
if self._bench_history is not None:
329
benchmark_time = self._extractBenchmarkTime(test)
473
benchmark_time = self._extractBenchmarkTime(test, details)
330
474
if benchmark_time is not None:
331
475
self._bench_history.write("%s %s\n" % (
332
476
self._formatTime(benchmark_time),
334
478
self.report_success(test)
335
self._cleanupLogFile(test)
336
unittest.TestResult.addSuccess(self, test)
479
super(ExtendedTestResult, self).addSuccess(test)
337
480
test._log_contents = ''
339
482
def addExpectedFailure(self, test, err):
340
483
self.known_failure_count += 1
341
484
self.report_known_failure(test, err)
486
def addUnexpectedSuccess(self, test, details=None):
487
"""Tell result the test unexpectedly passed, counting as a failure
489
When the minimum version of testtools required becomes 0.9.8 this
490
can be updated to use the new handling there.
492
super(ExtendedTestResult, self).addFailure(test, details=details)
493
self.failure_count += 1
494
self.report_unexpected_success(test,
495
"".join(details["reason"].iter_text()))
343
499
def addNotSupported(self, test, feature):
344
500
"""The test will not be run because of a missing feature.
362
518
self.not_applicable_count += 1
363
519
self.report_not_applicable(test, reason)
365
def printErrorList(self, flavour, errors):
366
for test, err in errors:
367
self.stream.writeln(self.separator1)
368
self.stream.write("%s: " % flavour)
369
self.stream.writeln(self.getDescription(test))
370
if getattr(test, '_get_log', None) is not None:
371
log_contents = test._get_log()
373
self.stream.write('\n')
375
('vvvv[log from %s]' % test.id()).ljust(78,'-'))
376
self.stream.write('\n')
377
self.stream.write(log_contents)
378
self.stream.write('\n')
380
('^^^^[log from %s]' % test.id()).ljust(78,'-'))
381
self.stream.write('\n')
382
self.stream.writeln(self.separator2)
383
self.stream.writeln("%s" % err)
521
def _count_stored_tests(self):
522
"""Count of tests instances kept alive due to not succeeding"""
523
return self.error_count + self.failure_count + self.known_failure_count
385
def _post_mortem(self):
525
def _post_mortem(self, tb=None):
386
526
"""Start a PDB post mortem session."""
387
527
if os.environ.get('BZR_TEST_PDB', None):
388
import pdb;pdb.post_mortem()
390
531
def progress(self, offset, whence):
391
532
"""The test is adjusting the count of tests to run."""
397
538
raise errors.BzrError("Unknown whence %r" % whence)
399
def report_cleaning_up(self):
540
def report_tests_starting(self):
541
"""Display information before the test run begins"""
542
if getattr(sys, 'frozen', None) is None:
543
bzr_path = osutils.realpath(sys.argv[0])
545
bzr_path = sys.executable
547
'bzr selftest: %s\n' % (bzr_path,))
550
bzrlib.__path__[0],))
552
' bzr-%s python-%s %s\n' % (
553
bzrlib.version_string,
554
bzrlib._format_version_tuple(sys.version_info),
555
platform.platform(aliased=1),
557
self.stream.write('\n')
559
def report_test_start(self, test):
560
"""Display information on the test just about to be run"""
562
def _report_thread_leak(self, test, leaked_threads, active_threads):
563
"""Display information on a test that leaked one or more threads"""
564
# GZ 2010-09-09: A leak summary reported separately from the general
565
# thread debugging would be nice. Tests under subunit
566
# need something not using stream, perhaps adding a
567
# testtools details object would be fitting.
568
if 'threads' in selftest_debug_flags:
569
self.stream.write('%s is leaking, active is now %d\n' %
570
(test.id(), len(active_threads)))
402
572
def startTestRun(self):
403
573
self.startTime = time.time()
526
692
result = a_string
527
693
return result.ljust(final_width)
529
def startTestRun(self):
530
super(VerboseTestResult, self).startTestRun()
695
def report_tests_starting(self):
531
696
self.stream.write('running %d tests...\n' % self.num_tests)
697
super(VerboseTestResult, self).report_tests_starting()
533
699
def report_test_start(self, test):
535
700
name = self._shortened_test_description(test)
536
# width needs space for 6 char status, plus 1 for slash, plus an
537
# 11-char time string, plus a trailing blank
538
# when NUMBERED_DIRS: plus 5 chars on test number, plus 1 char on space
539
self.stream.write(self._ellipsize_to_right(name,
540
osutils.terminal_width()-18))
701
width = osutils.terminal_width()
702
if width is not None:
703
# width needs space for 6 char status, plus 1 for slash, plus an
704
# 11-char time string, plus a trailing blank
705
# when NUMBERED_DIRS: plus 5 chars on test number, plus 1 char on
707
self.stream.write(self._ellipsize_to_right(name, width-18))
709
self.stream.write(name)
541
710
self.stream.flush()
543
712
def _error_summary(self, err):
545
714
return '%s%s' % (indent, err[1])
547
716
def report_error(self, test, err):
548
self.stream.writeln('ERROR %s\n%s'
717
self.stream.write('ERROR %s\n%s\n'
549
718
% (self._testTimeString(test),
550
719
self._error_summary(err)))
552
721
def report_failure(self, test, err):
553
self.stream.writeln(' FAIL %s\n%s'
722
self.stream.write(' FAIL %s\n%s\n'
554
723
% (self._testTimeString(test),
555
724
self._error_summary(err)))
557
726
def report_known_failure(self, test, err):
558
self.stream.writeln('XFAIL %s\n%s'
727
self.stream.write('XFAIL %s\n%s\n'
559
728
% (self._testTimeString(test),
560
729
self._error_summary(err)))
731
def report_unexpected_success(self, test, reason):
732
self.stream.write(' FAIL %s\n%s: %s\n'
733
% (self._testTimeString(test),
734
"Unexpected success. Should have failed",
562
737
def report_success(self, test):
563
self.stream.writeln(' OK %s' % self._testTimeString(test))
738
self.stream.write(' OK %s\n' % self._testTimeString(test))
564
739
for bench_called, stats in getattr(test, '_benchcalls', []):
565
self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
740
self.stream.write('LSProf output for %s(%s, %s)\n' % bench_called)
566
741
stats.pprint(file=self.stream)
567
742
# flush the stream so that we get smooth output. This verbose mode is
568
743
# used to show the output in PQM.
569
744
self.stream.flush()
571
746
def report_skip(self, test, reason):
572
self.stream.writeln(' SKIP %s\n%s'
747
self.stream.write(' SKIP %s\n%s\n'
573
748
% (self._testTimeString(test), reason))
575
750
def report_not_applicable(self, test, reason):
576
self.stream.writeln(' N/A %s\n %s'
751
self.stream.write(' N/A %s\n %s\n'
577
752
% (self._testTimeString(test), reason))
579
754
def report_unsupported(self, test, feature):
580
755
"""test cannot be run because feature is missing."""
581
self.stream.writeln("NODEP %s\n The feature '%s' is not available."
756
self.stream.write("NODEP %s\n The feature '%s' is not available.\n"
582
757
%(self._testTimeString(test), feature))
774
980
routine, and to build and check bzr trees.
776
982
In addition to the usual method of overriding tearDown(), this class also
777
allows subclasses to register functions into the _cleanups list, which is
983
allows subclasses to register cleanup functions via addCleanup, which are
778
984
run in order as the object is torn down. It's less likely this will be
779
985
accidentally overlooked.
782
_active_threads = None
783
_leaking_threads_tests = 0
784
_first_thread_leaker_id = None
785
_log_file_name = None
787
_keep_log_file = False
788
989
# record lsprof data when performing benchmark calls.
789
990
_gather_lsprof_in_benchmarks = False
790
attrs_to_keep = ('id', '_testMethodName', '_testMethodDoc',
791
'_log_contents', '_log_file_name', '_benchtime',
792
'_TestCase__testMethodName', '_TestCase__testMethodDoc',)
794
992
def __init__(self, methodName='testMethod'):
795
993
super(TestCase, self).__init__(methodName)
797
self._bzr_test_setUp_run = False
798
self._bzr_test_tearDown_run = False
799
994
self._directory_isolation = True
995
self.exception_handlers.insert(0,
996
(UnavailableFeature, self._do_unsupported_or_skip))
997
self.exception_handlers.insert(0,
998
(TestNotApplicable, self._do_not_applicable))
801
1000
def setUp(self):
802
unittest.TestCase.setUp(self)
803
self._bzr_test_setUp_run = True
1001
super(TestCase, self).setUp()
1003
timeout = config.GlobalStack().get('selftest.timeout')
1005
timeout_fixture = fixtures.TimeoutFixture(timeout)
1006
timeout_fixture.setUp()
1007
self.addCleanup(timeout_fixture.cleanUp)
1009
for feature in getattr(self, '_test_needs_features', []):
1010
self.requireFeature(feature)
804
1011
self._cleanEnvironment()
1013
if bzrlib.global_state is not None:
1014
self.overrideAttr(bzrlib.global_state, 'cmdline_overrides',
1015
config.CommandLineStore())
805
1017
self._silenceUI()
806
1018
self._startLogFile()
807
1019
self._benchcalls = []
810
1022
self._track_transports()
811
1023
self._track_locks()
812
1024
self._clear_debug_flags()
813
TestCase._active_threads = threading.activeCount()
814
self.addCleanup(self._check_leaked_threads)
1025
# Isolate global verbosity level, to make sure it's reproducible
1026
# between tests. We should get rid of this altogether: bug 656694. --
1028
self.overrideAttr(bzrlib.trace, '_verbosity_level', 0)
1029
# Isolate config option expansion until its default value for bzrlib is
1030
# settled on or a the FIXME associated with _get_expand_default_value
1031
# is addressed -- vila 20110219
1032
self.overrideAttr(config, '_expand_default_value', None)
1033
self._log_files = set()
1034
# Each key in the ``_counters`` dict holds a value for a different
1035
# counter. When the test ends, addDetail() should be used to output the
1036
# counter values. This happens in install_counter_hook().
1038
if 'config_stats' in selftest_debug_flags:
1039
self._install_config_stats_hooks()
1040
# Do not use i18n for tests (unless the test reverses this)
816
1043
def debug(self):
817
1044
# debug a frame up.
819
pdb.Pdb().set_trace(sys._getframe().f_back)
821
def _check_leaked_threads(self):
822
active = threading.activeCount()
823
leaked_threads = active - TestCase._active_threads
824
TestCase._active_threads = active
825
# If some tests make the number of threads *decrease*, we'll consider
826
# that they are just observing old threads dieing, not agressively kill
827
# random threads. So we don't report these tests as leaking. The risk
828
# is that we have false positives that way (the test see 2 threads
829
# going away but leak one) but it seems less likely than the actual
830
# false positives (the test see threads going away and does not leak).
831
if leaked_threads > 0:
832
TestCase._leaking_threads_tests += 1
833
if TestCase._first_thread_leaker_id is None:
834
TestCase._first_thread_leaker_id = self.id()
1046
# The sys preserved stdin/stdout should allow blackbox tests debugging
1047
pdb.Pdb(stdin=sys.__stdin__, stdout=sys.__stdout__
1048
).set_trace(sys._getframe().f_back)
1050
def discardDetail(self, name):
1051
"""Extend the addDetail, getDetails api so we can remove a detail.
1053
eg. bzr always adds the 'log' detail at startup, but we don't want to
1054
include it for skipped, xfail, etc tests.
1056
It is safe to call this for a detail that doesn't exist, in case this
1057
gets called multiple times.
1059
# We cheat. details is stored in __details which means we shouldn't
1060
# touch it. but getDetails() returns the dict directly, so we can
1062
details = self.getDetails()
1066
def install_counter_hook(self, hooks, name, counter_name=None):
1067
"""Install a counting hook.
1069
Any hook can be counted as long as it doesn't need to return a value.
1071
:param hooks: Where the hook should be installed.
1073
:param name: The hook name that will be counted.
1075
:param counter_name: The counter identifier in ``_counters``, defaults
1078
_counters = self._counters # Avoid closing over self
1079
if counter_name is None:
1081
if _counters.has_key(counter_name):
1082
raise AssertionError('%s is already used as a counter name'
1084
_counters[counter_name] = 0
1085
self.addDetail(counter_name, content.Content(content.UTF8_TEXT,
1086
lambda: ['%d' % (_counters[counter_name],)]))
1087
def increment_counter(*args, **kwargs):
1088
_counters[counter_name] += 1
1089
label = 'count %s calls' % (counter_name,)
1090
hooks.install_named_hook(name, increment_counter, label)
1091
self.addCleanup(hooks.uninstall_named_hook, name, label)
1093
def _install_config_stats_hooks(self):
1094
"""Install config hooks to count hook calls.
1097
for hook_name in ('get', 'set', 'remove', 'load', 'save'):
1098
self.install_counter_hook(config.ConfigHooks, hook_name,
1099
'config.%s' % (hook_name,))
1101
# The OldConfigHooks are private and need special handling to protect
1102
# against recursive tests (tests that run other tests), so we just do
1103
# manually what registering them into _builtin_known_hooks will provide
1105
self.overrideAttr(config, 'OldConfigHooks', config._OldConfigHooks())
1106
for hook_name in ('get', 'set', 'remove', 'load', 'save'):
1107
self.install_counter_hook(config.OldConfigHooks, hook_name,
1108
'old_config.%s' % (hook_name,))
836
1110
def _clear_debug_flags(self):
837
1111
"""Prevent externally set debug flags affecting tests.
839
1113
Tests that want to use debug flags can just set them in the
840
1114
debug_flags set during setup/teardown.
842
self._preserved_debug_flags = set(debug.debug_flags)
1116
# Start with a copy of the current debug flags we can safely modify.
1117
self.overrideAttr(debug, 'debug_flags', set(debug.debug_flags))
843
1118
if 'allow_debug' not in selftest_debug_flags:
844
1119
debug.debug_flags.clear()
845
1120
if 'disable_lock_checks' not in selftest_debug_flags:
846
1121
debug.debug_flags.add('strict_locks')
847
self.addCleanup(self._restore_debug_flags)
849
1123
def _clear_hooks(self):
850
1124
# prevent hooks affecting tests
1125
known_hooks = hooks.known_hooks
851
1126
self._preserved_hooks = {}
852
for key, factory in hooks.known_hooks.items():
853
parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
854
current_hooks = hooks.known_hooks_key_to_object(key)
1127
for key, (parent, name) in known_hooks.iter_parent_objects():
1128
current_hooks = getattr(parent, name)
855
1129
self._preserved_hooks[parent] = (name, current_hooks)
1130
self._preserved_lazy_hooks = hooks._lazy_hooks
1131
hooks._lazy_hooks = {}
856
1132
self.addCleanup(self._restoreHooks)
857
for key, factory in hooks.known_hooks.items():
858
parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
1133
for key, (parent, name) in known_hooks.iter_parent_objects():
1134
factory = known_hooks.get(key)
859
1135
setattr(parent, name, factory())
860
1136
# this hook should always be installed
861
1137
request._install_hook()
1285
1573
m += ": " + msg
1288
def expectFailure(self, reason, assertion, *args, **kwargs):
1289
"""Invoke a test, expecting it to fail for the given reason.
1291
This is for assertions that ought to succeed, but currently fail.
1292
(The failure is *expected* but not *wanted*.) Please be very precise
1293
about the failure you're expecting. If a new bug is introduced,
1294
AssertionError should be raised, not KnownFailure.
1296
Frequently, expectFailure should be followed by an opposite assertion.
1299
Intended to be used with a callable that raises AssertionError as the
1300
'assertion' parameter. args and kwargs are passed to the 'assertion'.
1302
Raises KnownFailure if the test fails. Raises AssertionError if the
1307
self.expectFailure('Math is broken', self.assertNotEqual, 54,
1309
self.assertEqual(42, dynamic_val)
1311
This means that a dynamic_val of 54 will cause the test to raise
1312
a KnownFailure. Once math is fixed and the expectFailure is removed,
1313
only a dynamic_val of 42 will allow the test to pass. Anything other
1314
than 54 or 42 will cause an AssertionError.
1317
assertion(*args, **kwargs)
1318
except AssertionError:
1319
raise KnownFailure(reason)
1321
self.fail('Unexpected success. Should have failed: %s' % reason)
1323
1576
def assertFileEqual(self, content, path):
1324
1577
"""Fail if path does not contain 'content'."""
1325
self.failUnlessExists(path)
1578
self.assertPathExists(path)
1326
1579
f = file(path, 'rb')
1331
1584
self.assertEqualDiff(content, s)
1586
def assertDocstring(self, expected_docstring, obj):
1587
"""Fail if obj does not have expected_docstring"""
1589
# With -OO the docstring should be None instead
1590
self.assertIs(obj.__doc__, None)
1592
self.assertEqual(expected_docstring, obj.__doc__)
1594
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1333
1595
def failUnlessExists(self, path):
1596
return self.assertPathExists(path)
1598
def assertPathExists(self, path):
1334
1599
"""Fail unless path or paths, which may be abs or relative, exist."""
1335
1600
if not isinstance(path, basestring):
1337
self.failUnlessExists(p)
1602
self.assertPathExists(p)
1339
self.failUnless(osutils.lexists(path),path+" does not exist")
1604
self.assertTrue(osutils.lexists(path),
1605
path + " does not exist")
1607
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1341
1608
def failIfExists(self, path):
1609
return self.assertPathDoesNotExist(path)
1611
def assertPathDoesNotExist(self, path):
1342
1612
"""Fail if path or paths, which may be abs or relative, exist."""
1343
1613
if not isinstance(path, basestring):
1345
self.failIfExists(p)
1615
self.assertPathDoesNotExist(p)
1347
self.failIf(osutils.lexists(path),path+" exists")
1617
self.assertFalse(osutils.lexists(path),
1349
1620
def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
1350
1621
"""A helper for callDeprecated and applyDeprecated.
1462
1734
def _startLogFile(self):
1463
"""Send bzr and test log messages to a temporary file.
1465
The file is removed as the test is torn down.
1467
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
1468
self._log_file = os.fdopen(fileno, 'w+')
1469
self._log_memento = bzrlib.trace.push_log_file(self._log_file)
1470
self._log_file_name = name
1735
"""Setup a in-memory target for bzr and testcase log messages"""
1736
pseudo_log_file = StringIO()
1737
def _get_log_contents_for_weird_testtools_api():
1738
return [pseudo_log_file.getvalue().decode(
1739
"utf-8", "replace").encode("utf-8")]
1740
self.addDetail("log", content.Content(content.ContentType("text",
1741
"plain", {"charset": "utf8"}),
1742
_get_log_contents_for_weird_testtools_api))
1743
self._log_file = pseudo_log_file
1744
self._log_memento = trace.push_log_file(self._log_file)
1471
1745
self.addCleanup(self._finishLogFile)
1473
1747
def _finishLogFile(self):
1474
"""Finished with the log file.
1476
Close the file and delete it, unless setKeepLogfile was called.
1478
if self._log_file is None:
1480
bzrlib.trace.pop_log_file(self._log_memento)
1481
self._log_file.close()
1482
self._log_file = None
1483
if not self._keep_log_file:
1484
os.remove(self._log_file_name)
1485
self._log_file_name = None
1487
def setKeepLogfile(self):
1488
"""Make the logfile not be deleted when _finishLogFile is called."""
1489
self._keep_log_file = True
1748
"""Flush and dereference the in-memory log for this testcase"""
1749
if trace._trace_file:
1750
# flush the log file, to get all content
1751
trace._trace_file.flush()
1752
trace.pop_log_file(self._log_memento)
1753
# The logging module now tracks references for cleanup so discard ours
1754
del self._log_memento
1491
1756
def thisFailsStrictLockCheck(self):
1492
1757
"""It is known that this test would fail with -Dstrict_locks.
1502
1767
debug.debug_flags.discard('strict_locks')
1504
def addCleanup(self, callable, *args, **kwargs):
1505
"""Arrange to run a callable when this case is torn down.
1507
Callables are run in the reverse of the order they are registered,
1508
ie last-in first-out.
1510
self._cleanups.append((callable, args, kwargs))
1769
def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1770
"""Overrides an object attribute restoring it after the test.
1772
:note: This should be used with discretion; you should think about
1773
whether it's better to make the code testable without monkey-patching.
1775
:param obj: The object that will be mutated.
1777
:param attr_name: The attribute name we want to preserve/override in
1780
:param new: The optional value we want to set the attribute to.
1782
:returns: The actual attr value.
1784
value = getattr(obj, attr_name)
1785
# The actual value is captured by the call below
1786
self.addCleanup(setattr, obj, attr_name, value)
1787
if new is not _unitialized_attr:
1788
setattr(obj, attr_name, new)
1791
def overrideEnv(self, name, new):
1792
"""Set an environment variable, and reset it after the test.
1794
:param name: The environment variable name.
1796
:param new: The value to set the variable to. If None, the
1797
variable is deleted from the environment.
1799
:returns: The actual variable value.
1801
value = osutils.set_or_unset_env(name, new)
1802
self.addCleanup(osutils.set_or_unset_env, name, value)
1805
def recordCalls(self, obj, attr_name):
1806
"""Monkeypatch in a wrapper that will record calls.
1808
The monkeypatch is automatically removed when the test concludes.
1810
:param obj: The namespace holding the reference to be replaced;
1811
typically a module, class, or object.
1812
:param attr_name: A string for the name of the attribute to
1814
:returns: A list that will be extended with one item every time the
1815
function is called, with a tuple of (args, kwargs).
1819
def decorator(*args, **kwargs):
1820
calls.append((args, kwargs))
1821
return orig(*args, **kwargs)
1822
orig = self.overrideAttr(obj, attr_name, decorator)
1512
1825
def _cleanEnvironment(self):
1514
'BZR_HOME': None, # Don't inherit BZR_HOME to all the tests.
1515
'HOME': os.getcwd(),
1516
# bzr now uses the Win32 API and doesn't rely on APPDATA, but the
1517
# tests do check our impls match APPDATA
1518
'BZR_EDITOR': None, # test_msgeditor manipulates this variable
1522
'BZREMAIL': None, # may still be present in the environment
1524
'BZR_PROGRESS_BAR': None,
1526
'BZR_PLUGIN_PATH': None,
1527
'BZR_CONCURRENCY': None,
1528
# Make sure that any text ui tests are consistent regardless of
1529
# the environment the test case is run in; you may want tests that
1530
# test other combinations. 'dumb' is a reasonable guess for tests
1531
# going to a pipe or a StringIO.
1536
'SSH_AUTH_SOCK': None,
1540
'https_proxy': None,
1541
'HTTPS_PROXY': None,
1546
# Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
1547
# least. If you do (care), please update this comment
1551
'BZR_REMOTE_PATH': None,
1554
self.addCleanup(self._restoreEnvironment)
1555
for name, value in new_env.iteritems():
1556
self._captureVar(name, value)
1558
def _captureVar(self, name, newvalue):
1559
"""Set an environment variable, and reset it when finished."""
1560
self.__old_env[name] = osutils.set_or_unset_env(name, newvalue)
1562
def _restore_debug_flags(self):
1563
debug.debug_flags.clear()
1564
debug.debug_flags.update(self._preserved_debug_flags)
1566
def _restoreEnvironment(self):
1567
for name, value in self.__old_env.iteritems():
1568
osutils.set_or_unset_env(name, value)
1826
for name, value in isolated_environ.iteritems():
1827
self.overrideEnv(name, value)
1570
1829
def _restoreHooks(self):
1571
1830
for klass, (name, hooks) in self._preserved_hooks.items():
1572
1831
setattr(klass, name, hooks)
1832
self._preserved_hooks.clear()
1833
bzrlib.hooks._lazy_hooks = self._preserved_lazy_hooks
1834
self._preserved_lazy_hooks.clear()
1574
1836
def knownFailure(self, reason):
1575
"""This test has failed for some known reason."""
1576
raise KnownFailure(reason)
1837
"""Declare that this test fails for a known reason
1839
Tests that are known to fail should generally be using expectedFailure
1840
with an appropriate reverse assertion if a change could cause the test
1841
to start passing. Conversely if the test has no immediate prospect of
1842
succeeding then using skip is more suitable.
1844
When this method is called while an exception is being handled, that
1845
traceback will be used, otherwise a new exception will be thrown to
1846
provide one but won't be reported.
1848
self._add_reason(reason)
1850
exc_info = sys.exc_info()
1851
if exc_info != (None, None, None):
1852
self._report_traceback(exc_info)
1855
raise self.failureException(reason)
1856
except self.failureException:
1857
exc_info = sys.exc_info()
1858
# GZ 02-08-2011: Maybe cleanup this err.exc_info attribute too?
1859
raise testtools.testcase._ExpectedFailure(exc_info)
1863
def _suppress_log(self):
1864
"""Remove the log info from details."""
1865
self.discardDetail('log')
1578
1867
def _do_skip(self, result, reason):
1868
self._suppress_log()
1579
1869
addSkip = getattr(result, 'addSkip', None)
1580
1870
if not callable(addSkip):
1581
1871
result.addSuccess(result)
1583
1873
addSkip(self, reason)
1585
def _do_known_failure(self, result):
1876
def _do_known_failure(self, result, e):
1877
self._suppress_log()
1586
1878
err = sys.exc_info()
1587
1879
addExpectedFailure = getattr(result, 'addExpectedFailure', None)
1588
1880
if addExpectedFailure is not None:
1591
1883
result.addSuccess(self)
1593
1886
def _do_not_applicable(self, result, e):
1595
1888
reason = 'No reason given'
1597
1890
reason = e.args[0]
1891
self._suppress_log ()
1598
1892
addNotApplicable = getattr(result, 'addNotApplicable', None)
1599
1893
if addNotApplicable is not None:
1600
1894
result.addNotApplicable(self, reason)
1602
1896
self._do_skip(result, reason)
1604
def _do_unsupported_or_skip(self, result, reason):
1899
def _report_skip(self, result, err):
1900
"""Override the default _report_skip.
1902
We want to strip the 'log' detail. If we waint until _do_skip, it has
1903
already been formatted into the 'reason' string, and we can't pull it
1906
self._suppress_log()
1907
super(TestCase, self)._report_skip(self, result, err)
1910
def _report_expected_failure(self, result, err):
1913
See _report_skip for motivation.
1915
self._suppress_log()
1916
super(TestCase, self)._report_expected_failure(self, result, err)
1919
def _do_unsupported_or_skip(self, result, e):
1921
self._suppress_log()
1605
1922
addNotSupported = getattr(result, 'addNotSupported', None)
1606
1923
if addNotSupported is not None:
1607
1924
result.addNotSupported(self, reason)
1609
1926
self._do_skip(result, reason)
1611
def run(self, result=None):
1612
if result is None: result = self.defaultTestResult()
1613
result.startTest(self)
1618
result.stopTest(self)
1620
def _run(self, result):
1621
for feature in getattr(self, '_test_needs_features', []):
1622
if not feature.available():
1623
return self._do_unsupported_or_skip(result, feature)
1625
absent_attr = object()
1627
method_name = getattr(self, '_testMethodName', absent_attr)
1628
if method_name is absent_attr:
1630
method_name = getattr(self, '_TestCase__testMethodName')
1631
testMethod = getattr(self, method_name)
1635
if not self._bzr_test_setUp_run:
1637
"test setUp did not invoke "
1638
"bzrlib.tests.TestCase's setUp")
1639
except KeyboardInterrupt:
1642
except KnownFailure:
1643
self._do_known_failure(result)
1646
except TestNotApplicable, e:
1647
self._do_not_applicable(result, e)
1650
except TestSkipped, e:
1651
self._do_skip(result, e.args[0])
1654
except UnavailableFeature, e:
1655
self._do_unsupported_or_skip(result, e.args[0])
1659
result.addError(self, sys.exc_info())
1667
except KnownFailure:
1668
self._do_known_failure(result)
1669
except self.failureException:
1670
result.addFailure(self, sys.exc_info())
1671
except TestNotApplicable, e:
1672
self._do_not_applicable(result, e)
1673
except TestSkipped, e:
1675
reason = "No reason given."
1678
self._do_skip(result, reason)
1679
except UnavailableFeature, e:
1680
self._do_unsupported_or_skip(result, e.args[0])
1681
except KeyboardInterrupt:
1685
result.addError(self, sys.exc_info())
1689
if not self._bzr_test_tearDown_run:
1691
"test tearDown did not invoke "
1692
"bzrlib.tests.TestCase's tearDown")
1693
except KeyboardInterrupt:
1697
result.addError(self, sys.exc_info())
1700
if ok: result.addSuccess(self)
1702
except KeyboardInterrupt:
1707
for attr_name in self.attrs_to_keep:
1708
if attr_name in self.__dict__:
1709
saved_attrs[attr_name] = self.__dict__[attr_name]
1710
self.__dict__ = saved_attrs
1714
self._log_contents = ''
1715
self._bzr_test_tearDown_run = True
1716
unittest.TestCase.tearDown(self)
1718
1928
def time(self, callable, *args, **kwargs):
1719
1929
"""Run callable and accrue the time it takes to the benchmark time.
1738
1950
self._benchtime += time.time() - start
1740
def _runCleanups(self):
1741
"""Run registered cleanup functions.
1743
This should only be called from TestCase.tearDown.
1745
# TODO: Perhaps this should keep running cleanups even if
1746
# one of them fails?
1748
# Actually pop the cleanups from the list so tearDown running
1749
# twice is safe (this happens for skipped tests).
1750
while self._cleanups:
1751
cleanup, args, kwargs = self._cleanups.pop()
1752
cleanup(*args, **kwargs)
1754
1952
def log(self, *args):
1757
def _get_log(self, keep_log_file=False):
1758
"""Get the log from bzrlib.trace calls from this test.
1760
:param keep_log_file: When True, if the log is still a file on disk
1761
leave it as a file on disk. When False, if the log is still a file
1762
on disk, the log file is deleted and the log preserved as
1764
:return: A string containing the log.
1956
"""Get a unicode string containing the log from bzrlib.trace.
1958
Undecodable characters are replaced.
1766
# flush the log file, to get all content
1768
if bzrlib.trace._trace_file:
1769
bzrlib.trace._trace_file.flush()
1770
if self._log_contents:
1771
# XXX: this can hardly contain the content flushed above --vila
1773
return self._log_contents
1774
if self._log_file_name is not None:
1775
logfile = open(self._log_file_name)
1777
log_contents = logfile.read()
1780
if not keep_log_file:
1781
self._log_contents = log_contents
1783
os.remove(self._log_file_name)
1785
if sys.platform == 'win32' and e.errno == errno.EACCES:
1786
sys.stderr.write(('Unable to delete log file '
1787
' %r\n' % self._log_file_name))
1792
return "DELETED log file to reduce memory footprint"
1960
return u"".join(self.getDetails()['log'].iter_text())
1794
1962
def requireFeature(self, feature):
1795
1963
"""This test requires a specific feature is available.
2237
def _add_subprocess_log(self, log_file_path):
2238
if len(self._log_files) == 0:
2239
# Register an addCleanup func. We do this on the first call to
2240
# _add_subprocess_log rather than in TestCase.setUp so that this
2241
# addCleanup is registered after any cleanups for tempdirs that
2242
# subclasses might create, which will probably remove the log file
2244
self.addCleanup(self._subprocess_log_cleanup)
2245
# self._log_files is a set, so if a log file is reused we won't grab it
2247
self._log_files.add(log_file_path)
2249
def _subprocess_log_cleanup(self):
2250
for count, log_file_path in enumerate(self._log_files):
2251
# We use buffer_now=True to avoid holding the file open beyond
2252
# the life of this function, which might interfere with e.g.
2253
# cleaning tempdirs on Windows.
2254
# XXX: Testtools 0.9.5 doesn't have the content_from_file helper
2255
#detail_content = content.content_from_file(
2256
# log_file_path, buffer_now=True)
2257
with open(log_file_path, 'rb') as log_file:
2258
log_file_bytes = log_file.read()
2259
detail_content = content.Content(content.ContentType("text",
2260
"plain", {"charset": "utf8"}), lambda: [log_file_bytes])
2261
self.addDetail("start_bzr_subprocess-log-%d" % (count,),
2055
2264
def _popen(self, *args, **kwargs):
2056
2265
"""Place a call to Popen.
2058
2267
Allows tests to override this method to intercept the calls made to
2059
2268
Popen for introspection.
2061
return Popen(*args, **kwargs)
2270
return subprocess.Popen(*args, **kwargs)
2063
2272
def get_source_path(self):
2064
2273
"""Return the path of the directory containing bzrlib."""
2094
2303
if retcode is not None and retcode != process.returncode:
2095
2304
if process_args is None:
2096
2305
process_args = "(unknown args)"
2097
mutter('Output of bzr %s:\n%s', process_args, out)
2098
mutter('Error for bzr %s:\n%s', process_args, err)
2306
trace.mutter('Output of bzr %s:\n%s', process_args, out)
2307
trace.mutter('Error for bzr %s:\n%s', process_args, err)
2099
2308
self.fail('Command bzr %s failed with retcode %s != %s'
2100
2309
% (process_args, retcode, process.returncode))
2101
2310
return [out, err]
2103
def check_inventory_shape(self, inv, shape):
2104
"""Compare an inventory to a list of expected names.
2312
def check_tree_shape(self, tree, shape):
2313
"""Compare a tree to a list of expected names.
2106
2315
Fail if they are not precisely equal.
2109
2318
shape = list(shape) # copy
2110
for path, ie in inv.entries():
2319
for path, ie in tree.iter_entries_by_dir():
2111
2320
name = path.replace('\\', '/')
2112
2321
if ie.kind == 'directory':
2113
2322
name = name + '/'
2324
pass # ignore root entry
2115
2326
shape.remove(name)
2117
2328
extras.append(name)
2213
2420
class TestCaseWithMemoryTransport(TestCase):
2214
2421
"""Common test class for tests that do not need disk resources.
2216
Tests that need disk resources should derive from TestCaseWithTransport.
2423
Tests that need disk resources should derive from TestCaseInTempDir
2424
orTestCaseWithTransport.
2218
2426
TestCaseWithMemoryTransport sets the TEST_ROOT variable for all bzr tests.
2220
For TestCaseWithMemoryTransport the test_home_dir is set to the name of
2428
For TestCaseWithMemoryTransport the ``test_home_dir`` is set to the name of
2221
2429
a directory which does not exist. This serves to help ensure test isolation
2222
is preserved. test_dir is set to the TEST_ROOT, as is cwd, because they
2223
must exist. However, TestCaseWithMemoryTransport does not offer local
2224
file defaults for the transport in tests, nor does it obey the command line
2430
is preserved. ``test_dir`` is set to the TEST_ROOT, as is cwd, because they
2431
must exist. However, TestCaseWithMemoryTransport does not offer local file
2432
defaults for the transport in tests, nor does it obey the command line
2225
2433
override, so tests that accidentally write to the common directory should
2228
:cvar TEST_ROOT: Directory containing all temporary directories, plus
2229
a .bzr directory that stops us ascending higher into the filesystem.
2436
:cvar TEST_ROOT: Directory containing all temporary directories, plus a
2437
``.bzr`` directory that stops us ascending higher into the filesystem.
2232
2440
TEST_ROOT = None
2442
2661
def make_branch(self, relpath, format=None):
2443
2662
"""Create a branch on the transport at relpath."""
2444
2663
repo = self.make_repository(relpath, format=format)
2445
return repo.bzrdir.create_branch()
2664
return repo.bzrdir.create_branch(append_revisions_only=False)
2666
def get_default_format(self):
2669
def resolve_format(self, format):
2670
"""Resolve an object to a ControlDir format object.
2672
The initial format object can either already be
2673
a ControlDirFormat, None (for the default format),
2674
or a string with the name of the control dir format.
2676
:param format: Object to resolve
2677
:return A ControlDirFormat instance
2680
format = self.get_default_format()
2681
if isinstance(format, basestring):
2682
format = bzrdir.format_registry.make_bzrdir(format)
2447
2685
def make_bzrdir(self, relpath, format=None):
2449
2687
# might be a relative or absolute path
2450
2688
maybe_a_url = self.get_url(relpath)
2451
2689
segments = maybe_a_url.rsplit('/', 1)
2452
t = get_transport(maybe_a_url)
2690
t = _mod_transport.get_transport(maybe_a_url)
2453
2691
if len(segments) > 1 and segments[-1] not in ('', '.'):
2454
2692
t.ensure_base()
2457
if isinstance(format, basestring):
2458
format = bzrdir.format_registry.make_bzrdir(format)
2693
format = self.resolve_format(format)
2459
2694
return format.initialize_on_transport(t)
2460
2695
except errors.UninitializableFormat:
2461
2696
raise TestSkipped("Format %s is not initializable." % format)
2463
def make_repository(self, relpath, shared=False, format=None):
2698
def make_repository(self, relpath, shared=None, format=None):
2464
2699
"""Create a repository on our default transport at relpath.
2466
2701
Note that relpath must be a relative path, not a full url.
2491
2729
test_home_dir = self.test_home_dir
2492
2730
if isinstance(test_home_dir, unicode):
2493
2731
test_home_dir = test_home_dir.encode(sys.getfilesystemencoding())
2494
os.environ['HOME'] = test_home_dir
2495
os.environ['BZR_HOME'] = test_home_dir
2732
self.overrideEnv('HOME', test_home_dir)
2733
self.overrideEnv('BZR_HOME', test_home_dir)
2497
2735
def setUp(self):
2498
2736
super(TestCaseWithMemoryTransport, self).setUp()
2737
# Ensure that ConnectedTransport doesn't leak sockets
2738
def get_transport_from_url_with_cleanup(*args, **kwargs):
2739
t = orig_get_transport_from_url(*args, **kwargs)
2740
if isinstance(t, _mod_transport.ConnectedTransport):
2741
self.addCleanup(t.disconnect)
2744
orig_get_transport_from_url = self.overrideAttr(
2745
_mod_transport, 'get_transport_from_url',
2746
get_transport_from_url_with_cleanup)
2499
2747
self._make_test_root()
2500
_currentdir = os.getcwdu()
2501
def _leaveDirectory():
2502
os.chdir(_currentdir)
2503
self.addCleanup(_leaveDirectory)
2748
self.addCleanup(os.chdir, os.getcwdu())
2504
2749
self.makeAndChdirToTestDir()
2505
2750
self.overrideEnvironmentForTesting()
2506
2751
self.__readonly_server = None
3169
3416
"""A decorator which excludes test matching an exclude pattern."""
3171
3418
def __init__(self, suite, exclude_pattern):
3172
TestDecorator.__init__(self, suite)
3173
self.exclude_pattern = exclude_pattern
3174
self.excluded = False
3178
return iter(self._tests)
3179
self.excluded = True
3180
suite = exclude_tests_by_re(self, self.exclude_pattern)
3182
self.addTests(suite)
3183
return iter(self._tests)
3419
super(ExcludeDecorator, self).__init__(
3420
exclude_tests_by_re(suite, exclude_pattern))
3186
3423
class FilterTestsDecorator(TestDecorator):
3187
3424
"""A decorator which filters tests to those matching a pattern."""
3189
3426
def __init__(self, suite, pattern):
3190
TestDecorator.__init__(self, suite)
3191
self.pattern = pattern
3192
self.filtered = False
3196
return iter(self._tests)
3197
self.filtered = True
3198
suite = filter_suite_by_re(self, self.pattern)
3200
self.addTests(suite)
3201
return iter(self._tests)
3427
super(FilterTestsDecorator, self).__init__(
3428
filter_suite_by_re(suite, pattern))
3204
3431
class RandomDecorator(TestDecorator):
3205
3432
"""A decorator which randomises the order of its tests."""
3207
3434
def __init__(self, suite, random_seed, stream):
3208
TestDecorator.__init__(self, suite)
3209
self.random_seed = random_seed
3210
self.randomised = False
3211
self.stream = stream
3215
return iter(self._tests)
3216
self.randomised = True
3217
self.stream.writeln("Randomizing test order using seed %s\n" %
3218
(self.actual_seed()))
3435
random_seed = self.actual_seed(random_seed)
3436
stream.write("Randomizing test order using seed %s\n\n" %
3219
3438
# Initialise the random number generator.
3220
random.seed(self.actual_seed())
3221
suite = randomize_suite(self)
3223
self.addTests(suite)
3224
return iter(self._tests)
3439
random.seed(random_seed)
3440
super(RandomDecorator, self).__init__(randomize_suite(suite))
3226
def actual_seed(self):
3227
if self.random_seed == "now":
3443
def actual_seed(seed):
3228
3445
# We convert the seed to a long to make it reuseable across
3229
3446
# invocations (because the user can reenter it).
3230
self.random_seed = long(time.time())
3447
return long(time.time())
3232
3449
# Convert the seed to a long if we can
3234
self.random_seed = long(self.random_seed)
3452
except (TypeError, ValueError):
3237
return self.random_seed
3240
3457
class TestFirstDecorator(TestDecorator):
3241
3458
"""A decorator which moves named tests to the front."""
3243
3460
def __init__(self, suite, pattern):
3244
TestDecorator.__init__(self, suite)
3245
self.pattern = pattern
3246
self.filtered = False
3250
return iter(self._tests)
3251
self.filtered = True
3252
suites = split_suite_by_re(self, self.pattern)
3254
self.addTests(suites)
3255
return iter(self._tests)
3461
super(TestFirstDecorator, self).__init__()
3462
self.addTests(split_suite_by_re(suite, pattern))
3258
3465
def partition_tests(suite, count):
3259
3466
"""Partition suite into count lists of tests."""
3261
tests = list(iter_suite_tests(suite))
3262
tests_per_process = int(math.ceil(float(len(tests)) / count))
3263
for block in range(count):
3264
low_test = block * tests_per_process
3265
high_test = low_test + tests_per_process
3266
process_tests = tests[low_test:high_test]
3267
result.append(process_tests)
3467
# This just assigns tests in a round-robin fashion. On one hand this
3468
# splits up blocks of related tests that might run faster if they shared
3469
# resources, but on the other it avoids assigning blocks of slow tests to
3470
# just one partition. So the slowest partition shouldn't be much slower
3472
partitions = [list() for i in range(count)]
3473
tests = iter_suite_tests(suite)
3474
for partition, test in itertools.izip(itertools.cycle(partitions), tests):
3475
partition.append(test)
3479
def workaround_zealous_crypto_random():
3480
"""Crypto.Random want to help us being secure, but we don't care here.
3482
This workaround some test failure related to the sftp server. Once paramiko
3483
stop using the controversial API in Crypto.Random, we may get rid of it.
3486
from Crypto.Random import atfork
3271
3492
def fork_for_tests(suite):
3288
3510
ProtocolTestCase.run(self, result)
3290
os.waitpid(self.pid, os.WNOHANG)
3512
pid, status = os.waitpid(self.pid, 0)
3513
# GZ 2011-10-18: If status is nonzero, should report to the result
3514
# that something went wrong.
3292
3516
test_blocks = partition_tests(suite, concurrency)
3517
# Clear the tests from the original suite so it doesn't keep them alive
3518
suite._tests[:] = []
3293
3519
for process_tests in test_blocks:
3294
process_suite = TestSuite()
3295
process_suite.addTests(process_tests)
3520
process_suite = TestUtil.TestSuite(process_tests)
3521
# Also clear each split list so new suite has only reference
3522
process_tests[:] = []
3296
3523
c2pread, c2pwrite = os.pipe()
3297
3524
pid = os.fork()
3527
stream = os.fdopen(c2pwrite, 'wb', 1)
3528
workaround_zealous_crypto_random()
3300
3529
os.close(c2pread)
3301
3530
# Leave stderr and stdout open so we can see test noise
3302
3531
# Close stdin so that the child goes away if it decides to
3303
3532
# read from stdin (otherwise its a roulette to see what
3304
3533
# child actually gets keystrokes for pdb etc).
3305
3534
sys.stdin.close()
3307
stream = os.fdopen(c2pwrite, 'wb', 1)
3308
subunit_result = BzrAutoTimingTestResultDecorator(
3309
TestProtocolClient(stream))
3535
subunit_result = AutoTimingTestResultDecorator(
3536
SubUnitBzrProtocolClient(stream))
3310
3537
process_suite.run(subunit_result)
3539
# Try and report traceback on stream, but exit with error even
3540
# if stream couldn't be created or something else goes wrong.
3541
# The traceback is formatted to a string and written in one go
3542
# to avoid interleaving lines from multiple failing children.
3544
stream.write(traceback.format_exc())
3314
3549
os.close(c2pwrite)
3315
3550
stream = os.fdopen(c2pread, 'rb', 1)
3377
class ForwardingResult(unittest.TestResult):
3379
def __init__(self, target):
3380
unittest.TestResult.__init__(self)
3381
self.result = target
3383
def startTest(self, test):
3384
self.result.startTest(test)
3386
def stopTest(self, test):
3387
self.result.stopTest(test)
3389
def startTestRun(self):
3390
self.result.startTestRun()
3392
def stopTestRun(self):
3393
self.result.stopTestRun()
3395
def addSkip(self, test, reason):
3396
self.result.addSkip(test, reason)
3398
def addSuccess(self, test):
3399
self.result.addSuccess(test)
3401
def addError(self, test, err):
3402
self.result.addError(test, err)
3404
def addFailure(self, test, err):
3405
self.result.addFailure(test, err)
3408
class BZRTransformingResult(ForwardingResult):
3410
def addError(self, test, err):
3411
feature = self._error_looks_like('UnavailableFeature: ', err)
3412
if feature is not None:
3413
self.result.addNotSupported(test, feature)
3415
self.result.addError(test, err)
3417
def addFailure(self, test, err):
3418
known = self._error_looks_like('KnownFailure: ', err)
3419
if known is not None:
3420
self.result.addExpectedFailure(test,
3421
[KnownFailure, KnownFailure(known), None])
3423
self.result.addFailure(test, err)
3425
def _error_looks_like(self, prefix, err):
3426
"""Deserialize exception and returns the stringify value."""
3430
if isinstance(exc, subunit.RemoteException):
3431
# stringify the exception gives access to the remote traceback
3432
# We search the last line for 'prefix'
3433
lines = str(exc).split('\n')
3434
while lines and not lines[-1]:
3437
if lines[-1].startswith(prefix):
3438
value = lines[-1][len(prefix):]
3443
from subunit.test_results import AutoTimingTestResultDecorator
3444
# Expected failure should be seen as a success not a failure Once subunit
3445
# provide native support for that, BZRTransformingResult and this class
3446
# will become useless.
3447
class BzrAutoTimingTestResultDecorator(AutoTimingTestResultDecorator):
3449
def addExpectedFailure(self, test, err):
3450
self._before_event()
3451
return self._call_maybe("addExpectedFailure", self._degrade_skip,
3454
# Let's just define a no-op decorator
3455
BzrAutoTimingTestResultDecorator = lambda x:x
3458
class ProfileResult(ForwardingResult):
3614
class ProfileResult(testtools.ExtendedToOriginalDecorator):
3459
3615
"""Generate profiling data for all activity between start and success.
3461
3617
The profile data is appended to the test's _benchcalls attribute and can
3734
3899
'bzrlib.tests.blackbox',
3735
3900
'bzrlib.tests.commands',
3901
'bzrlib.tests.doc_generate',
3736
3902
'bzrlib.tests.per_branch',
3737
3903
'bzrlib.tests.per_bzrdir',
3904
'bzrlib.tests.per_controldir',
3905
'bzrlib.tests.per_controldir_colo',
3738
3906
'bzrlib.tests.per_foreign_vcs',
3739
3907
'bzrlib.tests.per_interrepository',
3740
3908
'bzrlib.tests.per_intertree',
3741
3909
'bzrlib.tests.per_inventory',
3742
3910
'bzrlib.tests.per_interbranch',
3743
3911
'bzrlib.tests.per_lock',
3912
'bzrlib.tests.per_merger',
3744
3913
'bzrlib.tests.per_transport',
3745
3914
'bzrlib.tests.per_tree',
3746
3915
'bzrlib.tests.per_pack_repository',
3747
3916
'bzrlib.tests.per_repository',
3748
3917
'bzrlib.tests.per_repository_chk',
3749
3918
'bzrlib.tests.per_repository_reference',
3919
'bzrlib.tests.per_repository_vf',
3750
3920
'bzrlib.tests.per_uifactory',
3751
3921
'bzrlib.tests.per_versionedfile',
3752
3922
'bzrlib.tests.per_workingtree',
3753
3923
'bzrlib.tests.test__annotator',
3924
'bzrlib.tests.test__bencode',
3925
'bzrlib.tests.test__btree_serializer',
3754
3926
'bzrlib.tests.test__chk_map',
3755
3927
'bzrlib.tests.test__dirstate_helpers',
3756
3928
'bzrlib.tests.test__groupcompress',
3779
3950
'bzrlib.tests.test_chunk_writer',
3780
3951
'bzrlib.tests.test_clean_tree',
3781
3952
'bzrlib.tests.test_cleanup',
3953
'bzrlib.tests.test_cmdline',
3782
3954
'bzrlib.tests.test_commands',
3783
3955
'bzrlib.tests.test_commit',
3784
3956
'bzrlib.tests.test_commit_merge',
3785
3957
'bzrlib.tests.test_config',
3786
3958
'bzrlib.tests.test_conflicts',
3959
'bzrlib.tests.test_controldir',
3787
3960
'bzrlib.tests.test_counted_lock',
3788
3961
'bzrlib.tests.test_crash',
3789
3962
'bzrlib.tests.test_decorators',
3790
3963
'bzrlib.tests.test_delta',
3791
3964
'bzrlib.tests.test_debug',
3792
'bzrlib.tests.test_deprecated_graph',
3793
3965
'bzrlib.tests.test_diff',
3794
3966
'bzrlib.tests.test_directory_service',
3795
3967
'bzrlib.tests.test_dirstate',
3796
3968
'bzrlib.tests.test_email_message',
3797
3969
'bzrlib.tests.test_eol_filters',
3798
3970
'bzrlib.tests.test_errors',
3971
'bzrlib.tests.test_estimate_compressed_size',
3799
3972
'bzrlib.tests.test_export',
3973
'bzrlib.tests.test_export_pot',
3800
3974
'bzrlib.tests.test_extract',
3975
'bzrlib.tests.test_features',
3801
3976
'bzrlib.tests.test_fetch',
3977
'bzrlib.tests.test_fixtures',
3802
3978
'bzrlib.tests.test_fifo_cache',
3803
3979
'bzrlib.tests.test_filters',
3980
'bzrlib.tests.test_filter_tree',
3804
3981
'bzrlib.tests.test_ftp_transport',
3805
3982
'bzrlib.tests.test_foreign',
3806
3983
'bzrlib.tests.test_generate_docs',
4127
4336
:param new_id: The id to assign to it.
4128
4337
:return: The new test.
4130
new_test = copy(test)
4339
new_test = copy.copy(test)
4131
4340
new_test.id = lambda: new_id
4341
# XXX: Workaround <https://bugs.launchpad.net/testtools/+bug/637725>, which
4342
# causes cloned tests to share the 'details' dict. This makes it hard to
4343
# read the test output for parameterized tests, because tracebacks will be
4344
# associated with irrelevant tests.
4346
details = new_test._TestCase__details
4347
except AttributeError:
4348
# must be a different version of testtools than expected. Do nothing.
4351
# Reset the '__details' dict.
4352
new_test._TestCase__details = {}
4132
4353
return new_test
4356
def permute_tests_for_extension(standard_tests, loader, py_module_name,
4358
"""Helper for permutating tests against an extension module.
4360
This is meant to be used inside a modules 'load_tests()' function. It will
4361
create 2 scenarios, and cause all tests in the 'standard_tests' to be run
4362
against both implementations. Setting 'test.module' to the appropriate
4363
module. See bzrlib.tests.test__chk_map.load_tests as an example.
4365
:param standard_tests: A test suite to permute
4366
:param loader: A TestLoader
4367
:param py_module_name: The python path to a python module that can always
4368
be loaded, and will be considered the 'python' implementation. (eg
4369
'bzrlib._chk_map_py')
4370
:param ext_module_name: The python path to an extension module. If the
4371
module cannot be loaded, a single test will be added, which notes that
4372
the module is not available. If it can be loaded, all standard_tests
4373
will be run against that module.
4374
:return: (suite, feature) suite is a test-suite that has all the permuted
4375
tests. feature is the Feature object that can be used to determine if
4376
the module is available.
4379
from bzrlib.tests.features import ModuleAvailableFeature
4380
py_module = pyutils.get_named_object(py_module_name)
4382
('python', {'module': py_module}),
4384
suite = loader.suiteClass()
4385
feature = ModuleAvailableFeature(ext_module_name)
4386
if feature.available():
4387
scenarios.append(('C', {'module': feature.module}))
4389
# the compiled module isn't available, so we add a failing test
4390
class FailWithoutFeature(TestCase):
4391
def test_fail(self):
4392
self.requireFeature(feature)
4393
suite.addTest(loader.loadTestsFromTestCase(FailWithoutFeature))
4394
result = multiply_tests(standard_tests, scenarios, suite)
4395
return result, feature
4135
4398
def _rmtree_temp_dir(dirname, test_id=None):
4136
4399
# If LANG=C we probably have created some bogus paths
4137
4400
# which rmtree(unicode) will fail to delete
4152
4415
# possible info to the test runner is even worse.
4153
4416
if test_id != None:
4154
4417
ui.ui_factory.clear_term()
4155
sys.stderr.write('While running: %s\n' % (test_id,))
4418
sys.stderr.write('\nWhile running: %s\n' % (test_id,))
4419
# Ugly, but the last thing we want here is fail, so bear with it.
4420
printable_e = str(e).decode(osutils.get_user_encoding(), 'replace'
4421
).encode('ascii', 'replace')
4156
4422
sys.stderr.write('Unable to remove testing dir %s\n%s'
4157
% (os.path.basename(dirname), e))
4160
class Feature(object):
4161
"""An operating system Feature."""
4164
self._available = None
4166
def available(self):
4167
"""Is the feature available?
4169
:return: True if the feature is available.
4171
if self._available is None:
4172
self._available = self._probe()
4173
return self._available
4176
"""Implement this method in concrete features.
4178
:return: True if the feature is available.
4180
raise NotImplementedError
4183
if getattr(self, 'feature_name', None):
4184
return self.feature_name()
4185
return self.__class__.__name__
4188
class _SymlinkFeature(Feature):
4191
return osutils.has_symlinks()
4193
def feature_name(self):
4196
SymlinkFeature = _SymlinkFeature()
4199
class _HardlinkFeature(Feature):
4202
return osutils.has_hardlinks()
4204
def feature_name(self):
4207
HardlinkFeature = _HardlinkFeature()
4210
class _OsFifoFeature(Feature):
4213
return getattr(os, 'mkfifo', None)
4215
def feature_name(self):
4216
return 'filesystem fifos'
4218
OsFifoFeature = _OsFifoFeature()
4221
class _UnicodeFilenameFeature(Feature):
4222
"""Does the filesystem support Unicode filenames?"""
4226
# Check for character combinations unlikely to be covered by any
4227
# single non-unicode encoding. We use the characters
4228
# - greek small letter alpha (U+03B1) and
4229
# - braille pattern dots-123456 (U+283F).
4230
os.stat(u'\u03b1\u283f')
4231
except UnicodeEncodeError:
4233
except (IOError, OSError):
4234
# The filesystem allows the Unicode filename but the file doesn't
4238
# The filesystem allows the Unicode filename and the file exists,
4242
UnicodeFilenameFeature = _UnicodeFilenameFeature()
4423
% (os.path.basename(dirname), printable_e))
4245
4426
def probe_unicode_in_user_encoding():
4278
class _HTTPSServerFeature(Feature):
4279
"""Some tests want an https Server, check if one is available.
4281
Right now, the only way this is available is under python2.6 which provides
4292
def feature_name(self):
4293
return 'HTTPSServer'
4296
HTTPSServerFeature = _HTTPSServerFeature()
4299
class _ParamikoFeature(Feature):
4300
"""Is paramiko available?"""
4304
from bzrlib.transport.sftp import SFTPAbsoluteServer
4306
except errors.ParamikoNotPresent:
4309
def feature_name(self):
4313
ParamikoFeature = _ParamikoFeature()
4316
class _UnicodeFilename(Feature):
4317
"""Does the filesystem support Unicode filenames?"""
4322
except UnicodeEncodeError:
4324
except (IOError, OSError):
4325
# The filesystem allows the Unicode filename but the file doesn't
4329
# The filesystem allows the Unicode filename and the file exists,
4333
UnicodeFilename = _UnicodeFilename()
4336
class _UTF8Filesystem(Feature):
4337
"""Is the filesystem UTF-8?"""
4340
if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
4344
UTF8Filesystem = _UTF8Filesystem()
4347
class _BreakinFeature(Feature):
4348
"""Does this platform support the breakin feature?"""
4351
from bzrlib import breakin
4352
if breakin.determine_signal() is None:
4354
if sys.platform == 'win32':
4355
# Windows doesn't have os.kill, and we catch the SIGBREAK signal.
4356
# We trigger SIGBREAK via a Console api so we need ctypes to
4357
# access the function
4364
def feature_name(self):
4365
return "SIGQUIT or SIGBREAK w/ctypes on win32"
4368
BreakinFeature = _BreakinFeature()
4371
class _CaseInsCasePresFilenameFeature(Feature):
4372
"""Is the file-system case insensitive, but case-preserving?"""
4375
fileno, name = tempfile.mkstemp(prefix='MixedCase')
4377
# first check truly case-preserving for created files, then check
4378
# case insensitive when opening existing files.
4379
name = osutils.normpath(name)
4380
base, rel = osutils.split(name)
4381
found_rel = osutils.canonical_relpath(base, name)
4382
return (found_rel == rel
4383
and os.path.isfile(name.upper())
4384
and os.path.isfile(name.lower()))
4389
def feature_name(self):
4390
return "case-insensitive case-preserving filesystem"
4392
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
4395
class _CaseInsensitiveFilesystemFeature(Feature):
4396
"""Check if underlying filesystem is case-insensitive but *not* case
4399
# Note that on Windows, Cygwin, MacOS etc, the file-systems are far
4400
# more likely to be case preserving, so this case is rare.
4403
if CaseInsCasePresFilenameFeature.available():
4406
if TestCaseWithMemoryTransport.TEST_ROOT is None:
4407
root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
4408
TestCaseWithMemoryTransport.TEST_ROOT = root
4410
root = TestCaseWithMemoryTransport.TEST_ROOT
4411
tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
4413
name_a = osutils.pathjoin(tdir, 'a')
4414
name_A = osutils.pathjoin(tdir, 'A')
4416
result = osutils.isdir(name_A)
4417
_rmtree_temp_dir(tdir)
4420
def feature_name(self):
4421
return 'case-insensitive filesystem'
4423
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
4426
class _SubUnitFeature(Feature):
4427
"""Check if subunit is available."""
4436
def feature_name(self):
4439
SubUnitFeature = _SubUnitFeature()
4440
4459
# Only define SubUnitBzrRunner if subunit is available.
4442
4461
from subunit import TestProtocolClient
4462
from subunit.test_results import AutoTimingTestResultDecorator
4463
class SubUnitBzrProtocolClient(TestProtocolClient):
4465
def stopTest(self, test):
4466
super(SubUnitBzrProtocolClient, self).stopTest(test)
4467
_clear__type_equality_funcs(test)
4469
def addSuccess(self, test, details=None):
4470
# The subunit client always includes the details in the subunit
4471
# stream, but we don't want to include it in ours.
4472
if details is not None and 'log' in details:
4474
return super(SubUnitBzrProtocolClient, self).addSuccess(
4443
4477
class SubUnitBzrRunner(TextTestRunner):
4444
4478
def run(self, test):
4445
result = BzrAutoTimingTestResultDecorator(
4446
TestProtocolClient(self.stream))
4479
result = AutoTimingTestResultDecorator(
4480
SubUnitBzrProtocolClient(self.stream))
4447
4481
test.run(result)
4449
4483
except ImportError:
4487
# API compatibility for old plugins; see bug 892622.
4490
'HTTPServerFeature',
4491
'ModuleAvailableFeature',
4492
'HTTPSServerFeature', 'SymlinkFeature', 'HardlinkFeature',
4493
'OsFifoFeature', 'UnicodeFilenameFeature',
4494
'ByteStringNamedFilesystem', 'UTF8Filesystem',
4495
'BreakinFeature', 'CaseInsCasePresFilenameFeature',
4496
'CaseInsensitiveFilesystemFeature', 'case_sensitive_filesystem_feature',
4497
'posix_permissions_feature',
4499
globals()[name] = _CompatabilityThunkFeature(
4500
symbol_versioning.deprecated_in((2, 5, 0)),
4501
'bzrlib.tests', name,
4502
name, 'bzrlib.tests.features')
4505
for (old_name, new_name) in [
4506
('UnicodeFilename', 'UnicodeFilenameFeature'),
4508
globals()[name] = _CompatabilityThunkFeature(
4509
symbol_versioning.deprecated_in((2, 5, 0)),
4510
'bzrlib.tests', old_name,
4511
new_name, 'bzrlib.tests.features')