15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from cStringIO import StringIO
30
import bzrlib.commands
33
import bzrlib.osutils as osutils
34
from bzrlib.selftest import TestUtil
35
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
36
from bzrlib.selftest.treeshape import build_tree_contents
18
from testsweet import TestBase, run_suite, InTempDir
38
20
MODULES_TO_TEST = []
39
21
MODULES_TO_DOCTEST = []
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):
124
stop_on_failure = False
126
def _makeResult(self):
127
result = _MyResult(self.stream, self.descriptions, self.verbosity)
128
if self.stop_on_failure:
129
result = EarlyStoppingTestResultAdapter(result)
133
def iter_suite_tests(suite):
134
"""Return all tests in a suite, recursing through nested suites"""
135
for item in suite._tests:
136
if isinstance(item, unittest.TestCase):
138
elif isinstance(item, unittest.TestSuite):
139
for r in iter_suite_tests(item):
142
raise Exception('unknown object %r inside test suite %r'
146
class TestSkipped(Exception):
147
"""Indicates that a test was intentionally skipped, rather than failing."""
151
class CommandFailed(Exception):
154
class TestCase(unittest.TestCase):
155
"""Base class for bzr unit tests.
157
Tests that need access to disk resources should subclass
158
TestCaseInTempDir not TestCase.
160
Error and debug log messages are redirected from their usual
161
location into a temporary file, the contents of which can be
162
retrieved by _get_log().
164
There are also convenience functions to invoke bzr's command-line
165
routine, and to build and check bzr trees."""
168
_log_file_name = None
171
unittest.TestCase.setUp(self)
172
self.oldenv = os.environ.get('HOME', None)
173
os.environ['HOME'] = os.getcwd()
174
self.bzr_email = os.environ.get('BZREMAIL')
175
if self.bzr_email is not None:
176
del os.environ['BZREMAIL']
177
self.email = os.environ.get('EMAIL')
178
if self.email is not None:
179
del os.environ['EMAIL']
180
bzrlib.trace.disable_default_logging()
181
self._enable_file_logging()
183
def _ndiff_strings(self, a, b):
184
"""Return ndiff between two strings containing lines."""
185
difflines = difflib.ndiff(a.splitlines(True),
187
linejunk=lambda x: False,
188
charjunk=lambda x: False)
189
return ''.join(difflines)
191
def assertEqualDiff(self, a, b):
192
"""Assert two texts are equal, if not raise an exception.
194
This is intended for use with multi-line strings where it can
195
be hard to find the differences by eye.
197
# TODO: perhaps override assertEquals to call this for strings?
200
raise AssertionError("texts not equal:\n" +
201
self._ndiff_strings(a, b))
203
def assertContainsRe(self, haystack, needle_re):
204
"""Assert that a contains something matching a regular expression."""
205
if not re.search(needle_re, haystack):
206
raise AssertionError('pattern "%s" not found in "%s"'
207
% (needle_re, haystack))
209
def _enable_file_logging(self):
210
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
212
self._log_file = os.fdopen(fileno, 'w+')
214
hdlr = logging.StreamHandler(self._log_file)
215
hdlr.setLevel(logging.DEBUG)
216
hdlr.setFormatter(logging.Formatter('%(levelname)8s %(message)s'))
217
logging.getLogger('').addHandler(hdlr)
218
logging.getLogger('').setLevel(logging.DEBUG)
219
self._log_hdlr = hdlr
220
debug('opened log file %s', name)
222
self._log_file_name = name
225
os.environ['HOME'] = self.oldenv
226
if os.environ.get('BZREMAIL') is not None:
227
del os.environ['BZREMAIL']
228
if self.bzr_email is not None:
229
os.environ['BZREMAIL'] = self.bzr_email
230
if os.environ.get('EMAIL') is not None:
231
del os.environ['EMAIL']
232
if self.email is not None:
233
os.environ['EMAIL'] = self.email
234
logging.getLogger('').removeHandler(self._log_hdlr)
235
bzrlib.trace.enable_default_logging()
236
logging.debug('%s teardown', self.id())
237
self._log_file.close()
238
unittest.TestCase.tearDown(self)
240
def log(self, *args):
244
"""Return as a string the log for this test"""
245
if self._log_file_name:
246
return open(self._log_file_name).read()
250
def capture(self, cmd):
251
"""Shortcut that splits cmd into words, runs, and returns stdout"""
252
return self.run_bzr_captured(cmd.split())[0]
254
def run_bzr_captured(self, argv, retcode=0):
255
"""Invoke bzr and return (result, stdout, stderr).
257
Useful for code that wants to check the contents of the
258
output, the way error messages are presented, etc.
260
This should be the main method for tests that want to exercise the
261
overall behavior of the bzr application (rather than a unit test
262
or a functional test of the library.)
264
Much of the old code runs bzr by forking a new copy of Python, but
265
that is slower, harder to debug, and generally not necessary.
267
This runs bzr through the interface that catches and reports
268
errors, and with logging set to something approximating the
269
default, so that error reporting can be checked.
271
argv -- arguments to invoke bzr
272
retcode -- expected return code, or None for don't-care.
276
self.log('run bzr: %s', ' '.join(argv))
277
handler = logging.StreamHandler(stderr)
278
handler.setFormatter(bzrlib.trace.QuietFormatter())
279
handler.setLevel(logging.INFO)
280
logger = logging.getLogger('')
281
logger.addHandler(handler)
283
result = self.apply_redirected(None, stdout, stderr,
284
bzrlib.commands.run_bzr_catch_errors,
287
logger.removeHandler(handler)
288
out = stdout.getvalue()
289
err = stderr.getvalue()
291
self.log('output:\n%s', out)
293
self.log('errors:\n%s', err)
294
if retcode is not None:
295
self.assertEquals(result, retcode)
298
def run_bzr(self, *args, **kwargs):
299
"""Invoke bzr, as if it were run from the command line.
301
This should be the main method for tests that want to exercise the
302
overall behavior of the bzr application (rather than a unit test
303
or a functional test of the library.)
305
This sends the stdout/stderr results into the test's log,
306
where it may be useful for debugging. See also run_captured.
308
retcode = kwargs.pop('retcode', 0)
309
return self.run_bzr_captured(args, retcode)
311
def check_inventory_shape(self, inv, shape):
312
"""Compare an inventory to a list of expected names.
314
Fail if they are not precisely equal.
317
shape = list(shape) # copy
318
for path, ie in inv.entries():
319
name = path.replace('\\', '/')
327
self.fail("expected paths not found in inventory: %r" % shape)
329
self.fail("unexpected paths found in inventory: %r" % extras)
331
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
332
a_callable=None, *args, **kwargs):
333
"""Call callable with redirected std io pipes.
335
Returns the return code."""
336
if not callable(a_callable):
337
raise ValueError("a_callable must be callable.")
341
if hasattr(self, "_log_file"):
342
stdout = self._log_file
346
if hasattr(self, "_log_file"):
347
stderr = self._log_file
350
real_stdin = sys.stdin
351
real_stdout = sys.stdout
352
real_stderr = sys.stderr
357
return a_callable(*args, **kwargs)
359
sys.stdout = real_stdout
360
sys.stderr = real_stderr
361
sys.stdin = real_stdin
364
BzrTestBase = TestCase
367
class TestCaseInTempDir(TestCase):
368
"""Derived class that runs a test within a temporary directory.
370
This is useful for tests that need to create a branch, etc.
372
The directory is created in a slightly complex way: for each
373
Python invocation, a new temporary top-level directory is created.
374
All test cases create their own directory within that. If the
375
tests complete successfully, the directory is removed.
377
InTempDir is an old alias for FunctionalTestCase.
382
OVERRIDE_PYTHON = 'python'
384
def check_file_contents(self, filename, expect):
385
self.log("check contents of file %s" % filename)
386
contents = file(filename, 'r').read()
387
if contents != expect:
388
self.log("expected: %r" % expect)
389
self.log("actually: %r" % contents)
390
self.fail("contents of %s not as expected" % filename)
392
def _make_test_root(self):
393
if TestCaseInTempDir.TEST_ROOT is not None:
397
root = 'test%04d.tmp' % i
401
if e.errno == errno.EEXIST:
406
# successfully created
407
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
409
# make a fake bzr directory there to prevent any tests propagating
410
# up onto the source directory's real branch
411
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
414
self._make_test_root()
415
self._currentdir = os.getcwdu()
416
short_id = self.id().replace('bzrlib.selftest.', '') \
417
.replace('__main__.', '')
418
self.test_dir = os.path.join(self.TEST_ROOT, short_id)
419
os.mkdir(self.test_dir)
420
os.chdir(self.test_dir)
421
super(TestCaseInTempDir, self).setUp()
424
os.chdir(self._currentdir)
425
super(TestCaseInTempDir, self).tearDown()
427
def build_tree(self, shape):
428
"""Build a test tree according to a pattern.
430
shape is a sequence of file specifications. If the final
431
character is '/', a directory is created.
433
This doesn't add anything to a branch.
435
# XXX: It's OK to just create them using forward slashes on windows?
437
assert isinstance(name, basestring)
442
print >>f, "contents of", name
445
def build_tree_contents(self, shape):
446
bzrlib.selftest.build_tree_contents(shape)
448
def failUnlessExists(self, path):
449
"""Fail unless path, which may be abs or relative, exists."""
450
self.failUnless(osutils.lexists(path))
453
class MetaTestLog(TestCase):
454
def test_logging(self):
455
"""Test logs are captured when a test fails."""
456
logging.info('an info message')
457
warning('something looks dodgy...')
458
logging.debug('hello, test is running')
462
def filter_suite_by_re(suite, pattern):
463
result = TestUtil.TestSuite()
464
filter_re = re.compile(pattern)
465
for test in iter_suite_tests(suite):
466
if filter_re.search(test.id()):
471
def run_suite(suite, name='test', verbose=False, pattern=".*",
472
stop_on_failure=False):
473
TestCaseInTempDir._TEST_NAME = name
478
runner = TextTestRunner(stream=sys.stdout,
481
runner.stop_on_failure=stop_on_failure
483
suite = filter_suite_by_re(suite, pattern)
484
result = runner.run(suite)
485
# This is still a little bogus,
486
# but only a little. Folk not using our testrunner will
487
# have to delete their temp directories themselves.
488
if result.wasSuccessful():
489
if TestCaseInTempDir.TEST_ROOT is not None:
490
shutil.rmtree(TestCaseInTempDir.TEST_ROOT)
492
print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
493
return result.wasSuccessful()
496
def selftest(verbose=False, pattern=".*", stop_on_failure=True):
497
"""Run the whole test suite under the enhanced runner"""
498
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
499
stop_on_failure=stop_on_failure)
503
"""Build and return TestSuite for the whole program."""
504
import bzrlib.store, bzrlib.inventory, bzrlib.branch
505
import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
24
from unittest import TestLoader, TestSuite
25
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
26
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3
27
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.merge_core
506
35
from doctest import DocTestSuite
508
global MODULES_TO_TEST, MODULES_TO_DOCTEST
511
['bzrlib.selftest.MetaTestLog',
512
'bzrlib.selftest.testgpg',
513
'bzrlib.selftest.testidentitymap',
514
'bzrlib.selftest.testinv',
515
'bzrlib.selftest.test_ancestry',
516
'bzrlib.selftest.test_commit',
517
'bzrlib.selftest.test_commit_merge',
518
'bzrlib.selftest.testconfig',
519
'bzrlib.selftest.versioning',
520
'bzrlib.selftest.testmerge3',
521
'bzrlib.selftest.testmerge',
522
'bzrlib.selftest.testhashcache',
523
'bzrlib.selftest.teststatus',
524
'bzrlib.selftest.testlog',
525
'bzrlib.selftest.testrevisionnamespaces',
526
'bzrlib.selftest.testbranch',
527
'bzrlib.selftest.testrevision',
528
'bzrlib.selftest.test_revision_info',
529
'bzrlib.selftest.test_merge_core',
530
'bzrlib.selftest.test_smart_add',
531
'bzrlib.selftest.test_bad_files',
532
'bzrlib.selftest.testdiff',
533
'bzrlib.selftest.test_parent',
534
'bzrlib.selftest.test_xml',
535
'bzrlib.selftest.test_weave',
536
'bzrlib.selftest.testfetch',
537
'bzrlib.selftest.whitebox',
538
'bzrlib.selftest.teststore',
539
'bzrlib.selftest.blackbox',
540
'bzrlib.selftest.testsampler',
541
'bzrlib.selftest.testtransactions',
542
'bzrlib.selftest.testtransport',
543
'bzrlib.selftest.testgraph',
544
'bzrlib.selftest.testworkingtree',
545
'bzrlib.selftest.test_upgrade',
546
'bzrlib.selftest.test_conflicts',
547
'bzrlib.selftest.testtestament',
548
'bzrlib.selftest.testannotate',
549
'bzrlib.selftest.testrevprops',
550
'bzrlib.selftest.testoptions',
553
42
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
554
43
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
555
44
if m not in MODULES_TO_DOCTEST:
556
45
MODULES_TO_DOCTEST.append(m)
558
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
559
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
46
for m in (bzrlib.selftest.whitebox,
47
bzrlib.selftest.versioning,
48
bzrlib.selftest.testmerge3):
49
if m not in MODULES_TO_TEST:
50
MODULES_TO_TEST.append(m)
53
TestBase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
54
print '%-30s %s' % ('bzr binary', TestBase.BZRPATH)
561
58
suite = TestSuite()
562
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
60
# should also test bzrlib.merge_core, but they seem to be out of date with
64
# python2.3's TestLoader() doesn't seem to work well; don't know why
563
65
for m in MODULES_TO_TEST:
564
66
suite.addTest(TestLoader().loadTestsFromModule(m))
565
68
for m in (MODULES_TO_DOCTEST):
566
69
suite.addTest(DocTestSuite(m))
567
for p in bzrlib.plugin.all_plugins:
568
if hasattr(p, 'test_suite'):
569
suite.addTest(p.test_suite())
71
# for cl in (bzrlib.selftest.whitebox.TEST_CLASSES
72
# + bzrlib.selftest.versioning.TEST_CLASSES
73
# + bzrlib.selftest.testmerge3.TEST_CLASSES
74
# + bzrlib.selftest.testhashcache.TEST_CLASSES
75
# + bzrlib.selftest.blackbox.TEST_CLASSES):
78
suite.addTest(unittest.makeSuite(bzrlib.merge_core.MergeTest, 'test_'))
80
return run_suite(suite, 'testbzr')