103
105
import bzrlib.trace
104
106
from bzrlib.transport import (
109
import bzrlib.transport
110
110
from bzrlib.trace import mutter, note
111
111
from bzrlib.tests import (
115
from bzrlib.tests.http_server import HttpServer
116
from bzrlib.tests.TestUtil import (
120
from bzrlib.tests.treeshape import build_tree_contents
121
116
from bzrlib.ui import NullProgressView
122
117
from bzrlib.ui.text import TextUIFactory
123
118
import bzrlib.version_info_formats.format_custom
124
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
126
120
# Mark this python module as being part of the implementation
127
121
# of unittest: this gives us better tracebacks where the last
195
193
self._overall_start_time = time.time()
196
194
self._strict = strict
195
self._first_thread_leaker_id = None
196
self._tests_leaking_threads_count = 0
197
self._traceback_from_test = None
198
199
def stopTestRun(self):
199
200
run = self.testsRun
200
201
actionTaken = "Ran"
201
202
stopTime = time.time()
202
203
timeTaken = stopTime - self.startTime
204
self.stream.writeln(self.separator2)
205
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,
206
210
run, run != 1 and "s" or "", timeTaken))
207
self.stream.writeln()
208
211
if not self.wasSuccessful():
209
212
self.stream.write("FAILED (")
210
213
failed, errored = map(len, (self.failures, self.errors))
217
220
if failed or errored: self.stream.write(", ")
218
221
self.stream.write("known_failure_count=%d" %
219
222
self.known_failure_count)
220
self.stream.writeln(")")
223
self.stream.write(")\n")
222
225
if self.known_failure_count:
223
self.stream.writeln("OK (known_failures=%d)" %
226
self.stream.write("OK (known_failures=%d)\n" %
224
227
self.known_failure_count)
226
self.stream.writeln("OK")
229
self.stream.write("OK\n")
227
230
if self.skip_count > 0:
228
231
skipped = self.skip_count
229
self.stream.writeln('%d test%s skipped' %
232
self.stream.write('%d test%s skipped\n' %
230
233
(skipped, skipped != 1 and "s" or ""))
231
234
if self.unsupported:
232
235
for feature, count in sorted(self.unsupported.items()):
233
self.stream.writeln("Missing feature '%s' skipped %d tests." %
236
self.stream.write("Missing feature '%s' skipped %d tests.\n" %
234
237
(feature, count))
236
239
ok = self.wasStrictlySuccessful()
238
241
ok = self.wasSuccessful()
239
if TestCase._first_thread_leaker_id:
242
if self._first_thread_leaker_id:
240
243
self.stream.write(
241
244
'%s is leaking threads among %d leaking tests.\n' % (
242
TestCase._first_thread_leaker_id,
243
TestCase._leaking_threads_tests))
245
self._first_thread_leaker_id,
246
self._tests_leaking_threads_count))
244
247
# We don't report the main thread as an active one.
245
248
self.stream.write(
246
249
'%d non-main threads were left active in the end.\n'
247
% (TestCase._active_threads - 1))
250
% (len(self._active_threads) - 1))
249
252
def getDescription(self, test):
275
279
def _shortened_test_description(self, test):
277
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]
280
292
def startTest(self, test):
281
unittest.TestResult.startTest(self, test)
293
super(ExtendedTestResult, self).startTest(test)
282
294
if self.count == 0:
283
295
self.startTests()
284
297
self.report_test_start(test)
285
298
test.number = self.count
286
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)
288
309
def startTests(self):
290
if getattr(sys, 'frozen', None) is None:
291
bzr_path = osutils.realpath(sys.argv[0])
293
bzr_path = sys.executable
295
'bzr selftest: %s\n' % (bzr_path,))
298
bzrlib.__path__[0],))
300
' bzr-%s python-%s %s\n' % (
301
bzrlib.version_string,
302
bzrlib._format_version_tuple(sys.version_info),
303
platform.platform(aliased=1),
305
self.stream.write('\n')
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
307
332
def _recordTestStartTime(self):
308
333
"""Record that a test has started."""
309
self._start_time = time.time()
311
def _cleanupLogFile(self, test):
312
# We can only do this if we have one of our TestCases, not if
314
setKeepLogfile = getattr(test, 'setKeepLogfile', None)
315
if setKeepLogfile is not None:
334
self._start_datetime = self._now()
318
336
def addError(self, test, err):
319
337
"""Tell result that test finished with an error.
321
339
Called from the TestCase run() method when the test
322
340
fails with an unexpected error.
325
unittest.TestResult.addError(self, test, err)
342
self._post_mortem(self._traceback_from_test)
343
super(ExtendedTestResult, self).addError(test, err)
326
344
self.error_count += 1
327
345
self.report_error(test, err)
328
346
if self.stop_early:
330
self._cleanupLogFile(test)
332
349
def addFailure(self, test, err):
333
350
"""Tell result that test failed.
335
352
Called from the TestCase run() method when the test
336
353
fails because e.g. an assert() method failed.
339
unittest.TestResult.addFailure(self, test, err)
355
self._post_mortem(self._traceback_from_test)
356
super(ExtendedTestResult, self).addFailure(test, err)
340
357
self.failure_count += 1
341
358
self.report_failure(test, err)
342
359
if self.stop_early:
344
self._cleanupLogFile(test)
346
362
def addSuccess(self, test, details=None):
347
363
"""Tell result that test completed successfully.
400
416
raise errors.BzrError("Unknown whence %r" % whence)
402
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)))
405
450
def startTestRun(self):
406
451
self.startTime = time.time()
443
488
self.pb.finished()
444
489
super(TextTestResult, self).stopTestRun()
446
def startTestRun(self):
447
super(TextTestResult, self).startTestRun()
491
def report_tests_starting(self):
492
super(TextTestResult, self).report_tests_starting()
448
493
self.pb.update('[test 0/%d] Starting' % (self.num_tests))
450
def printErrors(self):
451
# clear the pb to make room for the error listing
453
super(TextTestResult, self).printErrors()
455
495
def _progress_prefix_text(self):
456
496
# the longer this text, the less space we have to show the test
489
528
return self._shortened_test_description(test)
491
530
def report_error(self, test, err):
492
self.ui.note('ERROR: %s\n %s\n' % (
531
self.stream.write('ERROR: %s\n %s\n' % (
493
532
self._test_description(test),
497
536
def report_failure(self, test, err):
498
self.ui.note('FAIL: %s\n %s\n' % (
537
self.stream.write('FAIL: %s\n %s\n' % (
499
538
self._test_description(test),
550
585
return '%s%s' % (indent, err[1])
552
587
def report_error(self, test, err):
553
self.stream.writeln('ERROR %s\n%s'
588
self.stream.write('ERROR %s\n%s\n'
554
589
% (self._testTimeString(test),
555
590
self._error_summary(err)))
557
592
def report_failure(self, test, err):
558
self.stream.writeln(' FAIL %s\n%s'
593
self.stream.write(' FAIL %s\n%s\n'
559
594
% (self._testTimeString(test),
560
595
self._error_summary(err)))
562
597
def report_known_failure(self, test, err):
563
self.stream.writeln('XFAIL %s\n%s'
598
self.stream.write('XFAIL %s\n%s\n'
564
599
% (self._testTimeString(test),
565
600
self._error_summary(err)))
567
602
def report_success(self, test):
568
self.stream.writeln(' OK %s' % self._testTimeString(test))
603
self.stream.write(' OK %s\n' % self._testTimeString(test))
569
604
for bench_called, stats in getattr(test, '_benchcalls', []):
570
self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
605
self.stream.write('LSProf output for %s(%s, %s)\n' % bench_called)
571
606
stats.pprint(file=self.stream)
572
607
# flush the stream so that we get smooth output. This verbose mode is
573
608
# used to show the output in PQM.
574
609
self.stream.flush()
576
611
def report_skip(self, test, reason):
577
self.stream.writeln(' SKIP %s\n%s'
612
self.stream.write(' SKIP %s\n%s\n'
578
613
% (self._testTimeString(test), reason))
580
615
def report_not_applicable(self, test, reason):
581
self.stream.writeln(' N/A %s\n %s'
616
self.stream.write(' N/A %s\n %s\n'
582
617
% (self._testTimeString(test), reason))
584
619
def report_unsupported(self, test, feature):
585
620
"""test cannot be run because feature is missing."""
586
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"
587
622
%(self._testTimeString(test), feature))
788
823
routine, and to build and check bzr trees.
790
825
In addition to the usual method of overriding tearDown(), this class also
791
allows subclasses to register functions into the _cleanups list, which is
826
allows subclasses to register cleanup functions via addCleanup, which are
792
827
run in order as the object is torn down. It's less likely this will be
793
828
accidentally overlooked.
796
_active_threads = None
797
_leaking_threads_tests = 0
798
_first_thread_leaker_id = None
799
_log_file_name = None
800
832
# record lsprof data when performing benchmark calls.
801
833
_gather_lsprof_in_benchmarks = False
803
835
def __init__(self, methodName='testMethod'):
804
836
super(TestCase, self).__init__(methodName)
806
837
self._directory_isolation = True
807
838
self.exception_handlers.insert(0,
808
839
(UnavailableFeature, self._do_unsupported_or_skip))
826
857
self._track_transports()
827
858
self._track_locks()
828
859
self._clear_debug_flags()
829
TestCase._active_threads = threading.activeCount()
830
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)
833
866
# debug a frame up.
835
868
pdb.Pdb().set_trace(sys._getframe().f_back)
837
def _check_leaked_threads(self):
838
active = threading.activeCount()
839
leaked_threads = active - TestCase._active_threads
840
TestCase._active_threads = active
841
# If some tests make the number of threads *decrease*, we'll consider
842
# that they are just observing old threads dieing, not agressively kill
843
# random threads. So we don't report these tests as leaking. The risk
844
# is that we have false positives that way (the test see 2 threads
845
# going away but leak one) but it seems less likely than the actual
846
# false positives (the test see threads going away and does not leak).
847
if leaked_threads > 0:
848
TestCase._leaking_threads_tests += 1
849
if TestCase._first_thread_leaker_id is None:
850
TestCase._first_thread_leaker_id = self.id()
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()
852
886
def _clear_debug_flags(self):
853
887
"""Prevent externally set debug flags affecting tests.
865
899
def _clear_hooks(self):
866
900
# prevent hooks affecting tests
901
known_hooks = hooks.known_hooks
867
902
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)
903
for key, (parent, name) in known_hooks.iter_parent_objects():
904
current_hooks = getattr(parent, name)
871
905
self._preserved_hooks[parent] = (name, current_hooks)
872
906
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)
907
for key, (parent, name) in known_hooks.iter_parent_objects():
908
factory = known_hooks.get(key)
875
909
setattr(parent, name, factory())
876
910
# this hook should always be installed
877
911
request._install_hook()
1313
1347
self.assertEqualDiff(content, s)
1349
def assertDocstring(self, expected_docstring, obj):
1350
"""Fail if obj does not have expected_docstring"""
1352
# With -OO the docstring should be None instead
1353
self.assertIs(obj.__doc__, None)
1355
self.assertEqual(expected_docstring, obj.__doc__)
1315
1357
def failUnlessExists(self, path):
1316
1358
"""Fail unless path or paths, which may be abs or relative, exist."""
1317
1359
if not isinstance(path, basestring):
1478
1518
debug.debug_flags.discard('strict_locks')
1480
def addCleanup(self, callable, *args, **kwargs):
1481
"""Arrange to run a callable when this case is torn down.
1483
Callables are run in the reverse of the order they are registered,
1484
ie last-in first-out.
1486
self._cleanups.append((callable, args, kwargs))
1488
1520
def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1489
1521
"""Overrides an object attribute restoring it after the test.
1603
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)
1606
1665
def _do_unsupported_or_skip(self, result, e):
1607
1666
reason = e.args[0]
1667
self._suppress_log()
1608
1668
addNotSupported = getattr(result, 'addNotSupported', None)
1609
1669
if addNotSupported is not None:
1610
1670
result.addNotSupported(self, reason)
1657
1717
unicodestr = self._log_contents.decode('utf8', 'replace')
1658
1718
self._log_contents = unicodestr.encode('utf8')
1659
1719
return self._log_contents
1661
if bzrlib.trace._trace_file:
1662
# flush the log file, to get all content
1663
bzrlib.trace._trace_file.flush()
1664
if self._log_file_name is not None:
1665
logfile = open(self._log_file_name)
1667
log_contents = logfile.read()
1720
if self._log_file is not None:
1721
log_contents = self._log_file.getvalue()
1671
1723
log_contents.decode('utf8')
1672
1724
except UnicodeDecodeError:
1673
1725
unicodestr = log_contents.decode('utf8', 'replace')
1674
1726
log_contents = unicodestr.encode('utf8')
1675
1727
if not keep_log_file:
1677
max_close_attempts = 100
1678
first_close_error = None
1679
while close_attempts < max_close_attempts:
1682
self._log_file.close()
1683
except IOError, ioe:
1684
if ioe.errno is None:
1685
# No errno implies 'close() called during
1686
# concurrent operation on the same file object', so
1687
# retry. Probably a thread is trying to write to
1689
if first_close_error is None:
1690
first_close_error = ioe
1695
if close_attempts > 1:
1697
'Unable to close log file on first attempt, '
1698
'will retry: %s\n' % (first_close_error,))
1699
if close_attempts == max_close_attempts:
1701
'Unable to close log file after %d attempts.\n'
1702
% (max_close_attempts,))
1703
1728
self._log_file = None
1704
1729
# Permit multiple calls to get_log until we clean it up in
1705
1730
# finishLogFile
1706
1731
self._log_contents = log_contents
1708
os.remove(self._log_file_name)
1710
if sys.platform == 'win32' and e.errno == errno.EACCES:
1711
sys.stderr.write(('Unable to delete log file '
1712
' %r\n' % self._log_file_name))
1715
self._log_file_name = None
1716
1732
return log_contents
1718
return "No log file content and no log file name."
1734
return "No log file content."
1720
1736
def get_log(self):
1721
1737
"""Get a unicode string containing the log from bzrlib.trace.
1936
1952
variables. A value of None will unset the env variable.
1937
1953
The values must be strings. The change will only occur in the
1938
1954
child, so you don't need to fix the environment after running.
1939
: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.
1941
1957
:param allow_plugins: If False (default) pass --no-plugins to bzr.
1943
1959
:returns: Popen object for the started process.
1945
1961
if skip_if_plan_to_signal:
1946
if not getattr(os, 'kill', None):
1947
raise TestSkipped("os.kill not available.")
1962
if os.name != "posix":
1963
raise TestSkipped("Sending signals not supported")
1949
1965
if env_changes is None:
1950
1966
env_changes = {}
2399
2417
made_control = self.make_bzrdir(relpath, format=format)
2400
2418
return made_control.create_repository(shared=shared)
2402
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()
2403
2423
smart_server = test_server.SmartTCPServer_for_testing()
2404
self.start_server(smart_server, self.get_server())
2405
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()
2406
2427
return remote_transport
2408
2429
def make_branch_and_memory_tree(self, relpath, format=None):
2424
2445
def setUp(self):
2425
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)
2426
2456
self._make_test_root()
2427
2457
self.addCleanup(os.chdir, os.getcwdu())
2428
2458
self.makeAndChdirToTestDir()
3182
3218
def partition_tests(suite, count):
3183
3219
"""Partition suite into count lists of tests."""
3185
tests = list(iter_suite_tests(suite))
3186
tests_per_process = int(math.ceil(float(len(tests)) / count))
3187
for block in range(count):
3188
low_test = block * tests_per_process
3189
high_test = low_test + tests_per_process
3190
process_tests = tests[low_test:high_test]
3191
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)
3232
def workaround_zealous_crypto_random():
3233
"""Crypto.Random want to help us being secure, but we don't care here.
3235
This workaround some test failure related to the sftp server. Once paramiko
3236
stop using the controversial API in Crypto.Random, we may get rid of it.
3239
from Crypto.Random import atfork
3195
3245
def fork_for_tests(suite):
3213
3263
ProtocolTestCase.run(self, result)
3215
os.waitpid(self.pid, os.WNOHANG)
3265
os.waitpid(self.pid, 0)
3217
3267
test_blocks = partition_tests(suite, concurrency)
3218
3268
for process_tests in test_blocks:
3219
process_suite = TestSuite()
3269
process_suite = TestUtil.TestSuite()
3220
3270
process_suite.addTests(process_tests)
3221
3271
c2pread, c2pwrite = os.pipe()
3222
3272
pid = os.fork()
3274
workaround_zealous_crypto_random()
3225
3276
os.close(c2pread)
3226
3277
# Leave stderr and stdout open so we can see test noise
3288
3339
if '--no-plugins' in sys.argv:
3289
3340
argv.append('--no-plugins')
3290
# stderr=STDOUT would be ideal, but until we prevent noise on
3291
# stderr it can interrupt the subunit protocol.
3292
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,
3294
3347
test = TestInSubprocess(process, test_list_file_name)
3295
3348
result.append(test)
3302
class ForwardingResult(unittest.TestResult):
3304
def __init__(self, target):
3305
unittest.TestResult.__init__(self)
3306
self.result = target
3308
def startTest(self, test):
3309
self.result.startTest(test)
3311
def stopTest(self, test):
3312
self.result.stopTest(test)
3314
def startTestRun(self):
3315
self.result.startTestRun()
3317
def stopTestRun(self):
3318
self.result.stopTestRun()
3320
def addSkip(self, test, reason):
3321
self.result.addSkip(test, reason)
3323
def addSuccess(self, test):
3324
self.result.addSuccess(test)
3326
def addError(self, test, err):
3327
self.result.addError(test, err)
3329
def addFailure(self, test, err):
3330
self.result.addFailure(test, err)
3331
ForwardingResult = testtools.ExtendedToOriginalDecorator
3334
class ProfileResult(ForwardingResult):
3355
class ProfileResult(testtools.ExtendedToOriginalDecorator):
3335
3356
"""Generate profiling data for all activity between start and success.
3337
3358
The profile data is appended to the test's _benchcalls attribute and can
3610
3636
'bzrlib.tests.blackbox',
3611
3637
'bzrlib.tests.commands',
3638
'bzrlib.tests.doc_generate',
3612
3639
'bzrlib.tests.per_branch',
3613
3640
'bzrlib.tests.per_bzrdir',
3641
'bzrlib.tests.per_controldir',
3642
'bzrlib.tests.per_controldir_colo',
3614
3643
'bzrlib.tests.per_foreign_vcs',
3615
3644
'bzrlib.tests.per_interrepository',
3616
3645
'bzrlib.tests.per_intertree',
3920
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):
3921
3977
"""Multiply two sets of scenarios.
3923
3979
:returns: the cartesian product of the two sets of scenarios, that is
4007
4063
:param new_id: The id to assign to it.
4008
4064
:return: The new test.
4010
new_test = copy(test)
4066
new_test = copy.copy(test)
4011
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 = {}
4012
4080
return new_test
4074
4142
if test_id != None:
4075
4143
ui.ui_factory.clear_term()
4076
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')
4077
4148
sys.stderr.write('Unable to remove testing dir %s\n%s'
4078
% (os.path.basename(dirname), e))
4149
% (os.path.basename(dirname), printable_e))
4081
4152
class Feature(object):
4427
4508
from subunit import TestProtocolClient
4428
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(
4429
4520
class SubUnitBzrRunner(TextTestRunner):
4430
4521
def run(self, test):
4431
4522
result = AutoTimingTestResultDecorator(
4432
TestProtocolClient(self.stream))
4523
SubUnitBzrProtocolClient(self.stream))
4433
4524
test.run(result)
4435
4526
except ImportError:
4529
class _PosixPermissionsFeature(Feature):
4533
# create temporary file and check if specified perms are maintained.
4536
write_perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
4537
f = tempfile.mkstemp(prefix='bzr_perms_chk_')
4540
os.chmod(name, write_perms)
4542
read_perms = os.stat(name).st_mode & 0777
4544
return (write_perms == read_perms)
4546
return (os.name == 'posix') and has_perms()
4548
def feature_name(self):
4549
return 'POSIX permissions support'
4551
posix_permissions_feature = _PosixPermissionsFeature()