35
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."""
37
148
class CommandFailed(Exception):
51
162
routine, and to build and check bzr trees."""
165
_log_file_name = None
56
# this replaces the default testsweet.TestCase; we don't want logging changed
57
168
unittest.TestCase.setUp(self)
58
169
bzrlib.trace.disable_default_logging()
59
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))
62
192
def _enable_file_logging(self):
63
193
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
87
217
def _get_log(self):
88
218
"""Return as a string the log for this test"""
89
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)
91
272
def run_bzr(self, *args, **kwargs):
92
273
"""Invoke bzr, as if it were run from the command line.
95
276
overall behavior of the bzr application (rather than a unit test
96
277
or a functional test of the library.)
98
Much of the old code runs bzr by forking a new copy of Python, but
99
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.
101
retcode = kwargs.get('retcode', 0)
102
result = self.apply_redirected(None, None, None,
103
bzrlib.commands.run_bzr, args)
104
self.assertEquals(result, retcode)
282
retcode = kwargs.pop('retcode', 0)
283
return self.run_bzr_captured(args, retcode)
107
285
def check_inventory_shape(self, inv, shape):
109
Compare an inventory to a list of expected names.
286
"""Compare an inventory to a list of expected names.
111
288
Fail if they are not precisely equal.
185
361
if contents != expect:
186
362
self.log("expected: %r" % expect)
187
363
self.log("actually: %r" % contents)
188
self.fail("contents of %s not as expected")
364
self.fail("contents of %s not as expected" % filename)
190
366
def _make_test_root(self):
195
367
if TestCaseInTempDir.TEST_ROOT is not None:
197
TestCaseInTempDir.TEST_ROOT = os.path.abspath(
198
tempfile.mkdtemp(suffix='.tmp',
199
prefix=self._TEST_NAME + '-',
371
root = 'test%04d.tmp' % i
375
if e.errno == errno.EEXIST:
380
# successfully created
381
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
202
383
# make a fake bzr directory there to prevent any tests propagating
203
384
# up onto the source directory's real branch
204
385
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
207
388
super(TestCaseInTempDir, self).setUp()
209
389
self._make_test_root()
210
390
self._currentdir = os.getcwdu()
211
self.test_dir = os.path.join(self.TEST_ROOT, self.id())
391
short_id = self.id().replace('bzrlib.selftest.', '') \
392
.replace('__main__.', '')
393
self.test_dir = os.path.join(self.TEST_ROOT, short_id)
212
394
os.mkdir(self.test_dir)
213
395
os.chdir(self.test_dir)
215
397
def tearDown(self):
217
398
os.chdir(self._currentdir)
218
399
super(TestCaseInTempDir, self).tearDown()
220
def _formcmd(self, cmd):
221
if isinstance(cmd, basestring):
224
cmd[0] = self.BZRPATH
225
if self.OVERRIDE_PYTHON:
226
cmd.insert(0, self.OVERRIDE_PYTHON)
227
self.log('$ %r' % cmd)
230
def runcmd(self, cmd, retcode=0):
231
"""Run one command and check the return code.
233
Returns a tuple of (stdout,stderr) strings.
235
If a single string is based, it is split into words.
236
For commands that are not simple space-separated words, please
237
pass a list instead."""
238
cmd = self._formcmd(cmd)
239
self.log('$ ' + ' '.join(cmd))
240
actual_retcode = subprocess.call(cmd, stdout=self._log_file,
241
stderr=self._log_file)
242
if retcode != actual_retcode:
243
raise CommandFailed("test failed: %r returned %d, expected %d"
244
% (cmd, actual_retcode, retcode))
246
def backtick(self, cmd, retcode=0):
247
"""Run a command and return its output"""
248
cmd = self._formcmd(cmd)
249
child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
250
outd, errd = child.communicate()
252
actual_retcode = child.wait()
254
outd = outd.replace('\r', '')
256
if retcode != actual_retcode:
257
raise CommandFailed("test failed: %r returned %d, expected %d"
258
% (cmd, actual_retcode, retcode))
264
401
def build_tree(self, shape):
265
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()
294
465
def selftest(verbose=False, pattern=".*"):
466
"""Run the whole test suite under the enhanced runner"""
295
467
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
298
470
def test_suite():
299
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
300
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
301
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
471
"""Build and return TestSuite for the whole program."""
472
import bzrlib.store, bzrlib.inventory, bzrlib.branch
473
import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
302
474
from doctest import DocTestSuite
308
476
global MODULES_TO_TEST, MODULES_TO_DOCTEST
310
478
testmod_names = \
311
479
['bzrlib.selftest.MetaTestLog',
312
'bzrlib.selftest.test_parent',
480
'bzrlib.selftest.testidentitymap',
313
481
'bzrlib.selftest.testinv',
314
'bzrlib.selftest.testfetch',
482
'bzrlib.selftest.test_ancestry',
483
'bzrlib.selftest.test_commit',
484
'bzrlib.selftest.test_commit_merge',
485
'bzrlib.selftest.testconfig',
315
486
'bzrlib.selftest.versioning',
316
'bzrlib.selftest.whitebox',
317
487
'bzrlib.selftest.testmerge3',
318
488
'bzrlib.selftest.testmerge',
319
489
'bzrlib.selftest.testhashcache',
320
490
'bzrlib.selftest.teststatus',
321
491
'bzrlib.selftest.testlog',
322
'bzrlib.selftest.blackbox',
323
492
'bzrlib.selftest.testrevisionnamespaces',
324
493
'bzrlib.selftest.testbranch',
325
'bzrlib.selftest.testremotebranch',
326
494
'bzrlib.selftest.testrevision',
495
'bzrlib.selftest.test_revision_info',
327
496
'bzrlib.selftest.test_merge_core',
328
497
'bzrlib.selftest.test_smart_add',
498
'bzrlib.selftest.test_bad_files',
329
499
'bzrlib.selftest.testdiff',
500
'bzrlib.selftest.test_parent',
330
501
'bzrlib.selftest.test_xml',
502
'bzrlib.selftest.test_weave',
503
'bzrlib.selftest.testfetch',
504
'bzrlib.selftest.whitebox',
332
505
'bzrlib.selftest.teststore',
506
'bzrlib.selftest.blackbox',
507
'bzrlib.selftest.testsampler',
508
'bzrlib.selftest.testtransactions',
333
509
'bzrlib.selftest.testtransport',
334
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',
337
518
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,