54
53
# nb: check this before importing anything else from within it
55
54
_testtools_version = getattr(testtools, '__version__', ())
56
if _testtools_version < (0, 9, 2):
57
raise ImportError("need at least testtools 0.9.2: %s is %r"
55
if _testtools_version < (0, 9, 5):
56
raise ImportError("need at least testtools 0.9.5: %s is %r"
58
57
% (testtools.__file__, _testtools_version))
59
58
from testtools import content
61
61
from bzrlib import (
65
commands as _mod_commands,
75
plugin as _mod_plugin,
82
transport as _mod_transport,
79
import bzrlib.commands
80
import bzrlib.timestamp
82
import bzrlib.inventory
83
import bzrlib.iterablefile
86
86
import bzrlib.lsprof
87
87
except ImportError:
88
88
# lsprof not available
90
from bzrlib.merge import merge_inner
93
from bzrlib.smart import client, request, server
95
from bzrlib import symbol_versioning
90
from bzrlib.smart import client, request
91
from bzrlib.transport import (
96
95
from bzrlib.symbol_versioning import (
98
96
deprecated_function,
104
from bzrlib.transport import get_transport, pathfilter
105
import bzrlib.transport
106
from bzrlib.transport.local import LocalURLServer
107
from bzrlib.transport.memory import MemoryServer
108
from bzrlib.transport.readonly import ReadonlyServer
109
from bzrlib.trace import mutter, note
110
from bzrlib.tests import TestUtil
111
from bzrlib.tests.http_server import HttpServer
112
from bzrlib.tests.TestUtil import (
116
from bzrlib.tests.treeshape import build_tree_contents
99
from bzrlib.tests import (
117
105
from bzrlib.ui import NullProgressView
118
106
from bzrlib.ui.text import TextUIFactory
119
import bzrlib.version_info_formats.format_custom
120
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
107
from bzrlib.tests.features import _CompatabilityThunkFeature
122
109
# Mark this python module as being part of the implementation
123
110
# of unittest: this gives us better tracebacks where the last
124
111
# shown frame is the test code, not our assertXYZ.
127
default_transport = LocalURLServer
114
default_transport = test_server.LocalURLServer
130
117
_unitialized_attr = object()
135
122
SUBUNIT_SEEK_SET = 0
136
123
SUBUNIT_SEEK_CUR = 1
139
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):
140
242
"""Accepts, reports and accumulates the results of running tests.
142
244
Compared to the unittest version this class adds support for
191
293
self._overall_start_time = time.time()
192
294
self._strict = strict
295
self._first_thread_leaker_id = None
296
self._tests_leaking_threads_count = 0
297
self._traceback_from_test = None
194
299
def stopTestRun(self):
195
300
run = self.testsRun
196
301
actionTaken = "Ran"
197
302
stopTime = time.time()
198
303
timeTaken = stopTime - self.startTime
200
self.stream.writeln(self.separator2)
201
self.stream.writeln("%s %d test%s in %.3fs" % (actionTaken,
304
# GZ 2010-07-19: Seems testtools has no printErrors method, and though
305
# the parent class method is similar have to duplicate
306
self._show_list('ERROR', self.errors)
307
self._show_list('FAIL', self.failures)
308
self.stream.write(self.sep2)
309
self.stream.write("%s %d test%s in %.3fs\n\n" % (actionTaken,
202
310
run, run != 1 and "s" or "", timeTaken))
203
self.stream.writeln()
204
311
if not self.wasSuccessful():
205
312
self.stream.write("FAILED (")
206
313
failed, errored = map(len, (self.failures, self.errors))
213
320
if failed or errored: self.stream.write(", ")
214
321
self.stream.write("known_failure_count=%d" %
215
322
self.known_failure_count)
216
self.stream.writeln(")")
323
self.stream.write(")\n")
218
325
if self.known_failure_count:
219
self.stream.writeln("OK (known_failures=%d)" %
326
self.stream.write("OK (known_failures=%d)\n" %
220
327
self.known_failure_count)
222
self.stream.writeln("OK")
329
self.stream.write("OK\n")
223
330
if self.skip_count > 0:
224
331
skipped = self.skip_count
225
self.stream.writeln('%d test%s skipped' %
332
self.stream.write('%d test%s skipped\n' %
226
333
(skipped, skipped != 1 and "s" or ""))
227
334
if self.unsupported:
228
335
for feature, count in sorted(self.unsupported.items()):
229
self.stream.writeln("Missing feature '%s' skipped %d tests." %
336
self.stream.write("Missing feature '%s' skipped %d tests.\n" %
230
337
(feature, count))
232
339
ok = self.wasStrictlySuccessful()
234
341
ok = self.wasSuccessful()
235
if TestCase._first_thread_leaker_id:
342
if self._first_thread_leaker_id:
236
343
self.stream.write(
237
344
'%s is leaking threads among %d leaking tests.\n' % (
238
TestCase._first_thread_leaker_id,
239
TestCase._leaking_threads_tests))
345
self._first_thread_leaker_id,
346
self._tests_leaking_threads_count))
240
347
# We don't report the main thread as an active one.
241
348
self.stream.write(
242
349
'%d non-main threads were left active in the end.\n'
243
% (TestCase._active_threads - 1))
350
% (len(self._active_threads) - 1))
245
352
def getDescription(self, test):
271
379
def _shortened_test_description(self, test):
273
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]
276
392
def startTest(self, test):
277
unittest.TestResult.startTest(self, test)
393
super(ExtendedTestResult, self).startTest(test)
278
394
if self.count == 0:
279
395
self.startTests()
280
397
self.report_test_start(test)
281
398
test.number = self.count
282
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
284
417
def startTests(self):
286
if getattr(sys, 'frozen', None) is None:
287
bzr_path = osutils.realpath(sys.argv[0])
289
bzr_path = sys.executable
291
'bzr selftest: %s\n' % (bzr_path,))
294
bzrlib.__path__[0],))
296
' bzr-%s python-%s %s\n' % (
297
bzrlib.version_string,
298
bzrlib._format_version_tuple(sys.version_info),
299
platform.platform(aliased=1),
301
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
303
437
def _recordTestStartTime(self):
304
438
"""Record that a test has started."""
305
self._start_time = time.time()
307
def _cleanupLogFile(self, test):
308
# We can only do this if we have one of our TestCases, not if
310
setKeepLogfile = getattr(test, 'setKeepLogfile', None)
311
if setKeepLogfile is not None:
439
self._start_datetime = self._now()
314
441
def addError(self, test, err):
315
442
"""Tell result that test finished with an error.
351
476
self._formatTime(benchmark_time),
353
478
self.report_success(test)
354
self._cleanupLogFile(test)
355
unittest.TestResult.addSuccess(self, test)
479
super(ExtendedTestResult, self).addSuccess(test)
356
480
test._log_contents = ''
358
482
def addExpectedFailure(self, test, err):
359
483
self.known_failure_count += 1
360
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()))
362
499
def addNotSupported(self, test, feature):
363
500
"""The test will not be run because of a missing feature.
396
538
raise errors.BzrError("Unknown whence %r" % whence)
398
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)))
401
572
def startTestRun(self):
402
573
self.startTime = time.time()
546
714
return '%s%s' % (indent, err[1])
548
716
def report_error(self, test, err):
549
self.stream.writeln('ERROR %s\n%s'
717
self.stream.write('ERROR %s\n%s\n'
550
718
% (self._testTimeString(test),
551
719
self._error_summary(err)))
553
721
def report_failure(self, test, err):
554
self.stream.writeln(' FAIL %s\n%s'
722
self.stream.write(' FAIL %s\n%s\n'
555
723
% (self._testTimeString(test),
556
724
self._error_summary(err)))
558
726
def report_known_failure(self, test, err):
559
self.stream.writeln('XFAIL %s\n%s'
727
self.stream.write('XFAIL %s\n%s\n'
560
728
% (self._testTimeString(test),
561
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",
563
737
def report_success(self, test):
564
self.stream.writeln(' OK %s' % self._testTimeString(test))
738
self.stream.write(' OK %s\n' % self._testTimeString(test))
565
739
for bench_called, stats in getattr(test, '_benchcalls', []):
566
self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
740
self.stream.write('LSProf output for %s(%s, %s)\n' % bench_called)
567
741
stats.pprint(file=self.stream)
568
742
# flush the stream so that we get smooth output. This verbose mode is
569
743
# used to show the output in PQM.
570
744
self.stream.flush()
572
746
def report_skip(self, test, reason):
573
self.stream.writeln(' SKIP %s\n%s'
747
self.stream.write(' SKIP %s\n%s\n'
574
748
% (self._testTimeString(test), reason))
576
750
def report_not_applicable(self, test, reason):
577
self.stream.writeln(' N/A %s\n %s'
751
self.stream.write(' N/A %s\n %s\n'
578
752
% (self._testTimeString(test), reason))
580
754
def report_unsupported(self, test, feature):
581
755
"""test cannot be run because feature is missing."""
582
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"
583
757
%(self._testTimeString(test), feature))
826
1022
self._track_transports()
827
1023
self._track_locks()
828
1024
self._clear_debug_flags()
829
TestCase._active_threads = threading.activeCount()
830
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)
832
1043
def debug(self):
833
1044
# debug a frame up.
835
pdb.Pdb().set_trace(sys._getframe().f_back)
837
def _check_leaked_threads(self):
838
active = threading.activeCount()
839
leaked_threads = active - TestCase._active_threads
840
TestCase._active_threads = active
841
# If some tests make the number of threads *decrease*, we'll consider
842
# that they are just observing old threads dieing, not agressively kill
843
# random threads. So we don't report these tests as leaking. The risk
844
# is that we have false positives that way (the test see 2 threads
845
# going away but leak one) but it seems less likely than the actual
846
# false positives (the test see threads going away and does not leak).
847
if leaked_threads > 0:
848
TestCase._leaking_threads_tests += 1
849
if TestCase._first_thread_leaker_id is None:
850
TestCase._first_thread_leaker_id = self.id()
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,))
852
1110
def _clear_debug_flags(self):
853
1111
"""Prevent externally set debug flags affecting tests.
865
1123
def _clear_hooks(self):
866
1124
# prevent hooks affecting tests
1125
known_hooks = hooks.known_hooks
867
1126
self._preserved_hooks = {}
868
for key, factory in hooks.known_hooks.items():
869
parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
870
current_hooks = hooks.known_hooks_key_to_object(key)
1127
for key, (parent, name) in known_hooks.iter_parent_objects():
1128
current_hooks = getattr(parent, name)
871
1129
self._preserved_hooks[parent] = (name, current_hooks)
1130
self._preserved_lazy_hooks = hooks._lazy_hooks
1131
hooks._lazy_hooks = {}
872
1132
self.addCleanup(self._restoreHooks)
873
for key, factory in hooks.known_hooks.items():
874
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)
875
1135
setattr(parent, name, factory())
876
1136
# this hook should always be installed
877
1137
request._install_hook()
1133
1398
'st_mtime did not match')
1134
1399
self.assertEqual(expected.st_ctime, actual.st_ctime,
1135
1400
'st_ctime did not match')
1136
if sys.platform != 'win32':
1401
if sys.platform == 'win32':
1137
1402
# On Win32 both 'dev' and 'ino' cannot be trusted. In python2.4 it
1138
1403
# is 'dev' that varies, in python 2.5 (6?) it is st_ino that is
1139
# odd. Regardless we shouldn't actually try to assert anything
1140
# about their values
1404
# odd. We just force it to always be 0 to avoid any problems.
1405
self.assertEqual(0, expected.st_dev)
1406
self.assertEqual(0, actual.st_dev)
1407
self.assertEqual(0, expected.st_ino)
1408
self.assertEqual(0, actual.st_ino)
1141
1410
self.assertEqual(expected.st_dev, actual.st_dev,
1142
1411
'st_dev did not match')
1143
1412
self.assertEqual(expected.st_ino, actual.st_ino,
1308
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)))
1310
1595
def failUnlessExists(self, path):
1596
return self.assertPathExists(path)
1598
def assertPathExists(self, path):
1311
1599
"""Fail unless path or paths, which may be abs or relative, exist."""
1312
1600
if not isinstance(path, basestring):
1314
self.failUnlessExists(p)
1602
self.assertPathExists(p)
1316
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)))
1318
1608
def failIfExists(self, path):
1609
return self.assertPathDoesNotExist(path)
1611
def assertPathDoesNotExist(self, path):
1319
1612
"""Fail if path or paths, which may be abs or relative, exist."""
1320
1613
if not isinstance(path, basestring):
1322
self.failIfExists(p)
1615
self.assertPathDoesNotExist(p)
1324
self.failIf(osutils.lexists(path),path+" exists")
1617
self.assertFalse(osutils.lexists(path),
1326
1620
def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
1327
1621
"""A helper for callDeprecated and applyDeprecated.
1439
1734
def _startLogFile(self):
1440
"""Send bzr and test log messages to a temporary file.
1442
The file is removed as the test is torn down.
1444
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
1445
self._log_file = os.fdopen(fileno, 'w+')
1446
self._log_memento = bzrlib.trace.push_log_file(self._log_file)
1447
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)
1448
1745
self.addCleanup(self._finishLogFile)
1450
1747
def _finishLogFile(self):
1451
"""Finished with the log file.
1453
Close the file and delete it, unless setKeepLogfile was called.
1455
if bzrlib.trace._trace_file:
1748
"""Flush and dereference the in-memory log for this testcase"""
1749
if trace._trace_file:
1456
1750
# flush the log file, to get all content
1457
bzrlib.trace._trace_file.flush()
1458
bzrlib.trace.pop_log_file(self._log_memento)
1459
# Cache the log result and delete the file on disk
1460
self._get_log(False)
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
1462
1756
def thisFailsStrictLockCheck(self):
1463
1757
"""It is known that this test would fail with -Dstrict_locks.
1499
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)
1502
1825
def _cleanEnvironment(self):
1504
'BZR_HOME': None, # Don't inherit BZR_HOME to all the tests.
1505
'HOME': os.getcwd(),
1506
# bzr now uses the Win32 API and doesn't rely on APPDATA, but the
1507
# tests do check our impls match APPDATA
1508
'BZR_EDITOR': None, # test_msgeditor manipulates this variable
1512
'BZREMAIL': None, # may still be present in the environment
1514
'BZR_PROGRESS_BAR': None,
1516
'BZR_PLUGIN_PATH': None,
1517
'BZR_CONCURRENCY': None,
1518
# Make sure that any text ui tests are consistent regardless of
1519
# the environment the test case is run in; you may want tests that
1520
# test other combinations. 'dumb' is a reasonable guess for tests
1521
# going to a pipe or a StringIO.
1525
'BZR_COLUMNS': '80',
1527
'SSH_AUTH_SOCK': None,
1531
'https_proxy': None,
1532
'HTTPS_PROXY': None,
1537
# Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
1538
# least. If you do (care), please update this comment
1542
'BZR_REMOTE_PATH': None,
1545
self.addCleanup(self._restoreEnvironment)
1546
for name, value in new_env.iteritems():
1547
self._captureVar(name, value)
1549
def _captureVar(self, name, newvalue):
1550
"""Set an environment variable, and reset it when finished."""
1551
self.__old_env[name] = osutils.set_or_unset_env(name, newvalue)
1553
def _restoreEnvironment(self):
1554
for name, value in self.__old_env.iteritems():
1555
osutils.set_or_unset_env(name, value)
1826
for name, value in isolated_environ.iteritems():
1827
self.overrideEnv(name, value)
1557
1829
def _restoreHooks(self):
1558
1830
for klass, (name, hooks) in self._preserved_hooks.items():
1559
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()
1561
1836
def knownFailure(self, reason):
1562
"""This test has failed for some known reason."""
1563
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')
1565
1867
def _do_skip(self, result, reason):
1868
self._suppress_log()
1566
1869
addSkip = getattr(result, 'addSkip', None)
1567
1870
if not callable(addSkip):
1568
1871
result.addSuccess(result)
1591
1896
self._do_skip(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)
1594
1919
def _do_unsupported_or_skip(self, result, e):
1595
1920
reason = e.args[0]
1921
self._suppress_log()
1596
1922
addNotSupported = getattr(result, 'addNotSupported', None)
1597
1923
if addNotSupported is not None:
1598
1924
result.addNotSupported(self, reason)
1624
1950
self._benchtime += time.time() - start
1626
1952
def log(self, *args):
1629
def _get_log(self, keep_log_file=False):
1630
"""Internal helper to get the log from bzrlib.trace for this test.
1632
Please use self.getDetails, or self.get_log to access this in test case
1635
:param keep_log_file: When True, if the log is still a file on disk
1636
leave it as a file on disk. When False, if the log is still a file
1637
on disk, the log file is deleted and the log preserved as
1639
:return: A string containing the log.
1641
if self._log_contents is not None:
1643
self._log_contents.decode('utf8')
1644
except UnicodeDecodeError:
1645
unicodestr = self._log_contents.decode('utf8', 'replace')
1646
self._log_contents = unicodestr.encode('utf8')
1647
return self._log_contents
1649
if bzrlib.trace._trace_file:
1650
# flush the log file, to get all content
1651
bzrlib.trace._trace_file.flush()
1652
if self._log_file_name is not None:
1653
logfile = open(self._log_file_name)
1655
log_contents = logfile.read()
1659
log_contents.decode('utf8')
1660
except UnicodeDecodeError:
1661
unicodestr = log_contents.decode('utf8', 'replace')
1662
log_contents = unicodestr.encode('utf8')
1663
if not keep_log_file:
1664
self._log_file.close()
1665
self._log_file = None
1666
# Permit multiple calls to get_log until we clean it up in
1668
self._log_contents = log_contents
1670
os.remove(self._log_file_name)
1672
if sys.platform == 'win32' and e.errno == errno.EACCES:
1673
sys.stderr.write(('Unable to delete log file '
1674
' %r\n' % self._log_file_name))
1677
self._log_file_name = None
1680
return "No log file content and no log file name."
1682
1955
def get_log(self):
1683
1956
"""Get a unicode string containing the log from bzrlib.trace.
1898
2172
variables. A value of None will unset the env variable.
1899
2173
The values must be strings. The change will only occur in the
1900
2174
child, so you don't need to fix the environment after running.
1901
:param skip_if_plan_to_signal: raise TestSkipped when true and os.kill
2175
:param skip_if_plan_to_signal: raise TestSkipped when true and system
2176
doesn't support signalling subprocesses.
1903
2177
:param allow_plugins: If False (default) pass --no-plugins to bzr.
2178
:param stderr: file to use for the subprocess's stderr. Valid values
2179
are those valid for the stderr argument of `subprocess.Popen`.
2180
Default value is ``subprocess.PIPE``.
1905
2182
:returns: Popen object for the started process.
1907
2184
if skip_if_plan_to_signal:
1908
if not getattr(os, 'kill', None):
1909
raise TestSkipped("os.kill not available.")
2185
if os.name != "posix":
2186
raise TestSkipped("Sending signals not supported")
1911
2188
if env_changes is None:
1912
2189
env_changes = {}
2190
# Because $HOME is set to a tempdir for the context of a test, modules
2191
# installed in the user dir will not be found unless $PYTHONUSERBASE
2192
# gets set to the computed directory of this parent process.
2193
if site.USER_BASE is not None:
2194
env_changes["PYTHONUSERBASE"] = site.USER_BASE
1915
2197
def cleanup_environment():
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,),
1950
2264
def _popen(self, *args, **kwargs):
1951
2265
"""Place a call to Popen.
1953
2267
Allows tests to override this method to intercept the calls made to
1954
2268
Popen for introspection.
1956
return Popen(*args, **kwargs)
2270
return subprocess.Popen(*args, **kwargs)
1958
2272
def get_source_path(self):
1959
2273
"""Return the path of the directory containing bzrlib."""
1989
2303
if retcode is not None and retcode != process.returncode:
1990
2304
if process_args is None:
1991
2305
process_args = "(unknown args)"
1992
mutter('Output of bzr %s:\n%s', process_args, out)
1993
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)
1994
2308
self.fail('Command bzr %s failed with retcode %s != %s'
1995
2309
% (process_args, retcode, process.returncode))
1996
2310
return [out, err]
1998
def check_inventory_shape(self, inv, shape):
1999
"""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.
2001
2315
Fail if they are not precisely equal.
2004
2318
shape = list(shape) # copy
2005
for path, ie in inv.entries():
2319
for path, ie in tree.iter_entries_by_dir():
2006
2320
name = path.replace('\\', '/')
2007
2321
if ie.kind == 'directory':
2008
2322
name = name + '/'
2324
pass # ignore root entry
2010
2326
shape.remove(name)
2012
2328
extras.append(name)
2102
2420
class TestCaseWithMemoryTransport(TestCase):
2103
2421
"""Common test class for tests that do not need disk resources.
2105
Tests that need disk resources should derive from TestCaseWithTransport.
2423
Tests that need disk resources should derive from TestCaseInTempDir
2424
orTestCaseWithTransport.
2107
2426
TestCaseWithMemoryTransport sets the TEST_ROOT variable for all bzr tests.
2109
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
2110
2429
a directory which does not exist. This serves to help ensure test isolation
2111
is preserved. test_dir is set to the TEST_ROOT, as is cwd, because they
2112
must exist. However, TestCaseWithMemoryTransport does not offer local
2113
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
2114
2433
override, so tests that accidentally write to the common directory should
2117
:cvar TEST_ROOT: Directory containing all temporary directories, plus
2118
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.
2121
2440
TEST_ROOT = None
2331
2661
def make_branch(self, relpath, format=None):
2332
2662
"""Create a branch on the transport at relpath."""
2333
2663
repo = self.make_repository(relpath, format=format)
2334
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)
2336
2685
def make_bzrdir(self, relpath, format=None):
2338
2687
# might be a relative or absolute path
2339
2688
maybe_a_url = self.get_url(relpath)
2340
2689
segments = maybe_a_url.rsplit('/', 1)
2341
t = get_transport(maybe_a_url)
2690
t = _mod_transport.get_transport(maybe_a_url)
2342
2691
if len(segments) > 1 and segments[-1] not in ('', '.'):
2343
2692
t.ensure_base()
2346
if isinstance(format, basestring):
2347
format = bzrdir.format_registry.make_bzrdir(format)
2693
format = self.resolve_format(format)
2348
2694
return format.initialize_on_transport(t)
2349
2695
except errors.UninitializableFormat:
2350
2696
raise TestSkipped("Format %s is not initializable." % format)
2352
def make_repository(self, relpath, shared=False, format=None):
2698
def make_repository(self, relpath, shared=None, format=None):
2353
2699
"""Create a repository on our default transport at relpath.
2355
2701
Note that relpath must be a relative path, not a full url.
2380
2729
test_home_dir = self.test_home_dir
2381
2730
if isinstance(test_home_dir, unicode):
2382
2731
test_home_dir = test_home_dir.encode(sys.getfilesystemencoding())
2383
os.environ['HOME'] = test_home_dir
2384
os.environ['BZR_HOME'] = test_home_dir
2732
self.overrideEnv('HOME', test_home_dir)
2733
self.overrideEnv('BZR_HOME', test_home_dir)
2386
2735
def setUp(self):
2387
2736
super(TestCaseWithMemoryTransport, self).setUp()
2738
def _add_disconnect_cleanup(transport):
2739
"""Schedule disconnection of given transport at test cleanup
2741
This needs to happen for all connected transports or leaks occur.
2743
Note reconnections may mean we call disconnect multiple times per
2744
transport which is suboptimal but seems harmless.
2746
self.addCleanup(transport.disconnect)
2748
_mod_transport.Transport.hooks.install_named_hook('post_connect',
2749
_add_disconnect_cleanup, None)
2388
2751
self._make_test_root()
2389
2752
self.addCleanup(os.chdir, os.getcwdu())
2390
2753
self.makeAndChdirToTestDir()
3012
class TestDecorator(TestSuite):
3400
class TestDecorator(TestUtil.TestSuite):
3013
3401
"""A decorator for TestCase/TestSuite objects.
3015
Usually, subclasses should override __iter__(used when flattening test
3016
suites), which we do to filter, reorder, parallelise and so on, run() and
3403
Contains rather than flattening suite passed on construction
3020
def __init__(self, suite):
3021
TestSuite.__init__(self)
3024
def countTestCases(self):
3027
cases += test.countTestCases()
3034
def run(self, result):
3035
# Use iteration on self, not self._tests, to allow subclasses to hook
3038
if result.shouldStop:
3406
def __init__(self, suite=None):
3407
super(TestDecorator, self).__init__()
3408
if suite is not None:
3411
# Don't need subclass run method with suite emptying
3412
run = unittest.TestSuite.run
3044
3415
class CountingDecorator(TestDecorator):
3055
3426
"""A decorator which excludes test matching an exclude pattern."""
3057
3428
def __init__(self, suite, exclude_pattern):
3058
TestDecorator.__init__(self, suite)
3059
self.exclude_pattern = exclude_pattern
3060
self.excluded = False
3064
return iter(self._tests)
3065
self.excluded = True
3066
suite = exclude_tests_by_re(self, self.exclude_pattern)
3068
self.addTests(suite)
3069
return iter(self._tests)
3429
super(ExcludeDecorator, self).__init__(
3430
exclude_tests_by_re(suite, exclude_pattern))
3072
3433
class FilterTestsDecorator(TestDecorator):
3073
3434
"""A decorator which filters tests to those matching a pattern."""
3075
3436
def __init__(self, suite, pattern):
3076
TestDecorator.__init__(self, suite)
3077
self.pattern = pattern
3078
self.filtered = False
3082
return iter(self._tests)
3083
self.filtered = True
3084
suite = filter_suite_by_re(self, self.pattern)
3086
self.addTests(suite)
3087
return iter(self._tests)
3437
super(FilterTestsDecorator, self).__init__(
3438
filter_suite_by_re(suite, pattern))
3090
3441
class RandomDecorator(TestDecorator):
3091
3442
"""A decorator which randomises the order of its tests."""
3093
3444
def __init__(self, suite, random_seed, stream):
3094
TestDecorator.__init__(self, suite)
3095
self.random_seed = random_seed
3096
self.randomised = False
3097
self.stream = stream
3101
return iter(self._tests)
3102
self.randomised = True
3103
self.stream.write("Randomizing test order using seed %s\n\n" %
3104
(self.actual_seed()))
3445
random_seed = self.actual_seed(random_seed)
3446
stream.write("Randomizing test order using seed %s\n\n" %
3105
3448
# Initialise the random number generator.
3106
random.seed(self.actual_seed())
3107
suite = randomize_suite(self)
3109
self.addTests(suite)
3110
return iter(self._tests)
3449
random.seed(random_seed)
3450
super(RandomDecorator, self).__init__(randomize_suite(suite))
3112
def actual_seed(self):
3113
if self.random_seed == "now":
3453
def actual_seed(seed):
3114
3455
# We convert the seed to a long to make it reuseable across
3115
3456
# invocations (because the user can reenter it).
3116
self.random_seed = long(time.time())
3457
return long(time.time())
3118
3459
# Convert the seed to a long if we can
3120
self.random_seed = long(self.random_seed)
3462
except (TypeError, ValueError):
3123
return self.random_seed
3126
3467
class TestFirstDecorator(TestDecorator):
3127
3468
"""A decorator which moves named tests to the front."""
3129
3470
def __init__(self, suite, pattern):
3130
TestDecorator.__init__(self, suite)
3131
self.pattern = pattern
3132
self.filtered = False
3136
return iter(self._tests)
3137
self.filtered = True
3138
suites = split_suite_by_re(self, self.pattern)
3140
self.addTests(suites)
3141
return iter(self._tests)
3471
super(TestFirstDecorator, self).__init__()
3472
self.addTests(split_suite_by_re(suite, pattern))
3144
3475
def partition_tests(suite, count):
3145
3476
"""Partition suite into count lists of tests."""
3147
tests = list(iter_suite_tests(suite))
3148
tests_per_process = int(math.ceil(float(len(tests)) / count))
3149
for block in range(count):
3150
low_test = block * tests_per_process
3151
high_test = low_test + tests_per_process
3152
process_tests = tests[low_test:high_test]
3153
result.append(process_tests)
3477
# This just assigns tests in a round-robin fashion. On one hand this
3478
# splits up blocks of related tests that might run faster if they shared
3479
# resources, but on the other it avoids assigning blocks of slow tests to
3480
# just one partition. So the slowest partition shouldn't be much slower
3482
partitions = [list() for i in range(count)]
3483
tests = iter_suite_tests(suite)
3484
for partition, test in itertools.izip(itertools.cycle(partitions), tests):
3485
partition.append(test)
3489
def workaround_zealous_crypto_random():
3490
"""Crypto.Random want to help us being secure, but we don't care here.
3492
This workaround some test failure related to the sftp server. Once paramiko
3493
stop using the controversial API in Crypto.Random, we may get rid of it.
3496
from Crypto.Random import atfork
3157
3502
def fork_for_tests(suite):
3175
3520
ProtocolTestCase.run(self, result)
3177
os.waitpid(self.pid, os.WNOHANG)
3522
pid, status = os.waitpid(self.pid, 0)
3523
# GZ 2011-10-18: If status is nonzero, should report to the result
3524
# that something went wrong.
3179
3526
test_blocks = partition_tests(suite, concurrency)
3527
# Clear the tests from the original suite so it doesn't keep them alive
3528
suite._tests[:] = []
3180
3529
for process_tests in test_blocks:
3181
process_suite = TestSuite()
3182
process_suite.addTests(process_tests)
3530
process_suite = TestUtil.TestSuite(process_tests)
3531
# Also clear each split list so new suite has only reference
3532
process_tests[:] = []
3183
3533
c2pread, c2pwrite = os.pipe()
3184
3534
pid = os.fork()
3537
stream = os.fdopen(c2pwrite, 'wb', 1)
3538
workaround_zealous_crypto_random()
3187
3539
os.close(c2pread)
3188
3540
# Leave stderr and stdout open so we can see test noise
3189
3541
# Close stdin so that the child goes away if it decides to
3190
3542
# read from stdin (otherwise its a roulette to see what
3191
3543
# child actually gets keystrokes for pdb etc).
3192
3544
sys.stdin.close()
3194
stream = os.fdopen(c2pwrite, 'wb', 1)
3195
3545
subunit_result = AutoTimingTestResultDecorator(
3196
TestProtocolClient(stream))
3546
SubUnitBzrProtocolClient(stream))
3197
3547
process_suite.run(subunit_result)
3549
# Try and report traceback on stream, but exit with error even
3550
# if stream couldn't be created or something else goes wrong.
3551
# The traceback is formatted to a string and written in one go
3552
# to avoid interleaving lines from multiple failing children.
3554
stream.write(traceback.format_exc())
3201
3559
os.close(c2pwrite)
3202
3560
stream = os.fdopen(c2pread, 'rb', 1)
3264
class ForwardingResult(unittest.TestResult):
3266
def __init__(self, target):
3267
unittest.TestResult.__init__(self)
3268
self.result = target
3270
def startTest(self, test):
3271
self.result.startTest(test)
3273
def stopTest(self, test):
3274
self.result.stopTest(test)
3276
def startTestRun(self):
3277
self.result.startTestRun()
3279
def stopTestRun(self):
3280
self.result.stopTestRun()
3282
def addSkip(self, test, reason):
3283
self.result.addSkip(test, reason)
3285
def addSuccess(self, test):
3286
self.result.addSuccess(test)
3288
def addError(self, test, err):
3289
self.result.addError(test, err)
3291
def addFailure(self, test, err):
3292
self.result.addFailure(test, err)
3293
ForwardingResult = testtools.ExtendedToOriginalDecorator
3296
class ProfileResult(ForwardingResult):
3624
class ProfileResult(testtools.ExtendedToOriginalDecorator):
3297
3625
"""Generate profiling data for all activity between start and success.
3299
3627
The profile data is appended to the test's _benchcalls attribute and can
3618
3960
'bzrlib.tests.test_chunk_writer',
3619
3961
'bzrlib.tests.test_clean_tree',
3620
3962
'bzrlib.tests.test_cleanup',
3963
'bzrlib.tests.test_cmdline',
3621
3964
'bzrlib.tests.test_commands',
3622
3965
'bzrlib.tests.test_commit',
3623
3966
'bzrlib.tests.test_commit_merge',
3624
3967
'bzrlib.tests.test_config',
3625
3968
'bzrlib.tests.test_conflicts',
3969
'bzrlib.tests.test_controldir',
3626
3970
'bzrlib.tests.test_counted_lock',
3627
3971
'bzrlib.tests.test_crash',
3628
3972
'bzrlib.tests.test_decorators',
3629
3973
'bzrlib.tests.test_delta',
3630
3974
'bzrlib.tests.test_debug',
3631
'bzrlib.tests.test_deprecated_graph',
3632
3975
'bzrlib.tests.test_diff',
3633
3976
'bzrlib.tests.test_directory_service',
3634
3977
'bzrlib.tests.test_dirstate',
3635
3978
'bzrlib.tests.test_email_message',
3636
3979
'bzrlib.tests.test_eol_filters',
3637
3980
'bzrlib.tests.test_errors',
3981
'bzrlib.tests.test_estimate_compressed_size',
3638
3982
'bzrlib.tests.test_export',
3983
'bzrlib.tests.test_export_pot',
3639
3984
'bzrlib.tests.test_extract',
3985
'bzrlib.tests.test_features',
3640
3986
'bzrlib.tests.test_fetch',
3987
'bzrlib.tests.test_fixtures',
3641
3988
'bzrlib.tests.test_fifo_cache',
3642
3989
'bzrlib.tests.test_filters',
3990
'bzrlib.tests.test_filter_tree',
3643
3991
'bzrlib.tests.test_ftp_transport',
3644
3992
'bzrlib.tests.test_foreign',
3645
3993
'bzrlib.tests.test_generate_docs',
3654
4002
'bzrlib.tests.test_http',
3655
4003
'bzrlib.tests.test_http_response',
3656
4004
'bzrlib.tests.test_https_ca_bundle',
4005
'bzrlib.tests.test_i18n',
3657
4006
'bzrlib.tests.test_identitymap',
3658
4007
'bzrlib.tests.test_ignores',
3659
4008
'bzrlib.tests.test_index',
4009
'bzrlib.tests.test_import_tariff',
3660
4010
'bzrlib.tests.test_info',
3661
4011
'bzrlib.tests.test_inv',
3662
4012
'bzrlib.tests.test_inventory_delta',
3663
4013
'bzrlib.tests.test_knit',
3664
4014
'bzrlib.tests.test_lazy_import',
3665
4015
'bzrlib.tests.test_lazy_regex',
4016
'bzrlib.tests.test_library_state',
3666
4017
'bzrlib.tests.test_lock',
3667
4018
'bzrlib.tests.test_lockable_files',
3668
4019
'bzrlib.tests.test_lockdir',
3870
4237
# Some tests mentioned in the list are not in the test suite. The
3871
4238
# list may be out of date, report to the tester.
3872
4239
for id in not_found:
3873
bzrlib.trace.warning('"%s" not found in the test suite', id)
4240
trace.warning('"%s" not found in the test suite', id)
3874
4241
for id in duplicates:
3875
bzrlib.trace.warning('"%s" is used as an id by several tests', id)
4242
trace.warning('"%s" is used as an id by several tests', id)
3880
def multiply_scenarios(scenarios_left, scenarios_right):
4247
def multiply_scenarios(*scenarios):
4248
"""Multiply two or more iterables of scenarios.
4250
It is safe to pass scenario generators or iterators.
4252
:returns: A list of compound scenarios: the cross-product of all
4253
scenarios, with the names concatenated and the parameters
4256
return reduce(_multiply_two_scenarios, map(list, scenarios))
4259
def _multiply_two_scenarios(scenarios_left, scenarios_right):
3881
4260
"""Multiply two sets of scenarios.
3883
4262
:returns: the cartesian product of the two sets of scenarios, that is
4034
4426
if test_id != None:
4035
4427
ui.ui_factory.clear_term()
4036
4428
sys.stderr.write('\nWhile running: %s\n' % (test_id,))
4429
# Ugly, but the last thing we want here is fail, so bear with it.
4430
printable_e = str(e).decode(osutils.get_user_encoding(), 'replace'
4431
).encode('ascii', 'replace')
4037
4432
sys.stderr.write('Unable to remove testing dir %s\n%s'
4038
% (os.path.basename(dirname), e))
4041
class Feature(object):
4042
"""An operating system Feature."""
4045
self._available = None
4047
def available(self):
4048
"""Is the feature available?
4050
:return: True if the feature is available.
4052
if self._available is None:
4053
self._available = self._probe()
4054
return self._available
4057
"""Implement this method in concrete features.
4059
:return: True if the feature is available.
4061
raise NotImplementedError
4064
if getattr(self, 'feature_name', None):
4065
return self.feature_name()
4066
return self.__class__.__name__
4069
class _SymlinkFeature(Feature):
4072
return osutils.has_symlinks()
4074
def feature_name(self):
4077
SymlinkFeature = _SymlinkFeature()
4080
class _HardlinkFeature(Feature):
4083
return osutils.has_hardlinks()
4085
def feature_name(self):
4088
HardlinkFeature = _HardlinkFeature()
4091
class _OsFifoFeature(Feature):
4094
return getattr(os, 'mkfifo', None)
4096
def feature_name(self):
4097
return 'filesystem fifos'
4099
OsFifoFeature = _OsFifoFeature()
4102
class _UnicodeFilenameFeature(Feature):
4103
"""Does the filesystem support Unicode filenames?"""
4107
# Check for character combinations unlikely to be covered by any
4108
# single non-unicode encoding. We use the characters
4109
# - greek small letter alpha (U+03B1) and
4110
# - braille pattern dots-123456 (U+283F).
4111
os.stat(u'\u03b1\u283f')
4112
except UnicodeEncodeError:
4114
except (IOError, OSError):
4115
# The filesystem allows the Unicode filename but the file doesn't
4119
# The filesystem allows the Unicode filename and the file exists,
4123
UnicodeFilenameFeature = _UnicodeFilenameFeature()
4126
class _CompatabilityThunkFeature(Feature):
4127
"""This feature is just a thunk to another feature.
4129
It issues a deprecation warning if it is accessed, to let you know that you
4130
should really use a different feature.
4133
def __init__(self, module, name, this_name, dep_version):
4134
super(_CompatabilityThunkFeature, self).__init__()
4135
self._module = module
4137
self._this_name = this_name
4138
self._dep_version = dep_version
4139
self._feature = None
4142
if self._feature is None:
4143
msg = (self._dep_version % self._this_name) + (
4144
' Use %s.%s instead.' % (self._module, self._name))
4145
symbol_versioning.warn(msg, DeprecationWarning)
4146
mod = __import__(self._module, {}, {}, [self._name])
4147
self._feature = getattr(mod, self._name)
4151
return self._feature._probe()
4154
class ModuleAvailableFeature(Feature):
4155
"""This is a feature than describes a module we want to be available.
4157
Declare the name of the module in __init__(), and then after probing, the
4158
module will be available as 'self.module'.
4160
:ivar module: The module if it is available, else None.
4163
def __init__(self, module_name):
4164
super(ModuleAvailableFeature, self).__init__()
4165
self.module_name = module_name
4169
self._module = __import__(self.module_name, {}, {}, [''])
4176
if self.available(): # Make sure the probe has been done
4180
def feature_name(self):
4181
return self.module_name
4184
# This is kept here for compatibility, it is recommended to use
4185
# 'bzrlib.tests.feature.paramiko' instead
4186
ParamikoFeature = _CompatabilityThunkFeature('bzrlib.tests.features',
4187
'paramiko', 'bzrlib.tests.ParamikoFeature', deprecated_in((2,1,0)))
4433
% (os.path.basename(dirname), printable_e))
4190
4436
def probe_unicode_in_user_encoding():
4223
class _HTTPSServerFeature(Feature):
4224
"""Some tests want an https Server, check if one is available.
4226
Right now, the only way this is available is under python2.6 which provides
4237
def feature_name(self):
4238
return 'HTTPSServer'
4241
HTTPSServerFeature = _HTTPSServerFeature()
4244
class _UnicodeFilename(Feature):
4245
"""Does the filesystem support Unicode filenames?"""
4250
except UnicodeEncodeError:
4252
except (IOError, OSError):
4253
# The filesystem allows the Unicode filename but the file doesn't
4257
# The filesystem allows the Unicode filename and the file exists,
4261
UnicodeFilename = _UnicodeFilename()
4264
class _UTF8Filesystem(Feature):
4265
"""Is the filesystem UTF-8?"""
4268
if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
4272
UTF8Filesystem = _UTF8Filesystem()
4275
class _BreakinFeature(Feature):
4276
"""Does this platform support the breakin feature?"""
4279
from bzrlib import breakin
4280
if breakin.determine_signal() is None:
4282
if sys.platform == 'win32':
4283
# Windows doesn't have os.kill, and we catch the SIGBREAK signal.
4284
# We trigger SIGBREAK via a Console api so we need ctypes to
4285
# access the function
4292
def feature_name(self):
4293
return "SIGQUIT or SIGBREAK w/ctypes on win32"
4296
BreakinFeature = _BreakinFeature()
4299
class _CaseInsCasePresFilenameFeature(Feature):
4300
"""Is the file-system case insensitive, but case-preserving?"""
4303
fileno, name = tempfile.mkstemp(prefix='MixedCase')
4305
# first check truly case-preserving for created files, then check
4306
# case insensitive when opening existing files.
4307
name = osutils.normpath(name)
4308
base, rel = osutils.split(name)
4309
found_rel = osutils.canonical_relpath(base, name)
4310
return (found_rel == rel
4311
and os.path.isfile(name.upper())
4312
and os.path.isfile(name.lower()))
4317
def feature_name(self):
4318
return "case-insensitive case-preserving filesystem"
4320
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
4323
class _CaseInsensitiveFilesystemFeature(Feature):
4324
"""Check if underlying filesystem is case-insensitive but *not* case
4327
# Note that on Windows, Cygwin, MacOS etc, the file-systems are far
4328
# more likely to be case preserving, so this case is rare.
4331
if CaseInsCasePresFilenameFeature.available():
4334
if TestCaseWithMemoryTransport.TEST_ROOT is None:
4335
root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
4336
TestCaseWithMemoryTransport.TEST_ROOT = root
4338
root = TestCaseWithMemoryTransport.TEST_ROOT
4339
tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
4341
name_a = osutils.pathjoin(tdir, 'a')
4342
name_A = osutils.pathjoin(tdir, 'A')
4344
result = osutils.isdir(name_A)
4345
_rmtree_temp_dir(tdir)
4348
def feature_name(self):
4349
return 'case-insensitive filesystem'
4351
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
4354
# Kept for compatibility, use bzrlib.tests.features.subunit instead
4355
SubUnitFeature = _CompatabilityThunkFeature('bzrlib.tests.features', 'subunit',
4356
'bzrlib.tests.SubUnitFeature', deprecated_in((2,1,0)))
4357
4469
# Only define SubUnitBzrRunner if subunit is available.
4359
4471
from subunit import TestProtocolClient
4360
4472
from subunit.test_results import AutoTimingTestResultDecorator
4473
class SubUnitBzrProtocolClient(TestProtocolClient):
4475
def stopTest(self, test):
4476
super(SubUnitBzrProtocolClient, self).stopTest(test)
4477
_clear__type_equality_funcs(test)
4479
def addSuccess(self, test, details=None):
4480
# The subunit client always includes the details in the subunit
4481
# stream, but we don't want to include it in ours.
4482
if details is not None and 'log' in details:
4484
return super(SubUnitBzrProtocolClient, self).addSuccess(
4361
4487
class SubUnitBzrRunner(TextTestRunner):
4362
4488
def run(self, test):
4363
4489
result = AutoTimingTestResultDecorator(
4364
TestProtocolClient(self.stream))
4490
SubUnitBzrProtocolClient(self.stream))
4365
4491
test.run(result)
4367
4493
except ImportError:
4497
# API compatibility for old plugins; see bug 892622.
4500
'HTTPServerFeature',
4501
'ModuleAvailableFeature',
4502
'HTTPSServerFeature', 'SymlinkFeature', 'HardlinkFeature',
4503
'OsFifoFeature', 'UnicodeFilenameFeature',
4504
'ByteStringNamedFilesystem', 'UTF8Filesystem',
4505
'BreakinFeature', 'CaseInsCasePresFilenameFeature',
4506
'CaseInsensitiveFilesystemFeature', 'case_sensitive_filesystem_feature',
4507
'posix_permissions_feature',
4509
globals()[name] = _CompatabilityThunkFeature(
4510
symbol_versioning.deprecated_in((2, 5, 0)),
4511
'bzrlib.tests', name,
4512
name, 'bzrlib.tests.features')
4515
for (old_name, new_name) in [
4516
('UnicodeFilename', 'UnicodeFilenameFeature'),
4518
globals()[name] = _CompatabilityThunkFeature(
4519
symbol_versioning.deprecated_in((2, 5, 0)),
4520
'bzrlib.tests', old_name,
4521
new_name, 'bzrlib.tests.features')