35
39
from logging import debug, warning, error
43
class EarlyStoppingTestResultAdapter(object):
44
"""An adapter for TestResult to stop at the first first failure or error"""
46
def __init__(self, result):
49
def addError(self, test, err):
50
self._result.addError(test, err)
53
def addFailure(self, test, err):
54
self._result.addFailure(test, err)
57
def __getattr__(self, name):
58
return getattr(self._result, name)
60
def __setattr__(self, name, value):
62
object.__setattr__(self, name, value)
63
return setattr(self._result, name, value)
66
class _MyResult(unittest._TextTestResult):
70
No special behaviour for now.
73
def startTest(self, test):
74
unittest.TestResult.startTest(self, test)
75
# TODO: Maybe show test.shortDescription somewhere?
76
what = test.shortDescription() or test.id()
78
self.stream.write('%-70.70s' % what)
81
def addError(self, test, err):
82
super(_MyResult, self).addError(test, err)
85
def addFailure(self, test, err):
86
super(_MyResult, self).addFailure(test, err)
89
def addSuccess(self, test):
91
self.stream.writeln('OK')
93
self.stream.write('~')
95
unittest.TestResult.addSuccess(self, test)
97
def printErrorList(self, flavour, errors):
98
for test, err in errors:
99
self.stream.writeln(self.separator1)
100
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
101
if hasattr(test, '_get_log'):
102
self.stream.writeln()
103
self.stream.writeln('log from this test:')
104
print >>self.stream, test._get_log()
105
self.stream.writeln(self.separator2)
106
self.stream.writeln("%s" % err)
109
class TextTestRunner(unittest.TextTestRunner):
111
def _makeResult(self):
112
result = _MyResult(self.stream, self.descriptions, self.verbosity)
113
return EarlyStoppingTestResultAdapter(result)
116
def iter_suite_tests(suite):
117
"""Return all tests in a suite, recursing through nested suites"""
118
for item in suite._tests:
119
if isinstance(item, unittest.TestCase):
121
elif isinstance(item, unittest.TestSuite):
122
for r in iter_suite_tests(item):
125
raise Exception('unknown object %r inside test suite %r'
129
class TestSkipped(Exception):
130
"""Indicates that a test was intentionally skipped, rather than failing."""
37
134
class CommandFailed(Exception):
90
184
"""Return as a string the log for this test"""
91
185
return open(self._log_file_name).read()
188
def capture(self, cmd):
189
"""Shortcut that splits cmd into words, runs, and returns stdout"""
190
return self.run_bzr_captured(cmd.split())[0]
192
def run_bzr_captured(self, argv, retcode=0):
193
"""Invoke bzr and return (result, stdout, stderr).
195
Useful for code that wants to check the contents of the
196
output, the way error messages are presented, etc.
198
This should be the main method for tests that want to exercise the
199
overall behavior of the bzr application (rather than a unit test
200
or a functional test of the library.)
202
Much of the old code runs bzr by forking a new copy of Python, but
203
that is slower, harder to debug, and generally not necessary.
205
This runs bzr through the interface that catches and reports
206
errors, and with logging set to something approximating the
207
default, so that error reporting can be checked.
209
argv -- arguments to invoke bzr
210
retcode -- expected return code, or None for don't-care.
214
self.log('run bzr: %s', ' '.join(argv))
215
handler = logging.StreamHandler(stderr)
216
handler.setFormatter(bzrlib.trace.QuietFormatter())
217
handler.setLevel(logging.INFO)
218
logger = logging.getLogger('')
219
logger.addHandler(handler)
221
result = self.apply_redirected(None, stdout, stderr,
222
bzrlib.commands.run_bzr_catch_errors,
225
logger.removeHandler(handler)
226
out = stdout.getvalue()
227
err = stderr.getvalue()
229
self.log('output:\n%s', out)
231
self.log('errors:\n%s', err)
232
if retcode is not None:
233
self.assertEquals(result, retcode)
93
236
def run_bzr(self, *args, **kwargs):
94
237
"""Invoke bzr, as if it were run from the command line.
97
240
overall behavior of the bzr application (rather than a unit test
98
241
or a functional test of the library.)
100
Much of the old code runs bzr by forking a new copy of Python, but
101
that is slower, harder to debug, and generally not necessary.
243
This sends the stdout/stderr results into the test's log,
244
where it may be useful for debugging. See also run_captured.
103
retcode = kwargs.get('retcode', 0)
104
result = self.apply_redirected(None, None, None,
105
bzrlib.commands.run_bzr, args)
106
self.assertEquals(result, retcode)
246
retcode = kwargs.pop('retcode', 0)
247
return self.run_bzr_captured(args, retcode)
109
249
def check_inventory_shape(self, inv, shape):
111
Compare an inventory to a list of expected names.
250
"""Compare an inventory to a list of expected names.
113
252
Fail if they are not precisely equal.
181
325
if contents != expect:
182
326
self.log("expected: %r" % expect)
183
327
self.log("actually: %r" % contents)
184
self.fail("contents of %s not as expected")
328
self.fail("contents of %s not as expected" % filename)
186
330
def _make_test_root(self):
191
331
if TestCaseInTempDir.TEST_ROOT is not None:
193
TestCaseInTempDir.TEST_ROOT = os.path.abspath(
194
tempfile.mkdtemp(suffix='.tmp',
195
prefix=self._TEST_NAME + '-',
335
root = 'test%04d.tmp' % i
339
if e.errno == errno.EEXIST:
344
# successfully created
345
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
198
347
# make a fake bzr directory there to prevent any tests propagating
199
348
# up onto the source directory's real branch
200
349
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
203
352
super(TestCaseInTempDir, self).setUp()
205
353
self._make_test_root()
206
354
self._currentdir = os.getcwdu()
207
self.test_dir = os.path.join(self.TEST_ROOT, self.id())
355
short_id = self.id().replace('bzrlib.selftest.', '') \
356
.replace('__main__.', '')
357
self.test_dir = os.path.join(self.TEST_ROOT, short_id)
208
358
os.mkdir(self.test_dir)
209
359
os.chdir(self.test_dir)
211
361
def tearDown(self):
213
362
os.chdir(self._currentdir)
214
363
super(TestCaseInTempDir, self).tearDown()
216
def _formcmd(self, cmd):
217
if isinstance(cmd, basestring):
220
cmd[0] = self.BZRPATH
221
if self.OVERRIDE_PYTHON:
222
cmd.insert(0, self.OVERRIDE_PYTHON)
223
self.log('$ %r' % cmd)
226
def runcmd(self, cmd, retcode=0):
227
"""Run one command and check the return code.
229
Returns a tuple of (stdout,stderr) strings.
231
If a single string is based, it is split into words.
232
For commands that are not simple space-separated words, please
233
pass a list instead."""
234
cmd = self._formcmd(cmd)
235
self.log('$ ' + ' '.join(cmd))
236
actual_retcode = subprocess.call(cmd, stdout=self._log_file,
237
stderr=self._log_file)
238
if retcode != actual_retcode:
239
raise CommandFailed("test failed: %r returned %d, expected %d"
240
% (cmd, actual_retcode, retcode))
242
def backtick(self, cmd, retcode=0):
243
"""Run a command and return its output"""
244
cmd = self._formcmd(cmd)
245
child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
246
outd, errd = child.communicate()
248
actual_retcode = child.wait()
250
outd = outd.replace('\r', '')
252
if retcode != actual_retcode:
253
raise CommandFailed("test failed: %r returned %d, expected %d"
254
% (cmd, actual_retcode, retcode))
260
365
def build_tree(self, shape):
261
366
"""Build a test tree according to a pattern.
290
def selftest(verbose=False, pattern=".*"):
291
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
397
def filter_suite_by_re(suite, pattern):
398
result = TestUtil.TestSuite()
399
filter_re = re.compile(pattern)
400
for test in iter_suite_tests(suite):
401
if filter_re.match(test.id()):
406
def filter_suite_by_names(suite, wanted_names):
407
"""Return a new suite containing only selected tests.
409
Names are considered to match if any name is a substring of the
410
fully-qualified test id (i.e. the class ."""
412
for test in iter_suite_tests(suite):
414
for p in wanted_names:
415
if this_id.find(p) != -1:
420
def run_suite(suite, name='test', verbose=False, pattern=".*", testnames=None):
421
TestCaseInTempDir._TEST_NAME = name
426
runner = TextTestRunner(stream=sys.stdout,
430
suite = filter_suite_by_names(suite, testnames)
432
suite = filter_suite_by_re(suite, pattern)
433
result = runner.run(suite)
434
# This is still a little bogus,
435
# but only a little. Folk not using our testrunner will
436
# have to delete their temp directories themselves.
437
if result.wasSuccessful():
438
if TestCaseInTempDir.TEST_ROOT is not None:
439
shutil.rmtree(TestCaseInTempDir.TEST_ROOT)
441
print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
442
return result.wasSuccessful()
445
def selftest(verbose=False, pattern=".*", testnames=None):
446
"""Run the whole test suite under the enhanced runner"""
447
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
294
451
def test_suite():
295
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
296
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
297
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
452
"""Build and return TestSuite for the whole program."""
453
import bzrlib.store, bzrlib.inventory, bzrlib.branch
454
import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
298
455
from doctest import DocTestSuite
304
457
global MODULES_TO_TEST, MODULES_TO_DOCTEST
306
459
testmod_names = \
307
460
['bzrlib.selftest.MetaTestLog',
308
'bzrlib.selftest.test_parent',
461
'bzrlib.selftest.testidentitymap',
309
462
'bzrlib.selftest.testinv',
310
'bzrlib.selftest.testfetch',
463
'bzrlib.selftest.test_ancestry',
464
'bzrlib.selftest.test_commit',
465
'bzrlib.selftest.test_commit_merge',
311
466
'bzrlib.selftest.versioning',
312
'bzrlib.selftest.whitebox',
313
467
'bzrlib.selftest.testmerge3',
468
'bzrlib.selftest.testmerge',
314
469
'bzrlib.selftest.testhashcache',
315
470
'bzrlib.selftest.teststatus',
316
471
'bzrlib.selftest.testlog',
317
'bzrlib.selftest.blackbox',
318
472
'bzrlib.selftest.testrevisionnamespaces',
319
473
'bzrlib.selftest.testbranch',
320
474
'bzrlib.selftest.testrevision',
475
'bzrlib.selftest.test_revision_info',
321
476
'bzrlib.selftest.test_merge_core',
322
477
'bzrlib.selftest.test_smart_add',
478
'bzrlib.selftest.test_bad_files',
323
479
'bzrlib.selftest.testdiff',
480
'bzrlib.selftest.test_parent',
481
'bzrlib.selftest.test_xml',
482
'bzrlib.selftest.test_weave',
483
'bzrlib.selftest.testfetch',
484
'bzrlib.selftest.whitebox',
325
485
'bzrlib.selftest.teststore',
486
'bzrlib.selftest.blackbox',
487
'bzrlib.selftest.testsampler',
488
'bzrlib.selftest.testtransactions',
489
'bzrlib.selftest.testtransport',
490
'bzrlib.selftest.testgraph',
491
'bzrlib.selftest.testworkingtree',
492
'bzrlib.selftest.test_upgrade',
493
'bzrlib.selftest.test_conflicts',
328
496
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,