104
105
import bzrlib.trace
105
106
from bzrlib.transport import (
110
import bzrlib.transport
111
110
from bzrlib.trace import mutter, note
112
111
from bzrlib.tests import (
116
from bzrlib.tests.http_server import HttpServer
117
from bzrlib.tests.TestUtil import (
121
from bzrlib.tests.treeshape import build_tree_contents
122
116
from bzrlib.ui import NullProgressView
123
117
from bzrlib.ui.text import TextUIFactory
124
118
import bzrlib.version_info_formats.format_custom
125
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
127
120
# Mark this python module as being part of the implementation
128
121
# of unittest: this gives us better tracebacks where the last
196
193
self._overall_start_time = time.time()
197
194
self._strict = strict
195
self._first_thread_leaker_id = None
196
self._tests_leaking_threads_count = 0
197
self._traceback_from_test = None
199
199
def stopTestRun(self):
200
200
run = self.testsRun
201
201
actionTaken = "Ran"
202
202
stopTime = time.time()
203
203
timeTaken = stopTime - self.startTime
205
self.stream.writeln(self.separator2)
206
self.stream.writeln("%s %d test%s in %.3fs" % (actionTaken,
204
# GZ 2010-07-19: Seems testtools has no printErrors method, and though
205
# the parent class method is similar have to duplicate
206
self._show_list('ERROR', self.errors)
207
self._show_list('FAIL', self.failures)
208
self.stream.write(self.sep2)
209
self.stream.write("%s %d test%s in %.3fs\n\n" % (actionTaken,
207
210
run, run != 1 and "s" or "", timeTaken))
208
self.stream.writeln()
209
211
if not self.wasSuccessful():
210
212
self.stream.write("FAILED (")
211
213
failed, errored = map(len, (self.failures, self.errors))
218
220
if failed or errored: self.stream.write(", ")
219
221
self.stream.write("known_failure_count=%d" %
220
222
self.known_failure_count)
221
self.stream.writeln(")")
223
self.stream.write(")\n")
223
225
if self.known_failure_count:
224
self.stream.writeln("OK (known_failures=%d)" %
226
self.stream.write("OK (known_failures=%d)\n" %
225
227
self.known_failure_count)
227
self.stream.writeln("OK")
229
self.stream.write("OK\n")
228
230
if self.skip_count > 0:
229
231
skipped = self.skip_count
230
self.stream.writeln('%d test%s skipped' %
232
self.stream.write('%d test%s skipped\n' %
231
233
(skipped, skipped != 1 and "s" or ""))
232
234
if self.unsupported:
233
235
for feature, count in sorted(self.unsupported.items()):
234
self.stream.writeln("Missing feature '%s' skipped %d tests." %
236
self.stream.write("Missing feature '%s' skipped %d tests.\n" %
235
237
(feature, count))
237
239
ok = self.wasStrictlySuccessful()
239
241
ok = self.wasSuccessful()
240
if TestCase._first_thread_leaker_id:
242
if self._first_thread_leaker_id:
241
243
self.stream.write(
242
244
'%s is leaking threads among %d leaking tests.\n' % (
243
TestCase._first_thread_leaker_id,
244
TestCase._leaking_threads_tests))
245
self._first_thread_leaker_id,
246
self._tests_leaking_threads_count))
245
247
# We don't report the main thread as an active one.
246
248
self.stream.write(
247
249
'%d non-main threads were left active in the end.\n'
248
% (TestCase._active_threads - 1))
250
% (len(self._active_threads) - 1))
250
252
def getDescription(self, test):
276
279
def _shortened_test_description(self, test):
278
what = re.sub(r'^bzrlib\.(tests|benchmarks)\.', '', what)
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]
281
292
def startTest(self, test):
282
unittest.TestResult.startTest(self, test)
293
super(ExtendedTestResult, self).startTest(test)
283
294
if self.count == 0:
284
295
self.startTests()
285
297
self.report_test_start(test)
286
298
test.number = self.count
287
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)
289
309
def startTests(self):
291
if getattr(sys, 'frozen', None) is None:
292
bzr_path = osutils.realpath(sys.argv[0])
294
bzr_path = sys.executable
296
'bzr selftest: %s\n' % (bzr_path,))
299
bzrlib.__path__[0],))
301
' bzr-%s python-%s %s\n' % (
302
bzrlib.version_string,
303
bzrlib._format_version_tuple(sys.version_info),
304
platform.platform(aliased=1),
306
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
308
332
def _recordTestStartTime(self):
309
333
"""Record that a test has started."""
310
self._start_time = time.time()
312
def _cleanupLogFile(self, test):
313
# We can only do this if we have one of our TestCases, not if
315
setKeepLogfile = getattr(test, 'setKeepLogfile', None)
316
if setKeepLogfile is not None:
334
self._start_datetime = self._now()
319
336
def addError(self, test, err):
320
337
"""Tell result that test finished with an error.
322
339
Called from the TestCase run() method when the test
323
340
fails with an unexpected error.
326
unittest.TestResult.addError(self, test, err)
342
self._post_mortem(self._traceback_from_test)
343
super(ExtendedTestResult, self).addError(test, err)
327
344
self.error_count += 1
328
345
self.report_error(test, err)
329
346
if self.stop_early:
331
self._cleanupLogFile(test)
333
349
def addFailure(self, test, err):
334
350
"""Tell result that test failed.
336
352
Called from the TestCase run() method when the test
337
353
fails because e.g. an assert() method failed.
340
unittest.TestResult.addFailure(self, test, err)
355
self._post_mortem(self._traceback_from_test)
356
super(ExtendedTestResult, self).addFailure(test, err)
341
357
self.failure_count += 1
342
358
self.report_failure(test, err)
343
359
if self.stop_early:
345
self._cleanupLogFile(test)
347
362
def addSuccess(self, test, details=None):
348
363
"""Tell result that test completed successfully.
401
416
raise errors.BzrError("Unknown whence %r" % whence)
403
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)))
406
450
def startTestRun(self):
407
451
self.startTime = time.time()
444
488
self.pb.finished()
445
489
super(TextTestResult, self).stopTestRun()
447
def startTestRun(self):
448
super(TextTestResult, self).startTestRun()
491
def report_tests_starting(self):
492
super(TextTestResult, self).report_tests_starting()
449
493
self.pb.update('[test 0/%d] Starting' % (self.num_tests))
451
def printErrors(self):
452
# clear the pb to make room for the error listing
454
super(TextTestResult, self).printErrors()
456
495
def _progress_prefix_text(self):
457
496
# the longer this text, the less space we have to show the test
551
585
return '%s%s' % (indent, err[1])
553
587
def report_error(self, test, err):
554
self.stream.writeln('ERROR %s\n%s'
588
self.stream.write('ERROR %s\n%s\n'
555
589
% (self._testTimeString(test),
556
590
self._error_summary(err)))
558
592
def report_failure(self, test, err):
559
self.stream.writeln(' FAIL %s\n%s'
593
self.stream.write(' FAIL %s\n%s\n'
560
594
% (self._testTimeString(test),
561
595
self._error_summary(err)))
563
597
def report_known_failure(self, test, err):
564
self.stream.writeln('XFAIL %s\n%s'
598
self.stream.write('XFAIL %s\n%s\n'
565
599
% (self._testTimeString(test),
566
600
self._error_summary(err)))
568
602
def report_success(self, test):
569
self.stream.writeln(' OK %s' % self._testTimeString(test))
603
self.stream.write(' OK %s\n' % self._testTimeString(test))
570
604
for bench_called, stats in getattr(test, '_benchcalls', []):
571
self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
605
self.stream.write('LSProf output for %s(%s, %s)\n' % bench_called)
572
606
stats.pprint(file=self.stream)
573
607
# flush the stream so that we get smooth output. This verbose mode is
574
608
# used to show the output in PQM.
575
609
self.stream.flush()
577
611
def report_skip(self, test, reason):
578
self.stream.writeln(' SKIP %s\n%s'
612
self.stream.write(' SKIP %s\n%s\n'
579
613
% (self._testTimeString(test), reason))
581
615
def report_not_applicable(self, test, reason):
582
self.stream.writeln(' N/A %s\n %s'
616
self.stream.write(' N/A %s\n %s\n'
583
617
% (self._testTimeString(test), reason))
585
619
def report_unsupported(self, test, feature):
586
620
"""test cannot be run because feature is missing."""
587
self.stream.writeln("NODEP %s\n The feature '%s' is not available."
621
self.stream.write("NODEP %s\n The feature '%s' is not available.\n"
588
622
%(self._testTimeString(test), feature))
789
823
routine, and to build and check bzr trees.
791
825
In addition to the usual method of overriding tearDown(), this class also
792
allows subclasses to register functions into the _cleanups list, which is
826
allows subclasses to register cleanup functions via addCleanup, which are
793
827
run in order as the object is torn down. It's less likely this will be
794
828
accidentally overlooked.
797
_active_threads = None
798
_leaking_threads_tests = 0
799
_first_thread_leaker_id = None
800
_log_file_name = None
801
832
# record lsprof data when performing benchmark calls.
802
833
_gather_lsprof_in_benchmarks = False
804
835
def __init__(self, methodName='testMethod'):
805
836
super(TestCase, self).__init__(methodName)
807
837
self._directory_isolation = True
808
838
self.exception_handlers.insert(0,
809
839
(UnavailableFeature, self._do_unsupported_or_skip))
827
857
self._track_transports()
828
858
self._track_locks()
829
859
self._clear_debug_flags()
830
TestCase._active_threads = threading.activeCount()
831
self.addCleanup(self._check_leaked_threads)
860
# Isolate global verbosity level, to make sure it's reproducible
861
# between tests. We should get rid of this altogether: bug 656694. --
863
self.overrideAttr(bzrlib.trace, '_verbosity_level', 0)
834
866
# debug a frame up.
836
868
pdb.Pdb().set_trace(sys._getframe().f_back)
838
def _check_leaked_threads(self):
839
active = threading.activeCount()
840
leaked_threads = active - TestCase._active_threads
841
TestCase._active_threads = active
842
# If some tests make the number of threads *decrease*, we'll consider
843
# that they are just observing old threads dieing, not agressively kill
844
# random threads. So we don't report these tests as leaking. The risk
845
# is that we have false positives that way (the test see 2 threads
846
# going away but leak one) but it seems less likely than the actual
847
# false positives (the test see threads going away and does not leak).
848
if leaked_threads > 0:
849
TestCase._leaking_threads_tests += 1
850
if TestCase._first_thread_leaker_id is None:
851
TestCase._first_thread_leaker_id = self.id()
870
def discardDetail(self, name):
871
"""Extend the addDetail, getDetails api so we can remove a detail.
873
eg. bzr always adds the 'log' detail at startup, but we don't want to
874
include it for skipped, xfail, etc tests.
876
It is safe to call this for a detail that doesn't exist, in case this
877
gets called multiple times.
879
# We cheat. details is stored in __details which means we shouldn't
880
# touch it. but getDetails() returns the dict directly, so we can
882
details = self.getDetails()
853
886
def _clear_debug_flags(self):
854
887
"""Prevent externally set debug flags affecting tests.
866
899
def _clear_hooks(self):
867
900
# prevent hooks affecting tests
901
known_hooks = hooks.known_hooks
868
902
self._preserved_hooks = {}
869
for key, factory in hooks.known_hooks.items():
870
parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
871
current_hooks = hooks.known_hooks_key_to_object(key)
903
for key, (parent, name) in known_hooks.iter_parent_objects():
904
current_hooks = getattr(parent, name)
872
905
self._preserved_hooks[parent] = (name, current_hooks)
873
906
self.addCleanup(self._restoreHooks)
874
for key, factory in hooks.known_hooks.items():
875
parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
907
for key, (parent, name) in known_hooks.iter_parent_objects():
908
factory = known_hooks.get(key)
876
909
setattr(parent, name, factory())
877
910
# this hook should always be installed
878
911
request._install_hook()
1487
1518
debug.debug_flags.discard('strict_locks')
1489
def addCleanup(self, callable, *args, **kwargs):
1490
"""Arrange to run a callable when this case is torn down.
1492
Callables are run in the reverse of the order they are registered,
1493
ie last-in first-out.
1495
self._cleanups.append((callable, args, kwargs))
1497
1520
def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1498
1521
"""Overrides an object attribute restoring it after the test.
1612
1642
self._do_skip(result, reason)
1645
def _report_skip(self, result, err):
1646
"""Override the default _report_skip.
1648
We want to strip the 'log' detail. If we waint until _do_skip, it has
1649
already been formatted into the 'reason' string, and we can't pull it
1652
self._suppress_log()
1653
super(TestCase, self)._report_skip(self, result, err)
1656
def _report_expected_failure(self, result, err):
1659
See _report_skip for motivation.
1661
self._suppress_log()
1662
super(TestCase, self)._report_expected_failure(self, result, err)
1615
1665
def _do_unsupported_or_skip(self, result, e):
1616
1666
reason = e.args[0]
1667
self._suppress_log()
1617
1668
addNotSupported = getattr(result, 'addNotSupported', None)
1618
1669
if addNotSupported is not None:
1619
1670
result.addNotSupported(self, reason)
1666
1717
unicodestr = self._log_contents.decode('utf8', 'replace')
1667
1718
self._log_contents = unicodestr.encode('utf8')
1668
1719
return self._log_contents
1670
if bzrlib.trace._trace_file:
1671
# flush the log file, to get all content
1672
bzrlib.trace._trace_file.flush()
1673
if self._log_file_name is not None:
1674
logfile = open(self._log_file_name)
1676
log_contents = logfile.read()
1720
if self._log_file is not None:
1721
log_contents = self._log_file.getvalue()
1680
1723
log_contents.decode('utf8')
1681
1724
except UnicodeDecodeError:
1682
1725
unicodestr = log_contents.decode('utf8', 'replace')
1683
1726
log_contents = unicodestr.encode('utf8')
1684
1727
if not keep_log_file:
1686
max_close_attempts = 100
1687
first_close_error = None
1688
while close_attempts < max_close_attempts:
1691
self._log_file.close()
1692
except IOError, ioe:
1693
if ioe.errno is None:
1694
# No errno implies 'close() called during
1695
# concurrent operation on the same file object', so
1696
# retry. Probably a thread is trying to write to
1698
if first_close_error is None:
1699
first_close_error = ioe
1704
if close_attempts > 1:
1706
'Unable to close log file on first attempt, '
1707
'will retry: %s\n' % (first_close_error,))
1708
if close_attempts == max_close_attempts:
1710
'Unable to close log file after %d attempts.\n'
1711
% (max_close_attempts,))
1712
1728
self._log_file = None
1713
1729
# Permit multiple calls to get_log until we clean it up in
1714
1730
# finishLogFile
1715
1731
self._log_contents = log_contents
1717
os.remove(self._log_file_name)
1719
if sys.platform == 'win32' and e.errno == errno.EACCES:
1720
sys.stderr.write(('Unable to delete log file '
1721
' %r\n' % self._log_file_name))
1724
self._log_file_name = None
1725
1732
return log_contents
1727
return "No log file content and no log file name."
1734
return "No log file content."
1729
1736
def get_log(self):
1730
1737
"""Get a unicode string containing the log from bzrlib.trace.
1945
1952
variables. A value of None will unset the env variable.
1946
1953
The values must be strings. The change will only occur in the
1947
1954
child, so you don't need to fix the environment after running.
1948
:param skip_if_plan_to_signal: raise TestSkipped when true and os.kill
1955
:param skip_if_plan_to_signal: raise TestSkipped when true and system
1956
doesn't support signalling subprocesses.
1950
1957
:param allow_plugins: If False (default) pass --no-plugins to bzr.
1952
1959
:returns: Popen object for the started process.
1954
1961
if skip_if_plan_to_signal:
1955
if not getattr(os, 'kill', None):
1956
raise TestSkipped("os.kill not available.")
1962
if os.name != "posix":
1963
raise TestSkipped("Sending signals not supported")
1958
1965
if env_changes is None:
1959
1966
env_changes = {}
2408
2417
made_control = self.make_bzrdir(relpath, format=format)
2409
2418
return made_control.create_repository(shared=shared)
2411
def make_smart_server(self, path):
2420
def make_smart_server(self, path, backing_server=None):
2421
if backing_server is None:
2422
backing_server = self.get_server()
2412
2423
smart_server = test_server.SmartTCPServer_for_testing()
2413
self.start_server(smart_server, self.get_server())
2414
remote_transport = get_transport(smart_server.get_url()).clone(path)
2424
self.start_server(smart_server, backing_server)
2425
remote_transport = _mod_transport.get_transport(smart_server.get_url()
2415
2427
return remote_transport
2417
2429
def make_branch_and_memory_tree(self, relpath, format=None):
2433
2445
def setUp(self):
2434
2446
super(TestCaseWithMemoryTransport, self).setUp()
2447
# Ensure that ConnectedTransport doesn't leak sockets
2448
def get_transport_with_cleanup(*args, **kwargs):
2449
t = orig_get_transport(*args, **kwargs)
2450
if isinstance(t, _mod_transport.ConnectedTransport):
2451
self.addCleanup(t.disconnect)
2454
orig_get_transport = self.overrideAttr(_mod_transport, '_get_transport',
2455
get_transport_with_cleanup)
2435
2456
self._make_test_root()
2436
2457
self.addCleanup(os.chdir, os.getcwdu())
2437
2458
self.makeAndChdirToTestDir()
3191
3218
def partition_tests(suite, count):
3192
3219
"""Partition suite into count lists of tests."""
3194
tests = list(iter_suite_tests(suite))
3195
tests_per_process = int(math.ceil(float(len(tests)) / count))
3196
for block in range(count):
3197
low_test = block * tests_per_process
3198
high_test = low_test + tests_per_process
3199
process_tests = tests[low_test:high_test]
3200
result.append(process_tests)
3220
# This just assigns tests in a round-robin fashion. On one hand this
3221
# splits up blocks of related tests that might run faster if they shared
3222
# resources, but on the other it avoids assigning blocks of slow tests to
3223
# just one partition. So the slowest partition shouldn't be much slower
3225
partitions = [list() for i in range(count)]
3226
tests = iter_suite_tests(suite)
3227
for partition, test in itertools.izip(itertools.cycle(partitions), tests):
3228
partition.append(test)
3204
3232
def workaround_zealous_crypto_random():
3311
3339
if '--no-plugins' in sys.argv:
3312
3340
argv.append('--no-plugins')
3313
# stderr=STDOUT would be ideal, but until we prevent noise on
3314
# stderr it can interrupt the subunit protocol.
3315
process = Popen(argv, stdin=PIPE, stdout=PIPE, stderr=PIPE,
3341
# stderr=subprocess.STDOUT would be ideal, but until we prevent
3342
# noise on stderr it can interrupt the subunit protocol.
3343
process = subprocess.Popen(argv, stdin=subprocess.PIPE,
3344
stdout=subprocess.PIPE,
3345
stderr=subprocess.PIPE,
3317
3347
test = TestInSubprocess(process, test_list_file_name)
3318
3348
result.append(test)
3325
class ForwardingResult(unittest.TestResult):
3327
def __init__(self, target):
3328
unittest.TestResult.__init__(self)
3329
self.result = target
3331
def startTest(self, test):
3332
self.result.startTest(test)
3334
def stopTest(self, test):
3335
self.result.stopTest(test)
3337
def startTestRun(self):
3338
self.result.startTestRun()
3340
def stopTestRun(self):
3341
self.result.stopTestRun()
3343
def addSkip(self, test, reason):
3344
self.result.addSkip(test, reason)
3346
def addSuccess(self, test):
3347
self.result.addSuccess(test)
3349
def addError(self, test, err):
3350
self.result.addError(test, err)
3352
def addFailure(self, test, err):
3353
self.result.addFailure(test, err)
3354
ForwardingResult = testtools.ExtendedToOriginalDecorator
3357
class ProfileResult(ForwardingResult):
3355
class ProfileResult(testtools.ExtendedToOriginalDecorator):
3358
3356
"""Generate profiling data for all activity between start and success.
3360
3358
The profile data is appended to the test's _benchcalls attribute and can
3633
3636
'bzrlib.tests.blackbox',
3634
3637
'bzrlib.tests.commands',
3638
'bzrlib.tests.doc_generate',
3635
3639
'bzrlib.tests.per_branch',
3636
3640
'bzrlib.tests.per_bzrdir',
3637
'bzrlib.tests.per_bzrdir_colo',
3641
'bzrlib.tests.per_controldir',
3642
'bzrlib.tests.per_controldir_colo',
3638
3643
'bzrlib.tests.per_foreign_vcs',
3639
3644
'bzrlib.tests.per_interrepository',
3640
3645
'bzrlib.tests.per_intertree',
3947
def multiply_scenarios(scenarios_left, scenarios_right):
3964
def multiply_scenarios(*scenarios):
3965
"""Multiply two or more iterables of scenarios.
3967
It is safe to pass scenario generators or iterators.
3969
:returns: A list of compound scenarios: the cross-product of all
3970
scenarios, with the names concatenated and the parameters
3973
return reduce(_multiply_two_scenarios, map(list, scenarios))
3976
def _multiply_two_scenarios(scenarios_left, scenarios_right):
3948
3977
"""Multiply two sets of scenarios.
3950
3979
:returns: the cartesian product of the two sets of scenarios, that is
4034
4063
:param new_id: The id to assign to it.
4035
4064
:return: The new test.
4037
new_test = copy(test)
4066
new_test = copy.copy(test)
4038
4067
new_test.id = lambda: new_id
4068
# XXX: Workaround <https://bugs.launchpad.net/testtools/+bug/637725>, which
4069
# causes cloned tests to share the 'details' dict. This makes it hard to
4070
# read the test output for parameterized tests, because tracebacks will be
4071
# associated with irrelevant tests.
4073
details = new_test._TestCase__details
4074
except AttributeError:
4075
# must be a different version of testtools than expected. Do nothing.
4078
# Reset the '__details' dict.
4079
new_test._TestCase__details = {}
4039
4080
return new_test
4101
4142
if test_id != None:
4102
4143
ui.ui_factory.clear_term()
4103
4144
sys.stderr.write('\nWhile running: %s\n' % (test_id,))
4145
# Ugly, but the last thing we want here is fail, so bear with it.
4146
printable_e = str(e).decode(osutils.get_user_encoding(), 'replace'
4147
).encode('ascii', 'replace')
4104
4148
sys.stderr.write('Unable to remove testing dir %s\n%s'
4105
% (os.path.basename(dirname), e))
4149
% (os.path.basename(dirname), printable_e))
4108
4152
class Feature(object):
4454
4508
from subunit import TestProtocolClient
4455
4509
from subunit.test_results import AutoTimingTestResultDecorator
4510
class SubUnitBzrProtocolClient(TestProtocolClient):
4512
def addSuccess(self, test, details=None):
4513
# The subunit client always includes the details in the subunit
4514
# stream, but we don't want to include it in ours.
4515
if details is not None and 'log' in details:
4517
return super(SubUnitBzrProtocolClient, self).addSuccess(
4456
4520
class SubUnitBzrRunner(TextTestRunner):
4457
4521
def run(self, test):
4458
4522
result = AutoTimingTestResultDecorator(
4459
TestProtocolClient(self.stream))
4523
SubUnitBzrProtocolClient(self.stream))
4460
4524
test.run(result)
4462
4526
except ImportError: