Merge lp:~lifeless/subunit/filter into lp:~subunit/subunit/trunk

Proposed by Robert Collins
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
Reviewer Review Type Date Requested Status
John A Meinel Pending
Subunit Developers Pending
Review via email: mp+16119@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

This makes subunit2junitxml easier to use for windows folk.

Revision history for this message
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 | \
    subunit2junitxml --forward -o test.xml | subunit2pyunit

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://enigmail.mozdev.org/

iEYEARECAAYFAksmt00ACgkQJdeBCYSNAAOyvQCfY1hPFTkJEp4Pn2KrIcEw+Xjm
PosAn3xROvemoB/di+UJ30XMpQS9I4YV
=jRvX
-----END PGP SIGNATURE-----

Revision history for this message
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'NEWS'
--- NEWS 2009-12-07 05:16:28 +0000
+++ NEWS 2009-12-14 11:03:15 +0000
@@ -19,6 +19,11 @@
19 * INSTALLDIRS can be set to control the perl MakeMaker 'INSTALLDIRS'19 * INSTALLDIRS can be set to control the perl MakeMaker 'INSTALLDIRS'
20 viarable when installing.20 viarable when installing.
2121
22 * subunit2junitxml supports a new option, --forward which causes it
23 to forward the raw subunit stream in a similar manner to tee. This
24 is used with the -o option to both write a xml report and get some
25 other subunit filter to process the stream.
26
22 * The C library now has ``subunit_test_skip``.27 * The C library now has ``subunit_test_skip``.
2328
24 BUG FIXES:29 BUG FIXES:
2530
=== modified file 'filters/subunit2junitxml'
--- filters/subunit2junitxml 2009-09-30 12:04:18 +0000
+++ filters/subunit2junitxml 2009-12-14 11:03:15 +0000
@@ -33,6 +33,7 @@
33 help="Hide all non subunit input.", default=False, dest="no_passthrough")33 help="Hide all non subunit input.", default=False, dest="no_passthrough")
34parser.add_option("-o", "--output-to",34parser.add_option("-o", "--output-to",
35 help="Output the XML to this path rather than stdout.")35 help="Output the XML to this path rather than stdout.")
36parser.add_option("-f", "--forward", help="Forward subunit stream on stdout.")
36(options, args) = parser.parse_args()37(options, args) = parser.parse_args()
37if options.output_to is None:38if options.output_to is None:
38 output_to = sys.stdout39 output_to = sys.stdout
@@ -44,7 +45,12 @@
44 passthrough_stream = DiscardStream()45 passthrough_stream = DiscardStream()
45 else:46 else:
46 passthrough_stream = None47 passthrough_stream = None
47 test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream)48 if options.forward:
49 forward_stream = sys.stdout
50 else:
51 forward_stream = None
52 test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream,
53 forward=forward_stream)
48 result.startTestRun()54 result.startTestRun()
49 test.run(result)55 test.run(result)
50 result.stopTestRun()56 result.stopTestRun()
5157
=== modified file 'python/subunit/__init__.py'
--- python/subunit/__init__.py 2009-10-10 01:17:06 +0000
+++ python/subunit/__init__.py 2009-12-14 11:03:15 +0000
@@ -169,7 +169,7 @@
169 READING_XFAIL = 5169 READING_XFAIL = 5
170 READING_SUCCESS = 6170 READING_SUCCESS = 6
171171
172 def __init__(self, client, stream=None):172 def __init__(self, client, stream=None, forward_stream=None):
173 """Create a TestProtocolServer instance.173 """Create a TestProtocolServer instance.
174174
175 :param client: An object meeting the unittest.TestResult protocol.175 :param client: An object meeting the unittest.TestResult protocol.
@@ -177,12 +177,17 @@
177 subunit protocol should be written to. This allows custom handling177 subunit protocol should be written to. This allows custom handling
178 of mixed protocols. By default, sys.stdout will be used for178 of mixed protocols. By default, sys.stdout will be used for
179 convenience.179 convenience.
180 :param forward_stream: A stream to forward subunit lines to. This
181 allows a filter to forward the entire stream while still parsing
182 and acting on it. By default forward_stream is set to
183 DiscardStream() and no forwarding happens.
180 """184 """
181 self.state = TestProtocolServer.OUTSIDE_TEST185 self.state = TestProtocolServer.OUTSIDE_TEST
182 self.client = client186 self.client = client
183 if stream is None:187 if stream is None:
184 stream = sys.stdout188 stream = sys.stdout
185 self._stream = stream189 self._stream = stream
190 self._forward_stream = forward_stream or DiscardStream()
186191
187 def _addError(self, offset, line):192 def _addError(self, offset, line):
188 if (self.state == TestProtocolServer.TEST_STARTED and193 if (self.state == TestProtocolServer.TEST_STARTED and
@@ -192,10 +197,12 @@
192 self.client.addError(self._current_test, RemoteError(""))197 self.client.addError(self._current_test, RemoteError(""))
193 self.client.stopTest(self._current_test)198 self.client.stopTest(self._current_test)
194 self._current_test = None199 self._current_test = None
200 self.subunitLineReceived(line)
195 elif (self.state == TestProtocolServer.TEST_STARTED and201 elif (self.state == TestProtocolServer.TEST_STARTED and
196 self.current_test_description + " [" == line[offset:-1]):202 self.current_test_description + " [" == line[offset:-1]):
197 self.state = TestProtocolServer.READING_ERROR203 self.state = TestProtocolServer.READING_ERROR
198 self._message = ""204 self._message = ""
205 self.subunitLineReceived(line)
199 else:206 else:
200 self.stdOutLineReceived(line)207 self.stdOutLineReceived(line)
201208
@@ -210,10 +217,12 @@
210 else:217 else:
211 self.client.addSuccess(self._current_test)218 self.client.addSuccess(self._current_test)
212 self.client.stopTest(self._current_test)219 self.client.stopTest(self._current_test)
220 self.subunitLineReceived(line)
213 elif (self.state == TestProtocolServer.TEST_STARTED and221 elif (self.state == TestProtocolServer.TEST_STARTED and
214 self.current_test_description + " [" == line[offset:-1]):222 self.current_test_description + " [" == line[offset:-1]):
215 self.state = TestProtocolServer.READING_XFAIL223 self.state = TestProtocolServer.READING_XFAIL
216 self._message = ""224 self._message = ""
225 self.subunitLineReceived(line)
217 else:226 else:
218 self.stdOutLineReceived(line)227 self.stdOutLineReceived(line)
219228
@@ -224,10 +233,12 @@
224 self.current_test_description = None233 self.current_test_description = None
225 self.client.addFailure(self._current_test, RemoteError())234 self.client.addFailure(self._current_test, RemoteError())
226 self.client.stopTest(self._current_test)235 self.client.stopTest(self._current_test)
236 self.subunitLineReceived(line)
227 elif (self.state == TestProtocolServer.TEST_STARTED and237 elif (self.state == TestProtocolServer.TEST_STARTED and
228 self.current_test_description + " [" == line[offset:-1]):238 self.current_test_description + " [" == line[offset:-1]):
229 self.state = TestProtocolServer.READING_FAILURE239 self.state = TestProtocolServer.READING_FAILURE
230 self._message = ""240 self._message = ""
241 self.subunitLineReceived(line)
231 else:242 else:
232 self.stdOutLineReceived(line)243 self.stdOutLineReceived(line)
233244
@@ -238,10 +249,12 @@
238 self.current_test_description = None249 self.current_test_description = None
239 self._skip_or_error()250 self._skip_or_error()
240 self.client.stopTest(self._current_test)251 self.client.stopTest(self._current_test)
252 self.subunitLineReceived(line)
241 elif (self.state == TestProtocolServer.TEST_STARTED and253 elif (self.state == TestProtocolServer.TEST_STARTED and
242 self.current_test_description + " [" == line[offset:-1]):254 self.current_test_description + " [" == line[offset:-1]):
243 self.state = TestProtocolServer.READING_SKIP255 self.state = TestProtocolServer.READING_SKIP
244 self._message = ""256 self._message = ""
257 self.subunitLineReceived(line)
245 else:258 else:
246 self.stdOutLineReceived(line)259 self.stdOutLineReceived(line)
247260
@@ -259,10 +272,12 @@
259 if (self.state == TestProtocolServer.TEST_STARTED and272 if (self.state == TestProtocolServer.TEST_STARTED and
260 self.current_test_description == line[offset:-1]):273 self.current_test_description == line[offset:-1]):
261 self._succeedTest()274 self._succeedTest()
275 self.subunitLineReceived(line)
262 elif (self.state == TestProtocolServer.TEST_STARTED and276 elif (self.state == TestProtocolServer.TEST_STARTED and
263 self.current_test_description + " [" == line[offset:-1]):277 self.current_test_description + " [" == line[offset:-1]):
264 self.state = TestProtocolServer.READING_SUCCESS278 self.state = TestProtocolServer.READING_SUCCESS
265 self._message = ""279 self._message = ""
280 self.subunitLineReceived(line)
266 else:281 else:
267 self.stdOutLineReceived(line)282 self.stdOutLineReceived(line)
268283
@@ -274,6 +289,7 @@
274 self._message += line289 self._message += line
275290
276 def endQuote(self, line):291 def endQuote(self, line):
292 stdout = False
277 if self.state == TestProtocolServer.READING_FAILURE:293 if self.state == TestProtocolServer.READING_FAILURE:
278 self.state = TestProtocolServer.OUTSIDE_TEST294 self.state = TestProtocolServer.OUTSIDE_TEST
279 self.current_test_description = None295 self.current_test_description = None
@@ -304,6 +320,9 @@
304 self._succeedTest()320 self._succeedTest()
305 else:321 else:
306 self.stdOutLineReceived(line)322 self.stdOutLineReceived(line)
323 stdout = True
324 if not stdout:
325 self.subunitLineReceived(line)
307326
308 def _handleProgress(self, offset, line):327 def _handleProgress(self, offset, line):
309 """Process a progress directive."""328 """Process a progress directive."""
@@ -352,8 +371,10 @@
352 TestProtocolServer.READING_XFAIL371 TestProtocolServer.READING_XFAIL
353 ):372 ):
354 self._appendMessage(line)373 self._appendMessage(line)
374 self.subunitLineReceived(line)
355 else:375 else:
356 parts = line.split(None, 1)376 parts = line.split(None, 1)
377 stdout = False
357 if len(parts) == 2:378 if len(parts) == 2:
358 cmd, rest = parts379 cmd, rest = parts
359 offset = len(cmd) + 1380 offset = len(cmd) + 1
@@ -366,14 +387,17 @@
366 self._addFailure(offset, line)387 self._addFailure(offset, line)
367 elif cmd == 'progress':388 elif cmd == 'progress':
368 self._handleProgress(offset, line)389 self._handleProgress(offset, line)
390 self.subunitLineReceived(line)
369 elif cmd == 'skip':391 elif cmd == 'skip':
370 self._addSkip(offset, line)392 self._addSkip(offset, line)
371 elif cmd in ('success', 'successful'):393 elif cmd in ('success', 'successful'):
372 self._addSuccess(offset, line)394 self._addSuccess(offset, line)
373 elif cmd in ('tags',):395 elif cmd in ('tags',):
374 self._handleTags(offset, line)396 self._handleTags(offset, line)
397 self.subunitLineReceived(line)
375 elif cmd in ('time',):398 elif cmd in ('time',):
376 self._handleTime(offset, line)399 self._handleTime(offset, line)
400 self.subunitLineReceived(line)
377 elif cmd == 'xfail':401 elif cmd == 'xfail':
378 self._addExpectedFail(offset, line)402 self._addExpectedFail(offset, line)
379 else:403 else:
@@ -418,9 +442,13 @@
418 self._current_test = RemotedTestCase(line[offset:-1])442 self._current_test = RemotedTestCase(line[offset:-1])
419 self.current_test_description = line[offset:-1]443 self.current_test_description = line[offset:-1]
420 self.client.startTest(self._current_test)444 self.client.startTest(self._current_test)
445 self.subunitLineReceived(line)
421 else:446 else:
422 self.stdOutLineReceived(line)447 self.stdOutLineReceived(line)
423448
449 def subunitLineReceived(self, line):
450 self._forward_stream.write(line)
451
424 def stdOutLineReceived(self, line):452 def stdOutLineReceived(self, line):
425 self._stream.write(line)453 self._stream.write(line)
426454
@@ -860,16 +888,19 @@
860 :seealso: TestProtocolServer (the subunit wire protocol parser).888 :seealso: TestProtocolServer (the subunit wire protocol parser).
861 """889 """
862890
863 def __init__(self, stream, passthrough=None):891 def __init__(self, stream, passthrough=None, forward=False):
864 """Create a ProtocolTestCase reading from stream.892 """Create a ProtocolTestCase reading from stream.
865893
866 :param stream: A filelike object which a subunit stream can be read894 :param stream: A filelike object which a subunit stream can be read
867 from.895 from.
868 :param passthrough: A stream pass non subunit input on to. If not896 :param passthrough: A stream pass non subunit input on to. If not
869 supplied, the TestProtocolServer default is used.897 supplied, the TestProtocolServer default is used.
898 :param forward: A stream to pass subunit input on to. If not supplied
899 subunit input is not forwarded.
870 """900 """
871 self._stream = stream901 self._stream = stream
872 self._passthrough = passthrough902 self._passthrough = passthrough
903 self._forward = forward
873904
874 def __call__(self, result=None):905 def __call__(self, result=None):
875 return self.run(result)906 return self.run(result)
@@ -877,7 +908,7 @@
877 def run(self, result=None):908 def run(self, result=None):
878 if result is None:909 if result is None:
879 result = self.defaultTestResult()910 result = self.defaultTestResult()
880 protocol = TestProtocolServer(result, self._passthrough)911 protocol = TestProtocolServer(result, self._passthrough, self._forward)
881 line = self._stream.readline()912 line = self._stream.readline()
882 while line:913 while line:
883 protocol.lineReceived(line)914 protocol.lineReceived(line)
884915
=== modified file 'python/subunit/tests/test_test_protocol.py'
--- python/subunit/tests/test_test_protocol.py 2009-09-30 12:04:18 +0000
+++ python/subunit/tests/test_test_protocol.py 2009-12-14 11:03:15 +0000
@@ -153,6 +153,7 @@
153 from subunit import ExecTestCase153 from subunit import ExecTestCase
154 from subunit import IsolatedTestCase154 from subunit import IsolatedTestCase
155 from subunit import TestProtocolClient155 from subunit import TestProtocolClient
156 from subunit import ProtocolTestCase
156157
157158
158class TestDiscardStream(unittest.TestCase):159class TestDiscardStream(unittest.TestCase):
@@ -161,6 +162,30 @@
161 subunit.DiscardStream().write("content")162 subunit.DiscardStream().write("content")
162163
163164
165class TestProtocolServerForward(unittest.TestCase):
166
167 def test_story(self):
168 client = unittest.TestResult()
169 out = StringIO()
170 protocol = subunit.TestProtocolServer(client, forward_stream=out)
171 pipe = StringIO("test old mcdonald\n"
172 "success old mcdonald\n")
173 protocol.readFrom(pipe)
174 mcdonald = subunit.RemotedTestCase("old mcdonald")
175 self.assertEqual(client.testsRun, 1)
176 self.assertEqual(pipe.getvalue(), out.getvalue())
177
178 def test_not_command(self):
179 client = unittest.TestResult()
180 out = StringIO()
181 protocol = subunit.TestProtocolServer(client,
182 stream=subunit.DiscardStream(), forward_stream=out)
183 pipe = StringIO("success old mcdonald\n")
184 protocol.readFrom(pipe)
185 self.assertEqual(client.testsRun, 0)
186 self.assertEqual("", out.getvalue())
187
188
164class TestTestProtocolServerPipe(unittest.TestCase):189class TestTestProtocolServerPipe(unittest.TestCase):
165190
166 def test_story(self):191 def test_story(self):

Subscribers

People subscribed via source and target branches