91
65
class _MyResult(unittest._TextTestResult):
94
Shows output in a different format, including displaying runtime for tests.
97
def _elapsedTime(self):
98
return "%5dms" % (1000 * (time.time() - self._start_time))
69
No special behaviour for now.
100
72
def startTest(self, test):
101
73
unittest.TestResult.startTest(self, test)
102
# In a short description, the important words are in
103
# the beginning, but in an id, the important words are
105
SHOW_DESCRIPTIONS = False
74
# TODO: Maybe show test.shortDescription somewhere?
75
what = test.shortDescription() or test.id()
107
width = osutils.terminal_width()
108
name_width = width - 15
110
if SHOW_DESCRIPTIONS:
111
what = test.shortDescription()
113
if len(what) > name_width:
114
what = what[:name_width-3] + '...'
117
if what.startswith('bzrlib.tests.'):
119
if len(what) > name_width:
120
what = '...' + what[3-name_width:]
121
what = what.ljust(name_width)
122
self.stream.write(what)
77
self.stream.write('%-70.70s' % what)
123
78
self.stream.flush()
124
self._start_time = time.time()
126
80
def addError(self, test, err):
127
if isinstance(err[1], TestSkipped):
128
return self.addSkipped(test, err)
129
unittest.TestResult.addError(self, test, err)
131
self.stream.writeln("ERROR %s" % self._elapsedTime())
133
self.stream.write('E')
81
super(_MyResult, self).addError(test, err)
134
82
self.stream.flush()
136
84
def addFailure(self, test, err):
137
unittest.TestResult.addFailure(self, test, err)
139
self.stream.writeln(" FAIL %s" % self._elapsedTime())
141
self.stream.write('F')
85
super(_MyResult, self).addFailure(test, err)
142
86
self.stream.flush()
144
88
def addSuccess(self, test):
146
self.stream.writeln(' OK %s' % self._elapsedTime())
90
self.stream.writeln('OK')
148
92
self.stream.write('~')
149
93
self.stream.flush()
150
94
unittest.TestResult.addSuccess(self, test)
152
def addSkipped(self, test, skip_excinfo):
154
print >>self.stream, ' SKIP %s' % self._elapsedTime()
155
print >>self.stream, ' %s' % skip_excinfo[1]
157
self.stream.write('S')
159
# seems best to treat this as success from point-of-view of unittest
160
# -- it actually does nothing so it barely matters :)
161
unittest.TestResult.addSuccess(self, test)
163
96
def printErrorList(self, flavour, errors):
164
97
for test, err in errors:
165
98
self.stream.writeln(self.separator1)
166
99
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
167
100
if hasattr(test, '_get_log'):
169
print >>self.stream, \
170
('vvvv[log from %s]' % test).ljust(78,'-')
101
self.stream.writeln()
102
self.stream.writeln('log from this test:')
171
103
print >>self.stream, test._get_log()
172
print >>self.stream, \
173
('^^^^[log from %s]' % test).ljust(78,'-')
174
104
self.stream.writeln(self.separator2)
175
105
self.stream.writeln("%s" % err)
178
108
class TextTestRunner(unittest.TextTestRunner):
179
stop_on_failure = False
181
110
def _makeResult(self):
182
111
result = _MyResult(self.stream, self.descriptions, self.verbosity)
183
if self.stop_on_failure:
184
result = EarlyStoppingTestResultAdapter(result)
188
def iter_suite_tests(suite):
189
"""Return all tests in a suite, recursing through nested suites"""
190
for item in suite._tests:
191
if isinstance(item, unittest.TestCase):
193
elif isinstance(item, unittest.TestSuite):
194
for r in iter_suite_tests(item):
197
raise Exception('unknown object %r inside test suite %r'
112
return EarlyStoppingTestResultAdapter(result)
115
class filteringVisitor(TestUtil.TestVisitor):
116
"""I accruse all the testCases I visit that pass a regexp filter on id
120
def __init__(self, filter):
122
TestUtil.TestVisitor.__init__(self)
124
self.filter=re.compile(filter)
127
"""answer the suite we are building"""
128
if self._suite is None:
129
self._suite=TestUtil.TestSuite()
132
def visitCase(self, aCase):
133
if self.filter.match(aCase.id()):
134
self.suite().addTest(aCase)
201
136
class TestSkipped(Exception):
202
137
"""Indicates that a test was intentionally skipped, rather than failing."""
215
150
Error and debug log messages are redirected from their usual
216
151
location into a temporary file, the contents of which can be
217
retrieved by _get_log(). We use a real OS file, not an in-memory object,
218
so that it can also capture file IO. When the test completes this file
219
is read into memory and removed from disk.
152
retrieved by _get_log().
221
154
There are also convenience functions to invoke bzr's command-line
222
routine, and to build and check bzr trees.
224
In addition to the usual method of overriding tearDown(), this class also
225
allows subclasses to register functions into the _cleanups list, which is
226
run in order as the object is torn down. It's less likely this will be
227
accidentally overlooked.
155
routine, and to build and check bzr trees."""
231
_log_file_name = None
235
160
unittest.TestCase.setUp(self)
237
self._cleanEnvironment()
238
161
bzrlib.trace.disable_default_logging()
241
def _ndiff_strings(self, a, b):
242
"""Return ndiff between two strings containing lines.
244
A trailing newline is added if missing to make the strings
246
if b and b[-1] != '\n':
248
if a and a[-1] != '\n':
250
difflines = difflib.ndiff(a.splitlines(True),
252
linejunk=lambda x: False,
253
charjunk=lambda x: False)
254
return ''.join(difflines)
256
def assertEqualDiff(self, a, b):
257
"""Assert two texts are equal, if not raise an exception.
259
This is intended for use with multi-line strings where it can
260
be hard to find the differences by eye.
262
# TODO: perhaps override assertEquals to call this for strings?
265
raise AssertionError("texts not equal:\n" +
266
self._ndiff_strings(a, b))
268
def assertStartsWith(self, s, prefix):
269
if not s.startswith(prefix):
270
raise AssertionError('string %r does not start with %r' % (s, prefix))
272
def assertEndsWith(self, s, suffix):
273
if not s.endswith(prefix):
274
raise AssertionError('string %r does not end with %r' % (s, suffix))
276
def assertContainsRe(self, haystack, needle_re):
277
"""Assert that a contains something matching a regular expression."""
278
if not re.search(needle_re, haystack):
279
raise AssertionError('pattern "%s" not found in "%s"'
280
% (needle_re, haystack))
282
def AssertSubset(self, sublist, superlist):
283
"""Assert that every entry in sublist is present in superlist."""
285
for entry in sublist:
286
if entry not in superlist:
287
missing.append(entry)
289
raise AssertionError("value(s) %r not present in container %r" %
290
(missing, superlist))
292
def _startLogFile(self):
293
"""Send bzr and test log messages to a temporary file.
295
The file is removed as the test is torn down.
162
self._enable_file_logging()
165
def _enable_file_logging(self):
297
166
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
298
encoder, decoder, stream_reader, stream_writer = codecs.lookup('UTF-8')
299
self._log_file = stream_writer(os.fdopen(fileno, 'w+'))
300
bzrlib.trace.enable_test_log(self._log_file)
168
self._log_file = os.fdopen(fileno, 'w+')
170
hdlr = logging.StreamHandler(self._log_file)
171
hdlr.setLevel(logging.DEBUG)
172
hdlr.setFormatter(logging.Formatter('%(levelname)8s %(message)s'))
173
logging.getLogger('').addHandler(hdlr)
174
logging.getLogger('').setLevel(logging.DEBUG)
175
self._log_hdlr = hdlr
176
debug('opened log file %s', name)
301
178
self._log_file_name = name
302
self.addCleanup(self._finishLogFile)
304
def _finishLogFile(self):
305
"""Finished with the log file.
307
Read contents into memory, close, and delete.
309
bzrlib.trace.disable_test_log()
310
self._log_file.seek(0)
311
self._log_contents = self._log_file.read()
181
logging.getLogger('').removeHandler(self._log_hdlr)
182
bzrlib.trace.enable_default_logging()
183
logging.debug('%s teardown', self.id())
312
184
self._log_file.close()
313
os.remove(self._log_file_name)
314
self._log_file = self._log_file_name = None
316
def addCleanup(self, callable):
317
"""Arrange to run a callable when this case is torn down.
319
Callables are run in the reverse of the order they are registered,
320
ie last-in first-out.
322
if callable in self._cleanups:
323
raise ValueError("cleanup function %r already registered on %s"
325
self._cleanups.append(callable)
327
def _cleanEnvironment(self):
330
'APPDATA': os.getcwd(),
335
self.addCleanup(self._restoreEnvironment)
336
for name, value in new_env.iteritems():
337
self._captureVar(name, value)
340
def _captureVar(self, name, newvalue):
341
"""Set an environment variable, preparing it to be reset when finished."""
342
self.__old_env[name] = os.environ.get(name, None)
344
if name in os.environ:
347
os.environ[name] = newvalue
350
def _restoreVar(name, value):
352
if name in os.environ:
355
os.environ[name] = value
357
def _restoreEnvironment(self):
358
for name, value in self.__old_env.iteritems():
359
self._restoreVar(name, value)
363
185
unittest.TestCase.tearDown(self)
365
def _runCleanups(self):
366
"""Run registered cleanup functions.
368
This should only be called from TestCase.tearDown.
370
for cleanup_fn in reversed(self._cleanups):
373
187
def log(self, *args):
376
190
def _get_log(self):
377
191
"""Return as a string the log for this test"""
378
if self._log_file_name:
379
return open(self._log_file_name).read()
381
return self._log_contents
382
# TODO: Delete the log after it's been read in
384
def capture(self, cmd, retcode=0):
192
return open(self._log_file_name).read()
195
def capture(self, cmd):
385
196
"""Shortcut that splits cmd into words, runs, and returns stdout"""
386
return self.run_bzr_captured(cmd.split(), retcode=retcode)[0]
197
return self.run_bzr_captured(cmd.split())[0]
388
199
def run_bzr_captured(self, argv, retcode=0):
389
"""Invoke bzr and return (stdout, stderr).
200
"""Invoke bzr and return (result, stdout, stderr).
391
202
Useful for code that wants to check the contents of the
392
203
output, the way error messages are presented, etc.
541
351
# successfully created
542
TestCaseInTempDir.TEST_ROOT = osutils.abspath(root)
352
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
544
354
# make a fake bzr directory there to prevent any tests propagating
545
355
# up onto the source directory's real branch
546
os.mkdir(osutils.pathjoin(TestCaseInTempDir.TEST_ROOT, '.bzr'))
356
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
549
359
super(TestCaseInTempDir, self).setUp()
550
360
self._make_test_root()
551
_currentdir = os.getcwdu()
552
short_id = self.id().replace('bzrlib.tests.', '') \
361
self._currentdir = os.getcwdu()
362
short_id = self.id().replace('bzrlib.selftest.', '') \
553
363
.replace('__main__.', '')
554
self.test_dir = osutils.pathjoin(self.TEST_ROOT, short_id)
364
self.test_dir = os.path.join(self.TEST_ROOT, short_id)
555
365
os.mkdir(self.test_dir)
556
366
os.chdir(self.test_dir)
557
os.environ['HOME'] = self.test_dir
558
os.environ['APPDATA'] = self.test_dir
559
def _leaveDirectory():
560
os.chdir(_currentdir)
561
self.addCleanup(_leaveDirectory)
563
def build_tree(self, shape, line_endings='native'):
369
os.chdir(self._currentdir)
370
super(TestCaseInTempDir, self).tearDown()
372
def build_tree(self, shape):
564
373
"""Build a test tree according to a pattern.
566
375
shape is a sequence of file specifications. If the final
567
376
character is '/', a directory is created.
569
378
This doesn't add anything to a branch.
570
:param line_endings: Either 'binary' or 'native'
571
in binary mode, exact contents are written
572
in native mode, the line endings match the
573
default platform endings.
575
380
# XXX: It's OK to just create them using forward slashes on windows?
576
381
for name in shape:
577
self.assert_(isinstance(name, basestring))
382
assert isinstance(name, basestring)
578
383
if name[-1] == '/':
579
384
os.mkdir(name[:-1])
581
if line_endings == 'binary':
583
elif line_endings == 'native':
586
raise BzrError('Invalid line ending request %r' % (line_endings,))
587
387
print >>f, "contents of", name
590
def build_tree_contents(self, shape):
591
build_tree_contents(shape)
593
def failUnlessExists(self, path):
594
"""Fail unless path, which may be abs or relative, exists."""
595
self.failUnless(osutils.lexists(path))
597
def failIfExists(self, path):
598
"""Fail if path, which may be abs or relative, exists."""
599
self.failIf(osutils.lexists(path))
601
def assertFileEqual(self, content, path):
602
"""Fail if path does not contain 'content'."""
603
self.failUnless(osutils.lexists(path))
604
self.assertEqualDiff(content, open(path, 'r').read())
607
def filter_suite_by_re(suite, pattern):
609
filter_re = re.compile(pattern)
610
for test in iter_suite_tests(suite):
611
if filter_re.search(test.id()):
616
def run_suite(suite, name='test', verbose=False, pattern=".*",
617
stop_on_failure=False, keep_output=False):
391
class MetaTestLog(TestCase):
392
def test_logging(self):
393
"""Test logs are captured when a test fails."""
394
logging.info('an info message')
395
warning('something looks dodgy...')
396
logging.debug('hello, test is running')
401
def run_suite(suite, name='test', verbose=False, pattern=".*"):
618
402
TestCaseInTempDir._TEST_NAME = name
638
421
return result.wasSuccessful()
641
def selftest(verbose=False, pattern=".*", stop_on_failure=True,
424
def selftest(verbose=False, pattern=".*"):
643
425
"""Run the whole test suite under the enhanced runner"""
644
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
645
stop_on_failure=stop_on_failure, keep_output=keep_output)
426
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
648
429
def test_suite():
649
430
"""Build and return TestSuite for the whole program."""
431
import bzrlib.store, bzrlib.inventory, bzrlib.branch
432
import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
650
433
from doctest import DocTestSuite
652
global MODULES_TO_DOCTEST
435
global MODULES_TO_TEST, MODULES_TO_DOCTEST
655
'bzrlib.tests.test_ancestry',
656
'bzrlib.tests.test_annotate',
657
'bzrlib.tests.test_api',
658
'bzrlib.tests.test_bad_files',
659
'bzrlib.tests.test_basis_inventory',
660
'bzrlib.tests.test_branch',
661
'bzrlib.tests.test_command',
662
'bzrlib.tests.test_commit',
663
'bzrlib.tests.test_commit_merge',
664
'bzrlib.tests.test_config',
665
'bzrlib.tests.test_conflicts',
666
'bzrlib.tests.test_diff',
667
'bzrlib.tests.test_fetch',
668
'bzrlib.tests.test_gpg',
669
'bzrlib.tests.test_graph',
670
'bzrlib.tests.test_hashcache',
671
'bzrlib.tests.test_http',
672
'bzrlib.tests.test_identitymap',
673
'bzrlib.tests.test_inv',
674
'bzrlib.tests.test_log',
675
'bzrlib.tests.test_merge',
676
'bzrlib.tests.test_merge3',
677
'bzrlib.tests.test_merge_core',
678
'bzrlib.tests.test_missing',
679
'bzrlib.tests.test_msgeditor',
680
'bzrlib.tests.test_nonascii',
681
'bzrlib.tests.test_options',
682
'bzrlib.tests.test_osutils',
683
'bzrlib.tests.test_parent',
684
'bzrlib.tests.test_permissions',
685
'bzrlib.tests.test_plugins',
686
'bzrlib.tests.test_remove',
687
'bzrlib.tests.test_revision',
688
'bzrlib.tests.test_revisionnamespaces',
689
'bzrlib.tests.test_revprops',
690
'bzrlib.tests.test_reweave',
691
'bzrlib.tests.test_rio',
692
'bzrlib.tests.test_sampler',
693
'bzrlib.tests.test_selftest',
694
'bzrlib.tests.test_setup',
695
'bzrlib.tests.test_sftp_transport',
696
'bzrlib.tests.test_smart_add',
697
'bzrlib.tests.test_source',
698
'bzrlib.tests.test_status',
699
'bzrlib.tests.test_store',
700
'bzrlib.tests.test_symbol_versioning',
701
'bzrlib.tests.test_testament',
702
'bzrlib.tests.test_trace',
703
'bzrlib.tests.test_transactions',
704
'bzrlib.tests.test_transport',
705
'bzrlib.tests.test_tsort',
706
'bzrlib.tests.test_ui',
707
'bzrlib.tests.test_uncommit',
708
'bzrlib.tests.test_upgrade',
709
'bzrlib.tests.test_weave',
710
'bzrlib.tests.test_whitebox',
711
'bzrlib.tests.test_workingtree',
712
'bzrlib.tests.test_xml',
438
['bzrlib.selftest.MetaTestLog',
439
'bzrlib.selftest.testinv',
440
'bzrlib.selftest.test_ancestry',
441
'bzrlib.selftest.test_commit',
442
'bzrlib.selftest.test_commit_merge',
443
'bzrlib.selftest.versioning',
444
'bzrlib.selftest.testmerge3',
445
'bzrlib.selftest.testmerge',
446
'bzrlib.selftest.testhashcache',
447
'bzrlib.selftest.teststatus',
448
'bzrlib.selftest.testlog',
449
'bzrlib.selftest.testrevisionnamespaces',
450
'bzrlib.selftest.testbranch',
451
'bzrlib.selftest.testrevision',
452
'bzrlib.selftest.test_revision_info',
453
'bzrlib.selftest.test_merge_core',
454
'bzrlib.selftest.test_smart_add',
455
'bzrlib.selftest.test_bad_files',
456
'bzrlib.selftest.testdiff',
457
'bzrlib.selftest.test_parent',
458
'bzrlib.selftest.test_xml',
459
'bzrlib.selftest.test_weave',
460
'bzrlib.selftest.testfetch',
461
'bzrlib.selftest.whitebox',
462
'bzrlib.selftest.teststore',
463
'bzrlib.selftest.blackbox',
464
'bzrlib.selftest.testtransport',
465
'bzrlib.selftest.testgraph',
715
TestCase.BZRPATH = osutils.pathjoin(
716
osutils.realpath(osutils.dirname(bzrlib.__path__[0])), 'bzr')
717
print '%10s: %s' % ('bzr', osutils.realpath(sys.argv[0]))
718
print '%10s: %s' % ('bzrlib', bzrlib.__path__[0])
468
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
469
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
470
if m not in MODULES_TO_DOCTEST:
471
MODULES_TO_DOCTEST.append(m)
473
TestCase.BZRPATH = os.path.join(os.path.realpath(os.path.dirname(bzrlib.__path__[0])), 'bzr')
474
print '%-30s %s' % ('bzr binary', TestCase.BZRPATH)
720
476
suite = TestSuite()
721
# python2.4's TestLoader.loadTestsFromNames gives very poor
722
# errors if it fails to load a named module - no indication of what's
723
# actually wrong, just "no such module". We should probably override that
724
# class, but for the moment just load them ourselves. (mbp 20051202)
725
loader = TestLoader()
726
for mod_name in testmod_names:
727
mod = _load_module_by_name(mod_name)
728
suite.addTest(loader.loadTestsFromModule(mod))
729
for package in packages_to_test():
730
suite.addTest(package.test_suite())
477
suite.addTest(TestLoader().loadTestsFromNames(testmod_names))
731
478
for m in MODULES_TO_TEST:
732
suite.addTest(loader.loadTestsFromModule(m))
479
suite.addTest(TestLoader().loadTestsFromModule(m))
733
480
for m in (MODULES_TO_DOCTEST):
734
481
suite.addTest(DocTestSuite(m))
735
for name, plugin in bzrlib.plugin.all_plugins().items():
736
if hasattr(plugin, 'test_suite'):
737
suite.addTest(plugin.test_suite())
482
for p in bzrlib.plugin.all_plugins:
483
if hasattr(p, 'test_suite'):
484
suite.addTest(p.test_suite())
741
def _load_module_by_name(mod_name):
742
parts = mod_name.split('.')
743
module = __import__(mod_name)
745
# for historical reasons python returns the top-level module even though
746
# it loads the submodule; we need to walk down to get the one we want.
748
module = getattr(module, parts.pop(0))