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 | * 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): |
This makes subunit2junitxml easier to use for windows folk.