69
68
OVERRIDE_PYTHON = None # to run with alternative python 'python'
72
def apply_redirected(self, stdin=None, stdout=None, stderr=None,
73
a_callable=None, *args, **kwargs):
74
"""Call callable with redirected std io pipes.
76
Returns the return code."""
77
from StringIO import StringIO
78
if not callable(a_callable):
79
raise ValueError("a_callable must be callable.")
83
stdout = self.TEST_LOG
85
stderr = self.TEST_LOG
86
real_stdin = sys.stdin
87
real_stdout = sys.stdout
88
real_stderr = sys.stderr
94
result = a_callable(*args, **kwargs)
96
sys.stdout = real_stdout
97
sys.stderr = real_stderr
98
sys.stdin = real_stdin
102
super(TestCase, self).setUp()
103
# setup a temporary log for the test
105
self.TEST_LOG = tempfile.NamedTemporaryFile(mode='wt', bufsize=0)
75
super(TestBase, self).setUp()
106
76
self.log("%s setup" % self.id())
108
79
def tearDown(self):
80
super(TestBase, self).tearDown()
109
81
self.log("%s teardown" % self.id())
111
super(TestCase, self).tearDown()
114
"""Log a message to a progress file"""
115
print >>self.TEST_LOG, msg
117
def check_inventory_shape(self, inv, shape):
119
Compare an inventory to a list of expected names.
121
Fail if they are not precisely equal.
124
shape = list(shape) # copy
125
for path, ie in inv.entries():
126
name = path.replace('\\', '/')
134
self.fail("expected paths not found in inventory: %r" % shape)
136
self.fail("unexpected paths found in inventory: %r" % extras)
139
"""Get the log the test case used. This can only be called once,
140
after which an exception will be raised.
142
self.TEST_LOG.flush()
143
log = open(self.TEST_LOG.name, 'rt').read()
144
self.TEST_LOG.close()
148
class FunctionalTestCase(TestCase):
149
"""Base class for tests that perform function testing - running bzr,
150
using files on disk, and similar activities.
152
InTempDir is an old alias for FunctionalTestCase.
158
def check_file_contents(self, filename, expect):
159
self.log("check contents of file %s" % filename)
160
contents = file(filename, 'r').read()
161
if contents != expect:
162
self.log("expected: %r" % expect)
163
self.log("actually: %r" % contents)
164
self.fail("contents of %s not as expected")
166
def _make_test_root(self):
171
if FunctionalTestCase.TEST_ROOT is not None:
173
FunctionalTestCase.TEST_ROOT = os.path.abspath(
174
tempfile.mkdtemp(suffix='.tmp',
175
prefix=self._TEST_NAME + '-',
178
# make a fake bzr directory there to prevent any tests propagating
179
# up onto the source directory's real branch
180
os.mkdir(os.path.join(FunctionalTestCase.TEST_ROOT, '.bzr'))
183
super(FunctionalTestCase, self).setUp()
185
self._make_test_root()
186
self._currentdir = os.getcwdu()
187
self.test_dir = os.path.join(self.TEST_ROOT, self.id())
188
os.mkdir(self.test_dir)
189
os.chdir(self.test_dir)
193
os.chdir(self._currentdir)
194
super(FunctionalTestCase, self).tearDown()
196
85
def formcmd(self, cmd):
197
86
if isinstance(cmd, basestring):
199
89
if cmd[0] == 'bzr':
200
90
cmd[0] = self.BZRPATH
201
91
if self.OVERRIDE_PYTHON:
202
92
cmd.insert(0, self.OVERRIDE_PYTHON)
203
94
self.log('$ %r' % cmd)
206
99
def runcmd(self, cmd, retcode=0):
207
100
"""Run one command and check the return code.
261
165
f = file(name, 'wt')
262
166
print >>f, "contents of", name
265
InTempDir = FunctionalTestCase
268
class EarlyStoppingTestResultAdapter(object):
269
"""An adapter for TestResult to stop at the first first failure or error"""
271
def __init__(self, result):
272
self._result = result
274
def addError(self, test, err):
275
self._result.addError(test, err)
278
def addFailure(self, test, err):
279
self._result.addFailure(test, err)
282
def __getattr__(self, name):
283
return getattr(self._result, name)
285
def __setattr__(self, name, value):
286
if name == '_result':
287
object.__setattr__(self, name, value)
288
return setattr(self._result, name, value)
291
class _MyResult(unittest._TextTestResult):
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):
293
226
Custom TestResult.
295
228
No special behaviour for now.
230
def __init__(self, out, style):
232
TestResult.__init__(self)
233
assert style in ('none', 'progress', 'verbose')
298
237
def startTest(self, test):
299
unittest.TestResult.startTest(self, test)
300
238
# TODO: Maybe show test.shortDescription somewhere?
302
240
# python2.3 has the bad habit of just "runit" for doctests
303
241
if what == 'runit':
304
242
what = test.shortDescription()
306
self.stream.write('%-60.60s' % what)
244
if self.style == 'verbose':
245
print >>self.out, '%-60.60s' % what,
247
elif self.style == 'progress':
250
TestResult.startTest(self, test)
253
def stopTest(self, test):
255
TestResult.stopTest(self, test)
309
258
def addError(self, test, err):
310
super(_MyResult, self).addError(test, err)
259
if self.style == 'verbose':
260
print >>self.out, 'ERROR'
261
TestResult.addError(self, test, err)
262
_show_test_failure('error', test, err, self.out)
313
264
def addFailure(self, test, err):
314
super(_MyResult, self).addFailure(test, err)
265
if self.style == 'verbose':
266
print >>self.out, 'FAILURE'
267
TestResult.addFailure(self, test, err)
268
_show_test_failure('failure', test, err, self.out)
317
270
def addSuccess(self, test):
319
self.stream.writeln('OK')
321
self.stream.write('~')
323
unittest.TestResult.addSuccess(self, test)
325
def printErrorList(self, flavour, errors):
326
for test, err in errors:
327
self.stream.writeln(self.separator1)
328
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
329
self.stream.writeln(self.separator2)
330
self.stream.writeln("%s" % err)
331
if isinstance(test, TestCase):
332
self.stream.writeln()
333
self.stream.writeln('log from this test:')
334
print >>self.stream, test._get_log()
337
class TextTestRunner(unittest.TextTestRunner):
339
def _makeResult(self):
340
result = _MyResult(self.stream, self.descriptions, self.verbosity)
341
return EarlyStoppingTestResultAdapter(result)
344
class filteringVisitor(TestUtil.TestVisitor):
345
"""I accruse all the testCases I visit that pass a regexp filter on id
349
def __init__(self, filter):
351
TestUtil.TestVisitor.__init__(self)
353
self.filter=re.compile(filter)
356
"""answer the suite we are building"""
357
if self._suite is None:
358
self._suite=TestUtil.TestSuite()
361
def visitCase(self, aCase):
362
if self.filter.match(aCase.id()):
363
self.suite().addTest(aCase)
366
def run_suite(suite, name='test', verbose=False, pattern=".*"):
271
if self.style == 'verbose':
272
print >>self.out, 'OK'
273
TestResult.addSuccess(self, test)
277
def run_suite(suite, name='test', verbose=False):
368
FunctionalTestCase._TEST_NAME = name
373
runner = TextTestRunner(stream=sys.stdout,
376
visitor = filteringVisitor(pattern)
378
result = runner.run(visitor.suite())
379
# This is still a little bogus,
380
# but only a little. Folk not using our testrunner will
381
# have to delete their temp directories themselves.
382
if result.wasSuccessful():
383
if FunctionalTestCase.TEST_ROOT is not None:
384
shutil.rmtree(FunctionalTestCase.TEST_ROOT)
386
print "Failed tests working directories are in '%s'\n" % FunctionalTestCase.TEST_ROOT
283
_setup_test_log(name)
284
_setup_test_dir(name)
287
# save stdout & stderr so there's no leakage from code-under-test
288
real_stdout = sys.stdout
289
real_stderr = sys.stderr
290
sys.stdout = sys.stderr = TestBase.TEST_LOG
296
result = _MyResult(real_stdout, style)
299
sys.stdout = real_stdout
300
sys.stderr = real_stderr
302
_show_results(result)
387
304
return result.wasSuccessful()
308
def _setup_test_log(name):
312
log_filename = os.path.abspath(name + '.log')
313
TestBase.TEST_LOG = open(log_filename, 'wt', buffering=1) # line buffered
315
print >>TestBase.TEST_LOG, "tests run at " + time.ctime()
316
print '%-30s %s' % ('test log', log_filename)
319
def _setup_test_dir(name):
323
TestBase.ORIG_DIR = os.getcwdu()
324
TestBase.TEST_ROOT = os.path.abspath(name + '.tmp')
326
print '%-30s %s' % ('running tests in', TestBase.TEST_ROOT)
328
if os.path.exists(TestBase.TEST_ROOT):
329
shutil.rmtree(TestBase.TEST_ROOT)
330
os.mkdir(TestBase.TEST_ROOT)
331
os.chdir(TestBase.TEST_ROOT)
333
# make a fake bzr directory there to prevent any tests propagating
334
# up onto the source directory's real branch
335
os.mkdir(os.path.join(TestBase.TEST_ROOT, '.bzr'))
339
def _show_results(result):
341
print '%4d tests run' % result.testsRun
342
print '%4d errors' % len(result.errors)
343
print '%4d failures' % len(result.failures)
347
def _show_test_failure(kind, case, exc_info, out):
348
from traceback import print_exception
350
print >>out, '-' * 60
353
desc = case.shortDescription()
355
print >>out, ' (%s)' % desc
357
print_exception(exc_info[0], exc_info[1], exc_info[2], None, out)
359
if isinstance(case, TestBase):
361
print >>out, 'log from this test:'
362
print >>out, case._log_buf
364
print >>out, '-' * 60