61
67
from bzrlib.merge import merge_inner
62
68
import bzrlib.merge3
63
69
import bzrlib.osutils
64
import bzrlib.osutils as osutils
65
70
import bzrlib.plugin
66
import bzrlib.progress as progress
67
71
from bzrlib.revision import common_ancestor
68
72
import bzrlib.store
69
73
from bzrlib import symbol_versioning
70
74
import bzrlib.trace
71
75
from bzrlib.transport import get_transport
72
76
import bzrlib.transport
73
from bzrlib.transport.local import LocalRelpathServer
77
from bzrlib.transport.local import LocalURLServer
74
78
from bzrlib.transport.memory import MemoryServer
75
79
from bzrlib.transport.readonly import ReadonlyServer
76
from bzrlib.trace import mutter
80
from bzrlib.trace import mutter, note
77
81
from bzrlib.tests import TestUtil
82
from bzrlib.tests.HttpServer import HttpServer
78
83
from bzrlib.tests.TestUtil import (
82
87
from bzrlib.tests.treeshape import build_tree_contents
83
import bzrlib.urlutils as urlutils
84
88
from bzrlib.workingtree import WorkingTree, WorkingTreeFormat2
86
default_transport = LocalRelpathServer
90
default_transport = LocalURLServer
88
92
MODULES_TO_TEST = []
89
93
MODULES_TO_DOCTEST = [
91
94
bzrlib.bundle.serializer,
138
class _MyResult(unittest._TextTestResult):
139
"""Custom TestResult.
138
class ExtendedTestResult(unittest._TextTestResult):
139
"""Accepts, reports and accumulates the results of running tests.
141
Shows output in a different format, including displaying runtime for tests.
141
Compared to this unittest version this class adds support for profiling,
142
benchmarking, stopping as soon as a test fails, and skipping tests.
143
There are further-specialized subclasses for different types of display.
143
146
stop_early = False
145
def __init__(self, stream, descriptions, verbosity, pb=None,
148
def __init__(self, stream, descriptions, verbosity,
147
152
"""Construct new TestResult.
149
154
:param bench_history: Optionally, a writable file object to accumulate
150
155
benchmark results.
152
157
unittest._TextTestResult.__init__(self, stream, descriptions, verbosity)
154
158
if bench_history is not None:
155
159
from bzrlib.version import _get_bzr_source_tree
156
160
src_tree = _get_bzr_source_tree()
181
192
self._formatTime(self._benchmarkTime),
182
193
self._elapsedTestTimeString())
184
return " %s" % self._elapsedTestTimeString()
195
return " %s" % self._elapsedTestTimeString()
186
197
def _formatTime(self, seconds):
187
198
"""Format seconds as milliseconds with leading spaces."""
188
return "%5dms" % (1000 * seconds)
199
# some benchmarks can take thousands of seconds to run, so we need 8
201
return "%8dms" % (1000 * seconds)
190
def _ellipsise_unimportant_words(self, a_string, final_width,
192
"""Add ellipses (sp?) for overly long strings.
194
:param keep_start: If true preserve the start of a_string rather
198
if len(a_string) > final_width:
199
result = a_string[:final_width-3] + '...'
203
if len(a_string) > final_width:
204
result = '...' + a_string[3-final_width:]
207
return result.ljust(final_width)
203
def _shortened_test_description(self, test):
205
what = re.sub(r'^bzrlib\.(tests|benchmarks)\.', '', what)
209
208
def startTest(self, test):
210
209
unittest.TestResult.startTest(self, test)
211
# In a short description, the important words are in
212
# the beginning, but in an id, the important words are
214
SHOW_DESCRIPTIONS = False
216
if not self.showAll and self.dots and self.pb is not None:
219
final_width = osutils.terminal_width()
220
final_width = final_width - 15 - 8
222
if SHOW_DESCRIPTIONS:
223
what = test.shortDescription()
225
what = self._ellipsise_unimportant_words(what, final_width, keep_start=True)
228
if what.startswith('bzrlib.tests.'):
230
what = self._ellipsise_unimportant_words(what, final_width)
232
self.stream.write(what)
233
elif self.dots and self.pb is not None:
234
self.pb.update(what, self.testsRun - 1, None)
210
self.report_test_start(test)
236
211
self._recordTestStartTime()
238
213
def _recordTestStartTime(self):
290
247
self._bench_history.write("%s %s\n" % (
291
248
self._formatTime(self._benchmarkTime),
294
self.stream.writeln(' OK %s' % self._testTimeString())
295
for bench_called, stats in getattr(test, '_benchcalls', []):
296
self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
297
stats.pprint(file=self.stream)
298
elif self.dots and self.pb is None:
299
self.stream.write('~')
301
self.pb.update(self._ellipsise_unimportant_words('OK', 13), self.testsRun, None)
250
self.report_success(test)
303
251
unittest.TestResult.addSuccess(self, test)
305
253
def addSkipped(self, test, skip_excinfo):
306
254
self.extractBenchmarkTime(test)
308
print >>self.stream, ' SKIP %s' % self._testTimeString()
309
print >>self.stream, ' %s' % skip_excinfo[1]
310
elif self.dots and self.pb is None:
311
self.stream.write('S')
313
self.pb.update(self._ellipsise_unimportant_words('SKIP', 13), self.testsRun, None)
255
self.report_skip(test, skip_excinfo)
315
256
# seems best to treat this as success from point-of-view of unittest
316
257
# -- it actually does nothing so it barely matters :)
337
278
self.stream.writeln(self.separator2)
338
279
self.stream.writeln("%s" % err)
284
def report_cleaning_up(self):
287
def report_success(self, test):
291
class TextTestResult(ExtendedTestResult):
292
"""Displays progress and results of tests in text form"""
294
def __init__(self, *args, **kw):
295
ExtendedTestResult.__init__(self, *args, **kw)
296
self.pb = self.ui.nested_progress_bar()
297
self.pb.show_pct = False
298
self.pb.show_spinner = False
299
self.pb.show_eta = False,
300
self.pb.show_count = False
301
self.pb.show_bar = False
303
def report_starting(self):
304
self.pb.update('[test 0/%d] starting...' % (self.num_tests))
306
def _progress_prefix_text(self):
307
a = '[%d' % self.count
308
if self.num_tests is not None:
309
a +='/%d' % self.num_tests
310
a += ' in %ds' % (time.time() - self._overall_start_time)
312
a += ', %d errors' % self.error_count
313
if self.failure_count:
314
a += ', %d failed' % self.failure_count
316
a += ', %d skipped' % self.skip_count
320
def report_test_start(self, test):
323
self._progress_prefix_text()
325
+ self._shortened_test_description(test))
327
def report_error(self, test, err):
328
self.error_count += 1
329
self.pb.note('ERROR: %s\n %s\n',
330
self._shortened_test_description(test),
334
def report_failure(self, test, err):
335
self.failure_count += 1
336
self.pb.note('FAIL: %s\n %s\n',
337
self._shortened_test_description(test),
341
def report_skip(self, test, skip_excinfo):
344
# at the moment these are mostly not things we can fix
345
# and so they just produce stipple; use the verbose reporter
348
# show test and reason for skip
349
self.pb.note('SKIP: %s\n %s\n',
350
self._shortened_test_description(test),
353
# since the class name was left behind in the still-visible
355
self.pb.note('SKIP: %s', skip_excinfo[1])
357
def report_cleaning_up(self):
358
self.pb.update('cleaning up...')
364
class VerboseTestResult(ExtendedTestResult):
365
"""Produce long output, with one line per test run plus times"""
367
def _ellipsize_to_right(self, a_string, final_width):
368
"""Truncate and pad a string, keeping the right hand side"""
369
if len(a_string) > final_width:
370
result = '...' + a_string[3-final_width:]
373
return result.ljust(final_width)
375
def report_starting(self):
376
self.stream.write('running %d tests...\n' % self.num_tests)
378
def report_test_start(self, test):
380
name = self._shortened_test_description(test)
381
# width needs space for 6 char status, plus 1 for slash, plus 2 10-char
382
# numbers, plus a trailing blank
383
self.stream.write(self._ellipsize_to_right(name,
384
osutils.terminal_width()-30))
387
def report_error(self, test, err):
388
self.error_count += 1
389
self.stream.writeln('ERROR %s\n %s'
390
% (self._testTimeString(), err[1]))
392
def report_failure(self, test, err):
393
self.failure_count += 1
394
self.stream.writeln(' FAIL %s\n %s'
395
% (self._testTimeString(), err[1]))
397
def report_success(self, test):
398
self.stream.writeln(' OK %s' % self._testTimeString())
399
for bench_called, stats in getattr(test, '_benchcalls', []):
400
self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
401
stats.pprint(file=self.stream)
404
def report_skip(self, test, skip_excinfo):
405
print >>self.stream, ' SKIP %s' % self._testTimeString()
406
print >>self.stream, ' %s' % skip_excinfo[1]
341
409
class TextTestRunner(object):
342
410
stop_on_failure = False
348
416
keep_output=False,
350
417
bench_history=None):
351
418
self.stream = unittest._WritelnDecorator(stream)
352
419
self.descriptions = descriptions
353
420
self.verbosity = verbosity
354
421
self.keep_output = keep_output
356
422
self._bench_history = bench_history
358
def _makeResult(self):
359
result = _MyResult(self.stream,
363
bench_history=self._bench_history)
364
result.stop_early = self.stop_on_failure
367
424
def run(self, test):
368
425
"Run the given test case or test suite."
369
result = self._makeResult()
370
426
startTime = time.time()
371
if self.pb is not None:
372
self.pb.update('Running tests', 0, test.countTestCases())
427
if self.verbosity == 1:
428
result_class = TextTestResult
429
elif self.verbosity >= 2:
430
result_class = VerboseTestResult
431
result = result_class(self.stream,
434
bench_history=self._bench_history,
435
num_tests=test.countTestCases(),
437
result.stop_early = self.stop_on_failure
438
result.report_starting()
374
440
stopTime = time.time()
375
441
timeTaken = stopTime - startTime
575
643
raise AssertionError("value(s) %r not present in container %r" %
576
644
(missing, superlist))
646
def assertListRaises(self, excClass, func, *args, **kwargs):
647
"""Fail unless excClass is raised when the iterator from func is used.
649
Many functions can return generators this makes sure
650
to wrap them in a list() call to make sure the whole generator
651
is run, and that the proper exception is raised.
654
list(func(*args, **kwargs))
658
if getattr(excClass,'__name__', None) is not None:
659
excName = excClass.__name__
661
excName = str(excClass)
662
raise self.failureException, "%s not raised" % excName
578
664
def assertIs(self, left, right):
579
665
if not (left is right):
580
666
raise AssertionError("%r is not %r." % (left, right))
931
1036
The values must be strings. The change will only occur in the
932
1037
child, so you don't need to fix the environment after running.
933
1038
:param universal_newlines: Convert CRLF => LF
1039
:param allow_plugins: By default the subprocess is run with
1040
--no-plugins to ensure test reproducibility. Also, it is possible
1041
for system-wide plugins to create unexpected output on stderr,
1042
which can cause unnecessary test failures.
935
1044
env_changes = kwargs.get('env_changes', {})
936
1045
working_dir = kwargs.get('working_dir', None)
1046
allow_plugins = kwargs.get('allow_plugins', False)
937
1047
process = self.start_bzr_subprocess(args, env_changes=env_changes,
938
working_dir=working_dir)
1048
working_dir=working_dir,
1049
allow_plugins=allow_plugins)
939
1050
# We distinguish between retcode=None and retcode not passed.
940
1051
supplied_retcode = kwargs.get('retcode', 0)
941
1052
return self.finish_bzr_subprocess(process, retcode=supplied_retcode,
1398
1523
elif line_endings == 'native':
1399
1524
end = os.linesep
1401
raise errors.BzrError('Invalid line ending request %r' % (line_endings,))
1526
raise errors.BzrError(
1527
'Invalid line ending request %r' % line_endings)
1402
1528
content = "contents of %s%s" % (name.encode('utf-8'), end)
1403
# Technically 'put()' is the right command. However, put
1404
# uses an AtomicFile, which requires an extra rename into place
1405
# As long as the files didn't exist in the past, append() will
1406
# do the same thing as put()
1407
# On jam's machine, make_kernel_like_tree is:
1408
# put: 4.5-7.5s (averaging 6s)
1410
# put_non_atomic: 2.9-4.5s
1411
1529
transport.put_bytes_non_atomic(urlutils.escape(name), content)
1413
1531
def build_tree_contents(self, shape):
1416
1534
def assertFileEqual(self, content, path):
1417
1535
"""Fail if path does not contain 'content'."""
1418
self.failUnless(osutils.lexists(path))
1536
self.failUnlessExists(path)
1419
1537
# TODO: jam 20060427 Shouldn't this be 'rb'?
1420
1538
self.assertEqualDiff(content, open(path, 'r').read())
1540
def failUnlessExists(self, path):
1541
"""Fail unless path, which may be abs or relative, exists."""
1542
self.failUnless(osutils.lexists(path))
1544
def failIfExists(self, path):
1545
"""Fail if path, which may be abs or relative, exists."""
1546
self.failIf(osutils.lexists(path))
1423
1549
class TestCaseWithTransport(TestCaseInTempDir):
1424
1550
"""A test case that provides get_url and get_readonly_url facilities.
1661
def sort_suite_by_re(suite, pattern):
1664
filter_re = re.compile(pattern)
1665
for test in iter_suite_tests(suite):
1666
if filter_re.search(test.id()):
1670
return TestUtil.TestSuite(first + second)
1529
1673
def run_suite(suite, name='test', verbose=False, pattern=".*",
1530
1674
stop_on_failure=False, keep_output=False,
1531
transport=None, lsprof_timed=None, bench_history=None):
1675
transport=None, lsprof_timed=None, bench_history=None,
1676
matching_tests_first=None):
1532
1677
TestCase._gather_lsprof_in_benchmarks = lsprof_timed
1538
pb = progress.ProgressBar()
1539
1682
runner = TextTestRunner(stream=sys.stdout,
1540
1683
descriptions=0,
1541
1684
verbosity=verbosity,
1542
1685
keep_output=keep_output,
1544
1686
bench_history=bench_history)
1545
1687
runner.stop_on_failure=stop_on_failure
1546
1688
if pattern != '.*':
1547
suite = filter_suite_by_re(suite, pattern)
1689
if matching_tests_first:
1690
suite = sort_suite_by_re(suite, pattern)
1692
suite = filter_suite_by_re(suite, pattern)
1548
1693
result = runner.run(suite)
1549
1694
return result.wasSuccessful()
1630
1782
'bzrlib.tests.test_nonascii',
1631
1783
'bzrlib.tests.test_options',
1632
1784
'bzrlib.tests.test_osutils',
1785
'bzrlib.tests.test_osutils_encodings',
1633
1786
'bzrlib.tests.test_patch',
1634
1787
'bzrlib.tests.test_patches',
1635
1788
'bzrlib.tests.test_permissions',
1636
1789
'bzrlib.tests.test_plugins',
1637
1790
'bzrlib.tests.test_progress',
1638
1791
'bzrlib.tests.test_reconcile',
1792
'bzrlib.tests.test_registry',
1639
1793
'bzrlib.tests.test_repository',
1640
1794
'bzrlib.tests.test_revert',
1641
1795
'bzrlib.tests.test_revision',