15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from cStringIO import StringIO
25
from testsweet import run_suite
30
26
import bzrlib.commands
31
28
import bzrlib.trace
32
29
import bzrlib.fetch
33
import bzrlib.osutils as osutils
34
from bzrlib.selftest import TestUtil
35
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
36
from bzrlib.selftest.treeshape import build_tree_contents
38
32
MODULES_TO_TEST = []
39
33
MODULES_TO_DOCTEST = []
41
35
from logging import debug, warning, error
45
class EarlyStoppingTestResultAdapter(object):
46
"""An adapter for TestResult to stop at the first first failure or error"""
48
def __init__(self, result):
51
def addError(self, test, err):
52
self._result.addError(test, err)
55
def addFailure(self, test, err):
56
self._result.addFailure(test, err)
59
def __getattr__(self, name):
60
return getattr(self._result, name)
62
def __setattr__(self, name, value):
64
object.__setattr__(self, name, value)
65
return setattr(self._result, name, value)
68
class _MyResult(unittest._TextTestResult):
72
No special behaviour for now.
75
def _elapsedTime(self):
76
return "(Took %.3fs)" % (time.time() - self._start_time)
78
def startTest(self, test):
79
unittest.TestResult.startTest(self, test)
80
# TODO: Maybe show test.shortDescription somewhere?
81
what = test.shortDescription() or test.id()
83
self.stream.write('%-70.70s' % what)
85
self._start_time = time.time()
87
def addError(self, test, err):
88
unittest.TestResult.addError(self, test, err)
90
self.stream.writeln("ERROR %s" % self._elapsedTime())
92
self.stream.write('E')
95
def addFailure(self, test, err):
96
unittest.TestResult.addFailure(self, test, err)
98
self.stream.writeln("FAIL %s" % self._elapsedTime())
100
self.stream.write('F')
103
def addSuccess(self, test):
105
self.stream.writeln('OK %s' % self._elapsedTime())
107
self.stream.write('~')
109
unittest.TestResult.addSuccess(self, test)
111
def printErrorList(self, flavour, errors):
112
for test, err in errors:
113
self.stream.writeln(self.separator1)
114
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
115
if hasattr(test, '_get_log'):
116
self.stream.writeln()
117
self.stream.writeln('log from this test:')
118
print >>self.stream, test._get_log()
119
self.stream.writeln(self.separator2)
120
self.stream.writeln("%s" % err)
123
class TextTestRunner(unittest.TextTestRunner):
124
stop_on_failure = False
126
def _makeResult(self):
127
result = _MyResult(self.stream, self.descriptions, self.verbosity)
128
if self.stop_on_failure:
129
result = EarlyStoppingTestResultAdapter(result)
133
def iter_suite_tests(suite):
134
"""Return all tests in a suite, recursing through nested suites"""
135
for item in suite._tests:
136
if isinstance(item, unittest.TestCase):
138
elif isinstance(item, unittest.TestSuite):
139
for r in iter_suite_tests(item):
142
raise Exception('unknown object %r inside test suite %r'
146
class TestSkipped(Exception):
147
"""Indicates that a test was intentionally skipped, rather than failing."""
151
37
class CommandFailed(Exception):
165
51
routine, and to build and check bzr trees."""
168
_log_file_name = None
56
# this replaces the default testsweet.TestCase; we don't want logging changed
171
57
unittest.TestCase.setUp(self)
172
self.oldenv = os.environ.get('HOME', None)
173
os.environ['HOME'] = os.getcwd()
174
self.bzr_email = os.environ.get('BZREMAIL')
175
if self.bzr_email is not None:
176
del os.environ['BZREMAIL']
177
self.email = os.environ.get('EMAIL')
178
if self.email is not None:
179
del os.environ['EMAIL']
180
58
bzrlib.trace.disable_default_logging()
181
59
self._enable_file_logging()
183
def _ndiff_strings(self, a, b):
184
"""Return ndiff between two strings containing lines.
186
A trailing newline is added if missing to make the strings
188
if b and b[-1] != '\n':
190
if a and a[-1] != '\n':
192
difflines = difflib.ndiff(a.splitlines(True),
194
linejunk=lambda x: False,
195
charjunk=lambda x: False)
196
return ''.join(difflines)
198
def assertEqualDiff(self, a, b):
199
"""Assert two texts are equal, if not raise an exception.
201
This is intended for use with multi-line strings where it can
202
be hard to find the differences by eye.
204
# TODO: perhaps override assertEquals to call this for strings?
207
raise AssertionError("texts not equal:\n" +
208
self._ndiff_strings(a, b))
210
def assertContainsRe(self, haystack, needle_re):
211
"""Assert that a contains something matching a regular expression."""
212
if not re.search(needle_re, haystack):
213
raise AssertionError('pattern "%s" not found in "%s"'
214
% (needle_re, haystack))
216
62
def _enable_file_logging(self):
217
63
fileno, name = tempfile.mkstemp(suffix='.log', prefix='testbzr')
229
75
self._log_file_name = name
231
78
def tearDown(self):
232
os.environ['HOME'] = self.oldenv
233
if os.environ.get('BZREMAIL') is not None:
234
del os.environ['BZREMAIL']
235
if self.bzr_email is not None:
236
os.environ['BZREMAIL'] = self.bzr_email
237
if os.environ.get('EMAIL') is not None:
238
del os.environ['EMAIL']
239
if self.email is not None:
240
os.environ['EMAIL'] = self.email
241
79
logging.getLogger('').removeHandler(self._log_hdlr)
242
80
bzrlib.trace.enable_default_logging()
243
81
logging.debug('%s teardown', self.id())
244
82
self._log_file.close()
245
83
unittest.TestCase.tearDown(self)
247
86
def log(self, *args):
248
87
logging.debug(*args)
250
89
def _get_log(self):
251
90
"""Return as a string the log for this test"""
252
if self._log_file_name:
253
return open(self._log_file_name).read()
257
def capture(self, cmd):
258
"""Shortcut that splits cmd into words, runs, and returns stdout"""
259
return self.run_bzr_captured(cmd.split())[0]
261
def run_bzr_captured(self, argv, retcode=0):
262
"""Invoke bzr and return (result, stdout, stderr).
264
Useful for code that wants to check the contents of the
265
output, the way error messages are presented, etc.
91
return open(self._log_file_name).read()
93
def run_bzr(self, *args, **kwargs):
94
"""Invoke bzr, as if it were run from the command line.
267
96
This should be the main method for tests that want to exercise the
268
97
overall behavior of the bzr application (rather than a unit test
271
100
Much of the old code runs bzr by forking a new copy of Python, but
272
101
that is slower, harder to debug, and generally not necessary.
274
This runs bzr through the interface that catches and reports
275
errors, and with logging set to something approximating the
276
default, so that error reporting can be checked.
278
argv -- arguments to invoke bzr
279
retcode -- expected return code, or None for don't-care.
283
self.log('run bzr: %s', ' '.join(argv))
284
handler = logging.StreamHandler(stderr)
285
handler.setFormatter(bzrlib.trace.QuietFormatter())
286
handler.setLevel(logging.INFO)
287
logger = logging.getLogger('')
288
logger.addHandler(handler)
290
result = self.apply_redirected(None, stdout, stderr,
291
bzrlib.commands.run_bzr_catch_errors,
294
logger.removeHandler(handler)
295
out = stdout.getvalue()
296
err = stderr.getvalue()
298
self.log('output:\n%s', out)
300
self.log('errors:\n%s', err)
301
if retcode is not None:
302
self.assertEquals(result, retcode)
305
def run_bzr(self, *args, **kwargs):
306
"""Invoke bzr, as if it were run from the command line.
308
This should be the main method for tests that want to exercise the
309
overall behavior of the bzr application (rather than a unit test
310
or a functional test of the library.)
312
This sends the stdout/stderr results into the test's log,
313
where it may be useful for debugging. See also run_captured.
315
retcode = kwargs.pop('retcode', 0)
316
return self.run_bzr_captured(args, retcode)
103
retcode = kwargs.get('retcode', 0)
104
result = self.apply_redirected(None, None, None,
105
bzrlib.commands.run_bzr, args)
106
self.assertEquals(result, retcode)
318
109
def check_inventory_shape(self, inv, shape):
319
"""Compare an inventory to a list of expected names.
111
Compare an inventory to a list of expected names.
321
113
Fail if they are not precisely equal.
394
181
if contents != expect:
395
182
self.log("expected: %r" % expect)
396
183
self.log("actually: %r" % contents)
397
self.fail("contents of %s not as expected" % filename)
184
self.fail("contents of %s not as expected")
399
186
def _make_test_root(self):
400
191
if TestCaseInTempDir.TEST_ROOT is not None:
404
root = 'test%04d.tmp' % i
408
if e.errno == errno.EEXIST:
413
# successfully created
414
TestCaseInTempDir.TEST_ROOT = os.path.abspath(root)
193
TestCaseInTempDir.TEST_ROOT = os.path.abspath(
194
tempfile.mkdtemp(suffix='.tmp',
195
prefix=self._TEST_NAME + '-',
416
198
# make a fake bzr directory there to prevent any tests propagating
417
199
# up onto the source directory's real branch
418
200
os.mkdir(os.path.join(TestCaseInTempDir.TEST_ROOT, '.bzr'))
203
super(TestCaseInTempDir, self).setUp()
421
205
self._make_test_root()
422
206
self._currentdir = os.getcwdu()
423
short_id = self.id().replace('bzrlib.selftest.', '') \
424
.replace('__main__.', '')
425
self.test_dir = os.path.join(self.TEST_ROOT, short_id)
207
self.test_dir = os.path.join(self.TEST_ROOT, self.id())
426
208
os.mkdir(self.test_dir)
427
209
os.chdir(self.test_dir)
428
super(TestCaseInTempDir, self).setUp()
430
211
def tearDown(self):
431
213
os.chdir(self._currentdir)
432
214
super(TestCaseInTempDir, self).tearDown()
216
def _formcmd(self, cmd):
217
if isinstance(cmd, basestring):
220
cmd[0] = self.BZRPATH
221
if self.OVERRIDE_PYTHON:
222
cmd.insert(0, self.OVERRIDE_PYTHON)
223
self.log('$ %r' % cmd)
226
def runcmd(self, cmd, retcode=0):
227
"""Run one command and check the return code.
229
Returns a tuple of (stdout,stderr) strings.
231
If a single string is based, it is split into words.
232
For commands that are not simple space-separated words, please
233
pass a list instead."""
234
cmd = self._formcmd(cmd)
235
self.log('$ ' + ' '.join(cmd))
236
actual_retcode = subprocess.call(cmd, stdout=self._log_file,
237
stderr=self._log_file)
238
if retcode != actual_retcode:
239
raise CommandFailed("test failed: %r returned %d, expected %d"
240
% (cmd, actual_retcode, retcode))
242
def backtick(self, cmd, retcode=0):
243
"""Run a command and return its output"""
244
cmd = self._formcmd(cmd)
245
child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self._log_file)
246
outd, errd = child.communicate()
248
actual_retcode = child.wait()
250
outd = outd.replace('\r', '')
252
if retcode != actual_retcode:
253
raise CommandFailed("test failed: %r returned %d, expected %d"
254
% (cmd, actual_retcode, retcode))
434
260
def build_tree(self, shape):
435
261
"""Build a test tree according to a pattern.
468
284
logging.info('an info message')
469
285
warning('something looks dodgy...')
470
286
logging.debug('hello, test is running')
474
def filter_suite_by_re(suite, pattern):
475
result = TestUtil.TestSuite()
476
filter_re = re.compile(pattern)
477
for test in iter_suite_tests(suite):
478
if filter_re.search(test.id()):
483
def run_suite(suite, name='test', verbose=False, pattern=".*",
484
stop_on_failure=False):
485
TestCaseInTempDir._TEST_NAME = name
490
runner = TextTestRunner(stream=sys.stdout,
493
runner.stop_on_failure=stop_on_failure
495
suite = filter_suite_by_re(suite, pattern)
496
result = runner.run(suite)
497
# This is still a little bogus,
498
# but only a little. Folk not using our testrunner will
499
# have to delete their temp directories themselves.
500
if result.wasSuccessful():
501
if TestCaseInTempDir.TEST_ROOT is not None:
502
shutil.rmtree(TestCaseInTempDir.TEST_ROOT)
504
print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
505
return result.wasSuccessful()
508
def selftest(verbose=False, pattern=".*", stop_on_failure=True):
509
"""Run the whole test suite under the enhanced runner"""
510
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern,
511
stop_on_failure=stop_on_failure)
290
def selftest(verbose=False, pattern=".*"):
291
return run_suite(test_suite(), 'testbzr', verbose=verbose, pattern=pattern)
514
294
def test_suite():
515
"""Build and return TestSuite for the whole program."""
516
import bzrlib.store, bzrlib.inventory, bzrlib.branch
517
import bzrlib.osutils, bzrlib.merge3, bzrlib.plugin
295
from bzrlib.selftest.TestUtil import TestLoader, TestSuite
296
import bzrlib, bzrlib.store, bzrlib.inventory, bzrlib.branch
297
import bzrlib.osutils, bzrlib.commands, bzrlib.merge3, bzrlib.plugin
518
298
from doctest import DocTestSuite
520
304
global MODULES_TO_TEST, MODULES_TO_DOCTEST
522
306
testmod_names = \
523
307
['bzrlib.selftest.MetaTestLog',
524
'bzrlib.selftest.testgpg',
525
'bzrlib.selftest.testidentitymap',
308
'bzrlib.selftest.test_parent',
526
309
'bzrlib.selftest.testinv',
527
'bzrlib.selftest.test_ancestry',
528
'bzrlib.selftest.test_commit',
529
'bzrlib.selftest.test_commit_merge',
530
'bzrlib.selftest.testconfig',
310
'bzrlib.selftest.testfetch',
531
311
'bzrlib.selftest.versioning',
312
'bzrlib.selftest.whitebox',
532
313
'bzrlib.selftest.testmerge3',
533
'bzrlib.selftest.testmerge',
534
314
'bzrlib.selftest.testhashcache',
535
315
'bzrlib.selftest.teststatus',
536
316
'bzrlib.selftest.testlog',
317
'bzrlib.selftest.blackbox',
537
318
'bzrlib.selftest.testrevisionnamespaces',
538
319
'bzrlib.selftest.testbranch',
539
320
'bzrlib.selftest.testrevision',
540
'bzrlib.selftest.test_revision_info',
541
321
'bzrlib.selftest.test_merge_core',
542
322
'bzrlib.selftest.test_smart_add',
543
'bzrlib.selftest.test_bad_files',
544
323
'bzrlib.selftest.testdiff',
545
'bzrlib.selftest.test_parent',
546
324
'bzrlib.selftest.test_xml',
547
'bzrlib.selftest.test_weave',
548
'bzrlib.selftest.testfetch',
549
'bzrlib.selftest.whitebox',
550
326
'bzrlib.selftest.teststore',
551
'bzrlib.selftest.blackbox',
552
'bzrlib.selftest.testsampler',
553
'bzrlib.selftest.testtransactions',
554
'bzrlib.selftest.testtransport',
555
'bzrlib.selftest.testgraph',
556
'bzrlib.selftest.testworkingtree',
557
'bzrlib.selftest.test_upgrade',
558
'bzrlib.selftest.test_conflicts',
559
'bzrlib.selftest.testtestament',
560
'bzrlib.selftest.testannotate',
561
'bzrlib.selftest.testrevprops',
562
'bzrlib.selftest.testoptions',
563
'bzrlib.selftest.testhttp',
564
'bzrlib.selftest.testnonascii',
567
329
for m in (bzrlib.store, bzrlib.inventory, bzrlib.branch,
568
bzrlib.osutils, bzrlib.commands, bzrlib.merge3,
330
bzrlib.osutils, bzrlib.commands, bzrlib.merge3):
571
331
if m not in MODULES_TO_DOCTEST:
572
332
MODULES_TO_DOCTEST.append(m)