56
54
# nb: check this before importing anything else from within it
57
55
_testtools_version = getattr(testtools, '__version__', ())
58
if _testtools_version < (0, 9, 5):
59
raise ImportError("need at least testtools 0.9.5: %s is %r"
56
if _testtools_version < (0, 9, 2):
57
raise ImportError("need at least testtools 0.9.2: %s is %r"
60
58
% (testtools.__file__, _testtools_version))
61
59
from testtools import content
64
61
from bzrlib import (
68
commands as _mod_commands,
77
plugin as _mod_plugin,
84
transport as _mod_transport,
79
import bzrlib.commands
80
import bzrlib.timestamp
82
import bzrlib.inventory
83
import bzrlib.iterablefile
88
86
import bzrlib.lsprof
89
87
except ImportError:
90
88
# lsprof not available
92
from bzrlib.smart import client, request
90
from bzrlib.merge import merge_inner
93
from bzrlib.smart import client, request, server
95
from bzrlib import symbol_versioning
96
from bzrlib.symbol_versioning import (
93
104
from bzrlib.transport import (
109
import bzrlib.transport
110
from bzrlib.trace import mutter, note
97
111
from bzrlib.tests import (
115
from bzrlib.tests.http_server import HttpServer
116
from bzrlib.tests.TestUtil import (
120
from bzrlib.tests.treeshape import build_tree_contents
102
121
from bzrlib.ui import NullProgressView
103
122
from bzrlib.ui.text import TextUIFactory
123
import bzrlib.version_info_formats.format_custom
124
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
105
126
# Mark this python module as being part of the implementation
106
127
# of unittest: this gives us better tracebacks where the last
118
139
SUBUNIT_SEEK_SET = 0
119
140
SUBUNIT_SEEK_CUR = 1
121
# These are intentionally brought into this namespace. That way plugins, etc
122
# can just "from bzrlib.tests import TestCase, TestLoader, etc"
123
TestSuite = TestUtil.TestSuite
124
TestLoader = TestUtil.TestLoader
126
# Tests should run in a clean and clearly defined environment. The goal is to
127
# keep them isolated from the running environment as mush as possible. The test
128
# framework ensures the variables defined below are set (or deleted if the
129
# value is None) before a test is run and reset to their original value after
130
# the test is run. Generally if some code depends on an environment variable,
131
# the tests should start without this variable in the environment. There are a
132
# few exceptions but you shouldn't violate this rule lightly.
136
# bzr now uses the Win32 API and doesn't rely on APPDATA, but the
137
# tests do check our impls match APPDATA
138
'BZR_EDITOR': None, # test_msgeditor manipulates this variable
142
'BZREMAIL': None, # may still be present in the environment
143
'EMAIL': 'jrandom@example.com', # set EMAIL as bzr does not guess
144
'BZR_PROGRESS_BAR': None,
145
# This should trap leaks to ~/.bzr.log. This occurs when tests use TestCase
146
# as a base class instead of TestCaseInTempDir. Tests inheriting from
147
# TestCase should not use disk resources, BZR_LOG is one.
148
'BZR_LOG': '/you-should-use-TestCaseInTempDir-if-you-need-a-log-file',
149
'BZR_PLUGIN_PATH': None,
150
'BZR_DISABLE_PLUGINS': None,
151
'BZR_PLUGINS_AT': None,
152
'BZR_CONCURRENCY': None,
153
# Make sure that any text ui tests are consistent regardless of
154
# the environment the test case is run in; you may want tests that
155
# test other combinations. 'dumb' is a reasonable guess for tests
156
# going to a pipe or a StringIO.
162
'SSH_AUTH_SOCK': None,
172
# Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
173
# least. If you do (care), please update this comment
177
'BZR_REMOTE_PATH': None,
178
# Generally speaking, we don't want apport reporting on crashes in
179
# the test envirnoment unless we're specifically testing apport,
180
# so that it doesn't leak into the real system environment. We
181
# use an env var so it propagates to subprocesses.
182
'APPORT_DISABLE': '1',
186
def override_os_environ(test, env=None):
187
"""Modify os.environ keeping a copy.
189
:param test: A test instance
191
:param env: A dict containing variable definitions to be installed
194
env = isolated_environ
195
test._original_os_environ = dict([(var, value)
196
for var, value in os.environ.iteritems()])
197
for var, value in env.iteritems():
198
osutils.set_or_unset_env(var, value)
199
if var not in test._original_os_environ:
200
# The var is new, add it with a value of None, so
201
# restore_os_environ will delete it
202
test._original_os_environ[var] = None
205
def restore_os_environ(test):
206
"""Restore os.environ to its original state.
208
:param test: A test instance previously passed to override_os_environ.
210
for var, value in test._original_os_environ.iteritems():
211
# Restore the original value (or delete it if the value has been set to
212
# None in override_os_environ).
213
osutils.set_or_unset_env(var, value)
216
class ExtendedTestResult(testtools.TextTestResult):
143
class ExtendedTestResult(unittest._TextTestResult):
217
144
"""Accepts, reports and accumulates the results of running tests.
219
146
Compared to the unittest version this class adds support for
268
195
self._overall_start_time = time.time()
269
196
self._strict = strict
270
self._first_thread_leaker_id = None
271
self._tests_leaking_threads_count = 0
272
self._traceback_from_test = None
274
198
def stopTestRun(self):
275
199
run = self.testsRun
276
200
actionTaken = "Ran"
277
201
stopTime = time.time()
278
202
timeTaken = stopTime - self.startTime
279
# GZ 2010-07-19: Seems testtools has no printErrors method, and though
280
# the parent class method is similar have to duplicate
281
self._show_list('ERROR', self.errors)
282
self._show_list('FAIL', self.failures)
283
self.stream.write(self.sep2)
284
self.stream.write("%s %d test%s in %.3fs\n\n" % (actionTaken,
204
self.stream.writeln(self.separator2)
205
self.stream.writeln("%s %d test%s in %.3fs" % (actionTaken,
285
206
run, run != 1 and "s" or "", timeTaken))
207
self.stream.writeln()
286
208
if not self.wasSuccessful():
287
209
self.stream.write("FAILED (")
288
210
failed, errored = map(len, (self.failures, self.errors))
295
217
if failed or errored: self.stream.write(", ")
296
218
self.stream.write("known_failure_count=%d" %
297
219
self.known_failure_count)
298
self.stream.write(")\n")
220
self.stream.writeln(")")
300
222
if self.known_failure_count:
301
self.stream.write("OK (known_failures=%d)\n" %
223
self.stream.writeln("OK (known_failures=%d)" %
302
224
self.known_failure_count)
304
self.stream.write("OK\n")
226
self.stream.writeln("OK")
305
227
if self.skip_count > 0:
306
228
skipped = self.skip_count
307
self.stream.write('%d test%s skipped\n' %
229
self.stream.writeln('%d test%s skipped' %
308
230
(skipped, skipped != 1 and "s" or ""))
309
231
if self.unsupported:
310
232
for feature, count in sorted(self.unsupported.items()):
311
self.stream.write("Missing feature '%s' skipped %d tests.\n" %
233
self.stream.writeln("Missing feature '%s' skipped %d tests." %
312
234
(feature, count))
314
236
ok = self.wasStrictlySuccessful()
316
238
ok = self.wasSuccessful()
317
if self._first_thread_leaker_id:
239
if TestCase._first_thread_leaker_id:
318
240
self.stream.write(
319
241
'%s is leaking threads among %d leaking tests.\n' % (
320
self._first_thread_leaker_id,
321
self._tests_leaking_threads_count))
242
TestCase._first_thread_leaker_id,
243
TestCase._leaking_threads_tests))
322
244
# We don't report the main thread as an active one.
323
245
self.stream.write(
324
246
'%d non-main threads were left active in the end.\n'
325
% (len(self._active_threads) - 1))
247
% (TestCase._active_threads - 1))
327
249
def getDescription(self, test):
354
275
def _shortened_test_description(self, test):
356
what = re.sub(r'^bzrlib\.tests\.', '', what)
277
what = re.sub(r'^bzrlib\.(tests|benchmarks)\.', '', what)
359
# GZ 2010-10-04: Cloned tests may end up harmlessly calling this method
360
# multiple times in a row, because the handler is added for
361
# each test but the container list is shared between cases.
362
# See lp:498869 lp:625574 and lp:637725 for background.
363
def _record_traceback_from_test(self, exc_info):
364
"""Store the traceback from passed exc_info tuple till"""
365
self._traceback_from_test = exc_info[2]
367
280
def startTest(self, test):
368
super(ExtendedTestResult, self).startTest(test)
281
unittest.TestResult.startTest(self, test)
369
282
if self.count == 0:
370
283
self.startTests()
372
284
self.report_test_start(test)
373
285
test.number = self.count
374
286
self._recordTestStartTime()
375
# Make testtools cases give us the real traceback on failure
376
addOnException = getattr(test, "addOnException", None)
377
if addOnException is not None:
378
addOnException(self._record_traceback_from_test)
379
# Only check for thread leaks on bzrlib derived test cases
380
if isinstance(test, TestCase):
381
test.addCleanup(self._check_leaked_threads, test)
383
def stopTest(self, test):
384
super(ExtendedTestResult, self).stopTest(test)
385
# Manually break cycles, means touching various private things but hey
386
getDetails = getattr(test, "getDetails", None)
387
if getDetails is not None:
389
type_equality_funcs = getattr(test, "_type_equality_funcs", None)
390
if type_equality_funcs is not None:
391
type_equality_funcs.clear()
392
self._traceback_from_test = None
394
288
def startTests(self):
395
self.report_tests_starting()
396
self._active_threads = threading.enumerate()
398
def _check_leaked_threads(self, test):
399
"""See if any threads have leaked since last call
401
A sample of live threads is stored in the _active_threads attribute,
402
when this method runs it compares the current live threads and any not
403
in the previous sample are treated as having leaked.
405
now_active_threads = set(threading.enumerate())
406
threads_leaked = now_active_threads.difference(self._active_threads)
408
self._report_thread_leak(test, threads_leaked, now_active_threads)
409
self._tests_leaking_threads_count += 1
410
if self._first_thread_leaker_id is None:
411
self._first_thread_leaker_id = test.id()
412
self._active_threads = now_active_threads
290
if getattr(sys, 'frozen', None) is None:
291
bzr_path = osutils.realpath(sys.argv[0])
293
bzr_path = sys.executable
295
'bzr selftest: %s\n' % (bzr_path,))
298
bzrlib.__path__[0],))
300
' bzr-%s python-%s %s\n' % (
301
bzrlib.version_string,
302
bzrlib._format_version_tuple(sys.version_info),
303
platform.platform(aliased=1),
305
self.stream.write('\n')
414
307
def _recordTestStartTime(self):
415
308
"""Record that a test has started."""
416
self._start_datetime = self._now()
309
self._start_time = time.time()
311
def _cleanupLogFile(self, test):
312
# We can only do this if we have one of our TestCases, not if
314
setKeepLogfile = getattr(test, 'setKeepLogfile', None)
315
if setKeepLogfile is not None:
418
318
def addError(self, test, err):
419
319
"""Tell result that test finished with an error.
453
355
self._formatTime(benchmark_time),
455
357
self.report_success(test)
456
super(ExtendedTestResult, self).addSuccess(test)
358
self._cleanupLogFile(test)
359
unittest.TestResult.addSuccess(self, test)
457
360
test._log_contents = ''
459
362
def addExpectedFailure(self, test, err):
460
363
self.known_failure_count += 1
461
364
self.report_known_failure(test, err)
463
def addUnexpectedSuccess(self, test, details=None):
464
"""Tell result the test unexpectedly passed, counting as a failure
466
When the minimum version of testtools required becomes 0.9.8 this
467
can be updated to use the new handling there.
469
super(ExtendedTestResult, self).addFailure(test, details=details)
470
self.failure_count += 1
471
self.report_unexpected_success(test,
472
"".join(details["reason"].iter_text()))
476
366
def addNotSupported(self, test, feature):
477
367
"""The test will not be run because of a missing feature.
511
400
raise errors.BzrError("Unknown whence %r" % whence)
513
def report_tests_starting(self):
514
"""Display information before the test run begins"""
515
if getattr(sys, 'frozen', None) is None:
516
bzr_path = osutils.realpath(sys.argv[0])
518
bzr_path = sys.executable
520
'bzr selftest: %s\n' % (bzr_path,))
523
bzrlib.__path__[0],))
525
' bzr-%s python-%s %s\n' % (
526
bzrlib.version_string,
527
bzrlib._format_version_tuple(sys.version_info),
528
platform.platform(aliased=1),
530
self.stream.write('\n')
532
def report_test_start(self, test):
533
"""Display information on the test just about to be run"""
535
def _report_thread_leak(self, test, leaked_threads, active_threads):
536
"""Display information on a test that leaked one or more threads"""
537
# GZ 2010-09-09: A leak summary reported separately from the general
538
# thread debugging would be nice. Tests under subunit
539
# need something not using stream, perhaps adding a
540
# testtools details object would be fitting.
541
if 'threads' in selftest_debug_flags:
542
self.stream.write('%s is leaking, active is now %d\n' %
543
(test.id(), len(active_threads)))
402
def report_cleaning_up(self):
545
405
def startTestRun(self):
546
406
self.startTime = time.time()
687
550
return '%s%s' % (indent, err[1])
689
552
def report_error(self, test, err):
690
self.stream.write('ERROR %s\n%s\n'
553
self.stream.writeln('ERROR %s\n%s'
691
554
% (self._testTimeString(test),
692
555
self._error_summary(err)))
694
557
def report_failure(self, test, err):
695
self.stream.write(' FAIL %s\n%s\n'
558
self.stream.writeln(' FAIL %s\n%s'
696
559
% (self._testTimeString(test),
697
560
self._error_summary(err)))
699
562
def report_known_failure(self, test, err):
700
self.stream.write('XFAIL %s\n%s\n'
563
self.stream.writeln('XFAIL %s\n%s'
701
564
% (self._testTimeString(test),
702
565
self._error_summary(err)))
704
def report_unexpected_success(self, test, reason):
705
self.stream.write(' FAIL %s\n%s: %s\n'
706
% (self._testTimeString(test),
707
"Unexpected success. Should have failed",
710
567
def report_success(self, test):
711
self.stream.write(' OK %s\n' % self._testTimeString(test))
568
self.stream.writeln(' OK %s' % self._testTimeString(test))
712
569
for bench_called, stats in getattr(test, '_benchcalls', []):
713
self.stream.write('LSProf output for %s(%s, %s)\n' % bench_called)
570
self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
714
571
stats.pprint(file=self.stream)
715
572
# flush the stream so that we get smooth output. This verbose mode is
716
573
# used to show the output in PQM.
717
574
self.stream.flush()
719
576
def report_skip(self, test, reason):
720
self.stream.write(' SKIP %s\n%s\n'
577
self.stream.writeln(' SKIP %s\n%s'
721
578
% (self._testTimeString(test), reason))
723
580
def report_not_applicable(self, test, reason):
724
self.stream.write(' N/A %s\n %s\n'
581
self.stream.writeln(' N/A %s\n %s'
725
582
% (self._testTimeString(test), reason))
727
584
def report_unsupported(self, test, feature):
728
585
"""test cannot be run because feature is missing."""
729
self.stream.write("NODEP %s\n The feature '%s' is not available.\n"
586
self.stream.writeln("NODEP %s\n The feature '%s' is not available."
730
587
%(self._testTimeString(test), feature))
953
788
routine, and to build and check bzr trees.
955
790
In addition to the usual method of overriding tearDown(), this class also
956
allows subclasses to register cleanup functions via addCleanup, which are
791
allows subclasses to register functions into the _cleanups list, which is
957
792
run in order as the object is torn down. It's less likely this will be
958
793
accidentally overlooked.
796
_active_threads = None
797
_leaking_threads_tests = 0
798
_first_thread_leaker_id = None
799
_log_file_name = None
962
800
# record lsprof data when performing benchmark calls.
963
801
_gather_lsprof_in_benchmarks = False
965
803
def __init__(self, methodName='testMethod'):
966
804
super(TestCase, self).__init__(methodName)
967
806
self._directory_isolation = True
968
807
self.exception_handlers.insert(0,
969
808
(UnavailableFeature, self._do_unsupported_or_skip))
983
826
self._track_transports()
984
827
self._track_locks()
985
828
self._clear_debug_flags()
986
# Isolate global verbosity level, to make sure it's reproducible
987
# between tests. We should get rid of this altogether: bug 656694. --
989
self.overrideAttr(bzrlib.trace, '_verbosity_level', 0)
990
# Isolate config option expansion until its default value for bzrlib is
991
# settled on or a the FIXME associated with _get_expand_default_value
992
# is addressed -- vila 20110219
993
self.overrideAttr(config, '_expand_default_value', None)
994
self._log_files = set()
995
# Each key in the ``_counters`` dict holds a value for a different
996
# counter. When the test ends, addDetail() should be used to output the
997
# counter values. This happens in install_counter_hook().
999
if 'config_stats' in selftest_debug_flags:
1000
self._install_config_stats_hooks()
829
TestCase._active_threads = threading.activeCount()
830
self.addCleanup(self._check_leaked_threads)
1002
832
def debug(self):
1003
833
# debug a frame up.
1005
835
pdb.Pdb().set_trace(sys._getframe().f_back)
1007
def discardDetail(self, name):
1008
"""Extend the addDetail, getDetails api so we can remove a detail.
1010
eg. bzr always adds the 'log' detail at startup, but we don't want to
1011
include it for skipped, xfail, etc tests.
1013
It is safe to call this for a detail that doesn't exist, in case this
1014
gets called multiple times.
1016
# We cheat. details is stored in __details which means we shouldn't
1017
# touch it. but getDetails() returns the dict directly, so we can
1019
details = self.getDetails()
1023
def install_counter_hook(self, hooks, name, counter_name=None):
1024
"""Install a counting hook.
1026
Any hook can be counted as long as it doesn't need to return a value.
1028
:param hooks: Where the hook should be installed.
1030
:param name: The hook name that will be counted.
1032
:param counter_name: The counter identifier in ``_counters``, defaults
1035
_counters = self._counters # Avoid closing over self
1036
if counter_name is None:
1038
if _counters.has_key(counter_name):
1039
raise AssertionError('%s is already used as a counter name'
1041
_counters[counter_name] = 0
1042
self.addDetail(counter_name, content.Content(content.UTF8_TEXT,
1043
lambda: ['%d' % (_counters[counter_name],)]))
1044
def increment_counter(*args, **kwargs):
1045
_counters[counter_name] += 1
1046
label = 'count %s calls' % (counter_name,)
1047
hooks.install_named_hook(name, increment_counter, label)
1048
self.addCleanup(hooks.uninstall_named_hook, name, label)
1050
def _install_config_stats_hooks(self):
1051
"""Install config hooks to count hook calls.
1054
for hook_name in ('get', 'set', 'remove', 'load', 'save'):
1055
self.install_counter_hook(config.ConfigHooks, hook_name,
1056
'config.%s' % (hook_name,))
1058
# The OldConfigHooks are private and need special handling to protect
1059
# against recursive tests (tests that run other tests), so we just do
1060
# manually what registering them into _builtin_known_hooks will provide
1062
self.overrideAttr(config, 'OldConfigHooks', config._OldConfigHooks())
1063
for hook_name in ('get', 'set', 'remove', 'load', 'save'):
1064
self.install_counter_hook(config.OldConfigHooks, hook_name,
1065
'old_config.%s' % (hook_name,))
837
def _check_leaked_threads(self):
838
active = threading.activeCount()
839
leaked_threads = active - TestCase._active_threads
840
TestCase._active_threads = active
841
# If some tests make the number of threads *decrease*, we'll consider
842
# that they are just observing old threads dieing, not agressively kill
843
# random threads. So we don't report these tests as leaking. The risk
844
# is that we have false positives that way (the test see 2 threads
845
# going away but leak one) but it seems less likely than the actual
846
# false positives (the test see threads going away and does not leak).
847
if leaked_threads > 0:
848
TestCase._leaking_threads_tests += 1
849
if TestCase._first_thread_leaker_id is None:
850
TestCase._first_thread_leaker_id = self.id()
1067
852
def _clear_debug_flags(self):
1068
853
"""Prevent externally set debug flags affecting tests.
1080
865
def _clear_hooks(self):
1081
866
# prevent hooks affecting tests
1082
known_hooks = hooks.known_hooks
1083
867
self._preserved_hooks = {}
1084
for key, (parent, name) in known_hooks.iter_parent_objects():
1085
current_hooks = getattr(parent, name)
868
for key, factory in hooks.known_hooks.items():
869
parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
870
current_hooks = hooks.known_hooks_key_to_object(key)
1086
871
self._preserved_hooks[parent] = (name, current_hooks)
1087
self._preserved_lazy_hooks = hooks._lazy_hooks
1088
hooks._lazy_hooks = {}
1089
872
self.addCleanup(self._restoreHooks)
1090
for key, (parent, name) in known_hooks.iter_parent_objects():
1091
factory = known_hooks.get(key)
873
for key, factory in hooks.known_hooks.items():
874
parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
1092
875
setattr(parent, name, factory())
1093
876
# this hook should always be installed
1094
877
request._install_hook()
1123
906
# break some locks on purpose and should be taken into account by
1124
907
# considering that breaking a lock is just a dirty way of releasing it.
1125
908
if len(acquired_locks) != (len(released_locks) + len(broken_locks)):
1127
'Different number of acquired and '
1128
'released or broken locks.\n'
1132
(acquired_locks, released_locks, broken_locks))
909
message = ('Different number of acquired and '
910
'released or broken locks. (%s, %s + %s)' %
911
(acquired_locks, released_locks, broken_locks))
1133
912
if not self._lock_check_thorough:
1134
913
# Rather than fail, just warn
1135
914
print "Broken test %s: %s" % (self, message)
1355
1134
'st_mtime did not match')
1356
1135
self.assertEqual(expected.st_ctime, actual.st_ctime,
1357
1136
'st_ctime did not match')
1358
if sys.platform == 'win32':
1137
if sys.platform != 'win32':
1359
1138
# On Win32 both 'dev' and 'ino' cannot be trusted. In python2.4 it
1360
1139
# is 'dev' that varies, in python 2.5 (6?) it is st_ino that is
1361
# odd. We just force it to always be 0 to avoid any problems.
1362
self.assertEqual(0, expected.st_dev)
1363
self.assertEqual(0, actual.st_dev)
1364
self.assertEqual(0, expected.st_ino)
1365
self.assertEqual(0, actual.st_ino)
1140
# odd. Regardless we shouldn't actually try to assert anything
1141
# about their values
1367
1142
self.assertEqual(expected.st_dev, actual.st_dev,
1368
1143
'st_dev did not match')
1369
1144
self.assertEqual(expected.st_ino, actual.st_ino,
1378
1153
length, len(obj_with_len), obj_with_len))
1380
1155
def assertLogsError(self, exception_class, func, *args, **kwargs):
1381
"""Assert that `func(*args, **kwargs)` quietly logs a specific error.
1156
"""Assert that func(*args, **kwargs) quietly logs a specific exception.
1158
from bzrlib import trace
1384
1160
orig_log_exception_quietly = trace.log_exception_quietly
1387
1163
orig_log_exception_quietly()
1388
captured.append(sys.exc_info()[1])
1164
captured.append(sys.exc_info())
1389
1165
trace.log_exception_quietly = capture
1390
1166
func(*args, **kwargs)
1392
1168
trace.log_exception_quietly = orig_log_exception_quietly
1393
1169
self.assertLength(1, captured)
1170
err = captured[0][1]
1395
1171
self.assertIsInstance(err, exception_class)
1541
1313
self.assertEqualDiff(content, s)
1543
def assertDocstring(self, expected_docstring, obj):
1544
"""Fail if obj does not have expected_docstring"""
1546
# With -OO the docstring should be None instead
1547
self.assertIs(obj.__doc__, None)
1549
self.assertEqual(expected_docstring, obj.__doc__)
1551
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1552
1315
def failUnlessExists(self, path):
1553
return self.assertPathExists(path)
1555
def assertPathExists(self, path):
1556
1316
"""Fail unless path or paths, which may be abs or relative, exist."""
1557
1317
if not isinstance(path, basestring):
1559
self.assertPathExists(p)
1319
self.failUnlessExists(p)
1561
self.assertTrue(osutils.lexists(path),
1562
path + " does not exist")
1321
self.failUnless(osutils.lexists(path),path+" does not exist")
1564
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4)))
1565
1323
def failIfExists(self, path):
1566
return self.assertPathDoesNotExist(path)
1568
def assertPathDoesNotExist(self, path):
1569
1324
"""Fail if path or paths, which may be abs or relative, exist."""
1570
1325
if not isinstance(path, basestring):
1572
self.assertPathDoesNotExist(p)
1327
self.failIfExists(p)
1574
self.assertFalse(osutils.lexists(path),
1329
self.failIf(osutils.lexists(path),path+" exists")
1577
1331
def _capture_deprecation_warnings(self, a_callable, *args, **kwargs):
1578
1332
"""A helper for callDeprecated and applyDeprecated.
1694
1447
The file is removed as the test is torn down.
1696
pseudo_log_file = StringIO()
1697
def _get_log_contents_for_weird_testtools_api():
1698
return [pseudo_log_file.getvalue().decode(
1699
"utf-8", "replace").encode("utf-8")]
1700
self.addDetail("log", content.Content(content.ContentType("text",
1701
"plain", {"charset": "utf8"}),
1702
_get_log_contents_for_weird_testtools_api))
1703
self._log_file = pseudo_log_file
1704
self._log_memento = trace.push_log_file(self._log_file)
1449
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
1450
self._log_file = os.fdopen(fileno, 'w+')
1451
self._log_memento = bzrlib.trace.push_log_file(self._log_file)
1452
self._log_file_name = name
1705
1453
self.addCleanup(self._finishLogFile)
1707
1455
def _finishLogFile(self):
1708
1456
"""Finished with the log file.
1710
Close the file and delete it.
1458
Close the file and delete it, unless setKeepLogfile was called.
1712
if trace._trace_file:
1460
if bzrlib.trace._trace_file:
1713
1461
# flush the log file, to get all content
1714
trace._trace_file.flush()
1715
trace.pop_log_file(self._log_memento)
1462
bzrlib.trace._trace_file.flush()
1463
bzrlib.trace.pop_log_file(self._log_memento)
1464
# Cache the log result and delete the file on disk
1465
self._get_log(False)
1717
1467
def thisFailsStrictLockCheck(self):
1718
1468
"""It is known that this test would fail with -Dstrict_locks.
1746
1504
setattr(obj, attr_name, new)
1749
def overrideEnv(self, name, new):
1750
"""Set an environment variable, and reset it after the test.
1752
:param name: The environment variable name.
1754
:param new: The value to set the variable to. If None, the
1755
variable is deleted from the environment.
1757
:returns: The actual variable value.
1759
value = osutils.set_or_unset_env(name, new)
1760
self.addCleanup(osutils.set_or_unset_env, name, value)
1763
1507
def _cleanEnvironment(self):
1764
for name, value in isolated_environ.iteritems():
1765
self.overrideEnv(name, value)
1509
'BZR_HOME': None, # Don't inherit BZR_HOME to all the tests.
1510
'HOME': os.getcwd(),
1511
# bzr now uses the Win32 API and doesn't rely on APPDATA, but the
1512
# tests do check our impls match APPDATA
1513
'BZR_EDITOR': None, # test_msgeditor manipulates this variable
1517
'BZREMAIL': None, # may still be present in the environment
1519
'BZR_PROGRESS_BAR': None,
1521
'BZR_PLUGIN_PATH': None,
1522
'BZR_CONCURRENCY': None,
1523
# Make sure that any text ui tests are consistent regardless of
1524
# the environment the test case is run in; you may want tests that
1525
# test other combinations. 'dumb' is a reasonable guess for tests
1526
# going to a pipe or a StringIO.
1530
'BZR_COLUMNS': '80',
1532
'SSH_AUTH_SOCK': None,
1536
'https_proxy': None,
1537
'HTTPS_PROXY': None,
1542
# Nobody cares about ftp_proxy, FTP_PROXY AFAIK. So far at
1543
# least. If you do (care), please update this comment
1547
'BZR_REMOTE_PATH': None,
1548
# Generally speaking, we don't want apport reporting on crashes in
1549
# the test envirnoment unless we're specifically testing apport,
1550
# so that it doesn't leak into the real system environment. We
1551
# use an env var so it propagates to subprocesses.
1552
'APPORT_DISABLE': '1',
1555
self.addCleanup(self._restoreEnvironment)
1556
for name, value in new_env.iteritems():
1557
self._captureVar(name, value)
1559
def _captureVar(self, name, newvalue):
1560
"""Set an environment variable, and reset it when finished."""
1561
self._old_env[name] = osutils.set_or_unset_env(name, newvalue)
1563
def _restoreEnvironment(self):
1564
for name, value in self._old_env.iteritems():
1565
osutils.set_or_unset_env(name, value)
1767
1567
def _restoreHooks(self):
1768
1568
for klass, (name, hooks) in self._preserved_hooks.items():
1769
1569
setattr(klass, name, hooks)
1770
self._preserved_hooks.clear()
1771
bzrlib.hooks._lazy_hooks = self._preserved_lazy_hooks
1772
self._preserved_lazy_hooks.clear()
1774
1571
def knownFailure(self, reason):
1775
1572
"""This test has failed for some known reason."""
1776
1573
raise KnownFailure(reason)
1778
def _suppress_log(self):
1779
"""Remove the log info from details."""
1780
self.discardDetail('log')
1782
1575
def _do_skip(self, result, reason):
1783
self._suppress_log()
1784
1576
addSkip = getattr(result, 'addSkip', None)
1785
1577
if not callable(addSkip):
1786
1578
result.addSuccess(result)
1811
1601
self._do_skip(result, reason)
1814
def _report_skip(self, result, err):
1815
"""Override the default _report_skip.
1817
We want to strip the 'log' detail. If we waint until _do_skip, it has
1818
already been formatted into the 'reason' string, and we can't pull it
1821
self._suppress_log()
1822
super(TestCase, self)._report_skip(self, result, err)
1825
def _report_expected_failure(self, result, err):
1828
See _report_skip for motivation.
1830
self._suppress_log()
1831
super(TestCase, self)._report_expected_failure(self, result, err)
1834
1604
def _do_unsupported_or_skip(self, result, e):
1835
1605
reason = e.args[0]
1836
self._suppress_log()
1837
1606
addNotSupported = getattr(result, 'addNotSupported', None)
1838
1607
if addNotSupported is not None:
1839
1608
result.addNotSupported(self, reason)
1865
1634
self._benchtime += time.time() - start
1867
1636
def log(self, *args):
1639
def _get_log(self, keep_log_file=False):
1640
"""Internal helper to get the log from bzrlib.trace for this test.
1642
Please use self.getDetails, or self.get_log to access this in test case
1645
:param keep_log_file: When True, if the log is still a file on disk
1646
leave it as a file on disk. When False, if the log is still a file
1647
on disk, the log file is deleted and the log preserved as
1649
:return: A string containing the log.
1651
if self._log_contents is not None:
1653
self._log_contents.decode('utf8')
1654
except UnicodeDecodeError:
1655
unicodestr = self._log_contents.decode('utf8', 'replace')
1656
self._log_contents = unicodestr.encode('utf8')
1657
return self._log_contents
1659
if bzrlib.trace._trace_file:
1660
# flush the log file, to get all content
1661
bzrlib.trace._trace_file.flush()
1662
if self._log_file_name is not None:
1663
logfile = open(self._log_file_name)
1665
log_contents = logfile.read()
1669
log_contents.decode('utf8')
1670
except UnicodeDecodeError:
1671
unicodestr = log_contents.decode('utf8', 'replace')
1672
log_contents = unicodestr.encode('utf8')
1673
if not keep_log_file:
1674
self._log_file.close()
1675
self._log_file = None
1676
# Permit multiple calls to get_log until we clean it up in
1678
self._log_contents = log_contents
1680
os.remove(self._log_file_name)
1682
if sys.platform == 'win32' and e.errno == errno.EACCES:
1683
sys.stderr.write(('Unable to delete log file '
1684
' %r\n' % self._log_file_name))
1687
self._log_file_name = None
1690
return "No log file content and no log file name."
1870
1692
def get_log(self):
1871
1693
"""Get a unicode string containing the log from bzrlib.trace.
2087
1908
variables. A value of None will unset the env variable.
2088
1909
The values must be strings. The change will only occur in the
2089
1910
child, so you don't need to fix the environment after running.
2090
:param skip_if_plan_to_signal: raise TestSkipped when true and system
2091
doesn't support signalling subprocesses.
1911
:param skip_if_plan_to_signal: raise TestSkipped when true and os.kill
2092
1913
:param allow_plugins: If False (default) pass --no-plugins to bzr.
2093
:param stderr: file to use for the subprocess's stderr. Valid values
2094
are those valid for the stderr argument of `subprocess.Popen`.
2095
Default value is ``subprocess.PIPE``.
2097
1915
:returns: Popen object for the started process.
2099
1917
if skip_if_plan_to_signal:
2100
if os.name != "posix":
2101
raise TestSkipped("Sending signals not supported")
1918
if not getattr(os, 'kill', None):
1919
raise TestSkipped("os.kill not available.")
2103
1921
if env_changes is None:
2104
1922
env_changes = {}
2147
def _add_subprocess_log(self, log_file_path):
2148
if len(self._log_files) == 0:
2149
# Register an addCleanup func. We do this on the first call to
2150
# _add_subprocess_log rather than in TestCase.setUp so that this
2151
# addCleanup is registered after any cleanups for tempdirs that
2152
# subclasses might create, which will probably remove the log file
2154
self.addCleanup(self._subprocess_log_cleanup)
2155
# self._log_files is a set, so if a log file is reused we won't grab it
2157
self._log_files.add(log_file_path)
2159
def _subprocess_log_cleanup(self):
2160
for count, log_file_path in enumerate(self._log_files):
2161
# We use buffer_now=True to avoid holding the file open beyond
2162
# the life of this function, which might interfere with e.g.
2163
# cleaning tempdirs on Windows.
2164
# XXX: Testtools 0.9.5 doesn't have the content_from_file helper
2165
#detail_content = content.content_from_file(
2166
# log_file_path, buffer_now=True)
2167
with open(log_file_path, 'rb') as log_file:
2168
log_file_bytes = log_file.read()
2169
detail_content = content.Content(content.ContentType("text",
2170
"plain", {"charset": "utf8"}), lambda: [log_file_bytes])
2171
self.addDetail("start_bzr_subprocess-log-%d" % (count,),
2174
1960
def _popen(self, *args, **kwargs):
2175
1961
"""Place a call to Popen.
2177
1963
Allows tests to override this method to intercept the calls made to
2178
1964
Popen for introspection.
2180
return subprocess.Popen(*args, **kwargs)
1966
return Popen(*args, **kwargs)
2182
1968
def get_source_path(self):
2183
1969
"""Return the path of the directory containing bzrlib."""
2213
1999
if retcode is not None and retcode != process.returncode:
2214
2000
if process_args is None:
2215
2001
process_args = "(unknown args)"
2216
trace.mutter('Output of bzr %s:\n%s', process_args, out)
2217
trace.mutter('Error for bzr %s:\n%s', process_args, err)
2002
mutter('Output of bzr %s:\n%s', process_args, out)
2003
mutter('Error for bzr %s:\n%s', process_args, err)
2218
2004
self.fail('Command bzr %s failed with retcode %s != %s'
2219
2005
% (process_args, retcode, process.returncode))
2220
2006
return [out, err]
2222
def check_tree_shape(self, tree, shape):
2223
"""Compare a tree to a list of expected names.
2008
def check_inventory_shape(self, inv, shape):
2009
"""Compare an inventory to a list of expected names.
2225
2011
Fail if they are not precisely equal.
2228
2014
shape = list(shape) # copy
2229
for path, ie in tree.iter_entries_by_dir():
2015
for path, ie in inv.entries():
2230
2016
name = path.replace('\\', '/')
2231
2017
if ie.kind == 'directory':
2232
2018
name = name + '/'
2234
pass # ignore root entry
2236
2020
shape.remove(name)
2238
2022
extras.append(name)
2328
2112
class TestCaseWithMemoryTransport(TestCase):
2329
2113
"""Common test class for tests that do not need disk resources.
2331
Tests that need disk resources should derive from TestCaseInTempDir
2332
orTestCaseWithTransport.
2115
Tests that need disk resources should derive from TestCaseWithTransport.
2334
2117
TestCaseWithMemoryTransport sets the TEST_ROOT variable for all bzr tests.
2336
For TestCaseWithMemoryTransport the ``test_home_dir`` is set to the name of
2119
For TestCaseWithMemoryTransport the test_home_dir is set to the name of
2337
2120
a directory which does not exist. This serves to help ensure test isolation
2338
is preserved. ``test_dir`` is set to the TEST_ROOT, as is cwd, because they
2339
must exist. However, TestCaseWithMemoryTransport does not offer local file
2340
defaults for the transport in tests, nor does it obey the command line
2121
is preserved. test_dir is set to the TEST_ROOT, as is cwd, because they
2122
must exist. However, TestCaseWithMemoryTransport does not offer local
2123
file defaults for the transport in tests, nor does it obey the command line
2341
2124
override, so tests that accidentally write to the common directory should
2344
:cvar TEST_ROOT: Directory containing all temporary directories, plus a
2345
``.bzr`` directory that stops us ascending higher into the filesystem.
2127
:cvar TEST_ROOT: Directory containing all temporary directories, plus
2128
a .bzr directory that stops us ascending higher into the filesystem.
2348
2131
TEST_ROOT = None
2588
2371
made_control = self.make_bzrdir(relpath, format=format)
2589
2372
return made_control.create_repository(shared=shared)
2591
def make_smart_server(self, path, backing_server=None):
2592
if backing_server is None:
2593
backing_server = self.get_server()
2374
def make_smart_server(self, path):
2594
2375
smart_server = test_server.SmartTCPServer_for_testing()
2595
self.start_server(smart_server, backing_server)
2596
remote_transport = _mod_transport.get_transport(smart_server.get_url()
2376
self.start_server(smart_server, self.get_server())
2377
remote_transport = get_transport(smart_server.get_url()).clone(path)
2598
2378
return remote_transport
2600
2380
def make_branch_and_memory_tree(self, relpath, format=None):
2610
2390
test_home_dir = self.test_home_dir
2611
2391
if isinstance(test_home_dir, unicode):
2612
2392
test_home_dir = test_home_dir.encode(sys.getfilesystemencoding())
2613
self.overrideEnv('HOME', test_home_dir)
2614
self.overrideEnv('BZR_HOME', test_home_dir)
2393
os.environ['HOME'] = test_home_dir
2394
os.environ['BZR_HOME'] = test_home_dir
2616
2396
def setUp(self):
2617
2397
super(TestCaseWithMemoryTransport, self).setUp()
2618
# Ensure that ConnectedTransport doesn't leak sockets
2619
def get_transport_with_cleanup(*args, **kwargs):
2620
t = orig_get_transport(*args, **kwargs)
2621
if isinstance(t, _mod_transport.ConnectedTransport):
2622
self.addCleanup(t.disconnect)
2625
orig_get_transport = self.overrideAttr(_mod_transport, 'get_transport',
2626
get_transport_with_cleanup)
2627
2398
self._make_test_root()
2628
2399
self.addCleanup(os.chdir, os.getcwdu())
2629
2400
self.makeAndChdirToTestDir()
3395
3154
def partition_tests(suite, count):
3396
3155
"""Partition suite into count lists of tests."""
3397
# This just assigns tests in a round-robin fashion. On one hand this
3398
# splits up blocks of related tests that might run faster if they shared
3399
# resources, but on the other it avoids assigning blocks of slow tests to
3400
# just one partition. So the slowest partition shouldn't be much slower
3402
partitions = [list() for i in range(count)]
3403
tests = iter_suite_tests(suite)
3404
for partition, test in itertools.izip(itertools.cycle(partitions), tests):
3405
partition.append(test)
3409
def workaround_zealous_crypto_random():
3410
"""Crypto.Random want to help us being secure, but we don't care here.
3412
This workaround some test failure related to the sftp server. Once paramiko
3413
stop using the controversial API in Crypto.Random, we may get rid of it.
3416
from Crypto.Random import atfork
3157
tests = list(iter_suite_tests(suite))
3158
tests_per_process = int(math.ceil(float(len(tests)) / count))
3159
for block in range(count):
3160
low_test = block * tests_per_process
3161
high_test = low_test + tests_per_process
3162
process_tests = tests[low_test:high_test]
3163
result.append(process_tests)
3422
3167
def fork_for_tests(suite):
3532
class ProfileResult(testtools.ExtendedToOriginalDecorator):
3274
class ForwardingResult(unittest.TestResult):
3276
def __init__(self, target):
3277
unittest.TestResult.__init__(self)
3278
self.result = target
3280
def startTest(self, test):
3281
self.result.startTest(test)
3283
def stopTest(self, test):
3284
self.result.stopTest(test)
3286
def startTestRun(self):
3287
self.result.startTestRun()
3289
def stopTestRun(self):
3290
self.result.stopTestRun()
3292
def addSkip(self, test, reason):
3293
self.result.addSkip(test, reason)
3295
def addSuccess(self, test):
3296
self.result.addSuccess(test)
3298
def addError(self, test, err):
3299
self.result.addError(test, err)
3301
def addFailure(self, test, err):
3302
self.result.addFailure(test, err)
3303
ForwardingResult = testtools.ExtendedToOriginalDecorator
3306
class ProfileResult(ForwardingResult):
3533
3307
"""Generate profiling data for all activity between start and success.
3535
3309
The profile data is appended to the test's _benchcalls attribute and can
3833
3596
'bzrlib.tests.per_repository',
3834
3597
'bzrlib.tests.per_repository_chk',
3835
3598
'bzrlib.tests.per_repository_reference',
3836
'bzrlib.tests.per_repository_vf',
3837
3599
'bzrlib.tests.per_uifactory',
3838
3600
'bzrlib.tests.per_versionedfile',
3839
3601
'bzrlib.tests.per_workingtree',
3840
3602
'bzrlib.tests.test__annotator',
3841
3603
'bzrlib.tests.test__bencode',
3842
'bzrlib.tests.test__btree_serializer',
3843
3604
'bzrlib.tests.test__chk_map',
3844
3605
'bzrlib.tests.test__dirstate_helpers',
3845
3606
'bzrlib.tests.test__groupcompress',
3867
3628
'bzrlib.tests.test_chunk_writer',
3868
3629
'bzrlib.tests.test_clean_tree',
3869
3630
'bzrlib.tests.test_cleanup',
3870
'bzrlib.tests.test_cmdline',
3871
3631
'bzrlib.tests.test_commands',
3872
3632
'bzrlib.tests.test_commit',
3873
3633
'bzrlib.tests.test_commit_merge',
3874
3634
'bzrlib.tests.test_config',
3875
3635
'bzrlib.tests.test_conflicts',
3876
'bzrlib.tests.test_controldir',
3877
3636
'bzrlib.tests.test_counted_lock',
3878
3637
'bzrlib.tests.test_crash',
3879
3638
'bzrlib.tests.test_decorators',
3880
3639
'bzrlib.tests.test_delta',
3881
3640
'bzrlib.tests.test_debug',
3641
'bzrlib.tests.test_deprecated_graph',
3882
3642
'bzrlib.tests.test_diff',
3883
3643
'bzrlib.tests.test_directory_service',
3884
3644
'bzrlib.tests.test_dirstate',
3925
3681
'bzrlib.tests.test_lru_cache',
3926
3682
'bzrlib.tests.test_lsprof',
3927
3683
'bzrlib.tests.test_mail_client',
3928
'bzrlib.tests.test_matchers',
3929
3684
'bzrlib.tests.test_memorytree',
3930
3685
'bzrlib.tests.test_merge',
3931
3686
'bzrlib.tests.test_merge3',
3932
3687
'bzrlib.tests.test_merge_core',
3933
3688
'bzrlib.tests.test_merge_directive',
3934
'bzrlib.tests.test_mergetools',
3935
3689
'bzrlib.tests.test_missing',
3936
3690
'bzrlib.tests.test_msgeditor',
3937
3691
'bzrlib.tests.test_multiparent',
4139
3881
# Some tests mentioned in the list are not in the test suite. The
4140
3882
# list may be out of date, report to the tester.
4141
3883
for id in not_found:
4142
trace.warning('"%s" not found in the test suite', id)
3884
bzrlib.trace.warning('"%s" not found in the test suite', id)
4143
3885
for id in duplicates:
4144
trace.warning('"%s" is used as an id by several tests', id)
3886
bzrlib.trace.warning('"%s" is used as an id by several tests', id)
4149
def multiply_scenarios(*scenarios):
4150
"""Multiply two or more iterables of scenarios.
4152
It is safe to pass scenario generators or iterators.
4154
:returns: A list of compound scenarios: the cross-product of all
4155
scenarios, with the names concatenated and the parameters
4158
return reduce(_multiply_two_scenarios, map(list, scenarios))
4161
def _multiply_two_scenarios(scenarios_left, scenarios_right):
3891
def multiply_scenarios(scenarios_left, scenarios_right):
4162
3892
"""Multiply two sets of scenarios.
4164
3894
:returns: the cartesian product of the two sets of scenarios, that is
4677
4389
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
4392
# Kept for compatibility, use bzrlib.tests.features.subunit instead
4393
SubUnitFeature = _CompatabilityThunkFeature(
4394
deprecated_in((2,1,0)),
4395
'bzrlib.tests.features', 'SubUnitFeature', 'subunit')
4680
4396
# Only define SubUnitBzrRunner if subunit is available.
4682
4398
from subunit import TestProtocolClient
4683
4399
from subunit.test_results import AutoTimingTestResultDecorator
4684
class SubUnitBzrProtocolClient(TestProtocolClient):
4686
def addSuccess(self, test, details=None):
4687
# The subunit client always includes the details in the subunit
4688
# stream, but we don't want to include it in ours.
4689
if details is not None and 'log' in details:
4691
return super(SubUnitBzrProtocolClient, self).addSuccess(
4694
4400
class SubUnitBzrRunner(TextTestRunner):
4695
4401
def run(self, test):
4696
4402
result = AutoTimingTestResultDecorator(
4697
SubUnitBzrProtocolClient(self.stream))
4403
TestProtocolClient(self.stream))
4698
4404
test.run(result)
4700
4406
except ImportError:
4703
class _PosixPermissionsFeature(Feature):
4707
# create temporary file and check if specified perms are maintained.
4710
write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
4711
f = tempfile.mkstemp(prefix='bzr_perms_chk_')
4714
os.chmod(name, write_perms)
4716
read_perms = os.stat(name).st_mode & 0777
4718
return (write_perms == read_perms)
4720
return (os.name == 'posix') and has_perms()
4722
def feature_name(self):
4723
return 'POSIX permissions support'
4725
posix_permissions_feature = _PosixPermissionsFeature()