15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from testsweet import TestBase, run_suite, InTempDir
18
from cStringIO import StringIO
19
30
import bzrlib.commands
33
import bzrlib.osutils as osutils
34
from bzrlib.selftest import TestUtil
35
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
21
38
MODULES_TO_TEST = []
22
39
MODULES_TO_DOCTEST = []
25
class BzrTestBase(InTempDir):
26
"""bzr-specific test base class"""
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."""
148
class CommandFailed(Exception):
151
class TestCase(unittest.TestCase):
152
"""Base class for bzr unit tests.
154
Tests that need access to disk resources should subclass
155
TestCaseInTempDir not TestCase.
157
Error and debug log messages are redirected from their usual
158
location into a temporary file, the contents of which can be
159
retrieved by _get_log().
161
There are also convenience functions to invoke bzr's command-line
162
routine, and to build and check bzr trees."""
165
_log_file_name = None
168
unittest.TestCase.setUp(self)
169
bzrlib.trace.disable_default_logging()
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))
192
def assertContainsRe(self, haystack, needle_re):
193
"""Assert that a contains something matching a regular expression."""
194
if not re.search(needle_re, haystack):
195
raise AssertionError('pattern "%s" not found in "%s"'
196
% (needle_re, haystack))
198
def _enable_file_logging(self):
199
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
201
self._log_file = os.fdopen(fileno, 'w+')
203
hdlr = logging.StreamHandler(self._log_file)
204
hdlr.setLevel(logging.DEBUG)
205
hdlr.setFormatter(logging.Formatter('%(levelname)8s %(message)s'))
206
logging.getLogger('').addHandler(hdlr)
207
logging.getLogger('').setLevel(logging.DEBUG)
208
self._log_hdlr = hdlr
209
debug('opened log file %s', name)
211
self._log_file_name = name
214
logging.getLogger('').removeHandler(self._log_hdlr)
215
bzrlib.trace.enable_default_logging()
216
logging.debug('%s teardown', self.id())
217
self._log_file.close()
218
unittest.TestCase.tearDown(self)
220
def log(self, *args):
224
"""Return as a string the log for this test"""
225
if self._log_file_name:
226
return open(self._log_file_name).read()
230
def capture(self, cmd):
231
"""Shortcut that splits cmd into words, runs, and returns stdout"""
232
return self.run_bzr_captured(cmd.split())[0]
234
def run_bzr_captured(self, argv, retcode=0):
235
"""Invoke bzr and return (result, stdout, stderr).
237
Useful for code that wants to check the contents of the
238
output, the way error messages are presented, etc.
240
This should be the main method for tests that want to exercise the
241
overall behavior of the bzr application (rather than a unit test
242
or a functional test of the library.)
244
Much of the old code runs bzr by forking a new copy of Python, but
245
that is slower, harder to debug, and generally not necessary.
247
This runs bzr through the interface that catches and reports
248
errors, and with logging set to something approximating the
249
default, so that error reporting can be checked.
251
argv -- arguments to invoke bzr
252
retcode -- expected return code, or None for don't-care.
256
self.log('run bzr: %s', ' '.join(argv))
257
handler = logging.StreamHandler(stderr)
258
handler.setFormatter(bzrlib.trace.QuietFormatter())
259
handler.setLevel(logging.INFO)
260
logger = logging.getLogger('')
261
logger.addHandler(handler)
263
result = self.apply_redirected(None, stdout, stderr,
264
bzrlib.commands.run_bzr_catch_errors,
267
logger.removeHandler(handler)
268
out = stdout.getvalue()
269
err = stderr.getvalue()
271
self.log('output:\n%s', out)
273
self.log('errors:\n%s', err)
274
if retcode is not None:
275
self.assertEquals(result, retcode)
27
278
def run_bzr(self, *args, **kwargs):
28
retcode = kwargs.get('retcode', 0)
29
self.assertEquals(bzrlib.commands.run_bzr(args), retcode)
32
def selftest(verbose=False):
33
from unittest import TestLoader, TestSuite
34
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
35
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
279
"""Invoke bzr, as if it were run from the command line.
281
This should be the main method for tests that want to exercise the
282
overall behavior of the bzr application (rather than a unit test
283
or a functional test of the library.)
285
This sends the stdout/stderr results into the test's log,
286
where it may be useful for debugging. See also run_captured.
288
retcode = kwargs.pop('retcode', 0)
289
return self.run_bzr_captured(args, retcode)
291
def check_inventory_shape(self, inv, shape):
292
"""Compare an inventory to a list of expected names.
294
Fail if they are not precisely equal.
297
shape = list(shape) # copy
298
for path, ie in inv.entries():
299
name = path.replace('\\', '/')
307
self.fail("expected paths not found in inventory: %r" % shape)
309
self.fail("unexpected paths found in inventory: %r" % extras)
311
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
312
a_callable=None, *args, **kwargs):
313
"""Call callable with redirected std io pipes.
315
Returns the return code."""
316
if not callable(a_callable):
317
raise ValueError("a_callable must be callable.")
321
if hasattr(self, "_log_file"):
322
stdout = self._log_file
326
if hasattr(self, "_log_file"):
327
stderr = self._log_file
330
real_stdin = sys.stdin
331
real_stdout = sys.stdout
332
real_stderr = sys.stderr
337
return a_callable(*args, **kwargs)
339
sys.stdout = real_stdout
340
sys.stderr = real_stderr
341
sys.stdin = real_stdin
344
BzrTestBase = TestCase
347
class TestCaseInTempDir(TestCase):
348
"""Derived class that runs a test within a temporary directory.
350
This is useful for tests that need to create a branch, etc.
352
The directory is created in a slightly complex way: for each
353
Python invocation, a new temporary top-level directory is created.
354
All test cases create their own directory within that. If the
355
tests complete successfully, the directory is removed.
357
InTempDir is an old alias for FunctionalTestCase.
362
OVERRIDE_PYTHON = 'python'
364
def check_file_contents(self, filename, expect):
365
self.log("check contents of file %s" % filename)
366
contents = file(filename, 'r').read()
367
if contents != expect:
368
self.log("expected: %r" % expect)
369
self.log("actually: %r" % contents)
370
self.fail("contents of %s not as expected" % filename)
372
def _make_test_root(self):
373
if TestCaseInTempDir.TEST_ROOT is not None:
377
root = 'test%04d.tmp' % i
381
if e.errno == errno.EEXIST:
386
# successfully created
387
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
389
# make a fake bzr directory there to prevent any tests propagating
390
# up onto the source directory's real branch
391
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
394
super(TestCaseInTempDir, self).setUp()
395
self._make_test_root()
396
self._currentdir = os.getcwdu()
397
short_id = self.id().replace('bzrlib.selftest.', '') \
398
.replace('__main__.', '')
399
self.test_dir = os.path.join(self.TEST_ROOT, short_id)
400
os.mkdir(self.test_dir)
401
os.chdir(self.test_dir)
404
os.chdir(self._currentdir)
405
super(TestCaseInTempDir, self).tearDown()
407
def build_tree(self, shape):
408
"""Build a test tree according to a pattern.
410
shape is a sequence of file specifications. If the final
411
character is '/', a directory is created.
413
This doesn't add anything to a branch.
415
# XXX: It's OK to just create them using forward slashes on windows?
417
assert isinstance(name, basestring)
422
print >>f, "contents of", name
425
def failUnlessExists(self, path):
426
"""Fail unless path, which may be abs or relative, exists."""
427
self.failUnless(osutils.lexists(path))
430
class MetaTestLog(TestCase):
431
def test_logging(self):
432
"""Test logs are captured when a test fails."""
433
logging.info('an info message')
434
warning('something looks dodgy...')
435
logging.debug('hello, test is running')
439
def filter_suite_by_re(suite, pattern):
440
result = TestUtil.TestSuite()
441
filter_re = re.compile(pattern)
442
for test in iter_suite_tests(suite):
443
if filter_re.search(test.id()):
448
def run_suite(suite, name='test', verbose=False, pattern=".*"):
449
TestCaseInTempDir._TEST_NAME = name
454
runner = TextTestRunner(stream=sys.stdout,
458
suite = filter_suite_by_re(suite, pattern)
459
result = runner.run(suite)
460
# This is still a little bogus,
461
# but only a little. Folk not using our testrunner will
462
# have to delete their temp directories themselves.
463
if result.wasSuccessful():
464
if TestCaseInTempDir.TEST_ROOT is not None:
465
shutil.rmtree(TestCaseInTempDir.TEST_ROOT)
467
print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
468
return result.wasSuccessful()
471
def selftest(verbose=False, pattern=".*"):
472
"""Run the whole test suite under the enhanced runner"""
473
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
477
"""Build and return TestSuite for the whole program."""
478
import bzrlib.store, bzrlib.inventory, bzrlib.branch
479
import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
36
480
from doctest import DocTestSuite
43
482
global MODULES_TO_TEST, MODULES_TO_DOCTEST
46
['bzrlib.selftest.whitebox',
485
['bzrlib.selftest.MetaTestLog',
486
'bzrlib.selftest.testidentitymap',
487
'bzrlib.selftest.testinv',
488
'bzrlib.selftest.test_ancestry',
489
'bzrlib.selftest.test_commit',
490
'bzrlib.selftest.test_commit_merge',
491
'bzrlib.selftest.testconfig',
47
492
'bzrlib.selftest.versioning',
48
'bzrlib.selftest.testinv',
49
493
'bzrlib.selftest.testmerge3',
494
'bzrlib.selftest.testmerge',
50
495
'bzrlib.selftest.testhashcache',
51
496
'bzrlib.selftest.teststatus',
52
497
'bzrlib.selftest.testlog',
53
'bzrlib.selftest.blackbox',
54
498
'bzrlib.selftest.testrevisionnamespaces',
55
499
'bzrlib.selftest.testbranch',
56
500
'bzrlib.selftest.testrevision',
501
'bzrlib.selftest.test_revision_info',
502
'bzrlib.selftest.test_merge_core',
503
'bzrlib.selftest.test_smart_add',
504
'bzrlib.selftest.test_bad_files',
58
505
'bzrlib.selftest.testdiff',
506
'bzrlib.selftest.test_parent',
507
'bzrlib.selftest.test_xml',
508
'bzrlib.selftest.test_weave',
509
'bzrlib.selftest.testfetch',
510
'bzrlib.selftest.whitebox',
511
'bzrlib.selftest.teststore',
512
'bzrlib.selftest.blackbox',
513
'bzrlib.selftest.testsampler',
514
'bzrlib.selftest.testtransactions',
515
'bzrlib.selftest.testtransport',
516
'bzrlib.selftest.testgraph',
517
'bzrlib.selftest.testworkingtree',
518
'bzrlib.selftest.test_upgrade',
519
'bzrlib.selftest.test_conflicts',
520
'bzrlib.selftest.testtestament',
521
'bzrlib.selftest.testannotate',
522
'bzrlib.selftest.testrevprops',
523
'bzrlib.selftest.testoptions',
61
# XXX: should also test bzrlib.merge_core, but they seem to be out
62
# of date with the code.
64
526
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
65
527
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
66
528
if m not in MODULES_TO_DOCTEST:
67
529
MODULES_TO_DOCTEST.append(m)
70
TestBase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
71
print '%-30s %s' % ('bzr binary', TestBase.BZRPATH)
531
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
532
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
75
534
suite = TestSuite()
77
535
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
79
536
for m in MODULES_TO_TEST:
80
537
suite.addTest(TestLoader().loadTestsFromModule(m))
82
538
for m in (MODULES_TO_DOCTEST):
83
539
suite.addTest(DocTestSuite(m))
85
540
for p in bzrlib.plugin.all_plugins:
86
541
if hasattr(p, 'test_suite'):
87
542
suite.addTest(p.test_suite())
89
import bzrlib.merge_core
90
suite.addTest(unittest.makeSuite(bzrlib.merge_core.MergeTest, 'test_'))
92
return run_suite(suite, 'testbzr', verbose=verbose)