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