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
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 * INSTALLDIRS can be set to control the perl MakeMaker 'INSTALLDIRS'
6 viarable when installing.
7
8+ * subunit2junitxml supports a new option, --forward which causes it
9+ to forward the raw subunit stream in a similar manner to tee. This
10+ is used with the -o option to both write a xml report and get some
11+ other subunit filter to process the stream.
12+
13 * The C library now has ``subunit_test_skip``.
14
15 BUG FIXES:
16
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 help="Hide all non subunit input.", default=False, dest="no_passthrough")
22 parser.add_option("-o", "--output-to",
23 help="Output the XML to this path rather than stdout.")
24+parser.add_option("-f", "--forward", help="Forward subunit stream on stdout.")
25 (options, args) = parser.parse_args()
26 if options.output_to is None:
27 output_to = sys.stdout
28@@ -44,7 +45,12 @@
29 passthrough_stream = DiscardStream()
30 else:
31 passthrough_stream = None
32- test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream)
33+ if options.forward:
34+ forward_stream = sys.stdout
35+ else:
36+ forward_stream = None
37+ test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream,
38+ forward=forward_stream)
39 result.startTestRun()
40 test.run(result)
41 result.stopTestRun()
42
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 READING_XFAIL = 5
48 READING_SUCCESS = 6
49
50- def __init__(self, client, stream=None):
51+ def __init__(self, client, stream=None, forward_stream=None):
52 """Create a TestProtocolServer instance.
53
54 :param client: An object meeting the unittest.TestResult protocol.
55@@ -177,12 +177,17 @@
56 subunit protocol should be written to. This allows custom handling
57 of mixed protocols. By default, sys.stdout will be used for
58 convenience.
59+ :param forward_stream: A stream to forward subunit lines to. This
60+ allows a filter to forward the entire stream while still parsing
61+ and acting on it. By default forward_stream is set to
62+ DiscardStream() and no forwarding happens.
63 """
64 self.state = TestProtocolServer.OUTSIDE_TEST
65 self.client = client
66 if stream is None:
67 stream = sys.stdout
68 self._stream = stream
69+ self._forward_stream = forward_stream or DiscardStream()
70
71 def _addError(self, offset, line):
72 if (self.state == TestProtocolServer.TEST_STARTED and
73@@ -192,10 +197,12 @@
74 self.client.addError(self._current_test, RemoteError(""))
75 self.client.stopTest(self._current_test)
76 self._current_test = None
77+ self.subunitLineReceived(line)
78 elif (self.state == TestProtocolServer.TEST_STARTED and
79 self.current_test_description + " [" == line[offset:-1]):
80 self.state = TestProtocolServer.READING_ERROR
81 self._message = ""
82+ self.subunitLineReceived(line)
83 else:
84 self.stdOutLineReceived(line)
85
86@@ -210,10 +217,12 @@
87 else:
88 self.client.addSuccess(self._current_test)
89 self.client.stopTest(self._current_test)
90+ self.subunitLineReceived(line)
91 elif (self.state == TestProtocolServer.TEST_STARTED and
92 self.current_test_description + " [" == line[offset:-1]):
93 self.state = TestProtocolServer.READING_XFAIL
94 self._message = ""
95+ self.subunitLineReceived(line)
96 else:
97 self.stdOutLineReceived(line)
98
99@@ -224,10 +233,12 @@
100 self.current_test_description = None
101 self.client.addFailure(self._current_test, RemoteError())
102 self.client.stopTest(self._current_test)
103+ self.subunitLineReceived(line)
104 elif (self.state == TestProtocolServer.TEST_STARTED and
105 self.current_test_description + " [" == line[offset:-1]):
106 self.state = TestProtocolServer.READING_FAILURE
107 self._message = ""
108+ self.subunitLineReceived(line)
109 else:
110 self.stdOutLineReceived(line)
111
112@@ -238,10 +249,12 @@
113 self.current_test_description = None
114 self._skip_or_error()
115 self.client.stopTest(self._current_test)
116+ self.subunitLineReceived(line)
117 elif (self.state == TestProtocolServer.TEST_STARTED and
118 self.current_test_description + " [" == line[offset:-1]):
119 self.state = TestProtocolServer.READING_SKIP
120 self._message = ""
121+ self.subunitLineReceived(line)
122 else:
123 self.stdOutLineReceived(line)
124
125@@ -259,10 +272,12 @@
126 if (self.state == TestProtocolServer.TEST_STARTED and
127 self.current_test_description == line[offset:-1]):
128 self._succeedTest()
129+ self.subunitLineReceived(line)
130 elif (self.state == TestProtocolServer.TEST_STARTED and
131 self.current_test_description + " [" == line[offset:-1]):
132 self.state = TestProtocolServer.READING_SUCCESS
133 self._message = ""
134+ self.subunitLineReceived(line)
135 else:
136 self.stdOutLineReceived(line)
137
138@@ -274,6 +289,7 @@
139 self._message += line
140
141 def endQuote(self, line):
142+ stdout = False
143 if self.state == TestProtocolServer.READING_FAILURE:
144 self.state = TestProtocolServer.OUTSIDE_TEST
145 self.current_test_description = None
146@@ -304,6 +320,9 @@
147 self._succeedTest()
148 else:
149 self.stdOutLineReceived(line)
150+ stdout = True
151+ if not stdout:
152+ self.subunitLineReceived(line)
153
154 def _handleProgress(self, offset, line):
155 """Process a progress directive."""
156@@ -352,8 +371,10 @@
157 TestProtocolServer.READING_XFAIL
158 ):
159 self._appendMessage(line)
160+ self.subunitLineReceived(line)
161 else:
162 parts = line.split(None, 1)
163+ stdout = False
164 if len(parts) == 2:
165 cmd, rest = parts
166 offset = len(cmd) + 1
167@@ -366,14 +387,17 @@
168 self._addFailure(offset, line)
169 elif cmd == 'progress':
170 self._handleProgress(offset, line)
171+ self.subunitLineReceived(line)
172 elif cmd == 'skip':
173 self._addSkip(offset, line)
174 elif cmd in ('success', 'successful'):
175 self._addSuccess(offset, line)
176 elif cmd in ('tags',):
177 self._handleTags(offset, line)
178+ self.subunitLineReceived(line)
179 elif cmd in ('time',):
180 self._handleTime(offset, line)
181+ self.subunitLineReceived(line)
182 elif cmd == 'xfail':
183 self._addExpectedFail(offset, line)
184 else:
185@@ -418,9 +442,13 @@
186 self._current_test = RemotedTestCase(line[offset:-1])
187 self.current_test_description = line[offset:-1]
188 self.client.startTest(self._current_test)
189+ self.subunitLineReceived(line)
190 else:
191 self.stdOutLineReceived(line)
192
193+ def subunitLineReceived(self, line):
194+ self._forward_stream.write(line)
195+
196 def stdOutLineReceived(self, line):
197 self._stream.write(line)
198
199@@ -860,16 +888,19 @@
200 :seealso: TestProtocolServer (the subunit wire protocol parser).
201 """
202
203- def __init__(self, stream, passthrough=None):
204+ def __init__(self, stream, passthrough=None, forward=False):
205 """Create a ProtocolTestCase reading from stream.
206
207 :param stream: A filelike object which a subunit stream can be read
208 from.
209 :param passthrough: A stream pass non subunit input on to. If not
210 supplied, the TestProtocolServer default is used.
211+ :param forward: A stream to pass subunit input on to. If not supplied
212+ subunit input is not forwarded.
213 """
214 self._stream = stream
215 self._passthrough = passthrough
216+ self._forward = forward
217
218 def __call__(self, result=None):
219 return self.run(result)
220@@ -877,7 +908,7 @@
221 def run(self, result=None):
222 if result is None:
223 result = self.defaultTestResult()
224- protocol = TestProtocolServer(result, self._passthrough)
225+ protocol = TestProtocolServer(result, self._passthrough, self._forward)
226 line = self._stream.readline()
227 while line:
228 protocol.lineReceived(line)
229
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 from subunit import ExecTestCase
235 from subunit import IsolatedTestCase
236 from subunit import TestProtocolClient
237+ from subunit import ProtocolTestCase
238
239
240 class TestDiscardStream(unittest.TestCase):
241@@ -161,6 +162,30 @@
242 subunit.DiscardStream().write("content")
243
244
245+class TestProtocolServerForward(unittest.TestCase):
246+
247+ def test_story(self):
248+ client = unittest.TestResult()
249+ out = StringIO()
250+ protocol = subunit.TestProtocolServer(client, forward_stream=out)
251+ pipe = StringIO("test old mcdonald\n"
252+ "success old mcdonald\n")
253+ protocol.readFrom(pipe)
254+ mcdonald = subunit.RemotedTestCase("old mcdonald")
255+ self.assertEqual(client.testsRun, 1)
256+ self.assertEqual(pipe.getvalue(), out.getvalue())
257+
258+ def test_not_command(self):
259+ client = unittest.TestResult()
260+ out = StringIO()
261+ protocol = subunit.TestProtocolServer(client,
262+ stream=subunit.DiscardStream(), forward_stream=out)
263+ pipe = StringIO("success old mcdonald\n")
264+ protocol.readFrom(pipe)
265+ self.assertEqual(client.testsRun, 0)
266+ self.assertEqual("", out.getvalue())
267+
268+
269 class TestTestProtocolServerPipe(unittest.TestCase):
270
271 def test_story(self):

Subscribers

People subscribed via source and target branches