387
404
def __init__(self, stream, descriptions, verbosity,
388
405
bench_history=None,
392
409
ExtendedTestResult.__init__(self, stream, descriptions, verbosity,
393
bench_history, num_tests)
395
self.pb = self.ui.nested_progress_bar()
396
self._supplied_pb = False
399
self._supplied_pb = True
410
bench_history, strict)
411
# We no longer pass them around, but just rely on the UIFactory stack
414
warnings.warn("Passing pb to TextTestResult is deprecated")
415
self.pb = self.ui.nested_progress_bar()
400
416
self.pb.show_pct = False
401
417
self.pb.show_spinner = False
402
418
self.pb.show_eta = False,
403
419
self.pb.show_count = False
404
420
self.pb.show_bar = False
421
self.pb.update_latency = 0
422
self.pb.show_transport_activity = False
425
# called when the tests that are going to run have run
427
super(TextTestResult, self).done()
406
432
def report_starting(self):
407
self.pb.update('[test 0/%d] starting...' % (self.num_tests))
433
self.pb.update('[test 0/%d] Starting' % (self.num_tests))
435
def printErrors(self):
436
# clear the pb to make room for the error listing
438
super(TextTestResult, self).printErrors()
409
440
def _progress_prefix_text(self):
410
a = '[%d' % self.count
411
if self.num_tests is not None:
441
# the longer this text, the less space we have to show the test
443
a = '[%d' % self.count # total that have been run
444
# tests skipped as known not to be relevant are not important enough
446
## if self.skip_count:
447
## a += ', %d skip' % self.skip_count
448
## if self.known_failure_count:
449
## a += '+%dX' % self.known_failure_count
412
451
a +='/%d' % self.num_tests
413
a += ' in %ds' % (time.time() - self._overall_start_time)
453
runtime = time.time() - self._overall_start_time
455
a += '%dm%ds' % (runtime / 60, runtime % 60)
414
458
if self.error_count:
415
a += ', %d errors' % self.error_count
459
a += ', %d err' % self.error_count
416
460
if self.failure_count:
417
a += ', %d failed' % self.failure_count
418
if self.known_failure_count:
419
a += ', %d known failures' % self.known_failure_count
421
a += ', %d skipped' % self.skip_count
461
a += ', %d fail' % self.failure_count
422
462
if self.unsupported:
423
a += ', %d missing features' % len(self.unsupported)
463
a += ', %d missing' % len(self.unsupported)
765
786
retrieved by _get_log(). We use a real OS file, not an in-memory object,
766
787
so that it can also capture file IO. When the test completes this file
767
788
is read into memory and removed from disk.
769
790
There are also convenience functions to invoke bzr's command-line
770
791
routine, and to build and check bzr trees.
772
793
In addition to the usual method of overriding tearDown(), this class also
773
794
allows subclasses to register functions into the _cleanups list, which is
774
795
run in order as the object is torn down. It's less likely this will be
775
796
accidentally overlooked.
799
_active_threads = None
800
_leaking_threads_tests = 0
801
_first_thread_leaker_id = None
778
802
_log_file_name = None
779
803
_log_contents = ''
780
804
_keep_log_file = False
781
805
# record lsprof data when performing benchmark calls.
782
806
_gather_lsprof_in_benchmarks = False
807
attrs_to_keep = ('id', '_testMethodName', '_testMethodDoc',
808
'_log_contents', '_log_file_name', '_benchtime',
809
'_TestCase__testMethodName', '_TestCase__testMethodDoc',)
784
811
def __init__(self, methodName='testMethod'):
785
812
super(TestCase, self).__init__(methodName)
786
813
self._cleanups = []
814
self._bzr_test_setUp_run = False
815
self._bzr_test_tearDown_run = False
789
818
unittest.TestCase.setUp(self)
819
self._bzr_test_setUp_run = True
790
820
self._cleanEnvironment()
791
bzrlib.trace.disable_default_logging()
792
821
self._silenceUI()
793
822
self._startLogFile()
794
823
self._benchcalls = []
795
824
self._benchtime = None
796
825
self._clear_hooks()
797
827
self._clear_debug_flags()
828
TestCase._active_threads = threading.activeCount()
829
self.addCleanup(self._check_leaked_threads)
834
pdb.Pdb().set_trace(sys._getframe().f_back)
836
def _check_leaked_threads(self):
837
active = threading.activeCount()
838
leaked_threads = active - TestCase._active_threads
839
TestCase._active_threads = active
841
TestCase._leaking_threads_tests += 1
842
if TestCase._first_thread_leaker_id is None:
843
TestCase._first_thread_leaker_id = self.id()
799
845
def _clear_debug_flags(self):
800
846
"""Prevent externally set debug flags affecting tests.
802
848
Tests that want to use debug flags can just set them in the
803
849
debug_flags set during setup/teardown.
805
851
self._preserved_debug_flags = set(debug.debug_flags)
806
debug.debug_flags.clear()
852
if 'allow_debug' not in selftest_debug_flags:
853
debug.debug_flags.clear()
854
if 'disable_lock_checks' not in selftest_debug_flags:
855
debug.debug_flags.add('strict_locks')
807
856
self.addCleanup(self._restore_debug_flags)
809
858
def _clear_hooks(self):
810
859
# prevent hooks affecting tests
812
import bzrlib.smart.server
813
self._preserved_hooks = {
814
bzrlib.branch.Branch: bzrlib.branch.Branch.hooks,
815
bzrlib.smart.server.SmartTCPServer: bzrlib.smart.server.SmartTCPServer.hooks,
860
self._preserved_hooks = {}
861
for key, factory in hooks.known_hooks.items():
862
parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
863
current_hooks = hooks.known_hooks_key_to_object(key)
864
self._preserved_hooks[parent] = (name, current_hooks)
817
865
self.addCleanup(self._restoreHooks)
818
# reset all hooks to an empty instance of the appropriate type
819
bzrlib.branch.Branch.hooks = bzrlib.branch.BranchHooks()
820
bzrlib.smart.server.SmartTCPServer.hooks = bzrlib.smart.server.SmartServerHooks()
866
for key, factory in hooks.known_hooks.items():
867
parent, name = hooks.known_hooks_key_to_parent_and_attribute(key)
868
setattr(parent, name, factory())
869
# this hook should always be installed
870
request._install_hook()
822
872
def _silenceUI(self):
823
873
"""Turn off UI for duration of test"""
828
878
ui.ui_factory = ui.SilentUIFactory()
829
879
self.addCleanup(_restore)
881
def _check_locks(self):
882
"""Check that all lock take/release actions have been paired."""
883
# We always check for mismatched locks. If a mismatch is found, we
884
# fail unless -Edisable_lock_checks is supplied to selftest, in which
885
# case we just print a warning.
887
acquired_locks = [lock for action, lock in self._lock_actions
888
if action == 'acquired']
889
released_locks = [lock for action, lock in self._lock_actions
890
if action == 'released']
891
broken_locks = [lock for action, lock in self._lock_actions
892
if action == 'broken']
893
# trivially, given the tests for lock acquistion and release, if we
894
# have as many in each list, it should be ok. Some lock tests also
895
# break some locks on purpose and should be taken into account by
896
# considering that breaking a lock is just a dirty way of releasing it.
897
if len(acquired_locks) != (len(released_locks) + len(broken_locks)):
898
message = ('Different number of acquired and '
899
'released or broken locks. (%s, %s + %s)' %
900
(acquired_locks, released_locks, broken_locks))
901
if not self._lock_check_thorough:
902
# Rather than fail, just warn
903
print "Broken test %s: %s" % (self, message)
907
def _track_locks(self):
908
"""Track lock activity during tests."""
909
self._lock_actions = []
910
if 'disable_lock_checks' in selftest_debug_flags:
911
self._lock_check_thorough = False
913
self._lock_check_thorough = True
915
self.addCleanup(self._check_locks)
916
_mod_lock.Lock.hooks.install_named_hook('lock_acquired',
917
self._lock_acquired, None)
918
_mod_lock.Lock.hooks.install_named_hook('lock_released',
919
self._lock_released, None)
920
_mod_lock.Lock.hooks.install_named_hook('lock_broken',
921
self._lock_broken, None)
923
def _lock_acquired(self, result):
924
self._lock_actions.append(('acquired', result))
926
def _lock_released(self, result):
927
self._lock_actions.append(('released', result))
929
def _lock_broken(self, result):
930
self._lock_actions.append(('broken', result))
831
932
def _ndiff_strings(self, a, b):
832
933
"""Return ndiff between two strings containing lines.
834
935
A trailing newline is added if missing to make the strings
835
936
print properly."""
836
937
if b and b[-1] != '\n':
2226
2566
self.transport_readonly_server = HttpServer
2229
def filter_suite_by_re(suite, pattern, exclude_pattern=None,
2230
random_order=False):
2231
"""Create a test suite by filtering another one.
2569
def condition_id_re(pattern):
2570
"""Create a condition filter which performs a re check on a test's id.
2572
:param pattern: A regular expression string.
2573
:return: A callable that returns True if the re matches.
2575
filter_re = osutils.re_compile_checked(pattern, 0,
2577
def condition(test):
2579
return filter_re.search(test_id)
2583
def condition_isinstance(klass_or_klass_list):
2584
"""Create a condition filter which returns isinstance(param, klass).
2586
:return: A callable which when called with one parameter obj return the
2587
result of isinstance(obj, klass_or_klass_list).
2590
return isinstance(obj, klass_or_klass_list)
2594
def condition_id_in_list(id_list):
2595
"""Create a condition filter which verify that test's id in a list.
2597
:param id_list: A TestIdList object.
2598
:return: A callable that returns True if the test's id appears in the list.
2600
def condition(test):
2601
return id_list.includes(test.id())
2605
def condition_id_startswith(starts):
2606
"""Create a condition filter verifying that test's id starts with a string.
2608
:param starts: A list of string.
2609
:return: A callable that returns True if the test's id starts with one of
2612
def condition(test):
2613
for start in starts:
2614
if test.id().startswith(start):
2620
def exclude_tests_by_condition(suite, condition):
2621
"""Create a test suite which excludes some tests from suite.
2623
:param suite: The suite to get tests from.
2624
:param condition: A callable whose result evaluates True when called with a
2625
test case which should be excluded from the result.
2626
:return: A suite which contains the tests found in suite that fail
2630
for test in iter_suite_tests(suite):
2631
if not condition(test):
2633
return TestUtil.TestSuite(result)
2636
def filter_suite_by_condition(suite, condition):
2637
"""Create a test suite by filtering another one.
2639
:param suite: The source suite.
2640
:param condition: A callable whose result evaluates True when called with a
2641
test case which should be included in the result.
2642
:return: A suite which contains the tests found in suite that pass
2646
for test in iter_suite_tests(suite):
2649
return TestUtil.TestSuite(result)
2652
def filter_suite_by_re(suite, pattern):
2653
"""Create a test suite by filtering another one.
2233
2655
:param suite: the source suite
2234
2656
:param pattern: pattern that names must match
2235
:param exclude_pattern: pattern that names must not match, if any
2236
:param random_order: if True, tests in the new suite will be put in
2238
:returns: the newly created suite
2240
return sort_suite_by_re(suite, pattern, exclude_pattern,
2241
random_order, False)
2244
def sort_suite_by_re(suite, pattern, exclude_pattern=None,
2245
random_order=False, append_rest=True):
2246
"""Create a test suite by sorting another one.
2248
:param suite: the source suite
2249
:param pattern: pattern that names must match in order to go
2250
first in the new suite
2251
:param exclude_pattern: pattern that names must not match, if any
2252
:param random_order: if True, tests in the new suite will be put in
2254
:param append_rest: if False, pattern is a strict filter and not
2255
just an ordering directive
2256
:returns: the newly created suite
2260
filter_re = re.compile(pattern)
2261
if exclude_pattern is not None:
2262
exclude_re = re.compile(exclude_pattern)
2657
:returns: the newly created suite
2659
condition = condition_id_re(pattern)
2660
result_suite = filter_suite_by_condition(suite, condition)
2664
def filter_suite_by_id_list(suite, test_id_list):
2665
"""Create a test suite by filtering another one.
2667
:param suite: The source suite.
2668
:param test_id_list: A list of the test ids to keep as strings.
2669
:returns: the newly created suite
2671
condition = condition_id_in_list(test_id_list)
2672
result_suite = filter_suite_by_condition(suite, condition)
2676
def filter_suite_by_id_startswith(suite, start):
2677
"""Create a test suite by filtering another one.
2679
:param suite: The source suite.
2680
:param start: A list of string the test id must start with one of.
2681
:returns: the newly created suite
2683
condition = condition_id_startswith(start)
2684
result_suite = filter_suite_by_condition(suite, condition)
2688
def exclude_tests_by_re(suite, pattern):
2689
"""Create a test suite which excludes some tests from suite.
2691
:param suite: The suite to get tests from.
2692
:param pattern: A regular expression string. Test ids that match this
2693
pattern will be excluded from the result.
2694
:return: A TestSuite that contains all the tests from suite without the
2695
tests that matched pattern. The order of tests is the same as it was in
2698
return exclude_tests_by_condition(suite, condition_id_re(pattern))
2701
def preserve_input(something):
2702
"""A helper for performing test suite transformation chains.
2704
:param something: Anything you want to preserve.
2710
def randomize_suite(suite):
2711
"""Return a new TestSuite with suite's tests in random order.
2713
The tests in the input suite are flattened into a single suite in order to
2714
accomplish this. Any nested TestSuites are removed to provide global
2717
tests = list(iter_suite_tests(suite))
2718
random.shuffle(tests)
2719
return TestUtil.TestSuite(tests)
2722
def split_suite_by_condition(suite, condition):
2723
"""Split a test suite into two by a condition.
2725
:param suite: The suite to split.
2726
:param condition: The condition to match on. Tests that match this
2727
condition are returned in the first test suite, ones that do not match
2728
are in the second suite.
2729
:return: A tuple of two test suites, where the first contains tests from
2730
suite matching the condition, and the second contains the remainder
2731
from suite. The order within each output suite is the same as it was in
2263
2736
for test in iter_suite_tests(suite):
2265
if exclude_pattern is None or not exclude_re.search(test_id):
2266
if filter_re.search(test_id):
2271
random.shuffle(first)
2272
random.shuffle(second)
2273
return TestUtil.TestSuite(first + second)
2738
matched.append(test)
2740
did_not_match.append(test)
2741
return TestUtil.TestSuite(matched), TestUtil.TestSuite(did_not_match)
2744
def split_suite_by_re(suite, pattern):
2745
"""Split a test suite into two by a regular expression.
2747
:param suite: The suite to split.
2748
:param pattern: A regular expression string. Test ids that match this
2749
pattern will be in the first test suite returned, and the others in the
2750
second test suite returned.
2751
:return: A tuple of two test suites, where the first contains tests from
2752
suite matching pattern, and the second contains the remainder from
2753
suite. The order within each output suite is the same as it was in
2756
return split_suite_by_condition(suite, condition_id_re(pattern))
2276
2759
def run_suite(suite, name='test', verbose=False, pattern=".*",
2281
2764
random_seed=None,
2282
2765
exclude_pattern=None,
2768
suite_decorators=None,
2770
"""Run a test suite for bzr selftest.
2772
:param runner_class: The class of runner to use. Must support the
2773
constructor arguments passed by run_suite which are more than standard
2775
:return: A boolean indicating success.
2285
2777
TestCase._gather_lsprof_in_benchmarks = lsprof_timed
2290
runner = TextTestRunner(stream=sys.stdout,
2782
if runner_class is None:
2783
runner_class = TextTestRunner
2786
runner = runner_class(stream=stream,
2291
2787
descriptions=0,
2292
2788
verbosity=verbosity,
2293
2789
bench_history=bench_history,
2294
2790
list_only=list_only,
2296
2793
runner.stop_on_failure=stop_on_failure
2297
# Initialise the random number generator and display the seed used.
2298
# We convert the seed to a long to make it reuseable across invocations.
2299
random_order = False
2300
if random_seed is not None:
2302
if random_seed == "now":
2303
random_seed = long(time.time())
2794
# built in decorator factories:
2796
random_order(random_seed, runner),
2797
exclude_tests(exclude_pattern),
2799
if matching_tests_first:
2800
decorators.append(tests_first(pattern))
2802
decorators.append(filter_tests(pattern))
2803
if suite_decorators:
2804
decorators.extend(suite_decorators)
2805
# tell the result object how many tests will be running: (except if
2806
# --parallel=fork is being used. Robert said he will provide a better
2807
# progress design later -- vila 20090817)
2808
if fork_decorator not in decorators:
2809
decorators.append(CountingDecorator)
2810
for decorator in decorators:
2811
suite = decorator(suite)
2812
result = runner.run(suite)
2817
return result.wasStrictlySuccessful()
2819
return result.wasSuccessful()
2822
# A registry where get() returns a suite decorator.
2823
parallel_registry = registry.Registry()
2826
def fork_decorator(suite):
2827
concurrency = osutils.local_concurrency()
2828
if concurrency == 1:
2830
from testtools import ConcurrentTestSuite
2831
return ConcurrentTestSuite(suite, fork_for_tests)
2832
parallel_registry.register('fork', fork_decorator)
2835
def subprocess_decorator(suite):
2836
concurrency = osutils.local_concurrency()
2837
if concurrency == 1:
2839
from testtools import ConcurrentTestSuite
2840
return ConcurrentTestSuite(suite, reinvoke_for_tests)
2841
parallel_registry.register('subprocess', subprocess_decorator)
2844
def exclude_tests(exclude_pattern):
2845
"""Return a test suite decorator that excludes tests."""
2846
if exclude_pattern is None:
2847
return identity_decorator
2848
def decorator(suite):
2849
return ExcludeDecorator(suite, exclude_pattern)
2853
def filter_tests(pattern):
2855
return identity_decorator
2856
def decorator(suite):
2857
return FilterTestsDecorator(suite, pattern)
2861
def random_order(random_seed, runner):
2862
"""Return a test suite decorator factory for randomising tests order.
2864
:param random_seed: now, a string which casts to a long, or a long.
2865
:param runner: A test runner with a stream attribute to report on.
2867
if random_seed is None:
2868
return identity_decorator
2869
def decorator(suite):
2870
return RandomDecorator(suite, random_seed, runner.stream)
2874
def tests_first(pattern):
2876
return identity_decorator
2877
def decorator(suite):
2878
return TestFirstDecorator(suite, pattern)
2882
def identity_decorator(suite):
2887
class TestDecorator(TestSuite):
2888
"""A decorator for TestCase/TestSuite objects.
2890
Usually, subclasses should override __iter__(used when flattening test
2891
suites), which we do to filter, reorder, parallelise and so on, run() and
2895
def __init__(self, suite):
2896
TestSuite.__init__(self)
2899
def countTestCases(self):
2902
cases += test.countTestCases()
2909
def run(self, result):
2910
# Use iteration on self, not self._tests, to allow subclasses to hook
2913
if result.shouldStop:
2919
class CountingDecorator(TestDecorator):
2920
"""A decorator which calls result.progress(self.countTestCases)."""
2922
def run(self, result):
2923
progress_method = getattr(result, 'progress', None)
2924
if callable(progress_method):
2925
progress_method(self.countTestCases(), SUBUNIT_SEEK_SET)
2926
return super(CountingDecorator, self).run(result)
2929
class ExcludeDecorator(TestDecorator):
2930
"""A decorator which excludes test matching an exclude pattern."""
2932
def __init__(self, suite, exclude_pattern):
2933
TestDecorator.__init__(self, suite)
2934
self.exclude_pattern = exclude_pattern
2935
self.excluded = False
2939
return iter(self._tests)
2940
self.excluded = True
2941
suite = exclude_tests_by_re(self, self.exclude_pattern)
2943
self.addTests(suite)
2944
return iter(self._tests)
2947
class FilterTestsDecorator(TestDecorator):
2948
"""A decorator which filters tests to those matching a pattern."""
2950
def __init__(self, suite, pattern):
2951
TestDecorator.__init__(self, suite)
2952
self.pattern = pattern
2953
self.filtered = False
2957
return iter(self._tests)
2958
self.filtered = True
2959
suite = filter_suite_by_re(self, self.pattern)
2961
self.addTests(suite)
2962
return iter(self._tests)
2965
class RandomDecorator(TestDecorator):
2966
"""A decorator which randomises the order of its tests."""
2968
def __init__(self, suite, random_seed, stream):
2969
TestDecorator.__init__(self, suite)
2970
self.random_seed = random_seed
2971
self.randomised = False
2972
self.stream = stream
2976
return iter(self._tests)
2977
self.randomised = True
2978
self.stream.writeln("Randomizing test order using seed %s\n" %
2979
(self.actual_seed()))
2980
# Initialise the random number generator.
2981
random.seed(self.actual_seed())
2982
suite = randomize_suite(self)
2984
self.addTests(suite)
2985
return iter(self._tests)
2987
def actual_seed(self):
2988
if self.random_seed == "now":
2989
# We convert the seed to a long to make it reuseable across
2990
# invocations (because the user can reenter it).
2991
self.random_seed = long(time.time())
2305
2993
# Convert the seed to a long if we can
2307
random_seed = long(random_seed)
2995
self.random_seed = long(self.random_seed)
2310
runner.stream.writeln("Randomizing test order using seed %s\n" %
2312
random.seed(random_seed)
2313
# Customise the list of tests if requested
2314
if pattern != '.*' or exclude_pattern is not None or random_order:
2315
if matching_tests_first:
2316
suite = sort_suite_by_re(suite, pattern, exclude_pattern,
2319
suite = filter_suite_by_re(suite, pattern, exclude_pattern,
2321
result = runner.run(suite)
2324
return result.wasStrictlySuccessful()
2326
return result.wasSuccessful()
2998
return self.random_seed
3001
class TestFirstDecorator(TestDecorator):
3002
"""A decorator which moves named tests to the front."""
3004
def __init__(self, suite, pattern):
3005
TestDecorator.__init__(self, suite)
3006
self.pattern = pattern
3007
self.filtered = False
3011
return iter(self._tests)
3012
self.filtered = True
3013
suites = split_suite_by_re(self, self.pattern)
3015
self.addTests(suites)
3016
return iter(self._tests)
3019
def partition_tests(suite, count):
3020
"""Partition suite into count lists of tests."""
3022
tests = list(iter_suite_tests(suite))
3023
tests_per_process = int(math.ceil(float(len(tests)) / count))
3024
for block in range(count):
3025
low_test = block * tests_per_process
3026
high_test = low_test + tests_per_process
3027
process_tests = tests[low_test:high_test]
3028
result.append(process_tests)
3032
def fork_for_tests(suite):
3033
"""Take suite and start up one runner per CPU by forking()
3035
:return: An iterable of TestCase-like objects which can each have
3036
run(result) called on them to feed tests to result.
3038
concurrency = osutils.local_concurrency()
3040
from subunit import TestProtocolClient, ProtocolTestCase
3042
from subunit.test_results import AutoTimingTestResultDecorator
3044
AutoTimingTestResultDecorator = lambda x:x
3045
class TestInOtherProcess(ProtocolTestCase):
3046
# Should be in subunit, I think. RBC.
3047
def __init__(self, stream, pid):
3048
ProtocolTestCase.__init__(self, stream)
3051
def run(self, result):
3053
ProtocolTestCase.run(self, result)
3055
os.waitpid(self.pid, os.WNOHANG)
3057
test_blocks = partition_tests(suite, concurrency)
3058
for process_tests in test_blocks:
3059
process_suite = TestSuite()
3060
process_suite.addTests(process_tests)
3061
c2pread, c2pwrite = os.pipe()
3066
# Leave stderr and stdout open so we can see test noise
3067
# Close stdin so that the child goes away if it decides to
3068
# read from stdin (otherwise its a roulette to see what
3069
# child actually gets keystrokes for pdb etc).
3072
stream = os.fdopen(c2pwrite, 'wb', 1)
3073
subunit_result = AutoTimingTestResultDecorator(
3074
TestProtocolClient(stream))
3075
process_suite.run(subunit_result)
3080
stream = os.fdopen(c2pread, 'rb', 1)
3081
test = TestInOtherProcess(stream, pid)
3086
def reinvoke_for_tests(suite):
3087
"""Take suite and start up one runner per CPU using subprocess().
3089
:return: An iterable of TestCase-like objects which can each have
3090
run(result) called on them to feed tests to result.
3092
concurrency = osutils.local_concurrency()
3094
from subunit import ProtocolTestCase
3095
class TestInSubprocess(ProtocolTestCase):
3096
def __init__(self, process, name):
3097
ProtocolTestCase.__init__(self, process.stdout)
3098
self.process = process
3099
self.process.stdin.close()
3102
def run(self, result):
3104
ProtocolTestCase.run(self, result)
3107
os.unlink(self.name)
3108
# print "pid %d finished" % finished_process
3109
test_blocks = partition_tests(suite, concurrency)
3110
for process_tests in test_blocks:
3111
# ugly; currently reimplement rather than reuses TestCase methods.
3112
bzr_path = os.path.dirname(os.path.dirname(bzrlib.__file__))+'/bzr'
3113
if not os.path.isfile(bzr_path):
3114
# We are probably installed. Assume sys.argv is the right file
3115
bzr_path = sys.argv[0]
3116
fd, test_list_file_name = tempfile.mkstemp()
3117
test_list_file = os.fdopen(fd, 'wb', 1)
3118
for test in process_tests:
3119
test_list_file.write(test.id() + '\n')
3120
test_list_file.close()
3122
argv = [bzr_path, 'selftest', '--load-list', test_list_file_name,
3124
if '--no-plugins' in sys.argv:
3125
argv.append('--no-plugins')
3126
# stderr=STDOUT would be ideal, but until we prevent noise on
3127
# stderr it can interrupt the subunit protocol.
3128
process = Popen(argv, stdin=PIPE, stdout=PIPE, stderr=PIPE,
3130
test = TestInSubprocess(process, test_list_file_name)
3133
os.unlink(test_list_file_name)
3138
class BZRTransformingResult(unittest.TestResult):
3140
def __init__(self, target):
3141
unittest.TestResult.__init__(self)
3142
self.result = target
3144
def startTest(self, test):
3145
self.result.startTest(test)
3147
def stopTest(self, test):
3148
self.result.stopTest(test)
3150
def addError(self, test, err):
3151
feature = self._error_looks_like('UnavailableFeature: ', err)
3152
if feature is not None:
3153
self.result.addNotSupported(test, feature)
3155
self.result.addError(test, err)
3157
def addFailure(self, test, err):
3158
known = self._error_looks_like('KnownFailure: ', err)
3159
if known is not None:
3160
self.result._addKnownFailure(test, [KnownFailure,
3161
KnownFailure(known), None])
3163
self.result.addFailure(test, err)
3165
def addSkip(self, test, reason):
3166
self.result.addSkip(test, reason)
3168
def addSuccess(self, test):
3169
self.result.addSuccess(test)
3171
def _error_looks_like(self, prefix, err):
3172
"""Deserialize exception and returns the stringify value."""
3176
if isinstance(exc, subunit.RemoteException):
3177
# stringify the exception gives access to the remote traceback
3178
# We search the last line for 'prefix'
3179
lines = str(exc).split('\n')
3180
while lines and not lines[-1]:
3183
if lines[-1].startswith(prefix):
3184
value = lines[-1][len(prefix):]
3188
# Controlled by "bzr selftest -E=..." option
3189
# Currently supported:
3190
# -Eallow_debug Will no longer clear debug.debug_flags() so it
3191
# preserves any flags supplied at the command line.
3192
# -Edisable_lock_checks Turns errors in mismatched locks into simple prints
3193
# rather than failing tests. And no longer raise
3194
# LockContention when fctnl locks are not being used
3195
# with proper exclusion rules.
3196
selftest_debug_flags = set()
2329
3199
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
2363
3255
list_only=list_only,
2364
3256
random_seed=random_seed,
2365
3257
exclude_pattern=exclude_pattern,
3259
runner_class=runner_class,
3260
suite_decorators=suite_decorators,
2368
3264
default_transport = old_transport
3265
selftest_debug_flags = old_debug_flags
3268
def load_test_id_list(file_name):
3269
"""Load a test id list from a text file.
3271
The format is one test id by line. No special care is taken to impose
3272
strict rules, these test ids are used to filter the test suite so a test id
3273
that do not match an existing test will do no harm. This allows user to add
3274
comments, leave blank lines, etc.
3278
ftest = open(file_name, 'rt')
3280
if e.errno != errno.ENOENT:
3283
raise errors.NoSuchFile(file_name)
3285
for test_name in ftest.readlines():
3286
test_list.append(test_name.strip())
3291
def suite_matches_id_list(test_suite, id_list):
3292
"""Warns about tests not appearing or appearing more than once.
3294
:param test_suite: A TestSuite object.
3295
:param test_id_list: The list of test ids that should be found in
3298
:return: (absents, duplicates) absents is a list containing the test found
3299
in id_list but not in test_suite, duplicates is a list containing the
3300
test found multiple times in test_suite.
3302
When using a prefined test id list, it may occurs that some tests do not
3303
exist anymore or that some tests use the same id. This function warns the
3304
tester about potential problems in his workflow (test lists are volatile)
3305
or in the test suite itself (using the same id for several tests does not
3306
help to localize defects).
3308
# Build a dict counting id occurrences
3310
for test in iter_suite_tests(test_suite):
3312
tests[id] = tests.get(id, 0) + 1
3317
occurs = tests.get(id, 0)
3319
not_found.append(id)
3321
duplicates.append(id)
3323
return not_found, duplicates
3326
class TestIdList(object):
3327
"""Test id list to filter a test suite.
3329
Relying on the assumption that test ids are built as:
3330
<module>[.<class>.<method>][(<param>+)], <module> being in python dotted
3331
notation, this class offers methods to :
3332
- avoid building a test suite for modules not refered to in the test list,
3333
- keep only the tests listed from the module test suite.
3336
def __init__(self, test_id_list):
3337
# When a test suite needs to be filtered against us we compare test ids
3338
# for equality, so a simple dict offers a quick and simple solution.
3339
self.tests = dict().fromkeys(test_id_list, True)
3341
# While unittest.TestCase have ids like:
3342
# <module>.<class>.<method>[(<param+)],
3343
# doctest.DocTestCase can have ids like:
3346
# <module>.<function>
3347
# <module>.<class>.<method>
3349
# Since we can't predict a test class from its name only, we settle on
3350
# a simple constraint: a test id always begins with its module name.
3353
for test_id in test_id_list:
3354
parts = test_id.split('.')
3355
mod_name = parts.pop(0)
3356
modules[mod_name] = True
3358
mod_name += '.' + part
3359
modules[mod_name] = True
3360
self.modules = modules
3362
def refers_to(self, module_name):
3363
"""Is there tests for the module or one of its sub modules."""
3364
return self.modules.has_key(module_name)
3366
def includes(self, test_id):
3367
return self.tests.has_key(test_id)
3370
class TestPrefixAliasRegistry(registry.Registry):
3371
"""A registry for test prefix aliases.
3373
This helps implement shorcuts for the --starting-with selftest
3374
option. Overriding existing prefixes is not allowed but not fatal (a
3375
warning will be emitted).
3378
def register(self, key, obj, help=None, info=None,
3379
override_existing=False):
3380
"""See Registry.register.
3382
Trying to override an existing alias causes a warning to be emitted,
3383
not a fatal execption.
3386
super(TestPrefixAliasRegistry, self).register(
3387
key, obj, help=help, info=info, override_existing=False)
3389
actual = self.get(key)
3390
note('Test prefix alias %s is already used for %s, ignoring %s'
3391
% (key, actual, obj))
3393
def resolve_alias(self, id_start):
3394
"""Replace the alias by the prefix in the given string.
3396
Using an unknown prefix is an error to help catching typos.
3398
parts = id_start.split('.')
3400
parts[0] = self.get(parts[0])
3402
raise errors.BzrCommandError(
3403
'%s is not a known test prefix alias' % parts[0])
3404
return '.'.join(parts)
3407
test_prefix_alias_registry = TestPrefixAliasRegistry()
3408
"""Registry of test prefix aliases."""
3411
# This alias allows to detect typos ('bzrlin.') by making all valid test ids
3412
# appear prefixed ('bzrlib.' is "replaced" by 'bzrlib.').
3413
test_prefix_alias_registry.register('bzrlib', 'bzrlib')
3415
# Obvious higest levels prefixes, feel free to add your own via a plugin
3416
test_prefix_alias_registry.register('bd', 'bzrlib.doc')
3417
test_prefix_alias_registry.register('bu', 'bzrlib.utils')
3418
test_prefix_alias_registry.register('bt', 'bzrlib.tests')
3419
test_prefix_alias_registry.register('bb', 'bzrlib.tests.blackbox')
3420
test_prefix_alias_registry.register('bp', 'bzrlib.plugins')
3423
def test_suite(keep_only=None, starting_with=None):
2372
3424
"""Build and return TestSuite for the whole of bzrlib.
3426
:param keep_only: A list of test ids limiting the suite returned.
3428
:param starting_with: An id limiting the suite returned to the tests
2374
3431
This function can be replaced if you need to change the default test
2375
3432
suite on a global basis, but it is not encouraged.
2377
3434
testmod_names = [
2378
'bzrlib.util.tests.test_bencode',
3436
'bzrlib.tests.blackbox',
3437
'bzrlib.tests.commands',
3438
'bzrlib.tests.per_branch',
3439
'bzrlib.tests.per_bzrdir',
3440
'bzrlib.tests.per_interrepository',
3441
'bzrlib.tests.per_intertree',
3442
'bzrlib.tests.per_inventory',
3443
'bzrlib.tests.per_interbranch',
3444
'bzrlib.tests.per_lock',
3445
'bzrlib.tests.per_transport',
3446
'bzrlib.tests.per_tree',
3447
'bzrlib.tests.per_pack_repository',
3448
'bzrlib.tests.per_repository',
3449
'bzrlib.tests.per_repository_chk',
3450
'bzrlib.tests.per_repository_reference',
3451
'bzrlib.tests.per_versionedfile',
3452
'bzrlib.tests.per_workingtree',
3453
'bzrlib.tests.test__annotator',
3454
'bzrlib.tests.test__chk_map',
2379
3455
'bzrlib.tests.test__dirstate_helpers',
3456
'bzrlib.tests.test__groupcompress',
3457
'bzrlib.tests.test__known_graph',
3458
'bzrlib.tests.test__rio',
3459
'bzrlib.tests.test__walkdirs_win32',
2380
3460
'bzrlib.tests.test_ancestry',
2381
3461
'bzrlib.tests.test_annotate',
2382
3462
'bzrlib.tests.test_api',
2383
3463
'bzrlib.tests.test_atomicfile',
2384
3464
'bzrlib.tests.test_bad_files',
3465
'bzrlib.tests.test_bencode',
2385
3466
'bzrlib.tests.test_bisect_multi',
2386
3467
'bzrlib.tests.test_branch',
2387
3468
'bzrlib.tests.test_branchbuilder',
3469
'bzrlib.tests.test_btree_index',
2388
3470
'bzrlib.tests.test_bugtracker',
2389
3471
'bzrlib.tests.test_bundle',
2390
3472
'bzrlib.tests.test_bzrdir',
3473
'bzrlib.tests.test__chunks_to_lines',
2391
3474
'bzrlib.tests.test_cache_utf8',
3475
'bzrlib.tests.test_chk_map',
3476
'bzrlib.tests.test_chk_serializer',
3477
'bzrlib.tests.test_chunk_writer',
3478
'bzrlib.tests.test_clean_tree',
2392
3479
'bzrlib.tests.test_commands',
2393
3480
'bzrlib.tests.test_commit',
2394
3481
'bzrlib.tests.test_commit_merge',
2395
3482
'bzrlib.tests.test_config',
2396
3483
'bzrlib.tests.test_conflicts',
2397
3484
'bzrlib.tests.test_counted_lock',
3485
'bzrlib.tests.test_crash',
2398
3486
'bzrlib.tests.test_decorators',
2399
3487
'bzrlib.tests.test_delta',
3488
'bzrlib.tests.test_debug',
2400
3489
'bzrlib.tests.test_deprecated_graph',
2401
3490
'bzrlib.tests.test_diff',
3491
'bzrlib.tests.test_directory_service',
2402
3492
'bzrlib.tests.test_dirstate',
2403
3493
'bzrlib.tests.test_email_message',
3494
'bzrlib.tests.test_eol_filters',
2404
3495
'bzrlib.tests.test_errors',
2405
'bzrlib.tests.test_escaped_store',
3496
'bzrlib.tests.test_export',
2406
3497
'bzrlib.tests.test_extract',
2407
3498
'bzrlib.tests.test_fetch',
3499
'bzrlib.tests.test_fifo_cache',
3500
'bzrlib.tests.test_filters',
2408
3501
'bzrlib.tests.test_ftp_transport',
3502
'bzrlib.tests.test_foreign',
2409
3503
'bzrlib.tests.test_generate_docs',
2410
3504
'bzrlib.tests.test_generate_ids',
2411
3505
'bzrlib.tests.test_globbing',
2412
3506
'bzrlib.tests.test_gpg',
2413
3507
'bzrlib.tests.test_graph',
3508
'bzrlib.tests.test_groupcompress',
2414
3509
'bzrlib.tests.test_hashcache',
2415
3510
'bzrlib.tests.test_help',
2416
3511
'bzrlib.tests.test_hooks',
2500
3609
'bzrlib.tests.test_wsgi',
2501
3610
'bzrlib.tests.test_xml',
2503
test_transport_implementations = [
2504
'bzrlib.tests.test_transport_implementations',
2505
'bzrlib.tests.test_read_bundle',
2507
suite = TestUtil.TestSuite()
2508
3613
loader = TestUtil.TestLoader()
3615
if keep_only is not None:
3616
id_filter = TestIdList(keep_only)
3618
# We take precedence over keep_only because *at loading time* using
3619
# both options means we will load less tests for the same final result.
3620
def interesting_module(name):
3621
for start in starting_with:
3623
# Either the module name starts with the specified string
3624
name.startswith(start)
3625
# or it may contain tests starting with the specified string
3626
or start.startswith(name)
3630
loader = TestUtil.FilteredByModuleTestLoader(interesting_module)
3632
elif keep_only is not None:
3633
loader = TestUtil.FilteredByModuleTestLoader(id_filter.refers_to)
3634
def interesting_module(name):
3635
return id_filter.refers_to(name)
3638
loader = TestUtil.TestLoader()
3639
def interesting_module(name):
3640
# No filtering, all modules are interesting
3643
suite = loader.suiteClass()
3645
# modules building their suite with loadTestsFromModuleNames
2509
3646
suite.addTest(loader.loadTestsFromModuleNames(testmod_names))
2510
from bzrlib.tests.test_transport_implementations import TransportTestProviderAdapter
2511
adapter = TransportTestProviderAdapter()
2512
adapt_modules(test_transport_implementations, adapter, loader, suite)
2513
for package in packages_to_test():
2514
suite.addTest(package.test_suite())
2515
for m in MODULES_TO_TEST:
2516
suite.addTest(loader.loadTestsFromModule(m))
2517
for m in MODULES_TO_DOCTEST:
3648
modules_to_doctest = [
3650
'bzrlib.branchbuilder',
3653
'bzrlib.iterablefile',
3657
'bzrlib.symbol_versioning',
3660
'bzrlib.version_info_formats.format_custom',
3663
for mod in modules_to_doctest:
3664
if not interesting_module(mod):
3665
# No tests to keep here, move along
2519
suite.addTest(doctest.DocTestSuite(m))
3668
# note that this really does mean "report only" -- doctest
3669
# still runs the rest of the examples
3670
doc_suite = doctest.DocTestSuite(mod,
3671
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
2520
3672
except ValueError, e:
2521
print '**failed to get doctest for: %s\n%s' %(m,e)
3673
print '**failed to get doctest for: %s\n%s' % (mod, e)
3675
if len(doc_suite._tests) == 0:
3676
raise errors.BzrError("no doctests found in %s" % (mod,))
3677
suite.addTest(doc_suite)
2523
3679
default_encoding = sys.getdefaultencoding()
2524
3680
for name, plugin in bzrlib.plugin.plugins().items():
2526
plugin_suite = plugin.test_suite()
2527
except ImportError, e:
2528
bzrlib.trace.warning(
2529
'Unable to test plugin "%s": %s', name, e)
2531
if plugin_suite is not None:
2532
suite.addTest(plugin_suite)
3681
if not interesting_module(plugin.module.__name__):
3683
plugin_suite = plugin.test_suite()
3684
# We used to catch ImportError here and turn it into just a warning,
3685
# but really if you don't have --no-plugins this should be a failure.
3686
# mbp 20080213 - see http://bugs.launchpad.net/bugs/189771
3687
if plugin_suite is None:
3688
plugin_suite = plugin.load_plugin_tests(loader)
3689
if plugin_suite is not None:
3690
suite.addTest(plugin_suite)
2533
3691
if default_encoding != sys.getdefaultencoding():
2534
3692
bzrlib.trace.warning(
2535
3693
'Plugin "%s" tried to reset default encoding to: %s', name,
2536
3694
sys.getdefaultencoding())
2538
3696
sys.setdefaultencoding(default_encoding)
2542
def multiply_tests_from_modules(module_name_list, scenario_iter):
2543
"""Adapt all tests in some given modules to given scenarios.
2545
This is the recommended public interface for test parameterization.
2546
Typically the test_suite() method for a per-implementation test
2547
suite will call multiply_tests_from_modules and return the
2550
:param module_name_list: List of fully-qualified names of test
2552
:param scenario_iter: Iterable of pairs of (scenario_name,
2553
scenario_param_dict).
2555
This returns a new TestSuite containing the cross product of
2556
all the tests in all the modules, each repeated for each scenario.
2557
Each test is adapted by adding the scenario name at the end
2558
of its name, and updating the test object's __dict__ with the
2559
scenario_param_dict.
2561
>>> r = multiply_tests_from_modules(
2562
... ['bzrlib.tests.test_sampler'],
2563
... [('one', dict(param=1)),
2564
... ('two', dict(param=2))])
2565
>>> tests = list(iter_suite_tests(r))
2569
'bzrlib.tests.test_sampler.DemoTest.test_nothing(one)'
2575
loader = TestLoader()
2577
adapter = TestScenarioApplier()
2578
adapter.scenarios = list(scenario_iter)
2579
adapt_modules(module_name_list, adapter, loader, suite)
3698
if keep_only is not None:
3699
# Now that the referred modules have loaded their tests, keep only the
3701
suite = filter_suite_by_id_list(suite, id_filter)
3702
# Do some sanity checks on the id_list filtering
3703
not_found, duplicates = suite_matches_id_list(suite, keep_only)
3705
# The tester has used both keep_only and starting_with, so he is
3706
# already aware that some tests are excluded from the list, there
3707
# is no need to tell him which.
3710
# Some tests mentioned in the list are not in the test suite. The
3711
# list may be out of date, report to the tester.
3712
for id in not_found:
3713
bzrlib.trace.warning('"%s" not found in the test suite', id)
3714
for id in duplicates:
3715
bzrlib.trace.warning('"%s" is used as an id by several tests', id)
2594
3731
for right_name, right_dict in scenarios_right]
2598
def adapt_modules(mods_list, adapter, loader, suite):
2599
"""Adapt the modules in mods_list using adapter and add to suite."""
2600
for test in iter_suite_tests(loader.loadTestsFromModuleNames(mods_list)):
2601
suite.addTests(adapter.adapt(test))
3734
def multiply_tests(tests, scenarios, result):
3735
"""Multiply tests_list by scenarios into result.
3737
This is the core workhorse for test parameterisation.
3739
Typically the load_tests() method for a per-implementation test suite will
3740
call multiply_tests and return the result.
3742
:param tests: The tests to parameterise.
3743
:param scenarios: The scenarios to apply: pairs of (scenario_name,
3744
scenario_param_dict).
3745
:param result: A TestSuite to add created tests to.
3747
This returns the passed in result TestSuite with the cross product of all
3748
the tests repeated once for each scenario. Each test is adapted by adding
3749
the scenario name at the end of its id(), and updating the test object's
3750
__dict__ with the scenario_param_dict.
3752
>>> import bzrlib.tests.test_sampler
3753
>>> r = multiply_tests(
3754
... bzrlib.tests.test_sampler.DemoTest('test_nothing'),
3755
... [('one', dict(param=1)),
3756
... ('two', dict(param=2))],
3758
>>> tests = list(iter_suite_tests(r))
3762
'bzrlib.tests.test_sampler.DemoTest.test_nothing(one)'
3768
for test in iter_suite_tests(tests):
3769
apply_scenarios(test, scenarios, result)
3773
def apply_scenarios(test, scenarios, result):
3774
"""Apply the scenarios in scenarios to test and add to result.
3776
:param test: The test to apply scenarios to.
3777
:param scenarios: An iterable of scenarios to apply to test.
3779
:seealso: apply_scenario
3781
for scenario in scenarios:
3782
result.addTest(apply_scenario(test, scenario))
3786
def apply_scenario(test, scenario):
3787
"""Copy test and apply scenario to it.
3789
:param test: A test to adapt.
3790
:param scenario: A tuple describing the scenarion.
3791
The first element of the tuple is the new test id.
3792
The second element is a dict containing attributes to set on the
3794
:return: The adapted test.
3796
new_id = "%s(%s)" % (test.id(), scenario[0])
3797
new_test = clone_test(test, new_id)
3798
for name, value in scenario[1].items():
3799
setattr(new_test, name, value)
3803
def clone_test(test, new_id):
3804
"""Clone a test giving it a new id.
3806
:param test: The test to clone.
3807
:param new_id: The id to assign to it.
3808
:return: The new test.
3810
from copy import deepcopy
3811
new_test = deepcopy(test)
3812
new_test.id = lambda: new_id
2604
3816
def _rmtree_temp_dir(dirname):
2739
class _FTPServerFeature(Feature):
2740
"""Some tests want an FTP Server, check if one is available.
2742
Right now, the only way this is available is if 'medusa' is installed.
2743
http://www.amk.ca/python/code/medusa.html
2748
import bzrlib.tests.ftp_server
2753
def feature_name(self):
2756
FTPServerFeature = _FTPServerFeature()
3956
class _HTTPSServerFeature(Feature):
3957
"""Some tests want an https Server, check if one is available.
3959
Right now, the only way this is available is under python2.6 which provides
3970
def feature_name(self):
3971
return 'HTTPSServer'
3974
HTTPSServerFeature = _HTTPSServerFeature()
3977
class _UnicodeFilename(Feature):
3978
"""Does the filesystem support Unicode filenames?"""
3983
except UnicodeEncodeError:
3985
except (IOError, OSError):
3986
# The filesystem allows the Unicode filename but the file doesn't
3990
# The filesystem allows the Unicode filename and the file exists,
3994
UnicodeFilename = _UnicodeFilename()
3997
class _UTF8Filesystem(Feature):
3998
"""Is the filesystem UTF-8?"""
4001
if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
4005
UTF8Filesystem = _UTF8Filesystem()
4008
class _CaseInsCasePresFilenameFeature(Feature):
4009
"""Is the file-system case insensitive, but case-preserving?"""
4012
fileno, name = tempfile.mkstemp(prefix='MixedCase')
4014
# first check truly case-preserving for created files, then check
4015
# case insensitive when opening existing files.
4016
name = osutils.normpath(name)
4017
base, rel = osutils.split(name)
4018
found_rel = osutils.canonical_relpath(base, name)
4019
return (found_rel == rel
4020
and os.path.isfile(name.upper())
4021
and os.path.isfile(name.lower()))
4026
def feature_name(self):
4027
return "case-insensitive case-preserving filesystem"
4029
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
4032
class _CaseInsensitiveFilesystemFeature(Feature):
4033
"""Check if underlying filesystem is case-insensitive but *not* case
4036
# Note that on Windows, Cygwin, MacOS etc, the file-systems are far
4037
# more likely to be case preserving, so this case is rare.
4040
if CaseInsCasePresFilenameFeature.available():
4043
if TestCaseWithMemoryTransport.TEST_ROOT is None:
4044
root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
4045
TestCaseWithMemoryTransport.TEST_ROOT = root
4047
root = TestCaseWithMemoryTransport.TEST_ROOT
4048
tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
4050
name_a = osutils.pathjoin(tdir, 'a')
4051
name_A = osutils.pathjoin(tdir, 'A')
4053
result = osutils.isdir(name_A)
4054
_rmtree_temp_dir(tdir)
4057
def feature_name(self):
4058
return 'case-insensitive filesystem'
4060
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
4063
class _SubUnitFeature(Feature):
4064
"""Check if subunit is available."""
4073
def feature_name(self):
4076
SubUnitFeature = _SubUnitFeature()
4077
# Only define SubUnitBzrRunner if subunit is available.
4079
from subunit import TestProtocolClient
4081
from subunit.test_results import AutoTimingTestResultDecorator
4083
AutoTimingTestResultDecorator = lambda x:x
4084
class SubUnitBzrRunner(TextTestRunner):
4085
def run(self, test):
4086
result = AutoTimingTestResultDecorator(
4087
TestProtocolClient(self.stream))