134
class _MyResult(unittest._TextTestResult):
135
"""Custom TestResult.
138
class ExtendedTestResult(unittest._TextTestResult):
139
"""Accepts, reports and accumulates the results of running tests.
137
Shows output in a different format, including displaying runtime for tests.
141
Compared to this unittest version this class adds support for profiling,
142
benchmarking, stopping as soon as a test fails, and skipping tests.
143
There are further-specialized subclasses for different types of display.
139
146
stop_early = False
141
def __init__(self, stream, descriptions, verbosity, pb=None,
148
def __init__(self, stream, descriptions, verbosity,
143
152
"""Construct new TestResult.
145
154
:param bench_history: Optionally, a writable file object to accumulate
146
155
benchmark results.
148
157
unittest._TextTestResult.__init__(self, stream, descriptions, verbosity)
150
158
if bench_history is not None:
151
159
from bzrlib.version import _get_bzr_source_tree
152
160
src_tree = _get_bzr_source_tree()
183
198
"""Format seconds as milliseconds with leading spaces."""
184
199
return "%5dms" % (1000 * seconds)
186
def _ellipsise_unimportant_words(self, a_string, final_width,
188
"""Add ellipses (sp?) for overly long strings.
190
:param keep_start: If true preserve the start of a_string rather
194
if len(a_string) > final_width:
195
result = a_string[:final_width-3] + '...'
199
if len(a_string) > final_width:
200
result = '...' + a_string[3-final_width:]
203
return result.ljust(final_width)
201
def _shortened_test_description(self, test):
203
what = re.sub(r'^bzrlib\.(tests|benchmark)\.', '', what)
205
206
def startTest(self, test):
206
207
unittest.TestResult.startTest(self, test)
207
# In a short description, the important words are in
208
# the beginning, but in an id, the important words are
210
SHOW_DESCRIPTIONS = False
212
if not self.showAll and self.dots and self.pb is not None:
215
final_width = osutils.terminal_width()
216
final_width = final_width - 15 - 8
218
if SHOW_DESCRIPTIONS:
219
what = test.shortDescription()
221
what = self._ellipsise_unimportant_words(what, final_width, keep_start=True)
224
if what.startswith('bzrlib.tests.'):
226
what = self._ellipsise_unimportant_words(what, final_width)
228
self.stream.write(what)
229
elif self.dots and self.pb is not None:
230
self.pb.update(what, self.testsRun - 1, None)
208
self.report_test_start(test)
232
209
self._recordTestStartTime()
234
211
def _recordTestStartTime(self):
286
245
self._bench_history.write("%s %s\n" % (
287
246
self._formatTime(self._benchmarkTime),
290
self.stream.writeln(' OK %s' % self._testTimeString())
291
for bench_called, stats in getattr(test, '_benchcalls', []):
292
self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
293
stats.pprint(file=self.stream)
294
elif self.dots and self.pb is None:
295
self.stream.write('~')
297
self.pb.update(self._ellipsise_unimportant_words('OK', 13), self.testsRun, None)
248
self.report_success(test)
299
249
unittest.TestResult.addSuccess(self, test)
301
251
def addSkipped(self, test, skip_excinfo):
302
252
self.extractBenchmarkTime(test)
304
print >>self.stream, ' SKIP %s' % self._testTimeString()
305
print >>self.stream, ' %s' % skip_excinfo[1]
306
elif self.dots and self.pb is None:
307
self.stream.write('S')
309
self.pb.update(self._ellipsise_unimportant_words('SKIP', 13), self.testsRun, None)
253
self.report_skip(test, skip_excinfo)
311
254
# seems best to treat this as success from point-of-view of unittest
312
255
# -- it actually does nothing so it barely matters :)
333
276
self.stream.writeln(self.separator2)
334
277
self.stream.writeln("%s" % err)
282
def report_cleaning_up(self):
285
def report_success(self, test):
289
class TextTestResult(ExtendedTestResult):
290
"""Displays progress and results of tests in text form"""
292
def __init__(self, *args, **kw):
293
ExtendedTestResult.__init__(self, *args, **kw)
294
self.pb = self.ui.nested_progress_bar()
295
self.pb.show_pct = False
296
self.pb.show_spinner = False
297
self.pb.show_eta = False,
298
self.pb.show_count = False
299
self.pb.show_bar = False
301
def report_starting(self):
302
self.pb.update('[test 0/%d] starting...' % (self.num_tests))
304
def _progress_prefix_text(self):
305
a = '[%d' % self.count
306
if self.num_tests is not None:
307
a +='/%d' % self.num_tests
308
a += ' in %ds' % (time.time() - self._overall_start_time)
310
a += ', %d errors' % self.error_count
311
if self.failure_count:
312
a += ', %d failed' % self.failure_count
314
a += ', %d skipped' % self.skip_count
318
def report_test_start(self, test):
321
self._progress_prefix_text()
323
+ self._shortened_test_description(test))
325
def report_error(self, test, err):
326
self.error_count += 1
327
self.pb.note('ERROR: %s\n %s\n',
328
self._shortened_test_description(test),
332
def report_failure(self, test, err):
333
self.failure_count += 1
334
self.pb.note('FAIL: %s\n %s\n',
335
self._shortened_test_description(test),
339
def report_skip(self, test, skip_excinfo):
342
# at the moment these are mostly not things we can fix
343
# and so they just produce stipple; use the verbose reporter
346
# show test and reason for skip
347
self.pb.note('SKIP: %s\n %s\n',
348
self._shortened_test_description(test),
351
# since the class name was left behind in the still-visible
353
self.pb.note('SKIP: %s', skip_excinfo[1])
355
def report_cleaning_up(self):
356
self.pb.update('cleaning up...')
362
class VerboseTestResult(ExtendedTestResult):
363
"""Produce long output, with one line per test run plus times"""
365
def _ellipsize_to_right(self, a_string, final_width):
366
"""Truncate and pad a string, keeping the right hand side"""
367
if len(a_string) > final_width:
368
result = '...' + a_string[3-final_width:]
371
return result.ljust(final_width)
373
def report_starting(self):
374
self.stream.write('running %d tests...\n' % self.num_tests)
376
def report_test_start(self, test):
378
name = self._shortened_test_description(test)
379
self.stream.write(self._ellipsize_to_right(name, 60))
382
def report_error(self, test, err):
383
self.error_count += 1
384
self.stream.writeln('ERROR %s\n %s'
385
% (self._testTimeString(), err[1]))
387
def report_failure(self, test, err):
388
self.failure_count += 1
389
self.stream.writeln('FAIL %s\n %s'
390
% (self._testTimeString(), err[1]))
392
def report_success(self, test):
393
self.stream.writeln(' OK %s' % self._testTimeString())
394
for bench_called, stats in getattr(test, '_benchcalls', []):
395
self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
396
stats.pprint(file=self.stream)
399
def report_skip(self, test, skip_excinfo):
400
print >>self.stream, ' SKIP %s' % self._testTimeString()
401
print >>self.stream, ' %s' % skip_excinfo[1]
337
404
class TextTestRunner(object):
338
405
stop_on_failure = False
344
411
keep_output=False,
346
412
bench_history=None):
347
413
self.stream = unittest._WritelnDecorator(stream)
348
414
self.descriptions = descriptions
349
415
self.verbosity = verbosity
350
416
self.keep_output = keep_output
352
417
self._bench_history = bench_history
354
def _makeResult(self):
355
result = _MyResult(self.stream,
359
bench_history=self._bench_history)
360
result.stop_early = self.stop_on_failure
363
419
def run(self, test):
364
420
"Run the given test case or test suite."
365
result = self._makeResult()
366
421
startTime = time.time()
367
if self.pb is not None:
368
self.pb.update('Running tests', 0, test.countTestCases())
422
if self.verbosity == 1:
423
result_class = TextTestResult
424
elif self.verbosity >= 2:
425
result_class = VerboseTestResult
426
result = result_class(self.stream,
429
bench_history=self._bench_history,
430
num_tests=test.countTestCases(),
432
result.stop_early = self.stop_on_failure
433
result.report_starting()
370
435
stopTime = time.time()
371
436
timeTaken = stopTime - startTime
571
638
raise AssertionError("value(s) %r not present in container %r" %
572
639
(missing, superlist))
641
def assertListRaises(self, excClass, func, *args, **kwargs):
642
"""Fail unless excClass is raised when the iterator from func is used.
644
Many functions can return generators this makes sure
645
to wrap them in a list() call to make sure the whole generator
646
is run, and that the proper exception is raised.
649
list(func(*args, **kwargs))
653
if getattr(excClass,'__name__', None) is not None:
654
excName = excClass.__name__
656
excName = str(excClass)
657
raise self.failureException, "%s not raised" % excName
574
659
def assertIs(self, left, right):
575
660
if not (left is right):
576
661
raise AssertionError("%r is not %r." % (left, right))