134
class _MyResult(unittest._TextTestResult):
135
"""Custom TestResult.
137
class ExtendedTestResult(unittest._TextTestResult):
138
"""Accepts, reports and accumulates the results of running tests.
137
Shows output in a different format, including displaying runtime for tests.
140
Compared to this unittest version this class adds support for profiling,
141
benchmarking, stopping as soon as a test fails, and skipping tests.
142
There are further-specialized subclasses for different types of display.
139
145
stop_early = False
141
def __init__(self, stream, descriptions, verbosity, pb=None,
147
def __init__(self, stream, descriptions, verbosity,
143
151
"""Construct new TestResult.
145
153
:param bench_history: Optionally, a writable file object to accumulate
146
154
benchmark results.
148
156
unittest._TextTestResult.__init__(self, stream, descriptions, verbosity)
150
157
if bench_history is not None:
151
158
from bzrlib.version import _get_bzr_source_tree
152
159
src_tree = _get_bzr_source_tree()
183
197
"""Format seconds as milliseconds with leading spaces."""
184
198
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)
200
def _shortened_test_description(self, test):
202
what = re.sub(r'^bzrlib\.(tests|benchmark)\.', '', what)
205
205
def startTest(self, test):
206
206
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)
207
self.report_test_start(test)
232
208
self._recordTestStartTime()
234
210
def _recordTestStartTime(self):
286
244
self._bench_history.write("%s %s\n" % (
287
245
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)
247
self.report_success(test)
299
248
unittest.TestResult.addSuccess(self, test)
301
250
def addSkipped(self, test, skip_excinfo):
302
251
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)
252
self.report_skip(test, skip_excinfo)
311
253
# seems best to treat this as success from point-of-view of unittest
312
254
# -- it actually does nothing so it barely matters :)
333
275
self.stream.writeln(self.separator2)
334
276
self.stream.writeln("%s" % err)
281
def report_cleaning_up(self):
284
def report_success(self, test):
288
class TextTestResult(ExtendedTestResult):
289
"""Displays progress and results of tests in text form"""
291
def __init__(self, *args, **kw):
292
ExtendedTestResult.__init__(self, *args, **kw)
293
self.pb = self.ui.nested_progress_bar()
294
self.pb.show_pct = False
295
self.pb.show_spinner = False
296
self.pb.show_eta = False,
297
self.pb.show_count = False
298
self.pb.show_bar = False
300
def report_starting(self):
301
self.pb.update('[test 0/%d] starting...' % (self.num_tests))
303
def _progress_prefix_text(self):
304
a = '[%d' % self.count
305
if self.num_tests is not None:
306
a +='/%d' % self.num_tests
307
a += ' in %ds' % (time.time() - self._overall_start_time)
309
a += ', %d errors' % self.error_count
310
if self.failure_count:
311
a += ', %d failed' % self.failure_count
313
a += ', %d skipped' % self.skip_count
317
def report_test_start(self, test):
320
self._progress_prefix_text()
322
+ self._shortened_test_description(test))
324
def report_error(self, test, err):
325
self.error_count += 1
326
self.pb.note('ERROR: %s\n %s\n',
327
self._shortened_test_description(test),
331
def report_failure(self, test, err):
332
self.failure_count += 1
333
self.pb.note('FAIL: %s\n %s\n',
334
self._shortened_test_description(test),
338
def report_skip(self, test, skip_excinfo):
341
# at the moment these are mostly not things we can fix
342
# and so they just produce stipple; use the verbose reporter
345
# show test and reason for skip
346
self.pb.note('SKIP: %s\n %s\n',
347
self._shortened_test_description(test),
350
# since the class name was left behind in the still-visible
352
self.pb.note('SKIP: %s', skip_excinfo[1])
354
def report_cleaning_up(self):
355
self.pb.update('cleaning up...')
361
class VerboseTestResult(ExtendedTestResult):
362
"""Produce long output, with one line per test run plus times"""
364
def _ellipsize_to_right(self, a_string, final_width):
365
"""Truncate and pad a string, keeping the right hand side"""
366
if len(a_string) > final_width:
367
result = '...' + a_string[3-final_width:]
370
return result.ljust(final_width)
372
def report_starting(self):
373
self.stream.write('running %d tests...\n' % self.num_tests)
375
def report_test_start(self, test):
377
name = self._shortened_test_description(test)
378
self.stream.write(self._ellipsize_to_right(name, 60))
381
def report_error(self, test, err):
382
self.error_count += 1
383
self.stream.writeln('ERROR %s\n %s'
384
% (self._testTimeString(), err[1]))
386
def report_failure(self, test, err):
387
self.failure_count += 1
388
self.stream.writeln('FAIL %s\n %s'
389
% (self._testTimeString(), err[1]))
391
def report_success(self, test):
392
self.stream.writeln(' OK %s' % self._testTimeString())
393
for bench_called, stats in getattr(test, '_benchcalls', []):
394
self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
395
stats.pprint(file=self.stream)
398
def report_skip(self, test, skip_excinfo):
399
print >>self.stream, ' SKIP %s' % self._testTimeString()
400
print >>self.stream, ' %s' % skip_excinfo[1]
337
403
class TextTestRunner(object):
338
404
stop_on_failure = False
344
410
keep_output=False,
346
411
bench_history=None):
347
412
self.stream = unittest._WritelnDecorator(stream)
348
413
self.descriptions = descriptions
349
414
self.verbosity = verbosity
350
415
self.keep_output = keep_output
352
416
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
418
def run(self, test):
364
419
"Run the given test case or test suite."
365
result = self._makeResult()
366
420
startTime = time.time()
367
if self.pb is not None:
368
self.pb.update('Running tests', 0, test.countTestCases())
421
if self.verbosity == 1:
422
result_class = TextTestResult
423
elif self.verbosity >= 2:
424
result_class = VerboseTestResult
425
result = result_class(self.stream,
428
bench_history=self._bench_history,
429
num_tests=test.countTestCases(),
431
result.stop_early = self.stop_on_failure
432
result.report_starting()
370
434
stopTime = time.time()
371
435
timeTaken = stopTime - startTime