Add a TestResultFilter supporting filtering out success/fail/errors.
authorRobert Collins <robertc@robertcollins.net>
Sun, 22 Feb 2009 06:28:08 +0000 (17:28 +1100)
committerRobert Collins <robertc@robertcollins.net>
Sun, 22 Feb 2009 06:28:08 +0000 (17:28 +1100)
python/subunit/__init__.py
python/subunit/tests/__init__.py
python/subunit/tests/test_subunit_filter.py [new file with mode: 0644]

index f63a3a051b06c692a01fb866e4e60bd96cb4efa7..c3d06ff3ba83a7c51dd21f8bf053eab0574ae6b8 100644 (file)
@@ -681,3 +681,52 @@ class TestResultStats(unittest.TestResult):
     def wasSuccessful(self):
         """Tells whether or not this result was a success"""
         return self.failed_tests == 0
+
+
+class TestResultFilter(unittest.TestResult):
+    """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.
+    """
+
+    def __init__(self, result, filter_error=False, filter_failure=False,
+        filter_success=True):
+        """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.
+        """
+        unittest.TestResult.__init__(self)
+        self.result = result
+        self._filter_error = filter_error
+        self._filter_failure = filter_failure
+        self._filter_success = filter_success
+        
+    def addError(self, test, err):
+        if not self._filter_error:
+            self.result.startTest(test)
+            self.result.addError(test, err)
+            self.result.stopTest(test)
+
+    def addFailure(self, test, err):
+        if not self._filter_failure:
+            self.result.startTest(test)
+            self.result.addFailure(test, err)
+            self.result.stopTest(test)
+
+    def addSuccess(self, test):
+        if not self._filter_success:
+            self.result.startTest(test)
+            self.result.addSuccess(test)
+            self.result.stopTest(test)
+
+    def id_to_orig_id(self, id):
+        if id.startswith("subunit.RemotedTestCase."):
+            return id[len("subunit.RemotedTestCase."):]
+        return id
+
index a506281d0f424cef07337f324d98bfcb96386d3c..fa41930e1ff6f87ffc174848d1e911ab78c93ff0 100644 (file)
@@ -19,6 +19,7 @@
 
 from subunit.tests import (
     TestUtil,
+    test_subunit_filter,
     test_subunit_stats,
     test_subunit_tags,
     test_tap2subunit,
@@ -29,6 +30,7 @@ def test_suite():
     result = TestUtil.TestSuite()
     result.addTest(test_test_protocol.test_suite())
     result.addTest(test_tap2subunit.test_suite())
+    result.addTest(test_subunit_filter.test_suite())
     result.addTest(test_subunit_tags.test_suite())
     result.addTest(test_subunit_stats.test_suite())
     return result
diff --git a/python/subunit/tests/test_subunit_filter.py b/python/subunit/tests/test_subunit_filter.py
new file mode 100644 (file)
index 0000000..825809c
--- /dev/null
@@ -0,0 +1,115 @@
+#
+#  subunit: extensions to python unittest to get test results from subprocesses.
+#  Copyright (C) 2005  Robert Collins <robertc@robertcollins.net>
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+
+"""Tests for subunit.TestResultFilter."""
+
+import unittest
+from StringIO import StringIO
+
+import subunit
+
+
+class TestTestResultFilter(unittest.TestCase):
+    """Test for TestResultFilter, a TestResult object which filters tests."""
+
+    def _setUp(self):
+        self.output = StringIO()
+
+    def test_default(self):
+        """The default is to exclude success and include everything else."""
+        self.filtered_result = unittest.TestResult()
+        self.filter = subunit.TestResultFilter(self.filtered_result)
+        self.run_tests()
+        self.assertEqual(['subunit.RemotedTestCase.error'],
+            [error[0].id() for error in self.filtered_result.errors])
+        self.assertEqual(['subunit.RemotedTestCase.failed'],
+            [failure[0].id() for failure in
+            self.filtered_result.failures])
+        self.assertEqual(2, self.filtered_result.testsRun)
+
+    def test_exclude_errors(self):
+        self.filtered_result = unittest.TestResult()
+        self.filter = subunit.TestResultFilter(self.filtered_result,
+            filter_error=True)
+        self.run_tests()
+        self.assertEqual([],
+            [error[0].id() for error in self.filtered_result.errors])
+        self.assertEqual(['subunit.RemotedTestCase.failed'],
+            [failure[0].id() for failure in
+            self.filtered_result.failures])
+        self.assertEqual(1, self.filtered_result.testsRun)
+
+    def test_exclude_failure(self):
+        self.filtered_result = unittest.TestResult()
+        self.filter = subunit.TestResultFilter(self.filtered_result,
+            filter_failure=True)
+        self.run_tests()
+        self.assertEqual(['subunit.RemotedTestCase.error'],
+            [error[0].id() for error in self.filtered_result.errors])
+        self.assertEqual([],
+            [failure[0].id() for failure in
+            self.filtered_result.failures])
+        self.assertEqual(1, self.filtered_result.testsRun)
+
+    def test_include_success(self):
+        """Success's can be included if requested."""
+        self.filtered_result = unittest.TestResult()
+        self.filter = subunit.TestResultFilter(self.filtered_result,
+            filter_success=False)
+        self.run_tests()
+        self.assertEqual(['subunit.RemotedTestCase.error'],
+            [error[0].id() for error in self.filtered_result.errors])
+        self.assertEqual(['subunit.RemotedTestCase.failed'],
+            [failure[0].id() for failure in
+            self.filtered_result.failures])
+        self.assertEqual(5, self.filtered_result.testsRun)
+
+
+    def run_tests(self):
+        self.setUpTestStream()
+        self.test = subunit.ProtocolTestCase(self.input_stream)
+        self.test.run(self.filter)
+
+    def setUpTestStream(self):
+        # While TestResultFilter works on python objects, using a subunit
+        # stream is an easy pithy way of getting a series of test objects to
+        # call into the TestResult, and as TestResultFilter is intended for use
+        # with subunit also has the benefit of detecting any interface skew issues.
+        self.input_stream = StringIO()
+        self.input_stream.write("""tags: global
+test passed
+success passed
+test failed
+tags: local
+failure failed
+test error
+error error
+test skipped
+skip skipped
+test todo
+xfail todo
+""")
+        self.input_stream.seek(0)
+    
+
+
+def test_suite():
+    loader = subunit.tests.TestUtil.TestLoader()
+    result = loader.loadTestsFromName(__name__)
+    return result