Merge lp:~lifeless/subunit/filter into lp:~subunit/subunit/trunk
- filter
- Merge into trunk
Proposed by
Jonathan Lange
Status: | Merged |
---|---|
Approved by: | Jonathan Lange |
Approved revision: | 56 |
Merged at revision: | not available |
Proposed branch: | lp:~lifeless/subunit/filter |
Merge into: | lp:~subunit/subunit/trunk |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~lifeless/subunit/filter |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jonathan Lange | Approve | ||
Review via email: mp+4006@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Jonathan Lange (jml) wrote : | # |
lp:~lifeless/subunit/filter
updated
- 57. By Robert Collins
-
Update skip support in python to be in line with testtools.
- 58. By Robert Collins
-
subunit-filter can now filter skips too.
Revision history for this message
Jonathan Lange (jml) wrote : | # |
Two things!
* Rename FilterResult in subunit-ls so the name indicates what kind of filtering it does. ListingFilterRe
* Spurious blank line at python/
review:
Approve
Revision history for this message
Jonathan Lange (jml) wrote : | # |
I meant line 307 of the diff, sorry.
lp:~lifeless/subunit/filter
updated
- 59. By Robert Collins
-
Review feedback on filters.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'README' |
2 | --- README 2009-02-15 11:55:00 +0000 |
3 | +++ README 2009-02-22 13:18:04 +0000 |
4 | @@ -40,6 +40,8 @@ |
5 | stream on-the-fly. Currently subunit provides: |
6 | * tap2subunit - convert perl's TestAnythingProtocol to subunit. |
7 | * subunit2pyunit - convert a subunit stream to pyunit test results. |
8 | + * subunit-filter - filter out tests from a subunit stream. |
9 | + * subunit-ls - list the tests present in a subunit stream. |
10 | * subunit-stats - generate a summary of a subunit stream. |
11 | * subunit-tags - add or remove tags from a stream. |
12 | |
13 | |
14 | === added file 'filters/subunit-filter' |
15 | --- filters/subunit-filter 1970-01-01 00:00:00 +0000 |
16 | +++ filters/subunit-filter 2009-02-22 07:51:04 +0000 |
17 | @@ -0,0 +1,50 @@ |
18 | +#!/usr/bin/env python |
19 | +# subunit: extensions to python unittest to get test results from subprocesses. |
20 | +# Copyright (C) 2008 Robert Collins <robertc@robertcollins.net> |
21 | +# |
22 | +# This program is free software; you can redistribute it and/or modify |
23 | +# it under the terms of the GNU General Public License as published by |
24 | +# the Free Software Foundation; either version 2 of the License, or |
25 | +# (at your option) any later version. |
26 | +# |
27 | +# This program is distributed in the hope that it will be useful, |
28 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
29 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
30 | +# GNU General Public License for more details. |
31 | +# |
32 | +# You should have received a copy of the GNU General Public License |
33 | +# along with this program; if not, write to the Free Software |
34 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
35 | +# |
36 | + |
37 | +"""Filter a subunit stream to include/exclude tests. |
38 | + |
39 | +The default is to strip successful tests. |
40 | +""" |
41 | + |
42 | +from optparse import OptionParser |
43 | +import sys |
44 | +import unittest |
45 | + |
46 | +from subunit import ProtocolTestCase, TestResultFilter, TestProtocolClient |
47 | + |
48 | +parser = OptionParser(description=__doc__) |
49 | +parser.add_option("--error", action="store_false", |
50 | + help="include errors", default=False, dest="error") |
51 | +parser.add_option("-e", "--no-error", action="store_true", |
52 | + help="exclude errors", dest="error") |
53 | +parser.add_option("--failure", action="store_false", |
54 | + help="include failures", default=False, dest="failure") |
55 | +parser.add_option("-f", "--no-failure", action="store_true", |
56 | + help="include failures", dest="failure") |
57 | +parser.add_option("-s", "--success", action="store_false", |
58 | + help="include successes", dest="success") |
59 | +parser.add_option("--no-success", action="store_true", |
60 | + help="exclude successes", default=True, dest="success") |
61 | +(options, args) = parser.parse_args() |
62 | +result = TestProtocolClient(sys.stdout) |
63 | +result = TestResultFilter(result, filter_error=options.error, filter_failure=options.failure, |
64 | + filter_success=options.success) |
65 | +test = ProtocolTestCase(sys.stdin) |
66 | +test.run(result) |
67 | +sys.exit(0) |
68 | |
69 | === added file 'filters/subunit-ls' |
70 | --- filters/subunit-ls 1970-01-01 00:00:00 +0000 |
71 | +++ filters/subunit-ls 2009-02-23 10:54:28 +0000 |
72 | @@ -0,0 +1,62 @@ |
73 | +#!/usr/bin/env python |
74 | +# subunit: extensions to python unittest to get test results from subprocesses. |
75 | +# Copyright (C) 2008 Robert Collins <robertc@robertcollins.net> |
76 | +# |
77 | +# This program is free software; you can redistribute it and/or modify |
78 | +# it under the terms of the GNU General Public License as published by |
79 | +# the Free Software Foundation; either version 2 of the License, or |
80 | +# (at your option) any later version. |
81 | +# |
82 | +# This program is distributed in the hope that it will be useful, |
83 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
84 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
85 | +# GNU General Public License for more details. |
86 | +# |
87 | +# You should have received a copy of the GNU General Public License |
88 | +# along with this program; if not, write to the Free Software |
89 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
90 | +# |
91 | + |
92 | +"""List tests in a subunit stream.""" |
93 | + |
94 | +import sys |
95 | +import unittest |
96 | + |
97 | +from subunit import ProtocolTestCase |
98 | + |
99 | +class FilterResult(unittest.TestResult): |
100 | + """Filter test objects for display.""" |
101 | + |
102 | + def __init__(self, stream): |
103 | + """Create a FilterResult object outputting to stream.""" |
104 | + unittest.TestResult.__init__(self) |
105 | + self._stream = stream |
106 | + self.failed_tests = 0 |
107 | + |
108 | + def addError(self, test, err): |
109 | + self.failed_tests += 1 |
110 | + self.reportTest(test) |
111 | + |
112 | + def addFailure(self, test, err): |
113 | + self.failed_tests += 1 |
114 | + self.reportTest(test) |
115 | + |
116 | + def addSuccess(self, test): |
117 | + self.reportTest(test) |
118 | + |
119 | + def reportTest(self, test): |
120 | + self._stream.write(test.id() + '\n') |
121 | + |
122 | + def wasSuccessful(self): |
123 | + "Tells whether or not this result was a success" |
124 | + return self.failed_tests == 0 |
125 | + |
126 | + |
127 | +result = FilterResult(sys.stdout) |
128 | +test = ProtocolTestCase(sys.stdin) |
129 | +test.run(result) |
130 | +if result.wasSuccessful(): |
131 | + exit_code = 0 |
132 | +else: |
133 | + exit_code = 1 |
134 | +sys.exit(exit_code) |
135 | |
136 | === modified file 'python/subunit/__init__.py' |
137 | --- python/subunit/__init__.py 2009-02-15 11:55:00 +0000 |
138 | +++ python/subunit/__init__.py 2009-02-23 10:54:28 +0000 |
139 | @@ -363,7 +363,7 @@ |
140 | return self.__description |
141 | |
142 | def id(self): |
143 | - return "%s.%s" % (self._strclass(), self.__description) |
144 | + return "%s" % (self.__description,) |
145 | |
146 | def __str__(self): |
147 | return "%s (%s)" % (self.__description, self._strclass()) |
148 | @@ -681,3 +681,52 @@ |
149 | def wasSuccessful(self): |
150 | """Tells whether or not this result was a success""" |
151 | return self.failed_tests == 0 |
152 | + |
153 | + |
154 | +class TestResultFilter(unittest.TestResult): |
155 | + """A pyunit TestResult interface implementation which filters tests. |
156 | + |
157 | + Tests that pass the filter are handed on to another TestResult instance |
158 | + for further processing/reporting. To obtain the filtered results, |
159 | + the other instance must be interrogated. |
160 | + |
161 | + :ivar result: The result that tests are passed to after filtering. |
162 | + """ |
163 | + |
164 | + def __init__(self, result, filter_error=False, filter_failure=False, |
165 | + filter_success=True): |
166 | + """Create a FilterResult object filtering to result. |
167 | + |
168 | + :param filter_error: Filter out errors. |
169 | + :param filter_failure: Filter out failures. |
170 | + :param filter_success: Filter out successful tests. |
171 | + """ |
172 | + unittest.TestResult.__init__(self) |
173 | + self.result = result |
174 | + self._filter_error = filter_error |
175 | + self._filter_failure = filter_failure |
176 | + self._filter_success = filter_success |
177 | + |
178 | + def addError(self, test, err): |
179 | + if not self._filter_error: |
180 | + self.result.startTest(test) |
181 | + self.result.addError(test, err) |
182 | + self.result.stopTest(test) |
183 | + |
184 | + def addFailure(self, test, err): |
185 | + if not self._filter_failure: |
186 | + self.result.startTest(test) |
187 | + self.result.addFailure(test, err) |
188 | + self.result.stopTest(test) |
189 | + |
190 | + def addSuccess(self, test): |
191 | + if not self._filter_success: |
192 | + self.result.startTest(test) |
193 | + self.result.addSuccess(test) |
194 | + self.result.stopTest(test) |
195 | + |
196 | + def id_to_orig_id(self, id): |
197 | + if id.startswith("subunit.RemotedTestCase."): |
198 | + return id[len("subunit.RemotedTestCase."):] |
199 | + return id |
200 | + |
201 | |
202 | === modified file 'python/subunit/tests/__init__.py' |
203 | --- python/subunit/tests/__init__.py 2008-12-09 01:00:03 +0000 |
204 | +++ python/subunit/tests/__init__.py 2009-02-22 06:28:08 +0000 |
205 | @@ -19,6 +19,7 @@ |
206 | |
207 | from subunit.tests import ( |
208 | TestUtil, |
209 | + test_subunit_filter, |
210 | test_subunit_stats, |
211 | test_subunit_tags, |
212 | test_tap2subunit, |
213 | @@ -29,6 +30,7 @@ |
214 | result = TestUtil.TestSuite() |
215 | result.addTest(test_test_protocol.test_suite()) |
216 | result.addTest(test_tap2subunit.test_suite()) |
217 | + result.addTest(test_subunit_filter.test_suite()) |
218 | result.addTest(test_subunit_tags.test_suite()) |
219 | result.addTest(test_subunit_stats.test_suite()) |
220 | return result |
221 | |
222 | === added file 'python/subunit/tests/test_subunit_filter.py' |
223 | --- python/subunit/tests/test_subunit_filter.py 1970-01-01 00:00:00 +0000 |
224 | +++ python/subunit/tests/test_subunit_filter.py 2009-02-23 10:54:28 +0000 |
225 | @@ -0,0 +1,115 @@ |
226 | +# |
227 | +# subunit: extensions to python unittest to get test results from subprocesses. |
228 | +# Copyright (C) 2005 Robert Collins <robertc@robertcollins.net> |
229 | +# |
230 | +# This program is free software; you can redistribute it and/or modify |
231 | +# it under the terms of the GNU General Public License as published by |
232 | +# the Free Software Foundation; either version 2 of the License, or |
233 | +# (at your option) any later version. |
234 | +# |
235 | +# This program is distributed in the hope that it will be useful, |
236 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
237 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
238 | +# GNU General Public License for more details. |
239 | +# |
240 | +# You should have received a copy of the GNU General Public License |
241 | +# along with this program; if not, write to the Free Software |
242 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
243 | +# |
244 | + |
245 | +"""Tests for subunit.TestResultFilter.""" |
246 | + |
247 | +import unittest |
248 | +from StringIO import StringIO |
249 | + |
250 | +import subunit |
251 | + |
252 | + |
253 | +class TestTestResultFilter(unittest.TestCase): |
254 | + """Test for TestResultFilter, a TestResult object which filters tests.""" |
255 | + |
256 | + def _setUp(self): |
257 | + self.output = StringIO() |
258 | + |
259 | + def test_default(self): |
260 | + """The default is to exclude success and include everything else.""" |
261 | + self.filtered_result = unittest.TestResult() |
262 | + self.filter = subunit.TestResultFilter(self.filtered_result) |
263 | + self.run_tests() |
264 | + self.assertEqual(['error'], |
265 | + [error[0].id() for error in self.filtered_result.errors]) |
266 | + self.assertEqual(['failed'], |
267 | + [failure[0].id() for failure in |
268 | + self.filtered_result.failures]) |
269 | + self.assertEqual(2, self.filtered_result.testsRun) |
270 | + |
271 | + def test_exclude_errors(self): |
272 | + self.filtered_result = unittest.TestResult() |
273 | + self.filter = subunit.TestResultFilter(self.filtered_result, |
274 | + filter_error=True) |
275 | + self.run_tests() |
276 | + self.assertEqual([], |
277 | + [error[0].id() for error in self.filtered_result.errors]) |
278 | + self.assertEqual(['failed'], |
279 | + [failure[0].id() for failure in |
280 | + self.filtered_result.failures]) |
281 | + self.assertEqual(1, self.filtered_result.testsRun) |
282 | + |
283 | + def test_exclude_failure(self): |
284 | + self.filtered_result = unittest.TestResult() |
285 | + self.filter = subunit.TestResultFilter(self.filtered_result, |
286 | + filter_failure=True) |
287 | + self.run_tests() |
288 | + self.assertEqual(['error'], |
289 | + [error[0].id() for error in self.filtered_result.errors]) |
290 | + self.assertEqual([], |
291 | + [failure[0].id() for failure in |
292 | + self.filtered_result.failures]) |
293 | + self.assertEqual(1, self.filtered_result.testsRun) |
294 | + |
295 | + def test_include_success(self): |
296 | + """Success's can be included if requested.""" |
297 | + self.filtered_result = unittest.TestResult() |
298 | + self.filter = subunit.TestResultFilter(self.filtered_result, |
299 | + filter_success=False) |
300 | + self.run_tests() |
301 | + self.assertEqual(['error'], |
302 | + [error[0].id() for error in self.filtered_result.errors]) |
303 | + self.assertEqual(['failed'], |
304 | + [failure[0].id() for failure in |
305 | + self.filtered_result.failures]) |
306 | + self.assertEqual(5, self.filtered_result.testsRun) |
307 | + |
308 | + |
309 | + def run_tests(self): |
310 | + self.setUpTestStream() |
311 | + self.test = subunit.ProtocolTestCase(self.input_stream) |
312 | + self.test.run(self.filter) |
313 | + |
314 | + def setUpTestStream(self): |
315 | + # While TestResultFilter works on python objects, using a subunit |
316 | + # stream is an easy pithy way of getting a series of test objects to |
317 | + # call into the TestResult, and as TestResultFilter is intended for use |
318 | + # with subunit also has the benefit of detecting any interface skew issues. |
319 | + self.input_stream = StringIO() |
320 | + self.input_stream.write("""tags: global |
321 | +test passed |
322 | +success passed |
323 | +test failed |
324 | +tags: local |
325 | +failure failed |
326 | +test error |
327 | +error error |
328 | +test skipped |
329 | +skip skipped |
330 | +test todo |
331 | +xfail todo |
332 | +""") |
333 | + self.input_stream.seek(0) |
334 | + |
335 | + |
336 | + |
337 | +def test_suite(): |
338 | + loader = subunit.tests.TestUtil.TestLoader() |
339 | + result = loader.loadTestsFromName(__name__) |
340 | + return result |
341 | |
342 | === modified file 'python/subunit/tests/test_test_protocol.py' |
343 | --- python/subunit/tests/test_test_protocol.py 2008-12-14 18:37:08 +0000 |
344 | +++ python/subunit/tests/test_test_protocol.py 2009-02-23 10:54:28 +0000 |
345 | @@ -772,7 +772,7 @@ |
346 | self.assertRaises(NotImplementedError, test.tearDown) |
347 | self.assertEqual("A test description", |
348 | test.shortDescription()) |
349 | - self.assertEqual("subunit.RemotedTestCase.A test description", |
350 | + self.assertEqual("A test description", |
351 | test.id()) |
352 | self.assertEqual("A test description (subunit.RemotedTestCase)", "%s" % test) |
353 | self.assertEqual("<subunit.RemotedTestCase description=" |
Add filters for listing a stream and selecting only pass|failed|errored tests.