37
41
from logging import debug, warning, error
45
class EarlyStoppingTestResultAdapter(object):
46
"""An adapter for TestResult to stop at the first first failure or error"""
48
def __init__(self, result):
51
def addError(self, test, err):
52
self._result.addError(test, err)
55
def addFailure(self, test, err):
56
self._result.addFailure(test, err)
59
def __getattr__(self, name):
60
return getattr(self._result, name)
62
def __setattr__(self, name, value):
64
object.__setattr__(self, name, value)
65
return setattr(self._result, name, value)
68
class _MyResult(unittest._TextTestResult):
72
No special behaviour for now.
75
def _elapsedTime(self):
76
return "(Took %.3fs)" % (time.time() - self._start_time)
78
def startTest(self, test):
79
unittest.TestResult.startTest(self, test)
80
# TODO: Maybe show test.shortDescription somewhere?
81
what = test.shortDescription() or test.id()
83
self.stream.write('%-70.70s' % what)
85
self._start_time = time.time()
87
def addError(self, test, err):
88
unittest.TestResult.addError(self, test, err)
90
self.stream.writeln("ERROR %s" % self._elapsedTime())
92
self.stream.write('E')
95
def addFailure(self, test, err):
96
unittest.TestResult.addFailure(self, test, err)
98
self.stream.writeln("FAIL %s" % self._elapsedTime())
100
self.stream.write('F')
103
def addSuccess(self, test):
105
self.stream.writeln('OK %s' % self._elapsedTime())
107
self.stream.write('~')
109
unittest.TestResult.addSuccess(self, test)
111
def printErrorList(self, flavour, errors):
112
for test, err in errors:
113
self.stream.writeln(self.separator1)
114
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
115
if hasattr(test, '_get_log'):
116
self.stream.writeln()
117
self.stream.writeln('log from this test:')
118
print >>self.stream, test._get_log()
119
self.stream.writeln(self.separator2)
120
self.stream.writeln("%s" % err)
123
class TextTestRunner(unittest.TextTestRunner):
125
def _makeResult(self):
126
result = _MyResult(self.stream, self.descriptions, self.verbosity)
127
return EarlyStoppingTestResultAdapter(result)
130
def iter_suite_tests(suite):
131
"""Return all tests in a suite, recursing through nested suites"""
132
for item in suite._tests:
133
if isinstance(item, unittest.TestCase):
135
elif isinstance(item, unittest.TestSuite):
136
for r in iter_suite_tests(item):
139
raise Exception('unknown object %r inside test suite %r'
143
class TestSkipped(Exception):
144
"""Indicates that a test was intentionally skipped, rather than failing."""
39
148
class CommandFailed(Exception):
53
162
routine, and to build and check bzr trees."""
165
_log_file_name = None
58
# this replaces the default testsweet.TestCase; we don't want logging changed
59
168
unittest.TestCase.setUp(self)
60
169
bzrlib.trace.disable_default_logging()
61
170
self._enable_file_logging()
172
def _ndiff_strings(self, a, b):
173
"""Return ndiff between two strings containing lines."""
174
difflines = difflib.ndiff(a.splitlines(True),
176
linejunk=lambda x: False,
177
charjunk=lambda x: False)
178
return ''.join(difflines)
180
def assertEqualDiff(self, a, b):
181
"""Assert two texts are equal, if not raise an exception.
183
This is intended for use with multi-line strings where it can
184
be hard to find the differences by eye.
186
# TODO: perhaps override assertEquals to call this for strings?
189
raise AssertionError("texts not equal:\n" +
190
self._ndiff_strings(a, b))
64
192
def _enable_file_logging(self):
65
193
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
84
211
self._log_file.close()
85
212
unittest.TestCase.tearDown(self)
88
214
def log(self, *args):
89
215
logging.debug(*args)
91
217
def _get_log(self):
92
218
"""Return as a string the log for this test"""
93
return open(self._log_file_name).read()
219
if self._log_file_name:
220
return open(self._log_file_name).read()
224
def capture(self, cmd):
225
"""Shortcut that splits cmd into words, runs, and returns stdout"""
226
return self.run_bzr_captured(cmd.split())[0]
228
def run_bzr_captured(self, argv, retcode=0):
229
"""Invoke bzr and return (result, stdout, stderr).
231
Useful for code that wants to check the contents of the
232
output, the way error messages are presented, etc.
234
This should be the main method for tests that want to exercise the
235
overall behavior of the bzr application (rather than a unit test
236
or a functional test of the library.)
238
Much of the old code runs bzr by forking a new copy of Python, but
239
that is slower, harder to debug, and generally not necessary.
241
This runs bzr through the interface that catches and reports
242
errors, and with logging set to something approximating the
243
default, so that error reporting can be checked.
245
argv -- arguments to invoke bzr
246
retcode -- expected return code, or None for don't-care.
250
self.log('run bzr: %s', ' '.join(argv))
251
handler = logging.StreamHandler(stderr)
252
handler.setFormatter(bzrlib.trace.QuietFormatter())
253
handler.setLevel(logging.INFO)
254
logger = logging.getLogger('')
255
logger.addHandler(handler)
257
result = self.apply_redirected(None, stdout, stderr,
258
bzrlib.commands.run_bzr_catch_errors,
261
logger.removeHandler(handler)
262
out = stdout.getvalue()
263
err = stderr.getvalue()
265
self.log('output:\n%s', out)
267
self.log('errors:\n%s', err)
268
if retcode is not None:
269
self.assertEquals(result, retcode)
95
272
def run_bzr(self, *args, **kwargs):
96
273
"""Invoke bzr, as if it were run from the command line.
99
276
overall behavior of the bzr application (rather than a unit test
100
277
or a functional test of the library.)
102
Much of the old code runs bzr by forking a new copy of Python, but
103
that is slower, harder to debug, and generally not necessary.
279
This sends the stdout/stderr results into the test's log,
280
where it may be useful for debugging. See also run_captured.
105
retcode = kwargs.get('retcode', 0)
106
result = self.apply_redirected(None, None, None,
107
bzrlib.commands.run_bzr, args)
108
self.assertEquals(result, retcode)
282
retcode = kwargs.pop('retcode', 0)
283
return self.run_bzr_captured(args, retcode)
111
285
def check_inventory_shape(self, inv, shape):
113
Compare an inventory to a list of expected names.
286
"""Compare an inventory to a list of expected names.
115
288
Fail if they are not precisely equal.
218
395
os.chdir(self.test_dir)
220
397
def tearDown(self):
222
398
os.chdir(self._currentdir)
223
399
super(TestCaseInTempDir, self).tearDown()
225
def _formcmd(self, cmd):
226
if isinstance(cmd, basestring):
229
cmd[0] = self.BZRPATH
230
if self.OVERRIDE_PYTHON:
231
cmd.insert(0, self.OVERRIDE_PYTHON)
232
self.log('$ %r' % cmd)
235
def runcmd(self, cmd, retcode=0):
236
"""Run one command and check the return code.
238
Returns a tuple of (stdout,stderr) strings.
240
If a single string is based, it is split into words.
241
For commands that are not simple space-separated words, please
242
pass a list instead."""
243
cmd = self._formcmd(cmd)
244
self.log('$ ' + ' '.join(cmd))
245
actual_retcode = subprocess.call(cmd, stdout=self._log_file,
246
stderr=self._log_file)
247
if retcode != actual_retcode:
248
raise CommandFailed("test failed: %r returned %d, expected %d"
249
% (cmd, actual_retcode, retcode))
251
def backtick(self, cmd, retcode=0):
252
"""Run a command and return its output"""
253
cmd = self._formcmd(cmd)
254
child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
255
outd, errd = child.communicate()
257
actual_retcode = child.wait()
259
outd = outd.replace('\r', '')
261
if retcode != actual_retcode:
262
raise CommandFailed("test failed: %r returned %d, expected %d"
263
% (cmd, actual_retcode, retcode))
269
401
def build_tree(self, shape):
270
402
"""Build a test tree according to a pattern.
433
def filter_suite_by_re(suite, pattern):
434
result = TestUtil.TestSuite()
435
filter_re = re.compile(pattern)
436
for test in iter_suite_tests(suite):
437
if filter_re.search(test.id()):
442
def run_suite(suite, name='test', verbose=False, pattern=".*"):
443
TestCaseInTempDir._TEST_NAME = name
448
runner = TextTestRunner(stream=sys.stdout,
452
suite = filter_suite_by_re(suite, pattern)
453
result = runner.run(suite)
454
# This is still a little bogus,
455
# but only a little. Folk not using our testrunner will
456
# have to delete their temp directories themselves.
457
if result.wasSuccessful():
458
if TestCaseInTempDir.TEST_ROOT is not None:
459
shutil.rmtree(TestCaseInTempDir.TEST_ROOT)
461
print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
462
return result.wasSuccessful()
299
465
def selftest(verbose=False, pattern=".*"):
300
466
"""Run the whole test suite under the enhanced runner"""
301
return testsweet.run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
467
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
304
470
def test_suite():
305
471
"""Build and return TestSuite for the whole program."""
306
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
307
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
308
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
472
import bzrlib.store, bzrlib.inventory, bzrlib.branch
473
import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
309
474
from doctest import DocTestSuite
315
476
global MODULES_TO_TEST, MODULES_TO_DOCTEST
317
478
testmod_names = \
318
479
['bzrlib.selftest.MetaTestLog',
480
'bzrlib.selftest.testidentitymap',
319
481
'bzrlib.selftest.testinv',
482
'bzrlib.selftest.test_ancestry',
483
'bzrlib.selftest.test_commit',
484
'bzrlib.selftest.test_commit_merge',
485
'bzrlib.selftest.testconfig',
320
486
'bzrlib.selftest.versioning',
321
487
'bzrlib.selftest.testmerge3',
488
'bzrlib.selftest.testmerge',
322
489
'bzrlib.selftest.testhashcache',
323
490
'bzrlib.selftest.teststatus',
324
491
'bzrlib.selftest.testlog',
325
492
'bzrlib.selftest.testrevisionnamespaces',
326
493
'bzrlib.selftest.testbranch',
327
# 'bzrlib.selftest.testrevision',
328
# 'bzrlib.selftest.test_merge_core',
494
'bzrlib.selftest.testrevision',
495
'bzrlib.selftest.test_revision_info',
496
'bzrlib.selftest.test_merge_core',
329
497
'bzrlib.selftest.test_smart_add',
498
'bzrlib.selftest.test_bad_files',
330
499
'bzrlib.selftest.testdiff',
331
# 'bzrlib.selftest.test_parent',
500
'bzrlib.selftest.test_parent',
332
501
'bzrlib.selftest.test_xml',
333
# 'bzrlib.selftest.testfetch',
334
# 'bzrlib.selftest.whitebox',
502
'bzrlib.selftest.test_weave',
503
'bzrlib.selftest.testfetch',
504
'bzrlib.selftest.whitebox',
335
505
'bzrlib.selftest.teststore',
336
# 'bzrlib.selftest.blackbox',
506
'bzrlib.selftest.blackbox',
507
'bzrlib.selftest.testsampler',
508
'bzrlib.selftest.testtransactions',
509
'bzrlib.selftest.testtransport',
510
'bzrlib.selftest.testgraph',
511
'bzrlib.selftest.testworkingtree',
512
'bzrlib.selftest.test_upgrade',
513
'bzrlib.selftest.test_conflicts',
514
'bzrlib.selftest.testtestament',
515
'bzrlib.selftest.testannotate',
516
'bzrlib.selftest.testrevprops',
339
519
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,