~bzr/ubuntu/hardy/subunit/bzr-ppa

« back to all changes in this revision

Viewing changes to filters/subunit2gtk

  • Committer: Robert Collins
  • Date: 2009-09-20 02:16:29 UTC
  • mfrom: (73.1.11 debian)
  • Revision ID: robertc@robertcollins.net-20090920021629-r3mal3003qren1or
Merge 0.0.2 release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
#  subunit: extensions to python unittest to get test results from subprocesses.
 
3
#  Copyright (C) 2009  Robert Collins <robertc@robertcollins.net>
 
4
#
 
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.
 
9
#
 
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.
 
14
#
 
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
 
18
#
 
19
 
 
20
### The GTK progress bar __init__ function is derived from the pygtk tutorial:
 
21
# The PyGTK Tutorial is Copyright (C) 2001-2005 John Finlay.
 
22
 
23
# The GTK Tutorial is Copyright (C) 1997 Ian Main.
 
24
 
25
# Copyright (C) 1998-1999 Tony Gale.
 
26
 
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
 
29
# copies.
 
30
 
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.
 
36
 
37
# Permission is granted to copy and distribute translations of this document
 
38
# into another language, under the above conditions for modified versions.
 
39
 
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.
 
43
 
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.
 
48
 
 
49
"""Display a subunit stream in a gtk progress window."""
 
50
 
 
51
import os
 
52
import sys
 
53
import unittest
 
54
 
 
55
import pygtk
 
56
pygtk.require('2.0')
 
57
import gtk, gtk.gdk, gobject
 
58
 
 
59
from subunit import (
 
60
    PROGRESS_POP,
 
61
    PROGRESS_PUSH,
 
62
    PROGRESS_SET,
 
63
    ProtocolTestCase,
 
64
    TestProtocolServer,
 
65
    )
 
66
from subunit.progress_model import  ProgressModel
 
67
 
 
68
 
 
69
class GTKTestResult(unittest.TestResult):
 
70
 
 
71
    def __init__(self):
 
72
        super(GTKTestResult, self).__init__()
 
73
        # Instance variables (in addition to TestResult)
 
74
        self.window = None
 
75
        self.run_label = None
 
76
        self.ok_label = None
 
77
        self.not_ok_label = None
 
78
        self.total_tests = None
 
79
 
 
80
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
 
81
        self.window.set_resizable(True)
 
82
 
 
83
        self.window.connect("destroy", gtk.main_quit)
 
84
        self.window.set_title("Tests...")
 
85
        self.window.set_border_width(0)
 
86
 
 
87
        vbox = gtk.VBox(False, 5)
 
88
        vbox.set_border_width(10)
 
89
        self.window.add(vbox)
 
90
        vbox.show()
 
91
  
 
92
        # Create a centering alignment object
 
93
        align = gtk.Alignment(0.5, 0.5, 0, 0)
 
94
        vbox.pack_start(align, False, False, 5)
 
95
        align.show()
 
96
 
 
97
        # Create the ProgressBar
 
98
        self.pbar = gtk.ProgressBar()
 
99
        align.add(self.pbar)
 
100
        self.pbar.set_text("Running")
 
101
        self.pbar.show()
 
102
        self.progress = ProgressModel()
 
103
 
 
104
        separator = gtk.HSeparator()
 
105
        vbox.pack_start(separator, False, False, 0)
 
106
        separator.show()
 
107
 
 
108
        # rows, columns, homogeneous
 
109
        table = gtk.Table(2, 3, False)
 
110
        vbox.pack_start(table, False, True, 0)
 
111
        table.show()
 
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)
 
116
        label.show()
 
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()
 
121
 
 
122
        label = gtk.Label("OK:")
 
123
        table.attach(label, 0, 1, 2, 3, gtk.EXPAND | gtk.FILL,
 
124
            gtk.EXPAND | gtk.FILL, 5, 5)
 
125
        label.show()
 
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)
 
129
        self.ok_label.show()
 
130
 
 
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)
 
134
        label.show()
 
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()
 
139
 
 
140
        self.window.show()
 
141
        # For the demo.
 
142
        self.window.set_keep_above(True)
 
143
        self.window.present()
 
144
 
 
145
    def stopTest(self, test):
 
146
        super(GTKTestResult, self).stopTest(test)
 
147
        self.progress.advance()
 
148
        if self.progress.width() == 0:
 
149
            self.pbar.pulse()
 
150
        else:
 
151
            self.pbar.set_fraction(
 
152
                self.progress.pos() / float(self.progress.width()))
 
153
 
 
154
    def stopTestRun(self):
 
155
        try:
 
156
            super(GTKTestResult, self).stopTestRun()
 
157
        except AttributeError:
 
158
            pass
 
159
        self.pbar.set_text('Finished')
 
160
 
 
161
    def addError(self, test, err):
 
162
        super(GTKTestResult, self).addError(test, err)
 
163
        self.update_counts()
 
164
 
 
165
    def addFailure(self, test, err):
 
166
        super(GTKTestResult, self).addFailure(test, err)
 
167
        self.update_counts()
 
168
 
 
169
    def addSuccess(self, test):
 
170
        super(GTKTestResult, self).addSuccess(test)
 
171
        self.update_counts()
 
172
 
 
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)
 
178
        self.update_counts()
 
179
 
 
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)
 
186
        self.update_counts()
 
187
 
 
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)
 
194
        self.update_counts()
 
195
 
 
196
    def progress(self, offset, whence):
 
197
        if whence == PROGRESS_PUSH:
 
198
            self.progress.push()
 
199
        elif whence == PROGRESS_POP:
 
200
            self.progress.pop()
 
201
        elif whence == PROGRESS_SET:
 
202
            self.total_tests = offset
 
203
            self.progress.set_width(offset)
 
204
        else:
 
205
            self.total_tests += offset
 
206
            self.progress.adjust_width(offset)
 
207
 
 
208
    def time(self, a_datetime):
 
209
        # We don't try to estimate completion yet.
 
210
        pass
 
211
 
 
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))
 
217
 
 
218
 
 
219
class GIOProtocolTestCase(object):
 
220
 
 
221
    def __init__(self, stream, result, on_finish):
 
222
        self.stream = stream
 
223
        self.schedule_read()
 
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
 
227
 
 
228
    def read(self, source, condition):
 
229
        #NB: \o/ actually blocks
 
230
        line = source.readline()
 
231
        if not line:
 
232
            self.protocol.lostConnection()
 
233
            self.on_finish()
 
234
            return False
 
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)
 
239
        return False
 
240
 
 
241
    def schedule_read(self):
 
242
        self.read_id = gobject.io_add_watch(self.stream, gobject.IO_IN, self.read)
 
243
 
 
244
    def hup(self, source, condition):
 
245
        self.protocol.lostConnection()
 
246
        gobject.source_remove(self.read_id)
 
247
        self.on_finish()
 
248
 
 
249
 
 
250
result = GTKTestResult()
 
251
test = GIOProtocolTestCase(sys.stdin, result, result.stopTestRun)
 
252
gtk.main()
 
253
if result.wasSuccessful():
 
254
    exit_code = 0
 
255
else:
 
256
    exit_code = 1
 
257
sys.exit(exit_code)