54
54
class TestSkipped(Exception):
55
55
"""Indicates that a test was intentionally skipped, rather than failing."""
56
56
# XXX: Not used yet
59
class TestBase(TestCase):
60
"""Base class for bzr test cases.
62
Just defines some useful helper functions; doesn't actually test
66
# TODO: Special methods to invoke bzr, so that we can run it
67
# through a specified Python intepreter
69
OVERRIDE_PYTHON = None # to run with alternative python 'python'
76
super(TestBase, self).setUp()
77
self.log("%s setup" % self.id())
81
super(TestBase, self).tearDown()
82
self.log("%s teardown" % self.id())
86
def formcmd(self, cmd):
87
if isinstance(cmd, basestring):
92
if self.OVERRIDE_PYTHON:
93
cmd.insert(0, self.OVERRIDE_PYTHON)
95
self.log('$ %r' % cmd)
100
def runcmd(self, cmd, retcode=0):
101
"""Run one command and check the return code.
103
Returns a tuple of (stdout,stderr) strings.
105
If a single string is based, it is split into words.
106
For commands that are not simple space-separated words, please
107
pass a list instead."""
110
from subprocess import call
111
except ImportError, e:
116
cmd = self.formcmd(cmd)
118
self.log('$ ' + ' '.join(cmd))
119
actual_retcode = call(cmd, stdout=self.TEST_LOG, stderr=self.TEST_LOG)
121
if retcode != actual_retcode:
122
raise CommandFailed("test failed: %r returned %d, expected %d"
123
% (cmd, actual_retcode, retcode))
126
def backtick(self, cmd, retcode=0):
127
"""Run a command and return its output"""
130
from subprocess import Popen, PIPE
131
except ImportError, e:
135
cmd = self.formcmd(cmd)
136
child = Popen(cmd, stdout=PIPE, stderr=self.TEST_LOG)
137
outd, errd = child.communicate()
139
actual_retcode = child.wait()
141
outd = outd.replace('\r', '')
143
if retcode != actual_retcode:
144
raise CommandFailed("test failed: %r returned %d, expected %d"
145
% (cmd, actual_retcode, retcode))
151
def build_tree(self, shape):
152
"""Build a test tree according to a pattern.
154
shape is a sequence of file specifications. If the final
155
character is '/', a directory is created.
157
This doesn't add anything to a branch.
159
# XXX: It's OK to just create them using forward slashes on windows?
162
assert isinstance(name, basestring)
167
print >>f, "contents of", name
172
"""Log a message to a progress file"""
173
# XXX: The problem with this is that code that writes straight
174
# to the log file won't be shown when we display the log
175
# buffer; would be better to not have the in-memory buffer and
176
# instead just a log file per test, which is read in and
177
# displayed if the test fails. That seems to imply one log
178
# per test case, not globally. OK?
179
self._log_buf = self._log_buf + str(msg) + '\n'
180
print >>self.TEST_LOG, msg
183
def check_inventory_shape(self, inv, shape):
185
Compare an inventory to a list of expected names.
187
Fail if they are not precisely equal.
190
shape = list(shape) # copy
191
for path, ie in inv.entries():
192
name = path.replace('\\', '/')
200
self.fail("expected paths not found in inventory: %r" % shape)
202
self.fail("unexpected paths found in inventory: %r" % extras)
205
def check_file_contents(self, filename, expect):
206
self.log("check contents of file %s" % filename)
207
contents = file(filename, 'r').read()
208
if contents != expect:
209
self.log("expected: %r" % expect)
210
self.log("actually: %r" % contents)
211
self.fail("contents of %s not as expected")
215
class InTempDir(TestBase):
216
"""Base class for tests run in a temporary branch."""
219
self.test_dir = os.path.join(self.TEST_ROOT, self.__class__.__name__)
220
os.mkdir(self.test_dir)
221
os.chdir(self.test_dir)
225
os.chdir(self.TEST_ROOT)
231
class _MyResult(TestResult):
60
class EarlyStoppingTestResultAdapter(object):
61
"""An adapter for TestResult to stop at the first first failure or error"""
63
def __init__(self, result):
66
def addError(self, test, err):
67
self._result.addError(test, err)
70
def addFailure(self, test, err):
71
self._result.addFailure(test, err)
74
def __getattr__(self, name):
75
return getattr(self._result, name)
77
def __setattr__(self, name, value):
79
object.__setattr__(self, name, value)
80
return setattr(self._result, name, value)
83
class _MyResult(unittest._TextTestResult):
233
85
Custom TestResult.
235
87
No special behaviour for now.
237
def __init__(self, out, style):
239
TestResult.__init__(self)
240
assert style in ('none', 'progress', 'verbose')
244
90
def startTest(self, test):
91
unittest.TestResult.startTest(self, test)
245
92
# TODO: Maybe show test.shortDescription somewhere?
247
94
# python2.3 has the bad habit of just "runit" for doctests
248
95
if what == 'runit':
249
96
what = test.shortDescription()
251
if self.style == 'verbose':
252
print >>self.out, '%-60.60s' % what,
254
elif self.style == 'progress':
257
TestResult.startTest(self, test)
260
def stopTest(self, test):
262
TestResult.stopTest(self, test)
98
self.stream.write('%-60.60s' % what)
265
101
def addError(self, test, err):
266
if self.style == 'verbose':
267
print >>self.out, 'ERROR'
268
TestResult.addError(self, test, err)
269
_show_test_failure('error', test, err, self.out)
102
super(_MyResult, self).addError(test, err)
271
105
def addFailure(self, test, err):
272
if self.style == 'verbose':
273
print >>self.out, 'FAILURE'
274
TestResult.addFailure(self, test, err)
275
_show_test_failure('failure', test, err, self.out)
106
super(_MyResult, self).addFailure(test, err)
277
109
def addSuccess(self, test):
278
if self.style == 'verbose':
279
print >>self.out, 'OK'
280
TestResult.addSuccess(self, test)
284
def run_suite(suite, name='test', verbose=False):
111
self.stream.writeln('OK')
113
self.stream.write('~')
115
unittest.TestResult.addSuccess(self, test)
117
def printErrorList(self, flavour, errors):
118
for test, err in errors:
119
self.stream.writeln(self.separator1)
120
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
121
self.stream.writeln(self.separator2)
122
self.stream.writeln("%s" % err)
123
if hasattr(test, '_get_log'):
124
self.stream.writeln()
125
self.stream.writeln('log from this test:')
126
print >>self.stream, test._get_log()
129
class TextTestRunner(unittest.TextTestRunner):
131
def _makeResult(self):
132
result = _MyResult(self.stream, self.descriptions, self.verbosity)
133
return EarlyStoppingTestResultAdapter(result)
136
class filteringVisitor(TestUtil.TestVisitor):
137
"""I accruse all the testCases I visit that pass a regexp filter on id
141
def __init__(self, filter):
143
TestUtil.TestVisitor.__init__(self)
145
self.filter=re.compile(filter)
148
"""answer the suite we are building"""
149
if self._suite is None:
150
self._suite=TestUtil.TestSuite()
153
def visitCase(self, aCase):
154
if self.filter.match(aCase.id()):
155
self.suite().addTest(aCase)
158
def run_suite(suite, name='test', verbose=False, pattern=".*"):
290
_setup_test_log(name)
291
_setup_test_dir(name)
294
# save stdout & stderr so there's no leakage from code-under-test
295
real_stdout = sys.stdout
296
real_stderr = sys.stderr
297
sys.stdout = sys.stderr = TestBase.TEST_LOG
303
result = _MyResult(real_stdout, style)
306
sys.stdout = real_stdout
307
sys.stderr = real_stderr
309
_show_results(result)
160
from bzrlib.selftest import TestCaseInTempDir
161
TestCaseInTempDir._TEST_NAME = name
166
runner = TextTestRunner(stream=sys.stdout,
169
visitor = filteringVisitor(pattern)
171
result = runner.run(visitor.suite())
172
# This is still a little bogus,
173
# but only a little. Folk not using our testrunner will
174
# have to delete their temp directories themselves.
175
if result.wasSuccessful():
176
if TestCaseInTempDir.TEST_ROOT:
177
shutil.rmtree(TestCaseInTempDir.TEST_ROOT)
179
print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
311
180
return result.wasSuccessful()
315
def _setup_test_log(name):
319
log_filename = os.path.abspath(name + '.log')
320
TestBase.TEST_LOG = open(log_filename, 'wt', buffering=1) # line buffered
322
print >>TestBase.TEST_LOG, "tests run at " + time.ctime()
323
print '%-30s %s' % ('test log', log_filename)
326
def _setup_test_dir(name):
330
TestBase.ORIG_DIR = os.getcwdu()
331
TestBase.TEST_ROOT = os.path.abspath(name + '.tmp')
333
print '%-30s %s' % ('running tests in', TestBase.TEST_ROOT)
335
if os.path.exists(TestBase.TEST_ROOT):
336
shutil.rmtree(TestBase.TEST_ROOT)
337
os.mkdir(TestBase.TEST_ROOT)
338
os.chdir(TestBase.TEST_ROOT)
340
# make a fake bzr directory there to prevent any tests propagating
341
# up onto the source directory's real branch
342
os.mkdir(os.path.join(TestBase.TEST_ROOT, '.bzr'))
346
def _show_results(result):
348
print '%4d tests run' % result.testsRun
349
print '%4d errors' % len(result.errors)
350
print '%4d failures' % len(result.failures)
354
def _show_test_failure(kind, case, exc_info, out):
355
from traceback import print_exception
358
print >>out, '-' * 60
361
desc = case.shortDescription()
363
print >>out, ' (%s)' % desc
365
print_exception(exc_info[0], exc_info[1], exc_info[2], None, out)
367
if isinstance(case, TestBase):
369
print >>out, 'log from this test:'
370
print >>out, case._log_buf
372
print >>out, '-' * 60