1
# Copyright (C) 2005 by Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from cStringIO import StringIO
32
import bzrlib.commands
33
from bzrlib.errors import BzrError
34
import bzrlib.inventory
40
from bzrlib.trace import mutter
41
from bzrlib.tests.TestUtil import TestLoader, TestSuite
42
from bzrlib.tests.treeshape import build_tree_contents
45
MODULES_TO_DOCTEST = [
54
def packages_to_test():
55
import bzrlib.tests.blackbox
61
class EarlyStoppingTestResultAdapter(object):
62
"""An adapter for TestResult to stop at the first first failure or error"""
64
def __init__(self, result):
67
def addError(self, test, err):
68
self._result.addError(test, err)
71
def addFailure(self, test, err):
72
self._result.addFailure(test, err)
75
def __getattr__(self, name):
76
return getattr(self._result, name)
78
def __setattr__(self, name, value):
80
object.__setattr__(self, name, value)
81
return setattr(self._result, name, value)
84
class _MyResult(unittest._TextTestResult):
87
Shows output in a different format, including displaying runtime for tests.
90
def _elapsedTime(self):
91
return "%5dms" % (1000 * (time.time() - self._start_time))
93
def startTest(self, test):
94
unittest.TestResult.startTest(self, test)
95
# In a short description, the important words are in
96
# the beginning, but in an id, the important words are
98
SHOW_DESCRIPTIONS = False
100
width = bzrlib.osutils.terminal_width()
101
name_width = width - 15
103
if SHOW_DESCRIPTIONS:
104
what = test.shortDescription()
106
if len(what) > name_width:
107
what = what[:name_width-3] + '...'
110
if what.startswith('bzrlib.tests.'):
112
if len(what) > name_width:
113
what = '...' + what[3-name_width:]
114
what = what.ljust(name_width)
115
self.stream.write(what)
117
self._start_time = time.time()
119
def addError(self, test, err):
120
if isinstance(err[1], TestSkipped):
121
return self.addSkipped(test, err)
122
unittest.TestResult.addError(self, test, err)
124
self.stream.writeln("ERROR %s" % self._elapsedTime())
126
self.stream.write('E')
129
def addFailure(self, test, err):
130
unittest.TestResult.addFailure(self, test, err)
132
self.stream.writeln(" FAIL %s" % self._elapsedTime())
134
self.stream.write('F')
137
def addSuccess(self, test):
139
self.stream.writeln(' OK %s' % self._elapsedTime())
141
self.stream.write('~')
143
unittest.TestResult.addSuccess(self, test)
145
def addSkipped(self, test, skip_excinfo):
147
print >>self.stream, ' SKIP %s' % self._elapsedTime()
148
print >>self.stream, ' %s' % skip_excinfo[1]
150
self.stream.write('S')
152
# seems best to treat this as success from point-of-view of unittest
153
# -- it actually does nothing so it barely matters :)
154
unittest.TestResult.addSuccess(self, test)
156
def printErrorList(self, flavour, errors):
157
for test, err in errors:
158
self.stream.writeln(self.separator1)
159
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
160
if hasattr(test, '_get_log'):
162
print >>self.stream, \
163
('vvvv[log from %s]' % test).ljust(78,'-')
164
print >>self.stream, test._get_log()
165
print >>self.stream, \
166
('^^^^[log from %s]' % test).ljust(78,'-')
167
self.stream.writeln(self.separator2)
168
self.stream.writeln("%s" % err)
171
class TextTestRunner(unittest.TextTestRunner):
172
stop_on_failure = False
174
def _makeResult(self):
175
result = _MyResult(self.stream, self.descriptions, self.verbosity)
176
if self.stop_on_failure:
177
result = EarlyStoppingTestResultAdapter(result)
181
def iter_suite_tests(suite):
182
"""Return all tests in a suite, recursing through nested suites"""
183
for item in suite._tests:
184
if isinstance(item, unittest.TestCase):
186
elif isinstance(item, unittest.TestSuite):
187
for r in iter_suite_tests(item):
190
raise Exception('unknown object %r inside test suite %r'
194
class TestSkipped(Exception):
195
"""Indicates that a test was intentionally skipped, rather than failing."""
199
class CommandFailed(Exception):
202
class TestCase(unittest.TestCase):
203
"""Base class for bzr unit tests.
205
Tests that need access to disk resources should subclass
206
TestCaseInTempDir not TestCase.
208
Error and debug log messages are redirected from their usual
209
location into a temporary file, the contents of which can be
210
retrieved by _get_log(). We use a real OS file, not an in-memory object,
211
so that it can also capture file IO. When the test completes this file
212
is read into memory and removed from disk.
214
There are also convenience functions to invoke bzr's command-line
215
routine, and to build and check bzr trees.
217
In addition to the usual method of overriding tearDown(), this class also
218
allows subclasses to register functions into the _cleanups list, which is
219
run in order as the object is torn down. It's less likely this will be
220
accidentally overlooked.
224
_log_file_name = None
228
unittest.TestCase.setUp(self)
230
self._cleanEnvironment()
231
bzrlib.trace.disable_default_logging()
234
def _ndiff_strings(self, a, b):
235
"""Return ndiff between two strings containing lines.
237
A trailing newline is added if missing to make the strings
239
if b and b[-1] != '\n':
241
if a and a[-1] != '\n':
243
difflines = difflib.ndiff(a.splitlines(True),
245
linejunk=lambda x: False,
246
charjunk=lambda x: False)
247
return ''.join(difflines)
249
def assertEqualDiff(self, a, b):
250
"""Assert two texts are equal, if not raise an exception.
252
This is intended for use with multi-line strings where it can
253
be hard to find the differences by eye.
255
# TODO: perhaps override assertEquals to call this for strings?
258
raise AssertionError("texts not equal:\n" +
259
self._ndiff_strings(a, b))
261
def assertContainsRe(self, haystack, needle_re):
262
"""Assert that a contains something matching a regular expression."""
263
if not re.search(needle_re, haystack):
264
raise AssertionError('pattern "%s" not found in "%s"'
265
% (needle_re, haystack))
267
def AssertSubset(self, sublist, superlist):
268
"""Assert that every entry in sublist is present in superlist."""
270
for entry in sublist:
271
if entry not in superlist:
272
missing.append(entry)
274
raise AssertionError("value(s) %r not present in container %r" %
275
(missing, superlist))
277
def _startLogFile(self):
278
"""Send bzr and test log messages to a temporary file.
280
The file is removed as the test is torn down.
282
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
283
encoder, decoder, stream_reader, stream_writer = codecs.lookup('UTF-8')
284
self._log_file = stream_writer(os.fdopen(fileno, 'w+'))
285
bzrlib.trace.enable_test_log(self._log_file)
286
self._log_file_name = name
287
self.addCleanup(self._finishLogFile)
289
def _finishLogFile(self):
290
"""Finished with the log file.
292
Read contents into memory, close, and delete.
294
bzrlib.trace.disable_test_log()
295
self._log_file.seek(0)
296
self._log_contents = self._log_file.read()
297
self._log_file.close()
298
os.remove(self._log_file_name)
299
self._log_file = self._log_file_name = None
301
def addCleanup(self, callable):
302
"""Arrange to run a callable when this case is torn down.
304
Callables are run in the reverse of the order they are registered,
305
ie last-in first-out.
307
if callable in self._cleanups:
308
raise ValueError("cleanup function %r already registered on %s"
310
self._cleanups.append(callable)
312
def _cleanEnvironment(self):
315
'APPDATA': os.getcwd(),
320
self.addCleanup(self._restoreEnvironment)
321
for name, value in new_env.iteritems():
322
self._captureVar(name, value)
325
def _captureVar(self, name, newvalue):
326
"""Set an environment variable, preparing it to be reset when finished."""
327
self.__old_env[name] = os.environ.get(name, None)
329
if name in os.environ:
332
os.environ[name] = newvalue
335
def _restoreVar(name, value):
337
if name in os.environ:
340
os.environ[name] = value
342
def _restoreEnvironment(self):
343
for name, value in self.__old_env.iteritems():
344
self._restoreVar(name, value)
348
unittest.TestCase.tearDown(self)
350
def _runCleanups(self):
351
"""Run registered cleanup functions.
353
This should only be called from TestCase.tearDown.
355
for cleanup_fn in reversed(self._cleanups):
358
def log(self, *args):
362
"""Return as a string the log for this test"""
363
if self._log_file_name:
364
return open(self._log_file_name).read()
366
return self._log_contents
367
# TODO: Delete the log after it's been read in
369
def capture(self, cmd, retcode=0):
370
"""Shortcut that splits cmd into words, runs, and returns stdout"""
371
return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
373
def run_bzr_captured(self, argv, retcode=0):
374
"""Invoke bzr and return (stdout, stderr).
376
Useful for code that wants to check the contents of the
377
output, the way error messages are presented, etc.
379
This should be the main method for tests that want to exercise the
380
overall behavior of the bzr application (rather than a unit test
381
or a functional test of the library.)
383
Much of the old code runs bzr by forking a new copy of Python, but
384
that is slower, harder to debug, and generally not necessary.
386
This runs bzr through the interface that catches and reports
387
errors, and with logging set to something approximating the
388
default, so that error reporting can be checked.
390
argv -- arguments to invoke bzr
391
retcode -- expected return code, or None for don't-care.
395
self.log('run bzr: %s', ' '.join(argv))
396
# FIXME: don't call into logging here
397
handler = logging.StreamHandler(stderr)
398
handler.setFormatter(bzrlib.trace.QuietFormatter())
399
handler.setLevel(logging.INFO)
400
logger = logging.getLogger('')
401
logger.addHandler(handler)
403
result = self.apply_redirected(None, stdout, stderr,
404
bzrlib.commands.run_bzr_catch_errors,
407
logger.removeHandler(handler)
408
out = stdout.getvalue()
409
err = stderr.getvalue()
411
self.log('output:\n%s', out)
413
self.log('errors:\n%s', err)
414
if retcode is not None:
415
self.assertEquals(result, retcode)
418
def run_bzr(self, *args, **kwargs):
419
"""Invoke bzr, as if it were run from the command line.
421
This should be the main method for tests that want to exercise the
422
overall behavior of the bzr application (rather than a unit test
423
or a functional test of the library.)
425
This sends the stdout/stderr results into the test's log,
426
where it may be useful for debugging. See also run_captured.
428
retcode = kwargs.pop('retcode', 0)
429
return self.run_bzr_captured(args, retcode)
431
def check_inventory_shape(self, inv, shape):
432
"""Compare an inventory to a list of expected names.
434
Fail if they are not precisely equal.
437
shape = list(shape) # copy
438
for path, ie in inv.entries():
439
name = path.replace('\\', '/')
447
self.fail("expected paths not found in inventory: %r" % shape)
449
self.fail("unexpected paths found in inventory: %r" % extras)
451
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
452
a_callable=None, *args, **kwargs):
453
"""Call callable with redirected std io pipes.
455
Returns the return code."""
456
if not callable(a_callable):
457
raise ValueError("a_callable must be callable.")
461
if hasattr(self, "_log_file"):
462
stdout = self._log_file
466
if hasattr(self, "_log_file"):
467
stderr = self._log_file
470
real_stdin = sys.stdin
471
real_stdout = sys.stdout
472
real_stderr = sys.stderr
477
return a_callable(*args, **kwargs)
479
sys.stdout = real_stdout
480
sys.stderr = real_stderr
481
sys.stdin = real_stdin
484
BzrTestBase = TestCase
487
class TestCaseInTempDir(TestCase):
488
"""Derived class that runs a test within a temporary directory.
490
This is useful for tests that need to create a branch, etc.
492
The directory is created in a slightly complex way: for each
493
Python invocation, a new temporary top-level directory is created.
494
All test cases create their own directory within that. If the
495
tests complete successfully, the directory is removed.
497
InTempDir is an old alias for FunctionalTestCase.
502
OVERRIDE_PYTHON = 'python'
504
def check_file_contents(self, filename, expect):
505
self.log("check contents of file %s" % filename)
506
contents = file(filename, 'r').read()
507
if contents != expect:
508
self.log("expected: %r" % expect)
509
self.log("actually: %r" % contents)
510
self.fail("contents of %s not as expected" % filename)
512
def _make_test_root(self):
513
if TestCaseInTempDir.TEST_ROOT is not None:
517
root = u'test%04d.tmp' % i
521
if e.errno == errno.EEXIST:
526
# successfully created
527
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
529
# make a fake bzr directory there to prevent any tests propagating
530
# up onto the source directory's real branch
531
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
534
super(TestCaseInTempDir, self).setUp()
535
self._make_test_root()
536
_currentdir = os.getcwdu()
537
short_id = self.id().replace('bzrlib.tests.', '') \
538
.replace('__main__.', '')
539
self.test_dir = os.path.join(self.TEST_ROOT, short_id)
540
os.mkdir(self.test_dir)
541
os.chdir(self.test_dir)
542
os.environ['HOME'] = self.test_dir
543
def _leaveDirectory():
544
os.chdir(_currentdir)
545
self.addCleanup(_leaveDirectory)
547
def build_tree(self, shape, line_endings='native'):
548
"""Build a test tree according to a pattern.
550
shape is a sequence of file specifications. If the final
551
character is '/', a directory is created.
553
This doesn't add anything to a branch.
554
:param line_endings: Either 'binary' or 'native'
555
in binary mode, exact contents are written
556
in native mode, the line endings match the
557
default platform endings.
559
# XXX: It's OK to just create them using forward slashes on windows?
561
self.assert_(isinstance(name, basestring))
565
if line_endings == 'binary':
567
elif line_endings == 'native':
570
raise BzrError('Invalid line ending request %r' % (line_endings,))
571
print >>f, "contents of", name
574
def build_tree_contents(self, shape):
575
build_tree_contents(shape)
577
def failUnlessExists(self, path):
578
"""Fail unless path, which may be abs or relative, exists."""
579
self.failUnless(bzrlib.osutils.lexists(path))
581
def assertFileEqual(self, content, path):
582
"""Fail if path does not contain 'content'."""
583
self.failUnless(bzrlib.osutils.lexists(path))
584
self.assertEqualDiff(content, open(path, 'r').read())
587
def filter_suite_by_re(suite, pattern):
589
filter_re = re.compile(pattern)
590
for test in iter_suite_tests(suite):
591
if filter_re.search(test.id()):
596
def run_suite(suite, name='test', verbose=False, pattern=".*",
597
stop_on_failure=False, keep_output=False):
598
TestCaseInTempDir._TEST_NAME = name
603
runner = TextTestRunner(stream=sys.stdout,
606
runner.stop_on_failure=stop_on_failure
608
suite = filter_suite_by_re(suite, pattern)
609
result = runner.run(suite)
610
# This is still a little bogus,
611
# but only a little. Folk not using our testrunner will
612
# have to delete their temp directories themselves.
613
if result.wasSuccessful() or not keep_output:
614
if TestCaseInTempDir.TEST_ROOT is not None:
615
shutil.rmtree(TestCaseInTempDir.TEST_ROOT)
617
print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
618
return result.wasSuccessful()
621
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
623
"""Run the whole test suite under the enhanced runner"""
624
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
625
stop_on_failure=stop_on_failure, keep_output=keep_output)
629
"""Build and return TestSuite for the whole program."""
630
from doctest import DocTestSuite
632
global MODULES_TO_DOCTEST
635
'bzrlib.tests.test_ancestry',
636
'bzrlib.tests.test_annotate',
637
'bzrlib.tests.test_api',
638
'bzrlib.tests.test_bad_files',
639
'bzrlib.tests.test_branch',
640
'bzrlib.tests.test_command',
641
'bzrlib.tests.test_commit',
642
'bzrlib.tests.test_commit_merge',
643
'bzrlib.tests.test_config',
644
'bzrlib.tests.test_conflicts',
645
'bzrlib.tests.test_diff',
646
'bzrlib.tests.test_fetch',
647
'bzrlib.tests.test_gpg',
648
'bzrlib.tests.test_graph',
649
'bzrlib.tests.test_hashcache',
650
'bzrlib.tests.test_http',
651
'bzrlib.tests.test_identitymap',
652
'bzrlib.tests.test_inv',
653
'bzrlib.tests.test_log',
654
'bzrlib.tests.test_merge',
655
'bzrlib.tests.test_merge3',
656
'bzrlib.tests.test_merge_core',
657
'bzrlib.tests.test_missing',
658
'bzrlib.tests.test_msgeditor',
659
'bzrlib.tests.test_nonascii',
660
'bzrlib.tests.test_options',
661
'bzrlib.tests.test_parent',
662
'bzrlib.tests.test_plugins',
663
'bzrlib.tests.test_remove',
664
'bzrlib.tests.test_revision',
665
'bzrlib.tests.test_revision_info',
666
'bzrlib.tests.test_revisionnamespaces',
667
'bzrlib.tests.test_revprops',
668
'bzrlib.tests.test_reweave',
669
'bzrlib.tests.test_rio',
670
'bzrlib.tests.test_sampler',
671
'bzrlib.tests.test_selftest',
672
'bzrlib.tests.test_setup',
673
'bzrlib.tests.test_sftp',
674
'bzrlib.tests.test_smart_add',
675
'bzrlib.tests.test_source',
676
'bzrlib.tests.test_status',
677
'bzrlib.tests.test_store',
678
'bzrlib.tests.test_testament',
679
'bzrlib.tests.test_trace',
680
'bzrlib.tests.test_transactions',
681
'bzrlib.tests.test_transport',
682
'bzrlib.tests.test_tsort',
683
'bzrlib.tests.test_ui',
684
'bzrlib.tests.test_uncommit',
685
'bzrlib.tests.test_upgrade',
686
'bzrlib.tests.test_weave',
687
'bzrlib.tests.test_whitebox',
688
'bzrlib.tests.test_workingtree',
689
'bzrlib.tests.test_xml',
692
print '%10s: %s' % ('bzr', os.path.realpath(sys.argv[0]))
693
print '%10s: %s' % ('bzrlib', bzrlib.__path__[0])
696
# python2.4's TestLoader.loadTestsFromNames gives very poor
697
# errors if it fails to load a named module - no indication of what's
698
# actually wrong, just "no such module". We should probably override that
699
# class, but for the moment just load them ourselves. (mbp 20051202)
700
loader = TestLoader()
701
for mod_name in testmod_names:
702
mod = _load_module_by_name(mod_name)
703
suite.addTest(loader.loadTestsFromModule(mod))
704
for package in packages_to_test():
705
suite.addTest(package.test_suite())
706
for m in MODULES_TO_TEST:
707
suite.addTest(loader.loadTestsFromModule(m))
708
for m in (MODULES_TO_DOCTEST):
709
suite.addTest(DocTestSuite(m))
710
for name, plugin in bzrlib.plugin.all_plugins().items():
711
if hasattr(plugin, 'test_suite'):
712
suite.addTest(plugin.test_suite())
716
def _load_module_by_name(mod_name):
717
parts = mod_name.split('.')
718
module = __import__(mod_name)
720
# for historical reasons python returns the top-level module even though
721
# it loads the submodule; we need to walk down to get the one we want.
723
module = getattr(module, parts.pop(0))