2
# subunit: extensions to python unittest to get test results from subprocesses.
3
# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
### The GTK progress bar __init__ function is derived from the pygtk tutorial:
21
# The PyGTK Tutorial is Copyright (C) 2001-2005 John Finlay.
23
# The GTK Tutorial is Copyright (C) 1997 Ian Main.
25
# Copyright (C) 1998-1999 Tony Gale.
27
# Permission is granted to make and distribute verbatim copies of this manual
28
# provided the copyright notice and this permission notice are preserved on all
31
# Permission is granted to copy and distribute modified versions of this
32
# document under the conditions for verbatim copying, provided that this
33
# copyright notice is included exactly as in the original, and that the entire
34
# resulting derived work is distributed under the terms of a permission notice
35
# identical to this one.
37
# Permission is granted to copy and distribute translations of this document
38
# into another language, under the above conditions for modified versions.
40
# If you are intending to incorporate this document into a published work,
41
# please contact the maintainer, and we will make an effort to ensure that you
42
# have the most up to date information available.
44
# There is no guarantee that this document lives up to its intended purpose.
45
# This is simply provided as a free resource. As such, the authors and
46
# maintainers of the information provided within can not make any guarantee
47
# that the information is even accurate.
49
"""Display a subunit stream in a gtk progress window."""
57
import gtk, gtk.gdk, gobject
66
from subunit.progress_model import ProgressModel
69
class GTKTestResult(unittest.TestResult):
72
super(GTKTestResult, self).__init__()
73
# Instance variables (in addition to TestResult)
77
self.not_ok_label = None
78
self.total_tests = None
80
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
81
self.window.set_resizable(True)
83
self.window.connect("destroy", gtk.main_quit)
84
self.window.set_title("Tests...")
85
self.window.set_border_width(0)
87
vbox = gtk.VBox(False, 5)
88
vbox.set_border_width(10)
92
# Create a centering alignment object
93
align = gtk.Alignment(0.5, 0.5, 0, 0)
94
vbox.pack_start(align, False, False, 5)
97
# Create the ProgressBar
98
self.pbar = gtk.ProgressBar()
100
self.pbar.set_text("Running")
102
self.progress = ProgressModel()
104
separator = gtk.HSeparator()
105
vbox.pack_start(separator, False, False, 0)
108
# rows, columns, homogeneous
109
table = gtk.Table(2, 3, False)
110
vbox.pack_start(table, False, True, 0)
112
# Show summary details about the run. Could use an expander.
113
label = gtk.Label("Run:")
114
table.attach(label, 0, 1, 1, 2, gtk.EXPAND | gtk.FILL,
115
gtk.EXPAND | gtk.FILL, 5, 5)
117
self.run_label = gtk.Label("N/A")
118
table.attach(self.run_label, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL,
119
gtk.EXPAND | gtk.FILL, 5, 5)
120
self.run_label.show()
122
label = gtk.Label("OK:")
123
table.attach(label, 0, 1, 2, 3, gtk.EXPAND | gtk.FILL,
124
gtk.EXPAND | gtk.FILL, 5, 5)
126
self.ok_label = gtk.Label("N/A")
127
table.attach(self.ok_label, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL,
128
gtk.EXPAND | gtk.FILL, 5, 5)
131
label = gtk.Label("Not OK:")
132
table.attach(label, 0, 1, 3, 4, gtk.EXPAND | gtk.FILL,
133
gtk.EXPAND | gtk.FILL, 5, 5)
135
self.not_ok_label = gtk.Label("N/A")
136
table.attach(self.not_ok_label, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL,
137
gtk.EXPAND | gtk.FILL, 5, 5)
138
self.not_ok_label.show()
142
self.window.set_keep_above(True)
143
self.window.present()
145
def stopTest(self, test):
146
super(GTKTestResult, self).stopTest(test)
147
self.progress.advance()
148
if self.progress.width() == 0:
151
self.pbar.set_fraction(
152
self.progress.pos() / float(self.progress.width()))
154
def stopTestRun(self):
156
super(GTKTestResult, self).stopTestRun()
157
except AttributeError:
159
self.pbar.set_text('Finished')
161
def addError(self, test, err):
162
super(GTKTestResult, self).addError(test, err)
165
def addFailure(self, test, err):
166
super(GTKTestResult, self).addFailure(test, err)
169
def addSuccess(self, test):
170
super(GTKTestResult, self).addSuccess(test)
173
def addSkip(self, test, reason):
174
# addSkip is new in Python 2.7/3.1
175
addSkip = getattr(super(GTKTestResult, self), 'addSkip', None)
176
if callable(addSkip):
177
addSkip(test, reason)
180
def addExpectedFailure(self, test, err):
181
# addExpectedFailure is new in Python 2.7/3.1
182
addExpectedFailure = getattr(super(GTKTestResult, self),
183
'addExpectedFailure', None)
184
if callable(addExpectedFailure):
185
addExpectedFailure(test, reason)
188
def addUnexpectedSuccess(self, test):
189
# addUnexpectedSuccess is new in Python 2.7/3.1
190
addUnexpectedSuccess = getattr(super(GTKTestResult, self),
191
'addUnexpectedSuccess', None)
192
if callable(addUnexpectedSuccess):
193
addUnexpectedSuccess(test, reason)
196
def progress(self, offset, whence):
197
if whence == PROGRESS_PUSH:
199
elif whence == PROGRESS_POP:
201
elif whence == PROGRESS_SET:
202
self.total_tests = offset
203
self.progress.set_width(offset)
205
self.total_tests += offset
206
self.progress.adjust_width(offset)
208
def time(self, a_datetime):
209
# We don't try to estimate completion yet.
212
def update_counts(self):
213
self.run_label.set_text(str(self.testsRun))
214
bad = len(self.failures + self.errors)
215
self.ok_label.set_text(str(self.testsRun - bad))
216
self.not_ok_label.set_text(str(bad))
219
class GIOProtocolTestCase(object):
221
def __init__(self, stream, result, on_finish):
224
self.hup_id = gobject.io_add_watch(stream, gobject.IO_HUP, self.hup)
225
self.protocol = TestProtocolServer(result)
226
self.on_finish = on_finish
228
def read(self, source, condition):
229
#NB: \o/ actually blocks
230
line = source.readline()
232
self.protocol.lostConnection()
235
self.protocol.lineReceived(line)
236
# schedule more IO shortly - if we say we're willing to do it
237
# immediately we starve things.
238
source_id = gobject.timeout_add(1, self.schedule_read)
241
def schedule_read(self):
242
self.read_id = gobject.io_add_watch(self.stream, gobject.IO_IN, self.read)
244
def hup(self, source, condition):
245
self.protocol.lostConnection()
246
gobject.source_remove(self.read_id)
250
result = GTKTestResult()
251
test = GIOProtocolTestCase(sys.stdin, result, result.stopTestRun)
253
if result.wasSuccessful():