56
56
# nb: check this before importing anything else from within it
57
57
_testtools_version = getattr(testtools, '__version__', ())
58
if _testtools_version < (0, 9, 2):
59
raise ImportError("need at least testtools 0.9.2: %s is %r"
58
if _testtools_version < (0, 9, 5):
59
raise ImportError("need at least testtools 0.9.5: %s is %r"
60
60
% (testtools.__file__, _testtools_version))
61
61
from testtools import content
234
239
ok = self.wasStrictlySuccessful()
236
241
ok = self.wasSuccessful()
237
if TestCase._first_thread_leaker_id:
242
if self._first_thread_leaker_id:
238
243
self.stream.write(
239
244
'%s is leaking threads among %d leaking tests.\n' % (
240
TestCase._first_thread_leaker_id,
241
TestCase._leaking_threads_tests))
245
self._first_thread_leaker_id,
246
self._tests_leaking_threads_count))
242
247
# We don't report the main thread as an active one.
243
248
self.stream.write(
244
249
'%d non-main threads were left active in the end.\n'
245
% (TestCase._active_threads - 1))
250
% (len(self._active_threads) - 1))
247
252
def getDescription(self, test):
275
281
what = re.sub(r'^bzrlib\.tests\.', '', what)
284
# GZ 2010-10-04: Cloned tests may end up harmlessly calling this method
285
# multiple times in a row, because the handler is added for
286
# each test but the container list is shared between cases.
287
# See lp:498869 lp:625574 and lp:637725 for background.
288
def _record_traceback_from_test(self, exc_info):
289
"""Store the traceback from passed exc_info tuple till"""
290
self._traceback_from_test = exc_info[2]
278
292
def startTest(self, test):
279
293
super(ExtendedTestResult, self).startTest(test)
280
294
if self.count == 0:
281
295
self.startTests()
282
297
self.report_test_start(test)
283
298
test.number = self.count
284
299
self._recordTestStartTime()
300
# Make testtools cases give us the real traceback on failure
301
addOnException = getattr(test, "addOnException", None)
302
if addOnException is not None:
303
addOnException(self._record_traceback_from_test)
304
# Only check for thread leaks if the test case supports cleanups
305
addCleanup = getattr(test, "addCleanup", None)
306
if addCleanup is not None:
307
addCleanup(self._check_leaked_threads, test)
286
309
def startTests(self):
288
if getattr(sys, 'frozen', None) is None:
289
bzr_path = osutils.realpath(sys.argv[0])
291
bzr_path = sys.executable
293
'bzr selftest: %s\n' % (bzr_path,))
296
bzrlib.__path__[0],))
298
' bzr-%s python-%s %s\n' % (
299
bzrlib.version_string,
300
bzrlib._format_version_tuple(sys.version_info),
301
platform.platform(aliased=1),
303
self.stream.write('\n')
310
self.report_tests_starting()
311
self._active_threads = threading.enumerate()
313
def stopTest(self, test):
314
self._traceback_from_test = None
316
def _check_leaked_threads(self, test):
317
"""See if any threads have leaked since last call
319
A sample of live threads is stored in the _active_threads attribute,
320
when this method runs it compares the current live threads and any not
321
in the previous sample are treated as having leaked.
323
now_active_threads = set(threading.enumerate())
324
threads_leaked = now_active_threads.difference(self._active_threads)
326
self._report_thread_leak(test, threads_leaked, now_active_threads)
327
self._tests_leaking_threads_count += 1
328
if self._first_thread_leaker_id is None:
329
self._first_thread_leaker_id = test.id()
330
self._active_threads = now_active_threads
305
332
def _recordTestStartTime(self):
306
333
"""Record that a test has started."""
307
self._start_time = time.time()
309
def _cleanupLogFile(self, test):
310
# We can only do this if we have one of our TestCases, not if
312
setKeepLogfile = getattr(test, 'setKeepLogfile', None)
313
if setKeepLogfile is not None:
334
self._start_datetime = self._now()
316
336
def addError(self, test, err):
317
337
"""Tell result that test finished with an error.
319
339
Called from the TestCase run() method when the test
320
340
fails with an unexpected error.
342
self._post_mortem(self._traceback_from_test)
323
343
super(ExtendedTestResult, self).addError(test, err)
324
344
self.error_count += 1
325
345
self.report_error(test, err)
326
346
if self.stop_early:
328
self._cleanupLogFile(test)
330
349
def addFailure(self, test, err):
331
350
"""Tell result that test failed.
333
352
Called from the TestCase run() method when the test
334
353
fails because e.g. an assert() method failed.
355
self._post_mortem(self._traceback_from_test)
337
356
super(ExtendedTestResult, self).addFailure(test, err)
338
357
self.failure_count += 1
339
358
self.report_failure(test, err)
340
359
if self.stop_early:
342
self._cleanupLogFile(test)
344
362
def addSuccess(self, test, details=None):
345
363
"""Tell result that test completed successfully.
383
400
self.not_applicable_count += 1
384
401
self.report_not_applicable(test, reason)
386
def _post_mortem(self):
403
def _post_mortem(self, tb=None):
387
404
"""Start a PDB post mortem session."""
388
405
if os.environ.get('BZR_TEST_PDB', None):
389
import pdb;pdb.post_mortem()
391
409
def progress(self, offset, whence):
392
410
"""The test is adjusting the count of tests to run."""
398
416
raise errors.BzrError("Unknown whence %r" % whence)
400
def report_cleaning_up(self):
418
def report_tests_starting(self):
419
"""Display information before the test run begins"""
420
if getattr(sys, 'frozen', None) is None:
421
bzr_path = osutils.realpath(sys.argv[0])
423
bzr_path = sys.executable
425
'bzr selftest: %s\n' % (bzr_path,))
428
bzrlib.__path__[0],))
430
' bzr-%s python-%s %s\n' % (
431
bzrlib.version_string,
432
bzrlib._format_version_tuple(sys.version_info),
433
platform.platform(aliased=1),
435
self.stream.write('\n')
437
def report_test_start(self, test):
438
"""Display information on the test just about to be run"""
440
def _report_thread_leak(self, test, leaked_threads, active_threads):
441
"""Display information on a test that leaked one or more threads"""
442
# GZ 2010-09-09: A leak summary reported separately from the general
443
# thread debugging would be nice. Tests under subunit
444
# need something not using stream, perhaps adding a
445
# testtools details object would be fitting.
446
if 'threads' in selftest_debug_flags:
447
self.stream.write('%s is leaking, active is now %d\n' %
448
(test.id(), len(active_threads)))
403
450
def startTestRun(self):
404
451
self.startTime = time.time()
441
488
self.pb.finished()
442
489
super(TextTestResult, self).stopTestRun()
444
def startTestRun(self):
445
super(TextTestResult, self).startTestRun()
491
def report_tests_starting(self):
492
super(TextTestResult, self).report_tests_starting()
446
493
self.pb.update('[test 0/%d] Starting' % (self.num_tests))
448
def printErrors(self):
449
# clear the pb to make room for the error listing
451
super(TextTestResult, self).printErrors()
453
495
def _progress_prefix_text(self):
454
496
# the longer this text, the less space we have to show the test
786
826
routine, and to build and check bzr trees.
788
828
In addition to the usual method of overriding tearDown(), this class also
789
allows subclasses to register functions into the _cleanups list, which is
829
allows subclasses to register cleanup functions via addCleanup, which are
790
830
run in order as the object is torn down. It's less likely this will be
791
831
accidentally overlooked.
794
_active_threads = None
795
_leaking_threads_tests = 0
796
_first_thread_leaker_id = None
797
_log_file_name = None
798
835
# record lsprof data when performing benchmark calls.
799
836
_gather_lsprof_in_benchmarks = False
801
838
def __init__(self, methodName='testMethod'):
802
839
super(TestCase, self).__init__(methodName)
804
840
self._directory_isolation = True
805
841
self.exception_handlers.insert(0,
806
842
(UnavailableFeature, self._do_unsupported_or_skip))
824
860
self._track_transports()
825
861
self._track_locks()
826
862
self._clear_debug_flags()
827
TestCase._active_threads = threading.activeCount()
828
self.addCleanup(self._check_leaked_threads)
863
# Isolate global verbosity level, to make sure it's reproducible
864
# between tests. We should get rid of this altogether: bug 656694. --
866
self.overrideAttr(bzrlib.trace, '_verbosity_level', 0)
831
869
# debug a frame up.
833
871
pdb.Pdb().set_trace(sys._getframe().f_back)
835
def _check_leaked_threads(self):
836
active = threading.activeCount()
837
leaked_threads = active - TestCase._active_threads
838
TestCase._active_threads = active
839
# If some tests make the number of threads *decrease*, we'll consider
840
# that they are just observing old threads dieing, not agressively kill
841
# random threads. So we don't report these tests as leaking. The risk
842
# is that we have false positives that way (the test see 2 threads
843
# going away but leak one) but it seems less likely than the actual
844
# false positives (the test see threads going away and does not leak).
845
if leaked_threads > 0:
846
if 'threads' in selftest_debug_flags:
847
print '%s is leaking, active is now %d' % (self.id(), active)
848
TestCase._leaking_threads_tests += 1
849
if TestCase._first_thread_leaker_id is None:
850
TestCase._first_thread_leaker_id = self.id()
873
def discardDetail(self, name):
874
"""Extend the addDetail, getDetails api so we can remove a detail.
876
eg. bzr always adds the 'log' detail at startup, but we don't want to
877
include it for skipped, xfail, etc tests.
879
It is safe to call this for a detail that doesn't exist, in case this
880
gets called multiple times.
882
# We cheat. details is stored in __details which means we shouldn't
883
# touch it. but getDetails() returns the dict directly, so we can
885
details = self.getDetails()
852
889
def _clear_debug_flags(self):
853
890
"""Prevent externally set debug flags affecting tests.
865
902
def _clear_hooks(self):
866
903
# prevent hooks affecting tests
904
known_hooks = hooks.known_hooks
867
905
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)
906
for key, (parent, name) in known_hooks.iter_parent_objects():
907
current_hooks = getattr(parent, name)
871
908
self._preserved_hooks[parent] = (name, current_hooks)
872
909
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)
910
for key, (parent, name) in known_hooks.iter_parent_objects():
911
factory = known_hooks.get(key)
875
912
setattr(parent, name, factory())
876
913
# this hook should always be installed
877
914
request._install_hook()
1486
1521
debug.debug_flags.discard('strict_locks')
1488
def addCleanup(self, callable, *args, **kwargs):
1489
"""Arrange to run a callable when this case is torn down.
1491
Callables are run in the reverse of the order they are registered,
1492
ie last-in first-out.
1494
self._cleanups.append((callable, args, kwargs))
1496
1523
def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1497
1524
"""Overrides an object attribute restoring it after the test.
1611
1645
self._do_skip(result, reason)
1648
def _report_skip(self, result, err):
1649
"""Override the default _report_skip.
1651
We want to strip the 'log' detail. If we waint until _do_skip, it has
1652
already been formatted into the 'reason' string, and we can't pull it
1655
self._suppress_log()
1656
super(TestCase, self)._report_skip(self, result, err)
1659
def _report_expected_failure(self, result, err):
1662
See _report_skip for motivation.
1664
self._suppress_log()
1665
super(TestCase, self)._report_expected_failure(self, result, err)
1614
1668
def _do_unsupported_or_skip(self, result, e):
1615
1669
reason = e.args[0]
1670
self._suppress_log()
1616
1671
addNotSupported = getattr(result, 'addNotSupported', None)
1617
1672
if addNotSupported is not None:
1618
1673
result.addNotSupported(self, reason)
1665
1720
unicodestr = self._log_contents.decode('utf8', 'replace')
1666
1721
self._log_contents = unicodestr.encode('utf8')
1667
1722
return self._log_contents
1669
if bzrlib.trace._trace_file:
1670
# flush the log file, to get all content
1671
bzrlib.trace._trace_file.flush()
1672
if self._log_file_name is not None:
1673
logfile = open(self._log_file_name)
1675
log_contents = logfile.read()
1723
if self._log_file is not None:
1724
log_contents = self._log_file.getvalue()
1679
1726
log_contents.decode('utf8')
1680
1727
except UnicodeDecodeError:
1681
1728
unicodestr = log_contents.decode('utf8', 'replace')
1682
1729
log_contents = unicodestr.encode('utf8')
1683
1730
if not keep_log_file:
1685
max_close_attempts = 100
1686
first_close_error = None
1687
while close_attempts < max_close_attempts:
1690
self._log_file.close()
1691
except IOError, ioe:
1692
if ioe.errno is None:
1693
# No errno implies 'close() called during
1694
# concurrent operation on the same file object', so
1695
# retry. Probably a thread is trying to write to
1697
if first_close_error is None:
1698
first_close_error = ioe
1703
if close_attempts > 1:
1705
'Unable to close log file on first attempt, '
1706
'will retry: %s\n' % (first_close_error,))
1707
if close_attempts == max_close_attempts:
1709
'Unable to close log file after %d attempts.\n'
1710
% (max_close_attempts,))
1711
1731
self._log_file = None
1712
1732
# Permit multiple calls to get_log until we clean it up in
1713
1733
# finishLogFile
1714
1734
self._log_contents = log_contents
1716
os.remove(self._log_file_name)
1718
if sys.platform == 'win32' and e.errno == errno.EACCES:
1719
sys.stderr.write(('Unable to delete log file '
1720
' %r\n' % self._log_file_name))
1723
self._log_file_name = None
1724
1735
return log_contents
1726
return "No log file content and no log file name."
1737
return "No log file content."
1728
1739
def get_log(self):
1729
1740
"""Get a unicode string containing the log from bzrlib.trace.
1944
1955
variables. A value of None will unset the env variable.
1945
1956
The values must be strings. The change will only occur in the
1946
1957
child, so you don't need to fix the environment after running.
1947
:param skip_if_plan_to_signal: raise TestSkipped when true and os.kill
1958
:param skip_if_plan_to_signal: raise TestSkipped when true and system
1959
doesn't support signalling subprocesses.
1949
1960
:param allow_plugins: If False (default) pass --no-plugins to bzr.
1951
1962
:returns: Popen object for the started process.
1953
1964
if skip_if_plan_to_signal:
1954
if not getattr(os, 'kill', None):
1955
raise TestSkipped("os.kill not available.")
1965
if os.name != "posix":
1966
raise TestSkipped("Sending signals not supported")
1957
1968
if env_changes is None:
1958
1969
env_changes = {}
3344
class ForwardingResult(unittest.TestResult):
3346
def __init__(self, target):
3347
unittest.TestResult.__init__(self)
3348
self.result = target
3350
def startTest(self, test):
3351
self.result.startTest(test)
3353
def stopTest(self, test):
3354
self.result.stopTest(test)
3356
def startTestRun(self):
3357
self.result.startTestRun()
3359
def stopTestRun(self):
3360
self.result.stopTestRun()
3362
def addSkip(self, test, reason):
3363
self.result.addSkip(test, reason)
3365
def addSuccess(self, test):
3366
self.result.addSuccess(test)
3368
def addError(self, test, err):
3369
self.result.addError(test, err)
3371
def addFailure(self, test, err):
3372
self.result.addFailure(test, err)
3373
ForwardingResult = testtools.ExtendedToOriginalDecorator
3376
class ProfileResult(ForwardingResult):
3358
class ProfileResult(testtools.ExtendedToOriginalDecorator):
3377
3359
"""Generate profiling data for all activity between start and success.
3379
3361
The profile data is appended to the test's _benchcalls attribute and can
3980
def multiply_scenarios(scenarios_left, scenarios_right):
3973
def multiply_scenarios(*scenarios):
3974
"""Multiply two or more iterables of scenarios.
3976
It is safe to pass scenario generators or iterators.
3978
:returns: A list of compound scenarios: the cross-product of all
3979
scenarios, with the names concatenated and the parameters
3982
return reduce(_multiply_two_scenarios, map(list, scenarios))
3985
def _multiply_two_scenarios(scenarios_left, scenarios_right):
3981
3986
"""Multiply two sets of scenarios.
3983
3988
:returns: the cartesian product of the two sets of scenarios, that is
4070
4075
new_test = copy.copy(test)
4071
4076
new_test.id = lambda: new_id
4077
# XXX: Workaround <https://bugs.launchpad.net/testtools/+bug/637725>, which
4078
# causes cloned tests to share the 'details' dict. This makes it hard to
4079
# read the test output for parameterized tests, because tracebacks will be
4080
# associated with irrelevant tests.
4082
details = new_test._TestCase__details
4083
except AttributeError:
4084
# must be a different version of testtools than expected. Do nothing.
4087
# Reset the '__details' dict.
4088
new_test._TestCase__details = {}
4072
4089
return new_test
4254
4271
symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning)
4255
4272
# Import the new feature and use it as a replacement for the
4256
4273
# deprecated one.
4257
mod = __import__(self._replacement_module, {}, {},
4258
[self._replacement_name])
4259
self._feature = getattr(mod, self._replacement_name)
4274
self._feature = pyutils.get_named_object(
4275
self._replacement_module, self._replacement_name)
4261
4277
def _probe(self):
4501
4517
from subunit import TestProtocolClient
4502
4518
from subunit.test_results import AutoTimingTestResultDecorator
4519
class SubUnitBzrProtocolClient(TestProtocolClient):
4521
def addSuccess(self, test, details=None):
4522
# The subunit client always includes the details in the subunit
4523
# stream, but we don't want to include it in ours.
4524
if details is not None and 'log' in details:
4526
return super(SubUnitBzrProtocolClient, self).addSuccess(
4503
4529
class SubUnitBzrRunner(TextTestRunner):
4504
4530
def run(self, test):
4505
4531
result = AutoTimingTestResultDecorator(
4506
TestProtocolClient(self.stream))
4532
SubUnitBzrProtocolClient(self.stream))
4507
4533
test.run(result)
4509
4535
except ImportError: