Merge lp:~lifeless/subunit/filter into lp:~subunit/subunit/trunk
- filter
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~lifeless/subunit/filter |
Merge into: | lp:~subunit/subunit/trunk |
Diff against target: |
271 lines (+71/-4) 4 files modified
NEWS (+5/-0) filters/subunit2junitxml (+7/-1) python/subunit/__init__.py (+34/-3) python/subunit/tests/test_test_protocol.py (+25/-0) |
To merge this branch: | bzr merge lp:~lifeless/subunit/filter |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
John A Meinel | Pending | ||
Subunit Developers | Pending | ||
Review via email: mp+16119@code.launchpad.net |
Commit message
Description of the change
Robert Collins (lifeless) wrote : | # |
John A Meinel (jameinel) wrote : | # |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Robert Collins wrote:
> You have been requested to review the proposed merge of lp:~lifeless/subunit/filter into lp:subunit.
>
> This makes subunit2junitxml easier to use for windows folk.
>
>
This does look like it would do what we mentioned:
bzr selftest --subunit | subunit2junitxml -o XXX --forward | subunit2pyunit
However, it doesn't really replace 'tee', in that I don't end up with a
test file on disk that can be used by a 3rd party to directly parse and
play with
bzr selftest --subunit | tee test.subunit | subunit2pyunit
subunit2junitxml test.subunit -o test.xml
Though I could do:
bzr selftest --subunit | tee test.subunit | \
subunit2jun
Which works around the 'start time' bug.
So I do think this is useful, I'm not sure if it really belongs in
subunit. If you would like to have it, then the code all looks good.
John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://
iEYEARECAAYFAks
PosAn3xROvemoB/
=jRvX
-----END PGP SIGNATURE-----
Robert Collins (lifeless) wrote : | # |
On Mon, 2009-12-14 at 22:09 +0000, John A Meinel wrote:
>
> This does look like it would do what we mentioned:
>
> bzr selftest --subunit | subunit2junitxml -o XXX --forward |
> subunit2pyunit
>
> However, it doesn't really replace 'tee', in that I don't end up with
> a
> test file on disk that can be used by a 3rd party to directly parse
> and
> play with
Well, it acts like tee in that you get the original output too ;)
Note that you can tee before or after:
bzr selftest --subunit | subunit2junitxml -o test.xml --forward | tee
test.log | subunit2pyunit
Do you have an actual tee in your test environment?
> bzr selftest --subunit | tee test.subunit | subunit2pyunit
> subunit2junitxml test.subunit -o test.xml
>
> Though I could do:
> bzr selftest --subunit | tee test.subunit | \
> subunit2junitxml --forward -o test.xml | subunit2pyunit
>
> Which works around the 'start time' bug.
Thats fixed in junitxml 0.5, on pypi now.
> So I do think this is useful, I'm not sure if it really belongs in
> subunit. If you would like to have it, then the code all looks good.
Great, landing it now.
Thanks,
Rob
Preview Diff
1 | === modified file 'NEWS' | |||
2 | --- NEWS 2009-12-07 05:16:28 +0000 | |||
3 | +++ NEWS 2009-12-14 11:03:15 +0000 | |||
4 | @@ -19,6 +19,11 @@ | |||
5 | 19 | * INSTALLDIRS can be set to control the perl MakeMaker 'INSTALLDIRS' | 19 | * INSTALLDIRS can be set to control the perl MakeMaker 'INSTALLDIRS' |
6 | 20 | viarable when installing. | 20 | viarable when installing. |
7 | 21 | 21 | ||
8 | 22 | * subunit2junitxml supports a new option, --forward which causes it | ||
9 | 23 | to forward the raw subunit stream in a similar manner to tee. This | ||
10 | 24 | is used with the -o option to both write a xml report and get some | ||
11 | 25 | other subunit filter to process the stream. | ||
12 | 26 | |||
13 | 22 | * The C library now has ``subunit_test_skip``. | 27 | * The C library now has ``subunit_test_skip``. |
14 | 23 | 28 | ||
15 | 24 | BUG FIXES: | 29 | BUG FIXES: |
16 | 25 | 30 | ||
17 | === modified file 'filters/subunit2junitxml' | |||
18 | --- filters/subunit2junitxml 2009-09-30 12:04:18 +0000 | |||
19 | +++ filters/subunit2junitxml 2009-12-14 11:03:15 +0000 | |||
20 | @@ -33,6 +33,7 @@ | |||
21 | 33 | help="Hide all non subunit input.", default=False, dest="no_passthrough") | 33 | help="Hide all non subunit input.", default=False, dest="no_passthrough") |
22 | 34 | parser.add_option("-o", "--output-to", | 34 | parser.add_option("-o", "--output-to", |
23 | 35 | help="Output the XML to this path rather than stdout.") | 35 | help="Output the XML to this path rather than stdout.") |
24 | 36 | parser.add_option("-f", "--forward", help="Forward subunit stream on stdout.") | ||
25 | 36 | (options, args) = parser.parse_args() | 37 | (options, args) = parser.parse_args() |
26 | 37 | if options.output_to is None: | 38 | if options.output_to is None: |
27 | 38 | output_to = sys.stdout | 39 | output_to = sys.stdout |
28 | @@ -44,7 +45,12 @@ | |||
29 | 44 | passthrough_stream = DiscardStream() | 45 | passthrough_stream = DiscardStream() |
30 | 45 | else: | 46 | else: |
31 | 46 | passthrough_stream = None | 47 | passthrough_stream = None |
33 | 47 | test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream) | 48 | if options.forward: |
34 | 49 | forward_stream = sys.stdout | ||
35 | 50 | else: | ||
36 | 51 | forward_stream = None | ||
37 | 52 | test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream, | ||
38 | 53 | forward=forward_stream) | ||
39 | 48 | result.startTestRun() | 54 | result.startTestRun() |
40 | 49 | test.run(result) | 55 | test.run(result) |
41 | 50 | result.stopTestRun() | 56 | result.stopTestRun() |
42 | 51 | 57 | ||
43 | === modified file 'python/subunit/__init__.py' | |||
44 | --- python/subunit/__init__.py 2009-10-10 01:17:06 +0000 | |||
45 | +++ python/subunit/__init__.py 2009-12-14 11:03:15 +0000 | |||
46 | @@ -169,7 +169,7 @@ | |||
47 | 169 | READING_XFAIL = 5 | 169 | READING_XFAIL = 5 |
48 | 170 | READING_SUCCESS = 6 | 170 | READING_SUCCESS = 6 |
49 | 171 | 171 | ||
51 | 172 | def __init__(self, client, stream=None): | 172 | def __init__(self, client, stream=None, forward_stream=None): |
52 | 173 | """Create a TestProtocolServer instance. | 173 | """Create a TestProtocolServer instance. |
53 | 174 | 174 | ||
54 | 175 | :param client: An object meeting the unittest.TestResult protocol. | 175 | :param client: An object meeting the unittest.TestResult protocol. |
55 | @@ -177,12 +177,17 @@ | |||
56 | 177 | subunit protocol should be written to. This allows custom handling | 177 | subunit protocol should be written to. This allows custom handling |
57 | 178 | of mixed protocols. By default, sys.stdout will be used for | 178 | of mixed protocols. By default, sys.stdout will be used for |
58 | 179 | convenience. | 179 | convenience. |
59 | 180 | :param forward_stream: A stream to forward subunit lines to. This | ||
60 | 181 | allows a filter to forward the entire stream while still parsing | ||
61 | 182 | and acting on it. By default forward_stream is set to | ||
62 | 183 | DiscardStream() and no forwarding happens. | ||
63 | 180 | """ | 184 | """ |
64 | 181 | self.state = TestProtocolServer.OUTSIDE_TEST | 185 | self.state = TestProtocolServer.OUTSIDE_TEST |
65 | 182 | self.client = client | 186 | self.client = client |
66 | 183 | if stream is None: | 187 | if stream is None: |
67 | 184 | stream = sys.stdout | 188 | stream = sys.stdout |
68 | 185 | self._stream = stream | 189 | self._stream = stream |
69 | 190 | self._forward_stream = forward_stream or DiscardStream() | ||
70 | 186 | 191 | ||
71 | 187 | def _addError(self, offset, line): | 192 | def _addError(self, offset, line): |
72 | 188 | if (self.state == TestProtocolServer.TEST_STARTED and | 193 | if (self.state == TestProtocolServer.TEST_STARTED and |
73 | @@ -192,10 +197,12 @@ | |||
74 | 192 | self.client.addError(self._current_test, RemoteError("")) | 197 | self.client.addError(self._current_test, RemoteError("")) |
75 | 193 | self.client.stopTest(self._current_test) | 198 | self.client.stopTest(self._current_test) |
76 | 194 | self._current_test = None | 199 | self._current_test = None |
77 | 200 | self.subunitLineReceived(line) | ||
78 | 195 | elif (self.state == TestProtocolServer.TEST_STARTED and | 201 | elif (self.state == TestProtocolServer.TEST_STARTED and |
79 | 196 | self.current_test_description + " [" == line[offset:-1]): | 202 | self.current_test_description + " [" == line[offset:-1]): |
80 | 197 | self.state = TestProtocolServer.READING_ERROR | 203 | self.state = TestProtocolServer.READING_ERROR |
81 | 198 | self._message = "" | 204 | self._message = "" |
82 | 205 | self.subunitLineReceived(line) | ||
83 | 199 | else: | 206 | else: |
84 | 200 | self.stdOutLineReceived(line) | 207 | self.stdOutLineReceived(line) |
85 | 201 | 208 | ||
86 | @@ -210,10 +217,12 @@ | |||
87 | 210 | else: | 217 | else: |
88 | 211 | self.client.addSuccess(self._current_test) | 218 | self.client.addSuccess(self._current_test) |
89 | 212 | self.client.stopTest(self._current_test) | 219 | self.client.stopTest(self._current_test) |
90 | 220 | self.subunitLineReceived(line) | ||
91 | 213 | elif (self.state == TestProtocolServer.TEST_STARTED and | 221 | elif (self.state == TestProtocolServer.TEST_STARTED and |
92 | 214 | self.current_test_description + " [" == line[offset:-1]): | 222 | self.current_test_description + " [" == line[offset:-1]): |
93 | 215 | self.state = TestProtocolServer.READING_XFAIL | 223 | self.state = TestProtocolServer.READING_XFAIL |
94 | 216 | self._message = "" | 224 | self._message = "" |
95 | 225 | self.subunitLineReceived(line) | ||
96 | 217 | else: | 226 | else: |
97 | 218 | self.stdOutLineReceived(line) | 227 | self.stdOutLineReceived(line) |
98 | 219 | 228 | ||
99 | @@ -224,10 +233,12 @@ | |||
100 | 224 | self.current_test_description = None | 233 | self.current_test_description = None |
101 | 225 | self.client.addFailure(self._current_test, RemoteError()) | 234 | self.client.addFailure(self._current_test, RemoteError()) |
102 | 226 | self.client.stopTest(self._current_test) | 235 | self.client.stopTest(self._current_test) |
103 | 236 | self.subunitLineReceived(line) | ||
104 | 227 | elif (self.state == TestProtocolServer.TEST_STARTED and | 237 | elif (self.state == TestProtocolServer.TEST_STARTED and |
105 | 228 | self.current_test_description + " [" == line[offset:-1]): | 238 | self.current_test_description + " [" == line[offset:-1]): |
106 | 229 | self.state = TestProtocolServer.READING_FAILURE | 239 | self.state = TestProtocolServer.READING_FAILURE |
107 | 230 | self._message = "" | 240 | self._message = "" |
108 | 241 | self.subunitLineReceived(line) | ||
109 | 231 | else: | 242 | else: |
110 | 232 | self.stdOutLineReceived(line) | 243 | self.stdOutLineReceived(line) |
111 | 233 | 244 | ||
112 | @@ -238,10 +249,12 @@ | |||
113 | 238 | self.current_test_description = None | 249 | self.current_test_description = None |
114 | 239 | self._skip_or_error() | 250 | self._skip_or_error() |
115 | 240 | self.client.stopTest(self._current_test) | 251 | self.client.stopTest(self._current_test) |
116 | 252 | self.subunitLineReceived(line) | ||
117 | 241 | elif (self.state == TestProtocolServer.TEST_STARTED and | 253 | elif (self.state == TestProtocolServer.TEST_STARTED and |
118 | 242 | self.current_test_description + " [" == line[offset:-1]): | 254 | self.current_test_description + " [" == line[offset:-1]): |
119 | 243 | self.state = TestProtocolServer.READING_SKIP | 255 | self.state = TestProtocolServer.READING_SKIP |
120 | 244 | self._message = "" | 256 | self._message = "" |
121 | 257 | self.subunitLineReceived(line) | ||
122 | 245 | else: | 258 | else: |
123 | 246 | self.stdOutLineReceived(line) | 259 | self.stdOutLineReceived(line) |
124 | 247 | 260 | ||
125 | @@ -259,10 +272,12 @@ | |||
126 | 259 | if (self.state == TestProtocolServer.TEST_STARTED and | 272 | if (self.state == TestProtocolServer.TEST_STARTED and |
127 | 260 | self.current_test_description == line[offset:-1]): | 273 | self.current_test_description == line[offset:-1]): |
128 | 261 | self._succeedTest() | 274 | self._succeedTest() |
129 | 275 | self.subunitLineReceived(line) | ||
130 | 262 | elif (self.state == TestProtocolServer.TEST_STARTED and | 276 | elif (self.state == TestProtocolServer.TEST_STARTED and |
131 | 263 | self.current_test_description + " [" == line[offset:-1]): | 277 | self.current_test_description + " [" == line[offset:-1]): |
132 | 264 | self.state = TestProtocolServer.READING_SUCCESS | 278 | self.state = TestProtocolServer.READING_SUCCESS |
133 | 265 | self._message = "" | 279 | self._message = "" |
134 | 280 | self.subunitLineReceived(line) | ||
135 | 266 | else: | 281 | else: |
136 | 267 | self.stdOutLineReceived(line) | 282 | self.stdOutLineReceived(line) |
137 | 268 | 283 | ||
138 | @@ -274,6 +289,7 @@ | |||
139 | 274 | self._message += line | 289 | self._message += line |
140 | 275 | 290 | ||
141 | 276 | def endQuote(self, line): | 291 | def endQuote(self, line): |
142 | 292 | stdout = False | ||
143 | 277 | if self.state == TestProtocolServer.READING_FAILURE: | 293 | if self.state == TestProtocolServer.READING_FAILURE: |
144 | 278 | self.state = TestProtocolServer.OUTSIDE_TEST | 294 | self.state = TestProtocolServer.OUTSIDE_TEST |
145 | 279 | self.current_test_description = None | 295 | self.current_test_description = None |
146 | @@ -304,6 +320,9 @@ | |||
147 | 304 | self._succeedTest() | 320 | self._succeedTest() |
148 | 305 | else: | 321 | else: |
149 | 306 | self.stdOutLineReceived(line) | 322 | self.stdOutLineReceived(line) |
150 | 323 | stdout = True | ||
151 | 324 | if not stdout: | ||
152 | 325 | self.subunitLineReceived(line) | ||
153 | 307 | 326 | ||
154 | 308 | def _handleProgress(self, offset, line): | 327 | def _handleProgress(self, offset, line): |
155 | 309 | """Process a progress directive.""" | 328 | """Process a progress directive.""" |
156 | @@ -352,8 +371,10 @@ | |||
157 | 352 | TestProtocolServer.READING_XFAIL | 371 | TestProtocolServer.READING_XFAIL |
158 | 353 | ): | 372 | ): |
159 | 354 | self._appendMessage(line) | 373 | self._appendMessage(line) |
160 | 374 | self.subunitLineReceived(line) | ||
161 | 355 | else: | 375 | else: |
162 | 356 | parts = line.split(None, 1) | 376 | parts = line.split(None, 1) |
163 | 377 | stdout = False | ||
164 | 357 | if len(parts) == 2: | 378 | if len(parts) == 2: |
165 | 358 | cmd, rest = parts | 379 | cmd, rest = parts |
166 | 359 | offset = len(cmd) + 1 | 380 | offset = len(cmd) + 1 |
167 | @@ -366,14 +387,17 @@ | |||
168 | 366 | self._addFailure(offset, line) | 387 | self._addFailure(offset, line) |
169 | 367 | elif cmd == 'progress': | 388 | elif cmd == 'progress': |
170 | 368 | self._handleProgress(offset, line) | 389 | self._handleProgress(offset, line) |
171 | 390 | self.subunitLineReceived(line) | ||
172 | 369 | elif cmd == 'skip': | 391 | elif cmd == 'skip': |
173 | 370 | self._addSkip(offset, line) | 392 | self._addSkip(offset, line) |
174 | 371 | elif cmd in ('success', 'successful'): | 393 | elif cmd in ('success', 'successful'): |
175 | 372 | self._addSuccess(offset, line) | 394 | self._addSuccess(offset, line) |
176 | 373 | elif cmd in ('tags',): | 395 | elif cmd in ('tags',): |
177 | 374 | self._handleTags(offset, line) | 396 | self._handleTags(offset, line) |
178 | 397 | self.subunitLineReceived(line) | ||
179 | 375 | elif cmd in ('time',): | 398 | elif cmd in ('time',): |
180 | 376 | self._handleTime(offset, line) | 399 | self._handleTime(offset, line) |
181 | 400 | self.subunitLineReceived(line) | ||
182 | 377 | elif cmd == 'xfail': | 401 | elif cmd == 'xfail': |
183 | 378 | self._addExpectedFail(offset, line) | 402 | self._addExpectedFail(offset, line) |
184 | 379 | else: | 403 | else: |
185 | @@ -418,9 +442,13 @@ | |||
186 | 418 | self._current_test = RemotedTestCase(line[offset:-1]) | 442 | self._current_test = RemotedTestCase(line[offset:-1]) |
187 | 419 | self.current_test_description = line[offset:-1] | 443 | self.current_test_description = line[offset:-1] |
188 | 420 | self.client.startTest(self._current_test) | 444 | self.client.startTest(self._current_test) |
189 | 445 | self.subunitLineReceived(line) | ||
190 | 421 | else: | 446 | else: |
191 | 422 | self.stdOutLineReceived(line) | 447 | self.stdOutLineReceived(line) |
192 | 423 | 448 | ||
193 | 449 | def subunitLineReceived(self, line): | ||
194 | 450 | self._forward_stream.write(line) | ||
195 | 451 | |||
196 | 424 | def stdOutLineReceived(self, line): | 452 | def stdOutLineReceived(self, line): |
197 | 425 | self._stream.write(line) | 453 | self._stream.write(line) |
198 | 426 | 454 | ||
199 | @@ -860,16 +888,19 @@ | |||
200 | 860 | :seealso: TestProtocolServer (the subunit wire protocol parser). | 888 | :seealso: TestProtocolServer (the subunit wire protocol parser). |
201 | 861 | """ | 889 | """ |
202 | 862 | 890 | ||
204 | 863 | def __init__(self, stream, passthrough=None): | 891 | def __init__(self, stream, passthrough=None, forward=False): |
205 | 864 | """Create a ProtocolTestCase reading from stream. | 892 | """Create a ProtocolTestCase reading from stream. |
206 | 865 | 893 | ||
207 | 866 | :param stream: A filelike object which a subunit stream can be read | 894 | :param stream: A filelike object which a subunit stream can be read |
208 | 867 | from. | 895 | from. |
209 | 868 | :param passthrough: A stream pass non subunit input on to. If not | 896 | :param passthrough: A stream pass non subunit input on to. If not |
210 | 869 | supplied, the TestProtocolServer default is used. | 897 | supplied, the TestProtocolServer default is used. |
211 | 898 | :param forward: A stream to pass subunit input on to. If not supplied | ||
212 | 899 | subunit input is not forwarded. | ||
213 | 870 | """ | 900 | """ |
214 | 871 | self._stream = stream | 901 | self._stream = stream |
215 | 872 | self._passthrough = passthrough | 902 | self._passthrough = passthrough |
216 | 903 | self._forward = forward | ||
217 | 873 | 904 | ||
218 | 874 | def __call__(self, result=None): | 905 | def __call__(self, result=None): |
219 | 875 | return self.run(result) | 906 | return self.run(result) |
220 | @@ -877,7 +908,7 @@ | |||
221 | 877 | def run(self, result=None): | 908 | def run(self, result=None): |
222 | 878 | if result is None: | 909 | if result is None: |
223 | 879 | result = self.defaultTestResult() | 910 | result = self.defaultTestResult() |
225 | 880 | protocol = TestProtocolServer(result, self._passthrough) | 911 | protocol = TestProtocolServer(result, self._passthrough, self._forward) |
226 | 881 | line = self._stream.readline() | 912 | line = self._stream.readline() |
227 | 882 | while line: | 913 | while line: |
228 | 883 | protocol.lineReceived(line) | 914 | protocol.lineReceived(line) |
229 | 884 | 915 | ||
230 | === modified file 'python/subunit/tests/test_test_protocol.py' | |||
231 | --- python/subunit/tests/test_test_protocol.py 2009-09-30 12:04:18 +0000 | |||
232 | +++ python/subunit/tests/test_test_protocol.py 2009-12-14 11:03:15 +0000 | |||
233 | @@ -153,6 +153,7 @@ | |||
234 | 153 | from subunit import ExecTestCase | 153 | from subunit import ExecTestCase |
235 | 154 | from subunit import IsolatedTestCase | 154 | from subunit import IsolatedTestCase |
236 | 155 | from subunit import TestProtocolClient | 155 | from subunit import TestProtocolClient |
237 | 156 | from subunit import ProtocolTestCase | ||
238 | 156 | 157 | ||
239 | 157 | 158 | ||
240 | 158 | class TestDiscardStream(unittest.TestCase): | 159 | class TestDiscardStream(unittest.TestCase): |
241 | @@ -161,6 +162,30 @@ | |||
242 | 161 | subunit.DiscardStream().write("content") | 162 | subunit.DiscardStream().write("content") |
243 | 162 | 163 | ||
244 | 163 | 164 | ||
245 | 165 | class TestProtocolServerForward(unittest.TestCase): | ||
246 | 166 | |||
247 | 167 | def test_story(self): | ||
248 | 168 | client = unittest.TestResult() | ||
249 | 169 | out = StringIO() | ||
250 | 170 | protocol = subunit.TestProtocolServer(client, forward_stream=out) | ||
251 | 171 | pipe = StringIO("test old mcdonald\n" | ||
252 | 172 | "success old mcdonald\n") | ||
253 | 173 | protocol.readFrom(pipe) | ||
254 | 174 | mcdonald = subunit.RemotedTestCase("old mcdonald") | ||
255 | 175 | self.assertEqual(client.testsRun, 1) | ||
256 | 176 | self.assertEqual(pipe.getvalue(), out.getvalue()) | ||
257 | 177 | |||
258 | 178 | def test_not_command(self): | ||
259 | 179 | client = unittest.TestResult() | ||
260 | 180 | out = StringIO() | ||
261 | 181 | protocol = subunit.TestProtocolServer(client, | ||
262 | 182 | stream=subunit.DiscardStream(), forward_stream=out) | ||
263 | 183 | pipe = StringIO("success old mcdonald\n") | ||
264 | 184 | protocol.readFrom(pipe) | ||
265 | 185 | self.assertEqual(client.testsRun, 0) | ||
266 | 186 | self.assertEqual("", out.getvalue()) | ||
267 | 187 | |||
268 | 188 | |||
269 | 164 | class TestTestProtocolServerPipe(unittest.TestCase): | 189 | class TestTestProtocolServerPipe(unittest.TestCase): |
270 | 165 | 190 | ||
271 | 166 | def test_story(self): | 191 | def test_story(self): |
This makes subunit2junitxml easier to use for windows folk.