140
134
SUBUNIT_SEEK_SET = 0
141
135
SUBUNIT_SEEK_CUR = 1
137
# These are intentionally brought into this namespace. That way plugins, etc
138
# can just "from bzrlib.tests import TestCase, TestLoader, etc"
139
TestSuite = TestUtil.TestSuite
140
TestLoader = TestUtil.TestLoader
144
142
class ExtendedTestResult(testtools.TextTestResult):
145
143
"""Accepts, reports and accumulates the results of running tests.
239
239
ok = self.wasStrictlySuccessful()
241
241
ok = self.wasSuccessful()
242
if TestCase._first_thread_leaker_id:
242
if self._first_thread_leaker_id:
243
243
self.stream.write(
244
244
'%s is leaking threads among %d leaking tests.\n' % (
245
TestCase._first_thread_leaker_id,
246
TestCase._leaking_threads_tests))
245
self._first_thread_leaker_id,
246
self._tests_leaking_threads_count))
247
247
# We don't report the main thread as an active one.
248
248
self.stream.write(
249
249
'%d non-main threads were left active in the end.\n'
250
% (TestCase._active_threads - 1))
250
% (len(self._active_threads) - 1))
252
252
def getDescription(self, test):
284
284
super(ExtendedTestResult, self).startTest(test)
285
285
if self.count == 0:
286
286
self.startTests()
287
288
self.report_test_start(test)
288
289
test.number = self.count
289
290
self._recordTestStartTime()
291
# Only check for thread leaks if the test case supports cleanups
292
addCleanup = getattr(test, "addCleanup", None)
293
if addCleanup is not None:
294
addCleanup(self._check_leaked_threads, test)
291
296
def startTests(self):
293
if getattr(sys, 'frozen', None) is None:
294
bzr_path = osutils.realpath(sys.argv[0])
296
bzr_path = sys.executable
298
'bzr selftest: %s\n' % (bzr_path,))
301
bzrlib.__path__[0],))
303
' bzr-%s python-%s %s\n' % (
304
bzrlib.version_string,
305
bzrlib._format_version_tuple(sys.version_info),
306
platform.platform(aliased=1),
308
self.stream.write('\n')
297
self.report_tests_starting()
298
self._active_threads = threading.enumerate()
300
def _check_leaked_threads(self, test):
301
"""See if any threads have leaked since last call
303
A sample of live threads is stored in the _active_threads attribute,
304
when this method runs it compares the current live threads and any not
305
in the previous sample are treated as having leaked.
307
now_active_threads = set(threading.enumerate())
308
threads_leaked = now_active_threads.difference(self._active_threads)
310
self._report_thread_leak(test, threads_leaked, now_active_threads)
311
self._tests_leaking_threads_count += 1
312
if self._first_thread_leaker_id is None:
313
self._first_thread_leaker_id = test.id()
314
self._active_threads = now_active_threads
310
316
def _recordTestStartTime(self):
311
317
"""Record that a test has started."""
312
318
self._start_time = time.time()
314
def _cleanupLogFile(self, test):
315
# We can only do this if we have one of our TestCases, not if
317
setKeepLogfile = getattr(test, 'setKeepLogfile', None)
318
if setKeepLogfile is not None:
321
320
def addError(self, test, err):
322
321
"""Tell result that test finished with an error.
403
399
raise errors.BzrError("Unknown whence %r" % whence)
405
def report_cleaning_up(self):
401
def report_tests_starting(self):
402
"""Display information before the test run begins"""
403
if getattr(sys, 'frozen', None) is None:
404
bzr_path = osutils.realpath(sys.argv[0])
406
bzr_path = sys.executable
408
'bzr selftest: %s\n' % (bzr_path,))
411
bzrlib.__path__[0],))
413
' bzr-%s python-%s %s\n' % (
414
bzrlib.version_string,
415
bzrlib._format_version_tuple(sys.version_info),
416
platform.platform(aliased=1),
418
self.stream.write('\n')
420
def report_test_start(self, test):
421
"""Display information on the test just about to be run"""
423
def _report_thread_leak(self, test, leaked_threads, active_threads):
424
"""Display information on a test that leaked one or more threads"""
425
# GZ 2010-09-09: A leak summary reported separately from the general
426
# thread debugging would be nice. Tests under subunit
427
# need something not using stream, perhaps adding a
428
# testtools details object would be fitting.
429
if 'threads' in selftest_debug_flags:
430
self.stream.write('%s is leaking, active is now %d\n' %
431
(test.id(), len(active_threads)))
408
433
def startTestRun(self):
409
434
self.startTime = time.time()
446
471
self.pb.finished()
447
472
super(TextTestResult, self).stopTestRun()
449
def startTestRun(self):
450
super(TextTestResult, self).startTestRun()
474
def report_tests_starting(self):
475
super(TextTestResult, self).report_tests_starting()
451
476
self.pb.update('[test 0/%d] Starting' % (self.num_tests))
453
def printErrors(self):
454
# clear the pb to make room for the error listing
456
super(TextTestResult, self).printErrors()
458
478
def _progress_prefix_text(self):
459
479
# the longer this text, the less space we have to show the test
515
534
def report_unsupported(self, test, feature):
516
535
"""test cannot be run because feature is missing."""
518
def report_cleaning_up(self):
519
self.pb.update('Cleaning up')
522
538
class VerboseTestResult(ExtendedTestResult):
523
539
"""Produce long output, with one line per test run plus times"""
530
546
result = a_string
531
547
return result.ljust(final_width)
533
def startTestRun(self):
534
super(VerboseTestResult, self).startTestRun()
549
def report_tests_starting(self):
535
550
self.stream.write('running %d tests...\n' % self.num_tests)
551
super(VerboseTestResult, self).report_tests_starting()
537
553
def report_test_start(self, test):
539
554
name = self._shortened_test_description(test)
540
555
width = osutils.terminal_width()
541
556
if width is not None:
791
806
routine, and to build and check bzr trees.
793
808
In addition to the usual method of overriding tearDown(), this class also
794
allows subclasses to register functions into the _cleanups list, which is
809
allows subclasses to register cleanup functions via addCleanup, which are
795
810
run in order as the object is torn down. It's less likely this will be
796
811
accidentally overlooked.
799
_active_threads = None
800
_leaking_threads_tests = 0
801
_first_thread_leaker_id = None
802
_log_file_name = None
803
815
# record lsprof data when performing benchmark calls.
804
816
_gather_lsprof_in_benchmarks = False
806
818
def __init__(self, methodName='testMethod'):
807
819
super(TestCase, self).__init__(methodName)
809
820
self._directory_isolation = True
810
821
self.exception_handlers.insert(0,
811
822
(UnavailableFeature, self._do_unsupported_or_skip))
853
862
if name in details:
854
863
del details[name]
856
def _check_leaked_threads(self):
857
active = threading.activeCount()
858
leaked_threads = active - TestCase._active_threads
859
TestCase._active_threads = active
860
# If some tests make the number of threads *decrease*, we'll consider
861
# that they are just observing old threads dieing, not agressively kill
862
# random threads. So we don't report these tests as leaking. The risk
863
# is that we have false positives that way (the test see 2 threads
864
# going away but leak one) but it seems less likely than the actual
865
# false positives (the test see threads going away and does not leak).
866
if leaked_threads > 0:
867
TestCase._leaking_threads_tests += 1
868
if TestCase._first_thread_leaker_id is None:
869
TestCase._first_thread_leaker_id = self.id()
871
865
def _clear_debug_flags(self):
872
866
"""Prevent externally set debug flags affecting tests.
1474
1468
The file is removed as the test is torn down.
1476
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
1477
self._log_file = os.fdopen(fileno, 'w+')
1470
self._log_file = StringIO()
1478
1471
self._log_memento = bzrlib.trace.push_log_file(self._log_file)
1479
self._log_file_name = name
1480
1472
self.addCleanup(self._finishLogFile)
1482
1474
def _finishLogFile(self):
1505
1497
debug.debug_flags.discard('strict_locks')
1507
def addCleanup(self, callable, *args, **kwargs):
1508
"""Arrange to run a callable when this case is torn down.
1510
Callables are run in the reverse of the order they are registered,
1511
ie last-in first-out.
1513
self._cleanups.append((callable, args, kwargs))
1515
1499
def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1516
1500
"""Overrides an object attribute restoring it after the test.
1712
1696
unicodestr = self._log_contents.decode('utf8', 'replace')
1713
1697
self._log_contents = unicodestr.encode('utf8')
1714
1698
return self._log_contents
1716
if bzrlib.trace._trace_file:
1717
# flush the log file, to get all content
1718
bzrlib.trace._trace_file.flush()
1719
if self._log_file_name is not None:
1720
logfile = open(self._log_file_name)
1722
log_contents = logfile.read()
1699
if self._log_file is not None:
1700
log_contents = self._log_file.getvalue()
1726
1702
log_contents.decode('utf8')
1727
1703
except UnicodeDecodeError:
1728
1704
unicodestr = log_contents.decode('utf8', 'replace')
1729
1705
log_contents = unicodestr.encode('utf8')
1730
1706
if not keep_log_file:
1732
max_close_attempts = 100
1733
first_close_error = None
1734
while close_attempts < max_close_attempts:
1737
self._log_file.close()
1738
except IOError, ioe:
1739
if ioe.errno is None:
1740
# No errno implies 'close() called during
1741
# concurrent operation on the same file object', so
1742
# retry. Probably a thread is trying to write to
1744
if first_close_error is None:
1745
first_close_error = ioe
1750
if close_attempts > 1:
1752
'Unable to close log file on first attempt, '
1753
'will retry: %s\n' % (first_close_error,))
1754
if close_attempts == max_close_attempts:
1756
'Unable to close log file after %d attempts.\n'
1757
% (max_close_attempts,))
1758
1707
self._log_file = None
1759
1708
# Permit multiple calls to get_log until we clean it up in
1760
1709
# finishLogFile
1761
1710
self._log_contents = log_contents
1763
os.remove(self._log_file_name)
1765
if sys.platform == 'win32' and e.errno == errno.EACCES:
1766
sys.stderr.write(('Unable to delete log file '
1767
' %r\n' % self._log_file_name))
1770
self._log_file_name = None
1771
1711
return log_contents
1773
return "No log file content and no log file name."
1713
return "No log file content."
1775
1715
def get_log(self):
1776
1716
"""Get a unicode string containing the log from bzrlib.trace.
1991
1931
variables. A value of None will unset the env variable.
1992
1932
The values must be strings. The change will only occur in the
1993
1933
child, so you don't need to fix the environment after running.
1994
:param skip_if_plan_to_signal: raise TestSkipped when true and os.kill
1934
:param skip_if_plan_to_signal: raise TestSkipped when true and system
1935
doesn't support signalling subprocesses.
1996
1936
:param allow_plugins: If False (default) pass --no-plugins to bzr.
1998
1938
:returns: Popen object for the started process.
2000
1940
if skip_if_plan_to_signal:
2001
if not getattr(os, 'kill', None):
2002
raise TestSkipped("os.kill not available.")
1941
if os.name != "posix":
1942
raise TestSkipped("Sending signals not supported")
2004
1944
if env_changes is None:
2005
1945
env_changes = {}
2484
2424
def setUp(self):
2485
2425
super(TestCaseWithMemoryTransport, self).setUp()
2426
# Ensure that ConnectedTransport doesn't leak sockets
2427
def get_transport_with_cleanup(*args, **kwargs):
2428
t = orig_get_transport(*args, **kwargs)
2429
if isinstance(t, _mod_transport.ConnectedTransport):
2430
self.addCleanup(t.disconnect)
2433
orig_get_transport = self.overrideAttr(_mod_transport, 'get_transport',
2434
get_transport_with_cleanup)
2486
2435
self._make_test_root()
2487
2436
self.addCleanup(os.chdir, os.getcwdu())
2488
2437
self.makeAndChdirToTestDir()
2782
2731
def setUp(self):
2732
from bzrlib.tests import http_server
2783
2733
super(ChrootedTestCase, self).setUp()
2784
2734
if not self.vfs_transport_factory == memory.MemoryServer:
2785
self.transport_readonly_server = HttpServer
2735
self.transport_readonly_server = http_server.HttpServer
2788
2738
def condition_id_re(pattern):
3051
3001
def fork_decorator(suite):
3002
if getattr(os, "fork", None) is None:
3003
raise errors.BzrCommandError("platform does not support fork,"
3004
" try --parallel=subprocess instead.")
3052
3005
concurrency = osutils.local_concurrency()
3053
3006
if concurrency == 1:
3293
3246
test_blocks = partition_tests(suite, concurrency)
3294
3247
for process_tests in test_blocks:
3295
process_suite = TestSuite()
3248
process_suite = TestUtil.TestSuite()
3296
3249
process_suite.addTests(process_tests)
3297
3250
c2pread, c2pwrite = os.pipe()
3298
3251
pid = os.fork()
3453
3406
# rather than failing tests. And no longer raise
3454
3407
# LockContention when fctnl locks are not being used
3455
3408
# with proper exclusion rules.
3409
# -Ethreads Will display thread ident at creation/join time to
3410
# help track thread leaks
3456
3411
selftest_debug_flags = set()
3694
3649
'bzrlib.tests.doc_generate',
3695
3650
'bzrlib.tests.per_branch',
3696
3651
'bzrlib.tests.per_bzrdir',
3697
'bzrlib.tests.per_bzrdir_colo',
3652
'bzrlib.tests.per_controldir',
3653
'bzrlib.tests.per_controldir_colo',
3698
3654
'bzrlib.tests.per_foreign_vcs',
3699
3655
'bzrlib.tests.per_interrepository',
3700
3656
'bzrlib.tests.per_intertree',
3713
3669
'bzrlib.tests.per_workingtree',
3714
3670
'bzrlib.tests.test__annotator',
3715
3671
'bzrlib.tests.test__bencode',
3672
'bzrlib.tests.test__btree_serializer',
3716
3673
'bzrlib.tests.test__chk_map',
3717
3674
'bzrlib.tests.test__dirstate_helpers',
3718
3675
'bzrlib.tests.test__groupcompress',
3851
3808
'bzrlib.tests.test_switch',
3852
3809
'bzrlib.tests.test_symbol_versioning',
3853
3810
'bzrlib.tests.test_tag',
3811
'bzrlib.tests.test_test_server',
3854
3812
'bzrlib.tests.test_testament',
3855
3813
'bzrlib.tests.test_textfile',
3856
3814
'bzrlib.tests.test_textmerge',
4047
4005
... bzrlib.tests.test_sampler.DemoTest('test_nothing'),
4048
4006
... [('one', dict(param=1)),
4049
4007
... ('two', dict(param=2))],
4008
... TestUtil.TestSuite())
4051
4009
>>> tests = list(iter_suite_tests(r))
4103
4061
new_test = copy.copy(test)
4104
4062
new_test.id = lambda: new_id
4063
# XXX: Workaround <https://bugs.launchpad.net/testtools/+bug/637725>, which
4064
# causes cloned tests to share the 'details' dict. This makes it hard to
4065
# read the test output for parameterized tests, because tracebacks will be
4066
# associated with irrelevant tests.
4068
details = new_test._TestCase__details
4069
except AttributeError:
4070
# must be a different version of testtools than expected. Do nothing.
4073
# Reset the '__details' dict.
4074
new_test._TestCase__details = {}
4105
4075
return new_test