15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from cStringIO import StringIO
24
from warnings import warn
25
from cStringIO import StringIO
32
28
import bzrlib.commands
33
from bzrlib.errors import BzrError
34
import bzrlib.inventory
39
30
import bzrlib.trace
40
from bzrlib.trace import mutter
41
from bzrlib.tests.TestUtil import TestLoader, TestSuite
42
from bzrlib.tests.treeshape import build_tree_contents
44
34
MODULES_TO_TEST = []
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."""
35
MODULES_TO_DOCTEST = []
37
from logging import debug, warning, error
183
39
class CommandFailed(Exception):
192
48
Error and debug log messages are redirected from their usual
193
49
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.
50
retrieved by _get_log().
198
52
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.
53
routine, and to build and check bzr trees."""
208
_log_file_name = None
58
# this replaces the default testsweet.TestCase; we don't want logging changed
212
59
unittest.TestCase.setUp(self)
214
self._cleanEnvironment()
215
60
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.
61
self._enable_file_logging()
64
def _enable_file_logging(self):
256
65
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)
67
self._log_file = os.fdopen(fileno, 'w+')
69
hdlr = logging.StreamHandler(self._log_file)
70
hdlr.setLevel(logging.DEBUG)
71
hdlr.setFormatter(logging.Formatter('%(levelname)4.4s %(message)s'))
72
logging.getLogger('').addHandler(hdlr)
73
logging.getLogger('').setLevel(logging.DEBUG)
75
debug('opened log file %s', name)
260
77
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()
80
logging.getLogger('').removeHandler(self._log_hdlr)
81
bzrlib.trace.enable_default_logging()
82
logging.debug('%s teardown', self.id())
271
83
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
84
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
86
def log(self, *args):
335
89
def _get_log(self):
336
90
"""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):
91
return open(self._log_file_name).read()
94
def capture(self, cmd):
344
95
"""Shortcut that splits cmd into words, runs, and returns stdout"""
345
return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
96
return self.run_bzr_captured(cmd.split())[0]
347
99
def run_bzr_captured(self, argv, retcode=0):
348
"""Invoke bzr and return (stdout, stderr).
100
"""Invoke bzr and return (result, stdout, stderr).
350
102
Useful for code that wants to check the contents of the
351
103
output, the way error messages are presented, etc.
508
262
super(TestCaseInTempDir, self).setUp()
509
263
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)
264
self._currentdir = os.getcwdu()
265
self.test_dir = os.path.join(self.TEST_ROOT, self.id())
514
266
os.mkdir(self.test_dir)
515
267
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'):
270
os.chdir(self._currentdir)
271
super(TestCaseInTempDir, self).tearDown()
274
def build_tree(self, shape):
522
275
"""Build a test tree according to a pattern.
524
277
shape is a sequence of file specifications. If the final
525
278
character is '/', a directory is created.
527
280
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
282
# XXX: It's OK to just create them using forward slashes on windows?
534
283
for name in shape:
535
self.assert_(isinstance(name, basestring))
284
assert isinstance(name, basestring)
536
285
if name[-1] == '/':
537
286
os.mkdir(name[:-1])
539
if line_endings == 'binary':
541
elif line_endings == 'native':
544
raise BzrError('Invalid line ending request %r' % (line_endings,))
545
289
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)
294
class MetaTestLog(TestCase):
295
def test_logging(self):
296
"""Test logs are captured when a test fails."""
297
logging.info('an info message')
298
warning('something looks dodgy...')
299
logging.debug('hello, test is running')
303
def selftest(verbose=False, pattern=".*"):
304
return testsweet.run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
602
307
def test_suite():
603
"""Build and return TestSuite for the whole program."""
308
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
309
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
310
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
604
311
from doctest import DocTestSuite
606
global MODULES_TO_DOCTEST
313
global MODULES_TO_TEST, 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',
316
['bzrlib.selftest.MetaTestLog',
317
'bzrlib.selftest.test_parent',
318
'bzrlib.selftest.testinv',
319
'bzrlib.selftest.testfetch',
320
'bzrlib.selftest.versioning',
321
'bzrlib.selftest.whitebox',
322
'bzrlib.selftest.testmerge3',
323
'bzrlib.selftest.testmerge',
324
'bzrlib.selftest.testhashcache',
325
'bzrlib.selftest.teststatus',
326
'bzrlib.selftest.testlog',
327
'bzrlib.selftest.blackbox',
328
'bzrlib.selftest.testrevisionnamespaces',
329
'bzrlib.selftest.testbranch',
330
'bzrlib.selftest.testremotebranch',
331
'bzrlib.selftest.testrevision',
332
'bzrlib.selftest.test_merge_core',
333
'bzrlib.selftest.test_smart_add',
334
'bzrlib.selftest.testdiff',
335
'bzrlib.selftest.test_xml',
337
'bzrlib.selftest.teststore',
338
'bzrlib.selftest.testgraph',
662
print '%10s: %s' % ('bzr', os.path.realpath(sys.argv[0]))
663
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)
665
349
suite = TestSuite()
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())
350
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
676
351
for m in MODULES_TO_TEST:
677
suite.addTest(loader.loadTestsFromModule(m))
352
suite.addTest(TestLoader().loadTestsFromModule(m))
678
353
for m in (MODULES_TO_DOCTEST):
679
354
suite.addTest(DocTestSuite(m))
680
for name, plugin in bzrlib.plugin.all_plugins().items():
681
if hasattr(plugin, 'test_suite'):
682
suite.addTest(plugin.test_suite())
355
for p in bzrlib.plugin.all_plugins:
356
if hasattr(p, 'test_suite'):
357
suite.addTest(p.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))