15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from cStringIO import StringIO
31
28
import bzrlib.commands
32
from bzrlib.errors import BzrError
33
import bzrlib.inventory
38
30
import bzrlib.trace
39
from bzrlib.trace import mutter
40
from bzrlib.tests.TestUtil import TestLoader, TestSuite
41
from bzrlib.tests.treeshape import build_tree_contents
43
34
MODULES_TO_TEST = []
44
MODULES_TO_DOCTEST = [
53
def packages_to_test():
54
import bzrlib.tests.blackbox
60
class EarlyStoppingTestResultAdapter(object):
61
"""An adapter for TestResult to stop at the first first failure or error"""
63
def __init__(self, result):
66
def addError(self, test, err):
67
self._result.addError(test, err)
70
def addFailure(self, test, err):
71
self._result.addFailure(test, err)
74
def __getattr__(self, name):
75
return getattr(self._result, name)
77
def __setattr__(self, name, value):
79
object.__setattr__(self, name, value)
80
return setattr(self._result, name, value)
83
class _MyResult(unittest._TextTestResult):
86
Shows output in a different format, including displaying runtime for tests.
89
def _elapsedTime(self):
90
return "%5dms" % (1000 * (time.time() - self._start_time))
92
def startTest(self, test):
93
unittest.TestResult.startTest(self, test)
94
# In a short description, the important words are in
95
# the beginning, but in an id, the important words are
97
SHOW_DESCRIPTIONS = False
99
width = bzrlib.osutils.terminal_width()
100
name_width = width - 15
102
if SHOW_DESCRIPTIONS:
103
what = test.shortDescription()
105
if len(what) > name_width:
106
what = what[:name_width-3] + '...'
109
if what.startswith('bzrlib.tests.'):
111
if len(what) > name_width:
112
what = '...' + what[3-name_width:]
113
what = what.ljust(name_width)
114
self.stream.write(what)
116
self._start_time = time.time()
118
def addError(self, test, err):
119
unittest.TestResult.addError(self, test, err)
121
self.stream.writeln("ERROR %s" % self._elapsedTime())
123
self.stream.write('E')
126
def addFailure(self, test, err):
127
unittest.TestResult.addFailure(self, test, err)
129
self.stream.writeln(" FAIL %s" % self._elapsedTime())
131
self.stream.write('F')
134
def addSuccess(self, test):
136
self.stream.writeln(' OK %s' % self._elapsedTime())
138
self.stream.write('~')
140
unittest.TestResult.addSuccess(self, test)
142
def printErrorList(self, flavour, errors):
143
for test, err in errors:
144
self.stream.writeln(self.separator1)
145
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
146
if hasattr(test, '_get_log'):
147
self.stream.writeln()
148
self.stream.writeln('log from this test:')
149
print >>self.stream, test._get_log()
150
self.stream.writeln(self.separator2)
151
self.stream.writeln("%s" % err)
154
class TextTestRunner(unittest.TextTestRunner):
155
stop_on_failure = False
157
def _makeResult(self):
158
result = _MyResult(self.stream, self.descriptions, self.verbosity)
159
if self.stop_on_failure:
160
result = EarlyStoppingTestResultAdapter(result)
164
def iter_suite_tests(suite):
165
"""Return all tests in a suite, recursing through nested suites"""
166
for item in suite._tests:
167
if isinstance(item, unittest.TestCase):
169
elif isinstance(item, unittest.TestSuite):
170
for r in iter_suite_tests(item):
173
raise Exception('unknown object %r inside test suite %r'
177
class TestSkipped(Exception):
178
"""Indicates that a test was intentionally skipped, rather than failing."""
35
MODULES_TO_DOCTEST = []
37
from logging import debug, warning, error
182
39
class CommandFailed(Exception):
191
48
Error and debug log messages are redirected from their usual
192
49
location into a temporary file, the contents of which can be
193
retrieved by _get_log(). We use a real OS file, not an in-memory object,
194
so that it can also capture file IO. When the test completes this file
195
is read into memory and removed from disk.
50
retrieved by _get_log().
197
52
There are also convenience functions to invoke bzr's command-line
198
routine, and to build and check bzr trees.
200
In addition to the usual method of overriding tearDown(), this class also
201
allows subclasses to register functions into the _cleanups list, which is
202
run in order as the object is torn down. It's less likely this will be
203
accidentally overlooked.
53
routine, and to build and check bzr trees."""
207
_log_file_name = None
58
# this replaces the default testsweet.TestCase; we don't want logging changed
211
59
unittest.TestCase.setUp(self)
213
self._cleanEnvironment()
214
60
bzrlib.trace.disable_default_logging()
217
def _ndiff_strings(self, a, b):
218
"""Return ndiff between two strings containing lines.
220
A trailing newline is added if missing to make the strings
222
if b and b[-1] != '\n':
224
if a and a[-1] != '\n':
226
difflines = difflib.ndiff(a.splitlines(True),
228
linejunk=lambda x: False,
229
charjunk=lambda x: False)
230
return ''.join(difflines)
232
def assertEqualDiff(self, a, b):
233
"""Assert two texts are equal, if not raise an exception.
235
This is intended for use with multi-line strings where it can
236
be hard to find the differences by eye.
238
# TODO: perhaps override assertEquals to call this for strings?
241
raise AssertionError("texts not equal:\n" +
242
self._ndiff_strings(a, b))
244
def assertContainsRe(self, haystack, needle_re):
245
"""Assert that a contains something matching a regular expression."""
246
if not re.search(needle_re, haystack):
247
raise AssertionError('pattern "%s" not found in "%s"'
248
% (needle_re, haystack))
250
def _startLogFile(self):
251
"""Send bzr and test log messages to a temporary file.
253
The file is removed as the test is torn down.
61
self._enable_file_logging()
64
def _enable_file_logging(self):
255
65
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
256
67
self._log_file = os.fdopen(fileno, 'w+')
257
bzrlib.trace.enable_test_log(self._log_file)
69
hdlr = logging.StreamHandler(self._log_file)
70
hdlr.setLevel(logging.DEBUG)
71
hdlr.setFormatter(logging.Formatter('%(levelname)8s %(message)s'))
72
logging.getLogger('').addHandler(hdlr)
73
logging.getLogger('').setLevel(logging.DEBUG)
75
debug('opened log file %s', name)
258
77
self._log_file_name = name
259
self.addCleanup(self._finishLogFile)
261
def _finishLogFile(self):
262
"""Finished with the log file.
264
Read contents into memory, close, and delete.
266
bzrlib.trace.disable_test_log()
267
self._log_file.seek(0)
268
self._log_contents = self._log_file.read()
81
logging.getLogger('').removeHandler(self._log_hdlr)
82
bzrlib.trace.enable_default_logging()
83
logging.debug('%s teardown', self.id())
269
84
self._log_file.close()
270
os.remove(self._log_file_name)
271
self._log_file = self._log_file_name = None
273
def addCleanup(self, callable):
274
"""Arrange to run a callable when this case is torn down.
276
Callables are run in the reverse of the order they are registered,
277
ie last-in first-out.
279
if callable in self._cleanups:
280
raise ValueError("cleanup function %r already registered on %s"
282
self._cleanups.append(callable)
284
def _cleanEnvironment(self):
287
'APPDATA': os.getcwd(),
292
self.addCleanup(self._restoreEnvironment)
293
for name, value in new_env.iteritems():
294
self._captureVar(name, value)
297
def _captureVar(self, name, newvalue):
298
"""Set an environment variable, preparing it to be reset when finished."""
299
self.__old_env[name] = os.environ.get(name, None)
301
if name in os.environ:
304
os.environ[name] = newvalue
307
def _restoreVar(name, value):
309
if name in os.environ:
312
os.environ[name] = value
314
def _restoreEnvironment(self):
315
for name, value in self.__old_env.iteritems():
316
self._restoreVar(name, value)
320
85
unittest.TestCase.tearDown(self)
322
def _runCleanups(self):
323
"""Run registered cleanup functions.
325
This should only be called from TestCase.tearDown.
327
for cleanup_fn in reversed(self._cleanups):
330
88
def log(self, *args):
333
91
def _get_log(self):
334
92
"""Return as a string the log for this test"""
335
if self._log_file_name:
336
return open(self._log_file_name).read()
338
return self._log_contents
339
# TODO: Delete the log after it's been read in
341
def capture(self, cmd, retcode=0):
342
"""Shortcut that splits cmd into words, runs, and returns stdout"""
343
return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
345
def run_bzr_captured(self, argv, retcode=0):
346
"""Invoke bzr and return (stdout, stderr).
348
Useful for code that wants to check the contents of the
349
output, the way error messages are presented, etc.
93
return open(self._log_file_name).read()
95
def run_bzr(self, *args, **kwargs):
96
"""Invoke bzr, as if it were run from the command line.
351
98
This should be the main method for tests that want to exercise the
352
99
overall behavior of the bzr application (rather than a unit test
355
102
Much of the old code runs bzr by forking a new copy of Python, but
356
103
that is slower, harder to debug, and generally not necessary.
358
This runs bzr through the interface that catches and reports
359
errors, and with logging set to something approximating the
360
default, so that error reporting can be checked.
362
argv -- arguments to invoke bzr
363
retcode -- expected return code, or None for don't-care.
367
self.log('run bzr: %s', ' '.join(argv))
368
# FIXME: don't call into logging here
369
handler = logging.StreamHandler(stderr)
370
handler.setFormatter(bzrlib.trace.QuietFormatter())
371
handler.setLevel(logging.INFO)
372
logger = logging.getLogger('')
373
logger.addHandler(handler)
375
result = self.apply_redirected(None, stdout, stderr,
376
bzrlib.commands.run_bzr_catch_errors,
379
logger.removeHandler(handler)
380
out = stdout.getvalue()
381
err = stderr.getvalue()
383
self.log('output:\n%s', out)
385
self.log('errors:\n%s', err)
386
if retcode is not None:
387
self.assertEquals(result, retcode)
390
def run_bzr(self, *args, **kwargs):
391
"""Invoke bzr, as if it were run from the command line.
393
This should be the main method for tests that want to exercise the
394
overall behavior of the bzr application (rather than a unit test
395
or a functional test of the library.)
397
This sends the stdout/stderr results into the test's log,
398
where it may be useful for debugging. See also run_captured.
400
retcode = kwargs.pop('retcode', 0)
401
return self.run_bzr_captured(args, retcode)
105
retcode = kwargs.get('retcode', 0)
106
result = self.apply_redirected(None, None, None,
107
bzrlib.commands.run_bzr, args)
108
self.assertEquals(result, retcode)
403
111
def check_inventory_shape(self, inv, shape):
404
112
"""Compare an inventory to a list of expected names.
506
209
super(TestCaseInTempDir, self).setUp()
507
211
self._make_test_root()
508
_currentdir = os.getcwdu()
509
short_id = self.id().replace('bzrlib.tests.', '') \
212
self._currentdir = os.getcwdu()
213
short_id = self.id().replace('bzrlib.selftest.', '') \
510
214
.replace('__main__.', '')
511
215
self.test_dir = os.path.join(self.TEST_ROOT, short_id)
512
216
os.mkdir(self.test_dir)
513
217
os.chdir(self.test_dir)
514
os.environ['HOME'] = self.test_dir
515
def _leaveDirectory():
516
os.chdir(_currentdir)
517
self.addCleanup(_leaveDirectory)
519
def build_tree(self, shape, line_endings='native'):
221
os.chdir(self._currentdir)
222
super(TestCaseInTempDir, self).tearDown()
224
def _formcmd(self, cmd):
225
if isinstance(cmd, basestring):
228
cmd[0] = self.BZRPATH
229
if self.OVERRIDE_PYTHON:
230
cmd.insert(0, self.OVERRIDE_PYTHON)
231
self.log('$ %r' % cmd)
234
def runcmd(self, cmd, retcode=0):
235
"""Run one command and check the return code.
237
Returns a tuple of (stdout,stderr) strings.
239
If a single string is based, it is split into words.
240
For commands that are not simple space-separated words, please
241
pass a list instead."""
242
cmd = self._formcmd(cmd)
243
self.log('$ ' + ' '.join(cmd))
244
actual_retcode = subprocess.call(cmd, stdout=self._log_file,
245
stderr=self._log_file)
246
if retcode != actual_retcode:
247
raise CommandFailed("test failed: %r returned %d, expected %d"
248
% (cmd, actual_retcode, retcode))
250
def backtick(self, cmd, retcode=0):
251
"""Run a command and return its output"""
252
cmd = self._formcmd(cmd)
253
child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
254
outd, errd = child.communicate()
256
actual_retcode = child.wait()
258
outd = outd.replace('\r', '')
260
if retcode != actual_retcode:
261
raise CommandFailed("test failed: %r returned %d, expected %d"
262
% (cmd, actual_retcode, retcode))
268
def build_tree(self, shape):
520
269
"""Build a test tree according to a pattern.
522
271
shape is a sequence of file specifications. If the final
523
272
character is '/', a directory is created.
525
274
This doesn't add anything to a branch.
526
:param line_endings: Either 'binary' or 'native'
527
in binary mode, exact contents are written
528
in native mode, the line endings match the
529
default platform endings.
531
276
# XXX: It's OK to just create them using forward slashes on windows?
532
278
for name in shape:
533
self.assert_(isinstance(name, basestring))
279
assert isinstance(name, basestring)
534
280
if name[-1] == '/':
535
281
os.mkdir(name[:-1])
537
if line_endings == 'binary':
539
elif line_endings == 'native':
542
raise BzrError('Invalid line ending request %r' % (line_endings,))
543
284
print >>f, "contents of", name
546
def build_tree_contents(self, shape):
547
build_tree_contents(shape)
549
def failUnlessExists(self, path):
550
"""Fail unless path, which may be abs or relative, exists."""
551
self.failUnless(bzrlib.osutils.lexists(path))
553
def assertFileEqual(self, content, path):
554
"""Fail if path does not contain 'content'."""
555
self.failUnless(bzrlib.osutils.lexists(path))
556
self.assertEqualDiff(content, open(path, 'r').read())
559
def filter_suite_by_re(suite, pattern):
561
filter_re = re.compile(pattern)
562
for test in iter_suite_tests(suite):
563
if filter_re.search(test.id()):
568
def run_suite(suite, name='test', verbose=False, pattern=".*",
569
stop_on_failure=False, keep_output=False):
570
TestCaseInTempDir._TEST_NAME = name
575
runner = TextTestRunner(stream=sys.stdout,
578
runner.stop_on_failure=stop_on_failure
580
suite = filter_suite_by_re(suite, pattern)
581
result = runner.run(suite)
582
# This is still a little bogus,
583
# but only a little. Folk not using our testrunner will
584
# have to delete their temp directories themselves.
585
if result.wasSuccessful() or not keep_output:
586
if TestCaseInTempDir.TEST_ROOT is not None:
587
shutil.rmtree(TestCaseInTempDir.TEST_ROOT)
589
print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
590
return result.wasSuccessful()
593
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
289
class MetaTestLog(TestCase):
290
def test_logging(self):
291
"""Test logs are captured when a test fails."""
292
logging.info('an info message')
293
warning('something looks dodgy...')
294
logging.debug('hello, test is running')
298
def selftest(verbose=False, pattern=".*"):
595
299
"""Run the whole test suite under the enhanced runner"""
596
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
597
stop_on_failure=stop_on_failure, keep_output=keep_output)
300
return testsweet.run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
600
303
def test_suite():
601
304
"""Build and return TestSuite for the whole program."""
305
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
306
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
307
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
602
308
from doctest import DocTestSuite
604
global MODULES_TO_DOCTEST
607
'bzrlib.tests.test_api',
608
'bzrlib.tests.test_gpg',
609
'bzrlib.tests.test_identitymap',
610
'bzrlib.tests.test_inv',
611
'bzrlib.tests.test_ancestry',
612
'bzrlib.tests.test_commit',
613
'bzrlib.tests.test_command',
614
'bzrlib.tests.test_commit_merge',
615
'bzrlib.tests.test_config',
616
'bzrlib.tests.test_merge3',
617
'bzrlib.tests.test_merge',
618
'bzrlib.tests.test_hashcache',
619
'bzrlib.tests.test_status',
620
'bzrlib.tests.test_log',
621
'bzrlib.tests.test_revisionnamespaces',
622
'bzrlib.tests.test_branch',
623
'bzrlib.tests.test_revision',
624
'bzrlib.tests.test_revision_info',
625
'bzrlib.tests.test_merge_core',
626
'bzrlib.tests.test_smart_add',
627
'bzrlib.tests.test_bad_files',
628
'bzrlib.tests.test_diff',
629
'bzrlib.tests.test_parent',
630
'bzrlib.tests.test_xml',
631
'bzrlib.tests.test_weave',
632
'bzrlib.tests.test_fetch',
633
'bzrlib.tests.test_whitebox',
634
'bzrlib.tests.test_store',
635
'bzrlib.tests.test_sampler',
636
'bzrlib.tests.test_transactions',
637
'bzrlib.tests.test_transport',
638
'bzrlib.tests.test_sftp',
639
'bzrlib.tests.test_graph',
640
'bzrlib.tests.test_workingtree',
641
'bzrlib.tests.test_upgrade',
642
'bzrlib.tests.test_uncommit',
643
'bzrlib.tests.test_ui',
644
'bzrlib.tests.test_conflicts',
645
'bzrlib.tests.test_testament',
646
'bzrlib.tests.test_annotate',
647
'bzrlib.tests.test_revprops',
648
'bzrlib.tests.test_options',
649
'bzrlib.tests.test_http',
650
'bzrlib.tests.test_nonascii',
651
'bzrlib.tests.test_reweave',
652
'bzrlib.tests.test_tsort',
653
'bzrlib.tests.test_trace',
654
'bzrlib.tests.test_rio',
655
'bzrlib.tests.test_msgeditor',
656
'bzrlib.tests.test_selftest',
314
global MODULES_TO_TEST, MODULES_TO_DOCTEST
317
['bzrlib.selftest.MetaTestLog',
318
'bzrlib.selftest.testinv',
319
'bzrlib.selftest.test_commit',
320
'bzrlib.selftest.test_commit_merge',
321
'bzrlib.selftest.versioning',
322
'bzrlib.selftest.testmerge3',
323
'bzrlib.selftest.testhashcache',
324
'bzrlib.selftest.teststatus',
325
'bzrlib.selftest.testlog',
326
'bzrlib.selftest.testrevisionnamespaces',
327
'bzrlib.selftest.testbranch',
328
'bzrlib.selftest.testrevision',
329
'bzrlib.selftest.test_merge_core',
330
'bzrlib.selftest.test_smart_add',
331
'bzrlib.selftest.testdiff',
332
'bzrlib.selftest.test_parent',
333
'bzrlib.selftest.test_xml',
334
'bzrlib.selftest.test_weave',
335
'bzrlib.selftest.testfetch',
336
'bzrlib.selftest.whitebox',
337
'bzrlib.selftest.teststore',
338
'bzrlib.selftest.blackbox',
659
print '%10s: %s' % ('bzr', os.path.realpath(sys.argv[0]))
660
print '%10s: %s' % ('bzrlib', bzrlib.__path__[0])
341
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
342
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
343
if m not in MODULES_TO_DOCTEST:
344
MODULES_TO_DOCTEST.append(m)
346
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
347
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
662
349
suite = TestSuite()
663
# python2.4's TestLoader.loadTestsFromNames gives very poor
664
# errors if it fails to load a named module - no indication of what's
665
# actually wrong, just "no such module". We should probably override that
666
# class, but for the moment just load them ourselves. (mbp 20051202)
667
loader = TestLoader()
668
for mod_name in testmod_names:
669
mod = _load_module_by_name(mod_name)
670
suite.addTest(loader.loadTestsFromModule(mod))
671
for package in packages_to_test():
672
suite.addTest(package.test_suite())
350
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
673
351
for m in MODULES_TO_TEST:
674
suite.addTest(loader.loadTestsFromModule(m))
352
suite.addTest(TestLoader().loadTestsFromModule(m))
675
353
for m in (MODULES_TO_DOCTEST):
676
354
suite.addTest(DocTestSuite(m))
677
355
for p in bzrlib.plugin.all_plugins: