53
54
class TestSkipped(Exception):
54
55
"""Indicates that a test was intentionally skipped, rather than failing."""
55
56
# XXX: Not used yet
58
class TestBase(TestCase):
59
"""Base class for bzr test cases.
61
Just defines some useful helper functions; doesn't actually test
65
# TODO: Special methods to invoke bzr, so that we can run it
66
# through a specified Python intepreter
68
OVERRIDE_PYTHON = None # to run with alternative python 'python'
75
super(TestBase, self).setUp()
76
self.log("%s setup" % self.id())
80
super(TestBase, self).tearDown()
81
self.log("%s teardown" % self.id())
85
def formcmd(self, cmd):
86
if isinstance(cmd, basestring):
91
if self.OVERRIDE_PYTHON:
92
cmd.insert(0, self.OVERRIDE_PYTHON)
94
self.log('$ %r' % cmd)
99
def runcmd(self, cmd, retcode=0):
100
"""Run one command and check the return code.
102
Returns a tuple of (stdout,stderr) strings.
104
If a single string is based, it is split into words.
105
For commands that are not simple space-separated words, please
106
pass a list instead."""
109
from subprocess import call
110
except ImportError, e:
115
cmd = self.formcmd(cmd)
117
self.log('$ ' + ' '.join(cmd))
118
actual_retcode = call(cmd, stdout=self.TEST_LOG, stderr=self.TEST_LOG)
120
if retcode != actual_retcode:
121
raise CommandFailed("test failed: %r returned %d, expected %d"
122
% (cmd, actual_retcode, retcode))
125
def backtick(self, cmd, retcode=0):
126
"""Run a command and return its output"""
129
from subprocess import Popen, PIPE
130
except ImportError, e:
134
cmd = self.formcmd(cmd)
135
child = Popen(cmd, stdout=PIPE, stderr=self.TEST_LOG)
136
outd, errd = child.communicate()
138
actual_retcode = child.wait()
140
outd = outd.replace('\r', '')
142
if retcode != actual_retcode:
143
raise CommandFailed("test failed: %r returned %d, expected %d"
144
% (cmd, actual_retcode, retcode))
150
def build_tree(self, shape):
151
"""Build a test tree according to a pattern.
153
shape is a sequence of file specifications. If the final
154
character is '/', a directory is created.
156
This doesn't add anything to a branch.
158
# XXX: It's OK to just create them using forward slashes on windows?
161
assert isinstance(name, basestring)
166
print >>f, "contents of", name
171
"""Log a message to a progress file"""
172
self._log_buf = self._log_buf + str(msg) + '\n'
173
print >>self.TEST_LOG, msg
176
def check_inventory_shape(self, inv, shape):
178
Compare an inventory to a list of expected names.
180
Fail if they are not precisely equal.
183
shape = list(shape) # copy
184
for path, ie in inv.entries():
185
name = path.replace('\\', '/')
193
self.fail("expected paths not found in inventory: %r" % shape)
195
self.fail("unexpected paths found in inventory: %r" % extras)
198
def check_file_contents(self, filename, expect):
199
self.log("check contents of file %s" % filename)
200
contents = file(filename, 'r').read()
201
if contents != expect:
202
self.log("expected: %r" % expect)
203
self.log("actually: %r" % contents)
204
self.fail("contents of %s not as expected")
208
class InTempDir(TestBase):
209
"""Base class for tests run in a temporary branch."""
212
self.test_dir = os.path.join(self.TEST_ROOT, self.__class__.__name__)
213
os.mkdir(self.test_dir)
214
os.chdir(self.test_dir)
218
os.chdir(self.TEST_ROOT)
224
class _MyResult(TestResult):
58
class EarlyStoppingTestResultAdapter(object):
59
"""An adapter for TestResult to stop at the first first failure or error"""
61
def __init__(self, result):
64
def addError(self, test, err):
65
self._result.addError(test, err)
68
def addFailure(self, test, err):
69
self._result.addFailure(test, err)
72
def __getattr__(self, name):
73
return getattr(self._result, name)
75
def __setattr__(self, name, value):
77
object.__setattr__(self, name, value)
78
return setattr(self._result, name, value)
81
class _MyResult(unittest._TextTestResult):
226
83
Custom TestResult.
228
85
No special behaviour for now.
230
def __init__(self, out):
232
TestResult.__init__(self)
234
88
def startTest(self, test):
89
unittest.TestResult.startTest(self, test)
235
90
# TODO: Maybe show test.shortDescription somewhere?
237
# python2.3 has the bad habit of just "runit" for doctests
239
what = test.shortDescription()
241
print >>self.out, '%-60.60s' % what,
243
TestResult.startTest(self, test)
245
def stopTest(self, test):
247
TestResult.stopTest(self, test)
91
what = test.shortDescription() or test.id()
93
self.stream.write('%-70.70s' % what)
250
96
def addError(self, test, err):
251
print >>self.out, 'ERROR'
252
TestResult.addError(self, test, err)
253
_show_test_failure('error', test, err, self.out)
97
super(_MyResult, self).addError(test, err)
255
100
def addFailure(self, test, err):
256
print >>self.out, 'FAILURE'
257
TestResult.addFailure(self, test, err)
258
_show_test_failure('failure', test, err, self.out)
101
super(_MyResult, self).addFailure(test, err)
260
104
def addSuccess(self, test):
261
print >>self.out, 'OK'
262
TestResult.addSuccess(self, test)
266
def run_suite(suite, name="test"):
106
self.stream.writeln('OK')
108
self.stream.write('~')
110
unittest.TestResult.addSuccess(self, test)
112
def printErrorList(self, flavour, errors):
113
for test, err in errors:
114
self.stream.writeln(self.separator1)
115
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
116
self.stream.writeln(self.separator2)
117
self.stream.writeln("%s" % err)
118
if hasattr(test, '_get_log'):
119
self.stream.writeln()
120
self.stream.writeln('log from this test:')
121
print >>self.stream, test._get_log()
124
class TextTestRunner(unittest.TextTestRunner):
126
def _makeResult(self):
127
result = _MyResult(self.stream, self.descriptions, self.verbosity)
128
return EarlyStoppingTestResultAdapter(result)
131
class filteringVisitor(TestUtil.TestVisitor):
132
"""I accruse all the testCases I visit that pass a regexp filter on id
136
def __init__(self, filter):
138
TestUtil.TestVisitor.__init__(self)
140
self.filter=re.compile(filter)
143
"""answer the suite we are building"""
144
if self._suite is None:
145
self._suite=TestUtil.TestSuite()
148
def visitCase(self, aCase):
149
if self.filter.match(aCase.id()):
150
self.suite().addTest(aCase)
153
def run_suite(suite, name='test', verbose=False, pattern=".*"):
272
_setup_test_log(name)
273
_setup_test_dir(name)
276
# save stdout & stderr so there's no leakage from code-under-test
277
real_stdout = sys.stdout
278
real_stderr = sys.stderr
279
sys.stdout = sys.stderr = TestBase.TEST_LOG
281
result = _MyResult(real_stdout)
284
sys.stdout = real_stdout
285
sys.stderr = real_stderr
287
_show_results(result)
155
from bzrlib.selftest import TestCaseInTempDir
156
TestCaseInTempDir._TEST_NAME = name
161
runner = TextTestRunner(stream=sys.stdout,
164
visitor = filteringVisitor(pattern)
166
result = runner.run(visitor.suite())
167
# This is still a little bogus,
168
# but only a little. Folk not using our testrunner will
169
# have to delete their temp directories themselves.
170
if result.wasSuccessful():
171
if TestCaseInTempDir.TEST_ROOT is not None:
172
shutil.rmtree(TestCaseInTempDir.TEST_ROOT)
174
print "Failed tests working directories are in '%s'\n" % TestCaseInTempDir.TEST_ROOT
289
175
return result.wasSuccessful()
293
def _setup_test_log(name):
297
log_filename = os.path.abspath(name + '.log')
298
TestBase.TEST_LOG = open(log_filename, 'wt', buffering=1) # line buffered
300
print >>TestBase.TEST_LOG, "tests run at " + time.ctime()
301
print '%-30s %s' % ('test log', log_filename)
304
def _setup_test_dir(name):
308
TestBase.ORIG_DIR = os.getcwdu()
309
TestBase.TEST_ROOT = os.path.abspath(name + '.tmp')
311
print '%-30s %s' % ('running tests in', TestBase.TEST_ROOT)
313
if os.path.exists(TestBase.TEST_ROOT):
314
shutil.rmtree(TestBase.TEST_ROOT)
315
os.mkdir(TestBase.TEST_ROOT)
316
os.chdir(TestBase.TEST_ROOT)
318
# make a fake bzr directory there to prevent any tests propagating
319
# up onto the source directory's real branch
320
os.mkdir(os.path.join(TestBase.TEST_ROOT, '.bzr'))
324
def _show_results(result):
326
print '%4d tests run' % result.testsRun
327
print '%4d errors' % len(result.errors)
328
print '%4d failures' % len(result.failures)
332
def _show_test_failure(kind, case, exc_info, out):
333
from traceback import print_exception
335
print >>out, '-' * 60
338
desc = case.shortDescription()
340
print >>out, ' (%s)' % desc
342
print_exception(exc_info[0], exc_info[1], exc_info[2], None, out)
344
if isinstance(case, TestBase):
346
print >>out, 'log from this test:'
347
print >>out, case._log_buf
349
print >>out, '-' * 60