1# Copyright 2018 The Abseil Authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""TestResult implementing default output for test execution status."""
16
17import unittest
18
19
20class TextTestResult(unittest.TextTestResult):
21  """TestResult class that provides the default text result formatting."""
22
23  def __init__(self, stream, descriptions, verbosity):
24    # Disable the verbose per-test output from the superclass, since it would
25    # conflict with our customized output.
26    super(TextTestResult, self).__init__(stream, descriptions, 0)
27    self._per_test_output = verbosity > 0
28
29  def _print_status(self, tag, test):
30    if self._per_test_output:
31      test_id = test.id()
32      if test_id.startswith('__main__.'):
33        test_id = test_id[len('__main__.'):]
34      print('[%s] %s' % (tag, test_id), file=self.stream)
35      self.stream.flush()
36
37  def startTest(self, test):
38    super(TextTestResult, self).startTest(test)
39    self._print_status(' RUN      ', test)
40
41  def addSuccess(self, test):
42    super(TextTestResult, self).addSuccess(test)
43    self._print_status('       OK ', test)
44
45  def addError(self, test, err):
46    super(TextTestResult, self).addError(test, err)
47    self._print_status('  FAILED  ', test)
48
49  def addFailure(self, test, err):
50    super(TextTestResult, self).addFailure(test, err)
51    self._print_status('  FAILED  ', test)
52
53  def addSkip(self, test, reason):
54    super(TextTestResult, self).addSkip(test, reason)
55    self._print_status('  SKIPPED ', test)
56
57  def addExpectedFailure(self, test, err):
58    super(TextTestResult, self).addExpectedFailure(test, err)
59    self._print_status('       OK ', test)
60
61  def addUnexpectedSuccess(self, test):
62    super(TextTestResult, self).addUnexpectedSuccess(test)
63    self._print_status('  FAILED  ', test)
64
65
66class TextTestRunner(unittest.TextTestRunner):
67  """A test runner that produces formatted text results."""
68
69  _TEST_RESULT_CLASS = TextTestResult
70
71  # Set this to true at the class or instance level to run tests using a
72  # debug-friendly method (e.g, one that doesn't catch exceptions and interacts
73  # better with debuggers).
74  # Usually this is set using --pdb_post_mortem.
75  run_for_debugging = False
76
77  def run(self, test):
78    # type: (TestCase) -> TestResult
79    if self.run_for_debugging:
80      return self._run_debug(test)
81    else:
82      return super(TextTestRunner, self).run(test)
83
84  def _run_debug(self, test):
85    # type: (TestCase) -> TestResult
86    test.debug()
87    # Return an empty result to indicate success.
88    return self._makeResult()
89
90  def _makeResult(self):
91    return TextTestResult(self.stream, self.descriptions, self.verbosity)
92