138
class _MyResult(unittest._TextTestResult):
139
"""Custom TestResult.
134
class ExtendedTestResult(unittest._TextTestResult):
135
"""Accepts, reports and accumulates the results of running tests.
141
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.
143
142
stop_early = False
145
def __init__(self, stream, descriptions, verbosity, pb=None,
144
def __init__(self, stream, descriptions, verbosity,
147
148
"""Construct new TestResult.
149
150
:param bench_history: Optionally, a writable file object to accumulate
150
151
benchmark results.
152
153
unittest._TextTestResult.__init__(self, stream, descriptions, verbosity)
154
154
if bench_history is not None:
155
155
from bzrlib.version import _get_bzr_source_tree
156
156
src_tree = _get_bzr_source_tree()
187
194
"""Format seconds as milliseconds with leading spaces."""
188
195
return "%5dms" % (1000 * seconds)
190
def _ellipsise_unimportant_words(self, a_string, final_width,
192
"""Add ellipses (sp?) for overly long strings.
194
:param keep_start: If true preserve the start of a_string rather
198
if len(a_string) > final_width:
199
result = a_string[:final_width-3] + '...'
203
if len(a_string) > final_width:
204
result = '...' + a_string[3-final_width:]
207
return result.ljust(final_width)
197
def _shortened_test_description(self, test):
199
what = re.sub(r'^bzrlib\.(tests|benchmark)\.', '', what)
209
202
def startTest(self, test):
210
203
unittest.TestResult.startTest(self, test)
211
# In a short description, the important words are in
212
# the beginning, but in an id, the important words are
214
SHOW_DESCRIPTIONS = False
216
if not self.showAll and self.dots and self.pb is not None:
219
final_width = osutils.terminal_width()
220
final_width = final_width - 15 - 8
222
if SHOW_DESCRIPTIONS:
223
what = test.shortDescription()
225
what = self._ellipsise_unimportant_words(what, final_width, keep_start=True)
228
if what.startswith('bzrlib.tests.'):
230
what = self._ellipsise_unimportant_words(what, final_width)
232
self.stream.write(what)
233
elif self.dots and self.pb is not None:
234
self.pb.update(what, self.testsRun - 1, None)
204
self.report_test_start(test)
236
205
self._recordTestStartTime()
238
207
def _recordTestStartTime(self):
290
241
self._bench_history.write("%s %s\n" % (
291
242
self._formatTime(self._benchmarkTime),
294
self.stream.writeln(' OK %s' % self._testTimeString())
295
for bench_called, stats in getattr(test, '_benchcalls', []):
296
self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
297
stats.pprint(file=self.stream)
298
elif self.dots and self.pb is None:
299
self.stream.write('~')
301
self.pb.update(self._ellipsise_unimportant_words('OK', 13), self.testsRun, None)
244
self.report_success(test)
303
245
unittest.TestResult.addSuccess(self, test)
305
247
def addSkipped(self, test, skip_excinfo):
306
248
self.extractBenchmarkTime(test)
308
print >>self.stream, ' SKIP %s' % self._testTimeString()
309
print >>self.stream, ' %s' % skip_excinfo[1]
310
elif self.dots and self.pb is None:
311
self.stream.write('S')
313
self.pb.update(self._ellipsise_unimportant_words('SKIP', 13), self.testsRun, None)
249
self.report_skip(test, skip_excinfo)
315
250
# seems best to treat this as success from point-of-view of unittest
316
251
# -- it actually does nothing so it barely matters :)
337
272
self.stream.writeln(self.separator2)
338
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]
341
400
class TextTestRunner(object):
342
401
stop_on_failure = False
348
407
keep_output=False,
350
408
bench_history=None):
351
409
self.stream = unittest._WritelnDecorator(stream)
352
410
self.descriptions = descriptions
353
411
self.verbosity = verbosity
354
412
self.keep_output = keep_output
356
413
self._bench_history = bench_history
358
def _makeResult(self):
359
result = _MyResult(self.stream,
363
bench_history=self._bench_history)
364
result.stop_early = self.stop_on_failure
367
415
def run(self, test):
368
416
"Run the given test case or test suite."
369
result = self._makeResult()
370
417
startTime = time.time()
371
if self.pb is not None:
372
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()
374
431
stopTime = time.time()
375
432
timeTaken = stopTime - startTime
931
990
The values must be strings. The change will only occur in the
932
991
child, so you don't need to fix the environment after running.
933
992
:param universal_newlines: Convert CRLF => LF
993
:param allow_plugins: By default the subprocess is run with
994
--no-plugins to ensure test reproducibility. Also, it is possible
995
for system-wide plugins to create unexpected output on stderr,
996
which can cause unnecessary test failures.
935
998
env_changes = kwargs.get('env_changes', {})
936
999
working_dir = kwargs.get('working_dir', None)
1000
allow_plugins = kwargs.get('allow_plugins', False)
937
1001
process = self.start_bzr_subprocess(args, env_changes=env_changes,
938
working_dir=working_dir)
1002
working_dir=working_dir,
1003
allow_plugins=allow_plugins)
939
1004
# We distinguish between retcode=None and retcode not passed.
940
1005
supplied_retcode = kwargs.get('retcode', 0)
941
1006
return self.finish_bzr_subprocess(process, retcode=supplied_retcode,