summaryrefslogtreecommitdiff
path: root/python/subunit/test_results.py
diff options
context:
space:
mode:
authorRobert Collins <robertc@robertcollins.net>2009-10-25 16:49:09 +1100
committerRobert Collins <robertc@robertcollins.net>2009-10-25 16:49:09 +1100
commitf6b397940103643565955391e73099bf54de6dfb (patch)
treec8f117d8a63487d916b76b24703150437486e8b7 /python/subunit/test_results.py
parent761e21ba507fa0d40509301b1f470944c8a4deb0 (diff)
downloadsubunit-git-f6b397940103643565955391e73099bf54de6dfb.tar.gz
Support the extended TestResult details API on TestResultFilter (but not yet on predicates).
Diffstat (limited to 'python/subunit/test_results.py')
-rw-r--r--python/subunit/test_results.py118
1 files changed, 118 insertions, 0 deletions
diff --git a/python/subunit/test_results.py b/python/subunit/test_results.py
index e0891a3..5401c32 100644
--- a/python/subunit/test_results.py
+++ b/python/subunit/test_results.py
@@ -196,6 +196,124 @@ class AutoTimingTestResultDecorator(HookedTestResultDecorator):
return self.decorated.time(a_datetime)
+class TestResultFilter(TestResultDecorator):
+ """A pyunit TestResult interface implementation which filters tests.
+
+ Tests that pass the filter are handed on to another TestResult instance
+ for further processing/reporting. To obtain the filtered results,
+ the other instance must be interrogated.
+
+ :ivar result: The result that tests are passed to after filtering.
+ :ivar filter_predicate: The callback run to decide whether to pass
+ a result.
+ """
+
+ def __init__(self, result, filter_error=False, filter_failure=False,
+ filter_success=True, filter_skip=False,
+ filter_predicate=None):
+ """Create a FilterResult object filtering to result.
+
+ :param filter_error: Filter out errors.
+ :param filter_failure: Filter out failures.
+ :param filter_success: Filter out successful tests.
+ :param filter_skip: Filter out skipped tests.
+ :param filter_predicate: A callable taking (test, err) and
+ returning True if the result should be passed through.
+ err is None for success.
+ """
+ TestResultDecorator.__init__(self, result)
+ self._filter_error = filter_error
+ self._filter_failure = filter_failure
+ self._filter_success = filter_success
+ self._filter_skip = filter_skip
+ if filter_predicate is None:
+ filter_predicate = lambda test, err: True
+ self.filter_predicate = filter_predicate
+ # The current test (for filtering tags)
+ self._current_test = None
+ # Has the current test been filtered (for outputting test tags)
+ self._current_test_filtered = None
+ # The (new, gone) tags for the current test.
+ self._current_test_tags = None
+
+ def addError(self, test, err, details=None):
+ if not self._filter_error and self.filter_predicate(test, err):
+ self.decorated.startTest(test)
+ self.decorated.addError(test, err, details=details)
+
+ def addFailure(self, test, err, details=None):
+ if not self._filter_failure and self.filter_predicate(test, err):
+ self.decorated.startTest(test)
+ self.decorated.addFailure(test, err, details=details)
+
+ def addSkip(self, test, reason, details=None):
+ if not self._filter_skip and self.filter_predicate(test, reason):
+ self.decorated.startTest(test)
+ self.decorated.addSkip(test, reason, details=details)
+
+ def addSuccess(self, test, details=None):
+ if not self._filter_success and self.filter_predicate(test, None):
+ self.decorated.startTest(test)
+ self.decorated.addSuccess(test, details=details)
+
+ def addExpectedFailure(self, test, err, details=None):
+ if self.filter_predicate(test, err):
+ self.decorated.startTest(test)
+ return self.decorated.addExpectedFailure(test, err,
+ details=details)
+
+ def addUnexpectedSuccess(self, test, details=None):
+ self.decorated.startTest(test)
+ return self.decorated.addUnexpectedSuccess(test, details=details)
+
+ def startTest(self, test):
+ """Start a test.
+
+ Not directly passed to the client, but used for handling of tags
+ correctly.
+ """
+ self._current_test = test
+ self._current_test_filtered = False
+ self._current_test_tags = set(), set()
+
+ def stopTest(self, test):
+ """Stop a test.
+
+ Not directly passed to the client, but used for handling of tags
+ correctly.
+ """
+ if not self._current_test_filtered:
+ # Tags to output for this test.
+ if self._current_test_tags[0] or self._current_test_tags[1]:
+ self.decorated.tags(*self._current_test_tags)
+ self.decorated.stopTest(test)
+ self._current_test = None
+ self._current_test_filtered = None
+ self._current_test_tags = None
+
+ def tags(self, new_tags, gone_tags):
+ """Handle tag instructions.
+
+ Adds and removes tags as appropriate. If a test is currently running,
+ tags are not affected for subsequent tests.
+
+ :param new_tags: Tags to add,
+ :param gone_tags: Tags to remove.
+ """
+ if self._current_test is not None:
+ # gather the tags until the test stops.
+ self._current_test_tags[0].update(new_tags)
+ self._current_test_tags[0].difference_update(gone_tags)
+ self._current_test_tags[1].update(gone_tags)
+ self._current_test_tags[1].difference_update(new_tags)
+ return self.decorated.tags(new_tags, gone_tags)
+
+ def id_to_orig_id(self, id):
+ if id.startswith("subunit.RemotedTestCase."):
+ return id[len("subunit.RemotedTestCase."):]
+ return id
+
+
class ExtendedToOriginalDecorator(object):
"""Permit new TestResult API code to degrade gracefully with old results.