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
28
import bzrlib.commands
31
from bzrlib.selftest import TestUtil
32
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
20
35
MODULES_TO_TEST = []
21
36
MODULES_TO_DOCTEST = []
23
def selftest(verbose=False):
24
from unittest import TestLoader, TestSuite
25
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
26
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
38
from logging import debug, warning, error
42
class EarlyStoppingTestResultAdapter(object):
43
"""An adapter for TestResult to stop at the first first failure or error"""
45
def __init__(self, result):
48
def addError(self, test, err):
49
self._result.addError(test, err)
52
def addFailure(self, test, err):
53
self._result.addFailure(test, err)
56
def __getattr__(self, name):
57
return getattr(self._result, name)
59
def __setattr__(self, name, value):
61
object.__setattr__(self, name, value)
62
return setattr(self._result, name, value)
65
class _MyResult(unittest._TextTestResult):
69
No special behaviour for now.
72
def startTest(self, test):
73
unittest.TestResult.startTest(self, test)
74
# TODO: Maybe show test.shortDescription somewhere?
75
what = test.shortDescription() or test.id()
77
self.stream.write('%-70.70s' % what)
80
def addError(self, test, err):
81
super(_MyResult, self).addError(test, err)
84
def addFailure(self, test, err):
85
super(_MyResult, self).addFailure(test, err)
88
def addSuccess(self, test):
90
self.stream.writeln('OK')
92
self.stream.write('~')
94
unittest.TestResult.addSuccess(self, test)
96
def printErrorList(self, flavour, errors):
97
for test, err in errors:
98
self.stream.writeln(self.separator1)
99
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
100
if hasattr(test, '_get_log'):
101
self.stream.writeln()
102
self.stream.writeln('log from this test:')
103
print >>self.stream, test._get_log()
104
self.stream.writeln(self.separator2)
105
self.stream.writeln("%s" % err)
108
class TextTestRunner(unittest.TextTestRunner):
110
def _makeResult(self):
111
result = _MyResult(self.stream, self.descriptions, self.verbosity)
112
return EarlyStoppingTestResultAdapter(result)
115
class filteringVisitor(TestUtil.TestVisitor):
116
"""I accruse all the testCases I visit that pass a regexp filter on id
120
def __init__(self, filter):
122
TestUtil.TestVisitor.__init__(self)
124
self.filter=re.compile(filter)
127
"""answer the suite we are building"""
128
if self._suite is None:
129
self._suite=TestUtil.TestSuite()
132
def visitCase(self, aCase):
133
if self.filter.match(aCase.id()):
134
self.suite().addTest(aCase)
136
class TestSkipped(Exception):
137
"""Indicates that a test was intentionally skipped, rather than failing."""
141
class CommandFailed(Exception):
144
class TestCase(unittest.TestCase):
145
"""Base class for bzr unit tests.
147
Tests that need access to disk resources should subclass
148
TestCaseInTempDir not TestCase.
150
Error and debug log messages are redirected from their usual
151
location into a temporary file, the contents of which can be
152
retrieved by _get_log().
154
There are also convenience functions to invoke bzr's command-line
155
routine, and to build and check bzr trees."""
160
unittest.TestCase.setUp(self)
161
bzrlib.trace.disable_default_logging()
162
self._enable_file_logging()
165
def _enable_file_logging(self):
166
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
168
self._log_file = os.fdopen(fileno, 'w+')
170
hdlr = logging.StreamHandler(self._log_file)
171
hdlr.setLevel(logging.DEBUG)
172
hdlr.setFormatter(logging.Formatter('%(levelname)8s %(message)s'))
173
logging.getLogger('').addHandler(hdlr)
174
logging.getLogger('').setLevel(logging.DEBUG)
175
self._log_hdlr = hdlr
176
debug('opened log file %s', name)
178
self._log_file_name = name
181
logging.getLogger('').removeHandler(self._log_hdlr)
182
bzrlib.trace.enable_default_logging()
183
logging.debug('%s teardown', self.id())
184
self._log_file.close()
185
unittest.TestCase.tearDown(self)
187
def log(self, *args):
191
"""Return as a string the log for this test"""
192
return open(self._log_file_name).read()
195
def capture(self, cmd):
196
"""Shortcut that splits cmd into words, runs, and returns stdout"""
197
return self.run_bzr_captured(cmd.split())[0]
199
def run_bzr_captured(self, argv, retcode=0):
200
"""Invoke bzr and return (result, stdout, stderr).
202
Useful for code that wants to check the contents of the
203
output, the way error messages are presented, etc.
205
This should be the main method for tests that want to exercise the
206
overall behavior of the bzr application (rather than a unit test
207
or a functional test of the library.)
209
Much of the old code runs bzr by forking a new copy of Python, but
210
that is slower, harder to debug, and generally not necessary.
212
This runs bzr through the interface that catches and reports
213
errors, and with logging set to something approximating the
214
default, so that error reporting can be checked.
216
argv -- arguments to invoke bzr
217
retcode -- expected return code, or None for don't-care.
221
self.log('run bzr: %s', ' '.join(argv))
222
handler = logging.StreamHandler(stderr)
223
handler.setFormatter(bzrlib.trace.QuietFormatter())
224
handler.setLevel(logging.INFO)
225
logger = logging.getLogger('')
226
logger.addHandler(handler)
228
result = self.apply_redirected(None, stdout, stderr,
229
bzrlib.commands.run_bzr_catch_errors,
232
logger.removeHandler(handler)
233
out = stdout.getvalue()
234
err = stderr.getvalue()
236
self.log('output:\n%s', out)
238
self.log('errors:\n%s', err)
239
if retcode is not None:
240
self.assertEquals(result, retcode)
243
def run_bzr(self, *args, **kwargs):
244
"""Invoke bzr, as if it were run from the command line.
246
This should be the main method for tests that want to exercise the
247
overall behavior of the bzr application (rather than a unit test
248
or a functional test of the library.)
250
This sends the stdout/stderr results into the test's log,
251
where it may be useful for debugging. See also run_captured.
253
retcode = kwargs.pop('retcode', 0)
254
return self.run_bzr_captured(args, retcode)
256
def check_inventory_shape(self, inv, shape):
257
"""Compare an inventory to a list of expected names.
259
Fail if they are not precisely equal.
262
shape = list(shape) # copy
263
for path, ie in inv.entries():
264
name = path.replace('\\', '/')
272
self.fail("expected paths not found in inventory: %r" % shape)
274
self.fail("unexpected paths found in inventory: %r" % extras)
276
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
277
a_callable=None, *args, **kwargs):
278
"""Call callable with redirected std io pipes.
280
Returns the return code."""
281
if not callable(a_callable):
282
raise ValueError("a_callable must be callable.")
286
if hasattr(self, "_log_file"):
287
stdout = self._log_file
291
if hasattr(self, "_log_file"):
292
stderr = self._log_file
295
real_stdin = sys.stdin
296
real_stdout = sys.stdout
297
real_stderr = sys.stderr
302
return a_callable(*args, **kwargs)
304
sys.stdout = real_stdout
305
sys.stderr = real_stderr
306
sys.stdin = real_stdin
309
BzrTestBase = TestCase
312
class TestCaseInTempDir(TestCase):
313
"""Derived class that runs a test within a temporary directory.
315
This is useful for tests that need to create a branch, etc.
317
The directory is created in a slightly complex way: for each
318
Python invocation, a new temporary top-level directory is created.
319
All test cases create their own directory within that. If the
320
tests complete successfully, the directory is removed.
322
InTempDir is an old alias for FunctionalTestCase.
327
OVERRIDE_PYTHON = 'python'
329
def check_file_contents(self, filename, expect):
330
self.log("check contents of file %s" % filename)
331
contents = file(filename, 'r').read()
332
if contents != expect:
333
self.log("expected: %r" % expect)
334
self.log("actually: %r" % contents)
335
self.fail("contents of %s not as expected" % filename)
337
def _make_test_root(self):
338
if TestCaseInTempDir.TEST_ROOT is not None:
342
root = 'test%04d.tmp' % i
346
if e.errno == errno.EEXIST:
351
# successfully created
352
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
354
# make a fake bzr directory there to prevent any tests propagating
355
# up onto the source directory's real branch
356
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
359
super(TestCaseInTempDir, self).setUp()
360
self._make_test_root()
361
self._currentdir = os.getcwdu()
362
short_id = self.id().replace('bzrlib.selftest.', '') \
363
.replace('__main__.', '')
364
self.test_dir = os.path.join(self.TEST_ROOT, short_id)
365
os.mkdir(self.test_dir)
366
os.chdir(self.test_dir)
369
os.chdir(self._currentdir)
370
super(TestCaseInTempDir, self).tearDown()
372
def build_tree(self, shape):
373
"""Build a test tree according to a pattern.
375
shape is a sequence of file specifications. If the final
376
character is '/', a directory is created.
378
This doesn't add anything to a branch.
380
# XXX: It's OK to just create them using forward slashes on windows?
382
assert isinstance(name, basestring)
387
print >>f, "contents of", name
391
class MetaTestLog(TestCase):
392
def test_logging(self):
393
"""Test logs are captured when a test fails."""
394
logging.info('an info message')
395
warning('something looks dodgy...')
396
logging.debug('hello, test is running')
401
def run_suite(suite, name='test', verbose=False, pattern=".*"):
402
TestCaseInTempDir._TEST_NAME = name
407
runner = TextTestRunner(stream=sys.stdout,
410
visitor = filteringVisitor(pattern)
412
result = runner.run(visitor.suite())
413
# This is still a little bogus,
414
# but only a little. Folk not using our testrunner will
415
# have to delete their temp directories themselves.
416
if result.wasSuccessful():
417
if TestCaseInTempDir.TEST_ROOT is not None:
418
shutil.rmtree(TestCaseInTempDir.TEST_ROOT)
420
print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
421
return result.wasSuccessful()
424
def selftest(verbose=False, pattern=".*"):
425
"""Run the whole test suite under the enhanced runner"""
426
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
430
"""Build and return TestSuite for the whole program."""
431
import bzrlib.store, bzrlib.inventory, bzrlib.branch
432
import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
433
from doctest import DocTestSuite
27
435
global MODULES_TO_TEST, MODULES_TO_DOCTEST
29
import bzrlib.selftest.whitebox
30
import bzrlib.selftest.blackbox
31
import bzrlib.selftest.versioning
32
import bzrlib.selftest.testmerge3
33
import bzrlib.selftest.testhashcache
34
import bzrlib.selftest.testrevisionnamespaces
35
import bzrlib.selftest.testbranch
36
import bzrlib.selftest.teststatus
37
import bzrlib.selftest.testinv
38
import bzrlib.merge_core
39
from doctest import DocTestSuite
438
['bzrlib.selftest.MetaTestLog',
439
'bzrlib.selftest.testinv',
440
'bzrlib.selftest.test_ancestry',
441
'bzrlib.selftest.test_commit',
442
'bzrlib.selftest.test_commit_merge',
443
'bzrlib.selftest.versioning',
444
'bzrlib.selftest.testmerge3',
445
'bzrlib.selftest.testmerge',
446
'bzrlib.selftest.testhashcache',
447
'bzrlib.selftest.teststatus',
448
'bzrlib.selftest.testlog',
449
'bzrlib.selftest.testrevisionnamespaces',
450
'bzrlib.selftest.testbranch',
451
'bzrlib.selftest.testrevision',
452
'bzrlib.selftest.test_revision_info',
453
'bzrlib.selftest.test_merge_core',
454
'bzrlib.selftest.test_smart_add',
455
'bzrlib.selftest.test_bad_files',
456
'bzrlib.selftest.testdiff',
457
'bzrlib.selftest.test_parent',
458
'bzrlib.selftest.test_xml',
459
'bzrlib.selftest.test_weave',
460
'bzrlib.selftest.testfetch',
461
'bzrlib.selftest.whitebox',
462
'bzrlib.selftest.teststore',
463
'bzrlib.selftest.blackbox',
464
'bzrlib.selftest.testtransport',
465
'bzrlib.selftest.testgraph',
466
'bzrlib.selftest.testworkingtree',
46
469
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
47
470
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
48
471
if m not in MODULES_TO_DOCTEST:
49
472
MODULES_TO_DOCTEST.append(m)
52
for m in (bzrlib.selftest.whitebox,
53
bzrlib.selftest.versioning,
54
bzrlib.selftest.testinv,
55
bzrlib.selftest.testmerge3,
56
bzrlib.selftest.testhashcache,
57
bzrlib.selftest.teststatus,
58
bzrlib.selftest.blackbox,
59
bzrlib.selftest.testhashcache,
60
bzrlib.selftest.testrevisionnamespaces,
61
bzrlib.selftest.testbranch,
63
if m not in MODULES_TO_TEST:
64
MODULES_TO_TEST.append(m)
67
TestBase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
68
print '%-30s %s' % ('bzr binary', TestBase.BZRPATH)
474
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
475
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
72
477
suite = TestSuite()
74
# should also test bzrlib.merge_core, but they seem to be out of date with
78
# XXX: python2.3's TestLoader() doesn't seem to find all the
79
# tests; don't know why
478
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
80
479
for m in MODULES_TO_TEST:
81
480
suite.addTest(TestLoader().loadTestsFromModule(m))
83
481
for m in (MODULES_TO_DOCTEST):
84
482
suite.addTest(DocTestSuite(m))
86
483
for p in bzrlib.plugin.all_plugins:
87
484
if hasattr(p, 'test_suite'):
88
485
suite.addTest(p.test_suite())
90
suite.addTest(unittest.makeSuite(bzrlib.merge_core.MergeTest, 'test_'))
92
return run_suite(suite, 'testbzr', verbose=verbose)