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

Proposed by Robert Collins
Status: Merged
Merged at revision: not available
Proposed branch: lp:~lifeless/subunit/subunit2junitxml
Merge into: lp:~subunit/subunit/trunk
Diff against target: None lines
To merge this branch: bzr merge lp:~lifeless/subunit/subunit2junitxml
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve
Review via email: mp+9534@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

junit outout. \o/

Revision history for this message
Jelmer Vernooij (jelmer) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2009-07-28 21:44:28 +0000
3+++ NEWS 2009-08-01 08:01:27 +0000
4@@ -15,10 +15,17 @@
5 about completion, when such information is available. See the README
6 under ``progress`` for more details.
7
8+ * ``subunit2gtk`` has been added, a filter that shows a GTK summary of a
9+ test stream.
10+
11 * ``subunit2pyunit`` has a --progress flag which will cause the bzrlib
12 test reporter to be used, which has a textual progress bar. This requires
13 a recent bzrlib as a minor bugfix was required in bzrlib to support this.
14
15+ * ``subunit2junitxml`` has been added. This filter converts a subunit
16+ stream to a single JUnit style XML stream using the pyjunitxml
17+ python library.
18+
19 BUG FIXES:
20
21 API CHANGES:
22
23=== modified file 'README'
24--- README 2009-07-28 13:32:10 +0000
25+++ README 2009-08-01 08:01:27 +0000
26@@ -47,6 +47,8 @@
27 Subunit supplies the following filters:
28 * tap2subunit - convert perl's TestAnythingProtocol to subunit.
29 * subunit2pyunit - convert a subunit stream to pyunit test results.
30+ * subunit2gtk - show a subunit stream in GTK.
31+ * subunit2junitxml - convert a subunit stream to JUnit's XML format.
32 * subunit-filter - filter out tests from a subunit stream.
33 * subunit-ls - list info about tests present in a subunit stream.
34 * subunit-stats - generate a summary of a subunit stream.
35
36=== added file 'filters/subunit2gtk'
37--- filters/subunit2gtk 1970-01-01 00:00:00 +0000
38+++ filters/subunit2gtk 2009-07-29 12:49:03 +0000
39@@ -0,0 +1,232 @@
40+#!/usr/bin/env python
41+# subunit: extensions to python unittest to get test results from subprocesses.
42+# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
43+#
44+# This program is free software; you can redistribute it and/or modify
45+# it under the terms of the GNU General Public License as published by
46+# the Free Software Foundation; either version 2 of the License, or
47+# (at your option) any later version.
48+#
49+# This program is distributed in the hope that it will be useful,
50+# but WITHOUT ANY WARRANTY; without even the implied warranty of
51+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52+# GNU General Public License for more details.
53+#
54+# You should have received a copy of the GNU General Public License
55+# along with this program; if not, write to the Free Software
56+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
57+#
58+
59+### The GTK progress bar __init__ function is derived from the pygtk tutorial:
60+# The PyGTK Tutorial is Copyright (C) 2001-2005 John Finlay.
61+#
62+# The GTK Tutorial is Copyright (C) 1997 Ian Main.
63+#
64+# Copyright (C) 1998-1999 Tony Gale.
65+#
66+# Permission is granted to make and distribute verbatim copies of this manual
67+# provided the copyright notice and this permission notice are preserved on all
68+# copies.
69+#
70+# Permission is granted to copy and distribute modified versions of this
71+# document under the conditions for verbatim copying, provided that this
72+# copyright notice is included exactly as in the original, and that the entire
73+# resulting derived work is distributed under the terms of a permission notice
74+# identical to this one.
75+#
76+# Permission is granted to copy and distribute translations of this document
77+# into another language, under the above conditions for modified versions.
78+#
79+# If you are intending to incorporate this document into a published work,
80+# please contact the maintainer, and we will make an effort to ensure that you
81+# have the most up to date information available.
82+#
83+# There is no guarantee that this document lives up to its intended purpose.
84+# This is simply provided as a free resource. As such, the authors and
85+# maintainers of the information provided within can not make any guarantee
86+# that the information is even accurate.
87+
88+"""Display a subunit stream in a gtk progress window."""
89+
90+import os
91+import sys
92+import unittest
93+
94+import pygtk
95+pygtk.require('2.0')
96+import gtk, gtk.gdk, gobject
97+
98+from subunit import ProtocolTestCase, TestProtocolServer
99+
100+class GTKTestResult(unittest.TestResult):
101+
102+ def __init__(self):
103+ super(GTKTestResult, self).__init__()
104+ # Instance variables (in addition to TestResult)
105+ self.window = None
106+ self.run_label = None
107+ self.ok_label = None
108+ self.not_ok_label = None
109+ self.total_tests = None
110+
111+ self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
112+ self.window.set_resizable(True)
113+
114+ self.window.connect("destroy", gtk.main_quit)
115+ self.window.set_title("Tests...")
116+ self.window.set_border_width(0)
117+
118+ vbox = gtk.VBox(False, 5)
119+ vbox.set_border_width(10)
120+ self.window.add(vbox)
121+ vbox.show()
122+
123+ # Create a centering alignment object
124+ align = gtk.Alignment(0.5, 0.5, 0, 0)
125+ vbox.pack_start(align, False, False, 5)
126+ align.show()
127+
128+ # Create the ProgressBar
129+ self.pbar = gtk.ProgressBar()
130+ align.add(self.pbar)
131+ self.pbar.set_text("Running")
132+ self.pbar.show()
133+
134+ separator = gtk.HSeparator()
135+ vbox.pack_start(separator, False, False, 0)
136+ separator.show()
137+
138+ # rows, columns, homogeneous
139+ table = gtk.Table(2, 3, False)
140+ vbox.pack_start(table, False, True, 0)
141+ table.show()
142+ # Show summary details about the run. Could use an expander.
143+ label = gtk.Label("Run:")
144+ table.attach(label, 0, 1, 1, 2, gtk.EXPAND | gtk.FILL,
145+ gtk.EXPAND | gtk.FILL, 5, 5)
146+ label.show()
147+ self.run_label = gtk.Label("N/A")
148+ table.attach(self.run_label, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL,
149+ gtk.EXPAND | gtk.FILL, 5, 5)
150+ self.run_label.show()
151+
152+ label = gtk.Label("OK:")
153+ table.attach(label, 0, 1, 2, 3, gtk.EXPAND | gtk.FILL,
154+ gtk.EXPAND | gtk.FILL, 5, 5)
155+ label.show()
156+ self.ok_label = gtk.Label("N/A")
157+ table.attach(self.ok_label, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL,
158+ gtk.EXPAND | gtk.FILL, 5, 5)
159+ self.ok_label.show()
160+
161+ label = gtk.Label("Not OK:")
162+ table.attach(label, 0, 1, 3, 4, gtk.EXPAND | gtk.FILL,
163+ gtk.EXPAND | gtk.FILL, 5, 5)
164+ label.show()
165+ self.not_ok_label = gtk.Label("N/A")
166+ table.attach(self.not_ok_label, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL,
167+ gtk.EXPAND | gtk.FILL, 5, 5)
168+ self.not_ok_label.show()
169+
170+ self.window.show()
171+ # For the demo.
172+ self.window.set_keep_above(True)
173+ self.window.present()
174+
175+ def stopTest(self, test):
176+ super(GTKTestResult, self).stopTest(test)
177+ if not self.total_tests:
178+ self.pbar.pulse()
179+ else:
180+ self.pbar.set_fraction(self.testsRun/float(self.total_tests))
181+
182+ def stopTestRun(self):
183+ try:
184+ super(GTKTestResult, self).stopTestRun()
185+ except AttributeError:
186+ pass
187+ self.pbar.set_text('Finished')
188+
189+ def addError(self, test, err):
190+ super(GTKTestResult, self).addError(test, err)
191+ self.update_counts()
192+
193+ def addFailure(self, test, err):
194+ super(GTKTestResult, self).addFailure(test, err)
195+ self.update_counts()
196+
197+ def addSuccess(self, test):
198+ super(GTKTestResult, self).addSuccess(test)
199+ self.update_counts()
200+
201+ def addSkip(self, test, reason):
202+ try:
203+ super(GTKTestResult, self).addSkipSuccess(test, reason)
204+ except AttributeError:
205+ pass
206+ self.update_counts()
207+
208+ def addExpectedFailure(self, test, err):
209+ super(GTKTestResult, self).addExpectedFailure(test, err)
210+ self.update_counts()
211+
212+ def addUnexpectedSuccess(self, test):
213+ super(GTKTestResult, self).addUnexpectedSuccess(test)
214+ self.update_counts()
215+
216+ def progress(self, offset, whence):
217+ if whence == os.SEEK_SET:
218+ self.total_tests = offset
219+ else:
220+ self.total_tests += offset
221+
222+ def time(self, a_datetime):
223+ # We don't try to estimate completion yet.
224+ pass
225+
226+ def update_counts(self):
227+ self.run_label.set_text(str(self.testsRun))
228+ bad = len(self.failures + self.errors)
229+ self.ok_label.set_text(str(self.testsRun - bad))
230+ self.not_ok_label.set_text(str(bad))
231+
232+
233+class GIOProtocolTestCase(object):
234+
235+ def __init__(self, stream, result, on_finish):
236+ self.stream = stream
237+ self.schedule_read()
238+ self.hup_id = gobject.io_add_watch(stream, gobject.IO_HUP, self.hup)
239+ self.protocol = TestProtocolServer(result)
240+ self.on_finish = on_finish
241+
242+ def read(self, source, condition):
243+ #NB: \o/ actually blocks
244+ line = source.readline()
245+ if not line:
246+ self.protocol.lostConnection()
247+ self.on_finish()
248+ return False
249+ self.protocol.lineReceived(line)
250+ # schedule more IO shortly - if we say we're willing to do it
251+ # immediately we starve things.
252+ source_id = gobject.timeout_add(1, self.schedule_read)
253+ return False
254+
255+ def schedule_read(self):
256+ self.read_id = gobject.io_add_watch(self.stream, gobject.IO_IN, self.read)
257+
258+ def hup(self, source, condition):
259+ self.protocol.lostConnection()
260+ gobject.source_remove(self.read_id)
261+ self.on_finish()
262+
263+
264+result = GTKTestResult()
265+test = GIOProtocolTestCase(sys.stdin, result, result.stopTestRun)
266+gtk.main()
267+if result.wasSuccessful():
268+ exit_code = 0
269+else:
270+ exit_code = 1
271+sys.exit(exit_code)
272
273=== added file 'filters/subunit2junitxml'
274--- filters/subunit2junitxml 1970-01-01 00:00:00 +0000
275+++ filters/subunit2junitxml 2009-08-01 08:01:27 +0000
276@@ -0,0 +1,42 @@
277+#!/usr/bin/env python
278+# subunit: extensions to python unittest to get test results from subprocesses.
279+# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
280+#
281+# This program is free software; you can redistribute it and/or modify
282+# it under the terms of the GNU General Public License as published by
283+# the Free Software Foundation; either version 2 of the License, or
284+# (at your option) any later version.
285+#
286+# This program is distributed in the hope that it will be useful,
287+# but WITHOUT ANY WARRANTY; without even the implied warranty of
288+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
289+# GNU General Public License for more details.
290+#
291+# You should have received a copy of the GNU General Public License
292+# along with this program; if not, write to the Free Software
293+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
294+#
295+
296+"""Filter a subunit stream to get aggregate statistics."""
297+
298+import sys
299+import unittest
300+
301+from subunit import ProtocolTestCase
302+try:
303+ from junitxml import JUnitXmlResult
304+except ImportError:
305+ sys.stderr.write("python-junitxml (https://launchpad.net/pyjunitxml or "
306+ "http://pypi.python.org/pypi/junitxml) is required for this filter.")
307+ raise
308+
309+result = JUnitXmlResult(sys.stdout)
310+test = ProtocolTestCase(sys.stdin)
311+result.startTestRun()
312+test.run(result)
313+result.stopTestRun()
314+if result.wasSuccessful():
315+ exit_code = 0
316+else:
317+ exit_code = 1
318+sys.exit(exit_code)

Subscribers

People subscribed via source and target branches