135
class _MyResult(unittest._TextTestResult):
136
"""Custom TestResult.
138
class ExtendedTestResult(unittest._TextTestResult):
139
"""Accepts, reports and accumulates the results of running tests.
138
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.
140
146
stop_early = False
142
def __init__(self, stream, descriptions, verbosity, pb=None,
148
def __init__(self, stream, descriptions, verbosity,
144
152
"""Construct new TestResult.
146
154
:param bench_history: Optionally, a writable file object to accumulate
147
155
benchmark results.
149
157
unittest._TextTestResult.__init__(self, stream, descriptions, verbosity)
151
158
if bench_history is not None:
152
159
from bzrlib.version import _get_bzr_source_tree
153
160
src_tree = _get_bzr_source_tree()
184
198
"""Format seconds as milliseconds with leading spaces."""
185
199
return "%5dms" % (1000 * seconds)
187
def _ellipsise_unimportant_words(self, a_string, final_width,
189
"""Add ellipses (sp?) for overly long strings.
191
:param keep_start: If true preserve the start of a_string rather
195
if len(a_string) > final_width:
196
result = a_string[:final_width-3] + '...'
200
if len(a_string) > final_width:
201
result = '...' + a_string[3-final_width:]
204
return result.ljust(final_width)
201
def _shortened_test_description(self, test):
203
what = re.sub(r'^bzrlib\.(tests|benchmark)\.', '', what)
206
206
def startTest(self, test):
207
207
unittest.TestResult.startTest(self, test)
208
# In a short description, the important words are in
209
# the beginning, but in an id, the important words are
211
SHOW_DESCRIPTIONS = False
213
if not self.showAll and self.dots and self.pb is not None:
216
final_width = osutils.terminal_width()
217
final_width = final_width - 15 - 8
219
if SHOW_DESCRIPTIONS:
220
what = test.shortDescription()
222
what = self._ellipsise_unimportant_words(what, final_width, keep_start=True)
225
if what.startswith('bzrlib.tests.'):
227
what = self._ellipsise_unimportant_words(what, final_width)
229
self.stream.write(what)
230
elif self.dots and self.pb is not None:
231
self.pb.update(what, self.testsRun - 1, None)
208
self.report_test_start(test)
233
209
self._recordTestStartTime()
235
211
def _recordTestStartTime(self):
287
245
self._bench_history.write("%s %s\n" % (
288
246
self._formatTime(self._benchmarkTime),
291
self.stream.writeln(' OK %s' % self._testTimeString())
292
for bench_called, stats in getattr(test, '_benchcalls', []):
293
self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
294
stats.pprint(file=self.stream)
295
elif self.dots and self.pb is None:
296
self.stream.write('~')
298
self.pb.update(self._ellipsise_unimportant_words('OK', 13), self.testsRun, None)
248
self.report_success(test)
300
249
unittest.TestResult.addSuccess(self, test)
302
251
def addSkipped(self, test, skip_excinfo):
303
252
self.extractBenchmarkTime(test)
305
print >>self.stream, ' SKIP %s' % self._testTimeString()
306
print >>self.stream, ' %s' % skip_excinfo[1]
307
elif self.dots and self.pb is None:
308
self.stream.write('S')
310
self.pb.update(self._ellipsise_unimportant_words('SKIP', 13), self.testsRun, None)
253
self.report_skip(test, skip_excinfo)
312
254
# seems best to treat this as success from point-of-view of unittest
313
255
# -- it actually does nothing so it barely matters :)
334
276
self.stream.writeln(self.separator2)
335
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]
338
404
class TextTestRunner(object):
339
405
stop_on_failure = False
345
411
keep_output=False,
347
412
bench_history=None):
348
413
self.stream = unittest._WritelnDecorator(stream)
349
414
self.descriptions = descriptions
350
415
self.verbosity = verbosity
351
416
self.keep_output = keep_output
353
417
self._bench_history = bench_history
355
def _makeResult(self):
356
result = _MyResult(self.stream,
360
bench_history=self._bench_history)
361
result.stop_early = self.stop_on_failure
364
419
def run(self, test):
365
420
"Run the given test case or test suite."
366
result = self._makeResult()
367
421
startTime = time.time()
368
if self.pb is not None:
369
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()
371
435
stopTime = time.time()
372
436
timeTaken = stopTime - startTime