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
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
21
44
MODULES_TO_TEST = []
22
MODULES_TO_DOCTEST = []
25
class BzrTestBase(InTempDir):
26
"""bzr-specific test base class"""
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
unittest.TestResult.addError(self, test, err)
122
self.stream.writeln("ERROR %s" % self._elapsedTime())
124
self.stream.write('E')
127
def addFailure(self, test, err):
128
unittest.TestResult.addFailure(self, test, err)
130
self.stream.writeln(" FAIL %s" % self._elapsedTime())
132
self.stream.write('F')
135
def addSuccess(self, test):
137
self.stream.writeln(' OK %s' % self._elapsedTime())
139
self.stream.write('~')
141
unittest.TestResult.addSuccess(self, test)
143
def printErrorList(self, flavour, errors):
144
for test, err in errors:
145
self.stream.writeln(self.separator1)
146
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
147
if hasattr(test, '_get_log'):
148
self.stream.writeln()
149
self.stream.writeln('log from this test:')
150
print >>self.stream, test._get_log()
151
self.stream.writeln(self.separator2)
152
self.stream.writeln("%s" % err)
155
class TextTestRunner(unittest.TextTestRunner):
156
stop_on_failure = False
158
def _makeResult(self):
159
result = _MyResult(self.stream, self.descriptions, self.verbosity)
160
if self.stop_on_failure:
161
result = EarlyStoppingTestResultAdapter(result)
165
def iter_suite_tests(suite):
166
"""Return all tests in a suite, recursing through nested suites"""
167
for item in suite._tests:
168
if isinstance(item, unittest.TestCase):
170
elif isinstance(item, unittest.TestSuite):
171
for r in iter_suite_tests(item):
174
raise Exception('unknown object %r inside test suite %r'
178
class TestSkipped(Exception):
179
"""Indicates that a test was intentionally skipped, rather than failing."""
183
class CommandFailed(Exception):
186
class TestCase(unittest.TestCase):
187
"""Base class for bzr unit tests.
189
Tests that need access to disk resources should subclass
190
TestCaseInTempDir not TestCase.
192
Error and debug log messages are redirected from their usual
193
location into a temporary file, the contents of which can be
194
retrieved by _get_log(). We use a real OS file, not an in-memory object,
195
so that it can also capture file IO. When the test completes this file
196
is read into memory and removed from disk.
198
There are also convenience functions to invoke bzr's command-line
199
routine, and to build and check bzr trees.
201
In addition to the usual method of overriding tearDown(), this class also
202
allows subclasses to register functions into the _cleanups list, which is
203
run in order as the object is torn down. It's less likely this will be
204
accidentally overlooked.
208
_log_file_name = None
212
unittest.TestCase.setUp(self)
214
self._cleanEnvironment()
215
bzrlib.trace.disable_default_logging()
218
def _ndiff_strings(self, a, b):
219
"""Return ndiff between two strings containing lines.
221
A trailing newline is added if missing to make the strings
223
if b and b[-1] != '\n':
225
if a and a[-1] != '\n':
227
difflines = difflib.ndiff(a.splitlines(True),
229
linejunk=lambda x: False,
230
charjunk=lambda x: False)
231
return ''.join(difflines)
233
def assertEqualDiff(self, a, b):
234
"""Assert two texts are equal, if not raise an exception.
236
This is intended for use with multi-line strings where it can
237
be hard to find the differences by eye.
239
# TODO: perhaps override assertEquals to call this for strings?
242
raise AssertionError("texts not equal:\n" +
243
self._ndiff_strings(a, b))
245
def assertContainsRe(self, haystack, needle_re):
246
"""Assert that a contains something matching a regular expression."""
247
if not re.search(needle_re, haystack):
248
raise AssertionError('pattern "%s" not found in "%s"'
249
% (needle_re, haystack))
251
def _startLogFile(self):
252
"""Send bzr and test log messages to a temporary file.
254
The file is removed as the test is torn down.
256
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
257
encoder, decoder, stream_reader, stream_writer = codecs.lookup('UTF-8')
258
self._log_file = stream_writer(os.fdopen(fileno, 'w+'))
259
bzrlib.trace.enable_test_log(self._log_file)
260
self._log_file_name = name
261
self.addCleanup(self._finishLogFile)
263
def _finishLogFile(self):
264
"""Finished with the log file.
266
Read contents into memory, close, and delete.
268
bzrlib.trace.disable_test_log()
269
self._log_file.seek(0)
270
self._log_contents = self._log_file.read()
271
self._log_file.close()
272
os.remove(self._log_file_name)
273
self._log_file = self._log_file_name = None
275
def addCleanup(self, callable):
276
"""Arrange to run a callable when this case is torn down.
278
Callables are run in the reverse of the order they are registered,
279
ie last-in first-out.
281
if callable in self._cleanups:
282
raise ValueError("cleanup function %r already registered on %s"
284
self._cleanups.append(callable)
286
def _cleanEnvironment(self):
289
'APPDATA': os.getcwd(),
294
self.addCleanup(self._restoreEnvironment)
295
for name, value in new_env.iteritems():
296
self._captureVar(name, value)
299
def _captureVar(self, name, newvalue):
300
"""Set an environment variable, preparing it to be reset when finished."""
301
self.__old_env[name] = os.environ.get(name, None)
303
if name in os.environ:
306
os.environ[name] = newvalue
309
def _restoreVar(name, value):
311
if name in os.environ:
314
os.environ[name] = value
316
def _restoreEnvironment(self):
317
for name, value in self.__old_env.iteritems():
318
self._restoreVar(name, value)
322
unittest.TestCase.tearDown(self)
324
def _runCleanups(self):
325
"""Run registered cleanup functions.
327
This should only be called from TestCase.tearDown.
329
for cleanup_fn in reversed(self._cleanups):
332
def log(self, *args):
336
"""Return as a string the log for this test"""
337
if self._log_file_name:
338
return open(self._log_file_name).read()
340
return self._log_contents
341
# TODO: Delete the log after it's been read in
343
def capture(self, cmd, retcode=0):
344
"""Shortcut that splits cmd into words, runs, and returns stdout"""
345
return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
347
def run_bzr_captured(self, argv, retcode=0):
348
"""Invoke bzr and return (stdout, stderr).
350
Useful for code that wants to check the contents of the
351
output, the way error messages are presented, etc.
353
This should be the main method for tests that want to exercise the
354
overall behavior of the bzr application (rather than a unit test
355
or a functional test of the library.)
357
Much of the old code runs bzr by forking a new copy of Python, but
358
that is slower, harder to debug, and generally not necessary.
360
This runs bzr through the interface that catches and reports
361
errors, and with logging set to something approximating the
362
default, so that error reporting can be checked.
364
argv -- arguments to invoke bzr
365
retcode -- expected return code, or None for don't-care.
369
self.log('run bzr: %s', ' '.join(argv))
370
# FIXME: don't call into logging here
371
handler = logging.StreamHandler(stderr)
372
handler.setFormatter(bzrlib.trace.QuietFormatter())
373
handler.setLevel(logging.INFO)
374
logger = logging.getLogger('')
375
logger.addHandler(handler)
377
result = self.apply_redirected(None, stdout, stderr,
378
bzrlib.commands.run_bzr_catch_errors,
381
logger.removeHandler(handler)
382
out = stdout.getvalue()
383
err = stderr.getvalue()
385
self.log('output:\n%s', out)
387
self.log('errors:\n%s', err)
388
if retcode is not None:
389
self.assertEquals(result, retcode)
27
392
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
393
"""Invoke bzr, as if it were run from the command line.
395
This should be the main method for tests that want to exercise the
396
overall behavior of the bzr application (rather than a unit test
397
or a functional test of the library.)
399
This sends the stdout/stderr results into the test's log,
400
where it may be useful for debugging. See also run_captured.
402
retcode = kwargs.pop('retcode', 0)
403
return self.run_bzr_captured(args, retcode)
405
def check_inventory_shape(self, inv, shape):
406
"""Compare an inventory to a list of expected names.
408
Fail if they are not precisely equal.
411
shape = list(shape) # copy
412
for path, ie in inv.entries():
413
name = path.replace('\\', '/')
421
self.fail("expected paths not found in inventory: %r" % shape)
423
self.fail("unexpected paths found in inventory: %r" % extras)
425
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
426
a_callable=None, *args, **kwargs):
427
"""Call callable with redirected std io pipes.
429
Returns the return code."""
430
if not callable(a_callable):
431
raise ValueError("a_callable must be callable.")
435
if hasattr(self, "_log_file"):
436
stdout = self._log_file
440
if hasattr(self, "_log_file"):
441
stderr = self._log_file
444
real_stdin = sys.stdin
445
real_stdout = sys.stdout
446
real_stderr = sys.stderr
451
return a_callable(*args, **kwargs)
453
sys.stdout = real_stdout
454
sys.stderr = real_stderr
455
sys.stdin = real_stdin
458
BzrTestBase = TestCase
461
class TestCaseInTempDir(TestCase):
462
"""Derived class that runs a test within a temporary directory.
464
This is useful for tests that need to create a branch, etc.
466
The directory is created in a slightly complex way: for each
467
Python invocation, a new temporary top-level directory is created.
468
All test cases create their own directory within that. If the
469
tests complete successfully, the directory is removed.
471
InTempDir is an old alias for FunctionalTestCase.
476
OVERRIDE_PYTHON = 'python'
478
def check_file_contents(self, filename, expect):
479
self.log("check contents of file %s" % filename)
480
contents = file(filename, 'r').read()
481
if contents != expect:
482
self.log("expected: %r" % expect)
483
self.log("actually: %r" % contents)
484
self.fail("contents of %s not as expected" % filename)
486
def _make_test_root(self):
487
if TestCaseInTempDir.TEST_ROOT is not None:
491
root = u'test%04d.tmp' % i
495
if e.errno == errno.EEXIST:
500
# successfully created
501
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
503
# make a fake bzr directory there to prevent any tests propagating
504
# up onto the source directory's real branch
505
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
508
super(TestCaseInTempDir, self).setUp()
509
self._make_test_root()
510
_currentdir = os.getcwdu()
511
short_id = self.id().replace('bzrlib.tests.', '') \
512
.replace('__main__.', '')
513
self.test_dir = os.path.join(self.TEST_ROOT, short_id)
514
os.mkdir(self.test_dir)
515
os.chdir(self.test_dir)
516
os.environ['HOME'] = self.test_dir
517
def _leaveDirectory():
518
os.chdir(_currentdir)
519
self.addCleanup(_leaveDirectory)
521
def build_tree(self, shape, line_endings='native'):
522
"""Build a test tree according to a pattern.
524
shape is a sequence of file specifications. If the final
525
character is '/', a directory is created.
527
This doesn't add anything to a branch.
528
:param line_endings: Either 'binary' or 'native'
529
in binary mode, exact contents are written
530
in native mode, the line endings match the
531
default platform endings.
533
# XXX: It's OK to just create them using forward slashes on windows?
535
self.assert_(isinstance(name, basestring))
539
if line_endings == 'binary':
541
elif line_endings == 'native':
544
raise BzrError('Invalid line ending request %r' % (line_endings,))
545
print >>f, "contents of", name
548
def build_tree_contents(self, shape):
549
build_tree_contents(shape)
551
def failUnlessExists(self, path):
552
"""Fail unless path, which may be abs or relative, exists."""
553
self.failUnless(bzrlib.osutils.lexists(path))
555
def assertFileEqual(self, content, path):
556
"""Fail if path does not contain 'content'."""
557
self.failUnless(bzrlib.osutils.lexists(path))
558
self.assertEqualDiff(content, open(path, 'r').read())
561
def filter_suite_by_re(suite, pattern):
563
filter_re = re.compile(pattern)
564
for test in iter_suite_tests(suite):
565
if filter_re.search(test.id()):
570
def run_suite(suite, name='test', verbose=False, pattern=".*",
571
stop_on_failure=False, keep_output=False):
572
TestCaseInTempDir._TEST_NAME = name
577
runner = TextTestRunner(stream=sys.stdout,
580
runner.stop_on_failure=stop_on_failure
582
suite = filter_suite_by_re(suite, pattern)
583
result = runner.run(suite)
584
# This is still a little bogus,
585
# but only a little. Folk not using our testrunner will
586
# have to delete their temp directories themselves.
587
if result.wasSuccessful() or not keep_output:
588
if TestCaseInTempDir.TEST_ROOT is not None:
589
shutil.rmtree(TestCaseInTempDir.TEST_ROOT)
591
print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
592
return result.wasSuccessful()
595
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
597
"""Run the whole test suite under the enhanced runner"""
598
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
599
stop_on_failure=stop_on_failure, keep_output=keep_output)
603
"""Build and return TestSuite for the whole program."""
36
604
from doctest import DocTestSuite
43
global MODULES_TO_TEST, MODULES_TO_DOCTEST
46
['bzrlib.selftest.whitebox',
47
'bzrlib.selftest.versioning',
48
'bzrlib.selftest.testinv',
49
'bzrlib.selftest.testmerge3',
50
'bzrlib.selftest.testhashcache',
51
'bzrlib.selftest.teststatus',
52
'bzrlib.selftest.testlog',
53
'bzrlib.selftest.blackbox',
54
'bzrlib.selftest.testrevisionnamespaces',
55
'bzrlib.selftest.testbranch',
56
'bzrlib.selftest.testrevision',
58
'bzrlib.selftest.testdiff',
606
global MODULES_TO_DOCTEST
609
'bzrlib.tests.test_api',
610
'bzrlib.tests.test_gpg',
611
'bzrlib.tests.test_identitymap',
612
'bzrlib.tests.test_inv',
613
'bzrlib.tests.test_ancestry',
614
'bzrlib.tests.test_commit',
615
'bzrlib.tests.test_command',
616
'bzrlib.tests.test_commit_merge',
617
'bzrlib.tests.test_config',
618
'bzrlib.tests.test_merge3',
619
'bzrlib.tests.test_merge',
620
'bzrlib.tests.test_hashcache',
621
'bzrlib.tests.test_status',
622
'bzrlib.tests.test_log',
623
'bzrlib.tests.test_revisionnamespaces',
624
'bzrlib.tests.test_branch',
625
'bzrlib.tests.test_revision',
626
'bzrlib.tests.test_revision_info',
627
'bzrlib.tests.test_merge_core',
628
'bzrlib.tests.test_smart_add',
629
'bzrlib.tests.test_bad_files',
630
'bzrlib.tests.test_diff',
631
'bzrlib.tests.test_parent',
632
'bzrlib.tests.test_xml',
633
'bzrlib.tests.test_weave',
634
'bzrlib.tests.test_fetch',
635
'bzrlib.tests.test_whitebox',
636
'bzrlib.tests.test_store',
637
'bzrlib.tests.test_sampler',
638
'bzrlib.tests.test_transactions',
639
'bzrlib.tests.test_transport',
640
'bzrlib.tests.test_sftp',
641
'bzrlib.tests.test_graph',
642
'bzrlib.tests.test_workingtree',
643
'bzrlib.tests.test_upgrade',
644
'bzrlib.tests.test_uncommit',
645
'bzrlib.tests.test_ui',
646
'bzrlib.tests.test_conflicts',
647
'bzrlib.tests.test_testament',
648
'bzrlib.tests.test_annotate',
649
'bzrlib.tests.test_revprops',
650
'bzrlib.tests.test_options',
651
'bzrlib.tests.test_http',
652
'bzrlib.tests.test_nonascii',
653
'bzrlib.tests.test_plugins',
654
'bzrlib.tests.test_reweave',
655
'bzrlib.tests.test_tsort',
656
'bzrlib.tests.test_trace',
657
'bzrlib.tests.test_rio',
658
'bzrlib.tests.test_msgeditor',
659
'bzrlib.tests.test_selftest',
61
# XXX: should also test bzrlib.merge_core, but they seem to be out
62
# of date with the code.
64
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
65
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
66
if m not in MODULES_TO_DOCTEST:
67
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)
662
print '%10s: %s' % ('bzr', os.path.realpath(sys.argv[0]))
663
print '%10s: %s' % ('bzrlib', bzrlib.__path__[0])
75
665
suite = TestSuite()
77
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
666
# python2.4's TestLoader.loadTestsFromNames gives very poor
667
# errors if it fails to load a named module - no indication of what's
668
# actually wrong, just "no such module". We should probably override that
669
# class, but for the moment just load them ourselves. (mbp 20051202)
670
loader = TestLoader()
671
for mod_name in testmod_names:
672
mod = _load_module_by_name(mod_name)
673
suite.addTest(loader.loadTestsFromModule(mod))
674
for package in packages_to_test():
675
suite.addTest(package.test_suite())
79
676
for m in MODULES_TO_TEST:
80
suite.addTest(TestLoader().loadTestsFromModule(m))
677
suite.addTest(loader.loadTestsFromModule(m))
82
678
for m in (MODULES_TO_DOCTEST):
83
679
suite.addTest(DocTestSuite(m))
85
for p in bzrlib.plugin.all_plugins:
86
if hasattr(p, 'test_suite'):
87
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)
680
for name, plugin in bzrlib.plugin.all_plugins().items():
681
if hasattr(plugin, 'test_suite'):
682
suite.addTest(plugin.test_suite())
686
def _load_module_by_name(mod_name):
687
parts = mod_name.split('.')
688
module = __import__(mod_name)
690
# for historical reasons python returns the top-level module even though
691
# it loads the submodule; we need to walk down to get the one we want.
693
module = getattr(module, parts.pop(0))