Merge lp:~lifeless/tribunal/testrepository into lp:tribunal

Proposed by Robert Collins
Status: Merged
Approved by: Martin Pool
Approved revision: no longer in the revision history of the source branch.
Merge reported by: Martin Pool
Merged at revision: not available
Proposed branch: lp:~lifeless/tribunal/testrepository
Merge into: lp:tribunal
Diff against target: 204 lines (+144/-8)
2 files modified
bin/tribunal-subunit (+37/-8)
tribunal/trui.py (+107/-0)
To merge this branch: bzr merge lp:~lifeless/tribunal/testrepository
Reviewer Review Type Date Requested Status
Martin Pool Approve
Review via email: mp+20321@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

This hooks up testr run to the reload button in tribunal-subunit.

So, if you have a .testr.conf for a project, and hit reload, it will run the projects tests. I've tested this by starting with a clean environment, breaking a test, hitting reload, waiting - et voila, the errors showed up. I then fixed them, hit reload, and they went away.

\o/ success.

Revision history for this message
Martin Pool (mbp) wrote :

Great feature, though the reload button does not seem like the right place for it.

What's trui?

review: Needs Fixing
Revision history for this message
Martin Pool (mbp) wrote :

(discussed offline)

I'll tweak and merge.

review: Approve
lp:~lifeless/tribunal/testrepository updated
121. By Martin Pool

Alternate success and failure in emit-tests

122. By Martin Pool

Update todos

Revision history for this message
Martin Pool (mbp) wrote :

this is now actually merged; I don't know why Launchpad doesn't think so

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/tribunal-subunit'
2--- bin/tribunal-subunit 2010-02-19 08:16:29 +0000
3+++ bin/tribunal-subunit 2010-02-28 22:05:24 +0000
4@@ -37,6 +37,7 @@
5
6 import errno
7 import optparse
8+import os.path
9 import sys
10
11 import gobject
12@@ -80,8 +81,9 @@
13 saying whether they should be shown
14 """
15
16- def __init__(self, glade_xml):
17+ def __init__(self, glade_xml, opts):
18 self.glade = glade_xml
19+ self.opts = opts # An OptParse options object.
20 for i in [
21 'result_frame',
22 'result_frame_label',
23@@ -260,6 +262,27 @@
24 self.model_filter.refilter()
25
26 def reload_stream(self):
27+ if self.opts.testr:
28+ # run the test suite again.
29+ from testrepository.commands import run
30+ from tribunal import trui
31+ # Work around bug in testrepository (just filed, no # yet).
32+ url = os.path.expanduser(self.opts.testr)
33+ ui = trui.GTKUI(self, url)
34+ cmd = run.run(ui)
35+ # Work around 'testr run' loading to the wrong repo.
36+ here = os.getcwd()
37+ os.chdir(url)
38+ try:
39+ res = cmd.execute()
40+ finally:
41+ os.chdir(here)
42+ # Ignoring detail of exit code: the subunit parsing + ui error
43+ # methods will inform the user.
44+ if res == 3:
45+ # But if it went badly wrong, don't both doing work in the UI.
46+ return
47+ self._last_input_file = testrepository_last_stream(self.opts)
48 try:
49 self._last_input_file.seek(0)
50 except IOError, e:
51@@ -286,7 +309,7 @@
52
53 def _construct_and_show_window(self):
54 glade_xml = load_glade()
55- self._window = SubunitWindow(glade_xml)
56+ self._window = SubunitWindow(glade_xml, self.opts)
57 glade_xml.signal_autoconnect(self)
58 self._set_default_icons()
59 self._window.show_all()
60@@ -307,13 +330,10 @@
61 parser.add_option('--testr',
62 help='read from a testrepository in the given directory')
63
64- opts, args = parser.parse_args(argv)
65+ self.opts, args = parser.parse_args(argv)
66
67- if opts.testr:
68- import testrepository.repository.file
69- factory = testrepository.repository.file.RepositoryFactory()
70- repo = factory.open(opts.testr)
71- stream = repo.get_test_run(repo.latest_id()).get_subunit_stream()
72+ if self.opts.testr:
73+ stream = testrepository_last_stream(self.opts)
74 elif args == ['-']:
75 stream = sys.stdin
76 elif len(args) == 1:
77@@ -345,6 +365,15 @@
78 return '\n'.join(bits)
79
80
81+def testrepository_last_stream(opts):
82+ import testrepository.repository.file
83+ factory = testrepository.repository.file.RepositoryFactory()
84+ # Work around bug in testrepository (just filed, no # yet).
85+ url = os.path.expanduser(opts.testr)
86+ repo = factory.open(url)
87+ return repo.get_test_run(repo.latest_id()).get_subunit_stream()
88+
89+
90 if __name__ == '__main__':
91 app = ViewSubunitApp(GtkLoop())
92 app.run(sys.argv[1:])
93
94=== added file 'tribunal/trui.py'
95--- tribunal/trui.py 1970-01-01 00:00:00 +0000
96+++ tribunal/trui.py 2010-02-28 22:05:24 +0000
97@@ -0,0 +1,107 @@
98+#! /usr/bin/env python
99+
100+# Copyright (C) 2010 Robert Collins <robertc@robertcollins.net>
101+#
102+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
103+# use this file except in compliance with the License.
104+#
105+# You may obtain a copy of the License at
106+# http://www.apache.org/licenses/LICENSE-2.0
107+#
108+# Unless required by applicable law or agreed to in writing, software
109+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
110+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
111+# License for the specific language governing permissions and limitations
112+# under the License.
113+#
114+# Part of Tribunal <https://launchpad.net/tribunal>
115+
116+"""Support for running testrepository commands."""
117+
118+__all__ = ['GTKUI']
119+
120+import optparse
121+
122+import gtk
123+from testrepository import ui
124+
125+
126+class GTKUI(ui.AbstractUI):
127+ """A GTK testrepository.ui.AbstractUI implementation."""
128+
129+ def __init__(self, subunit_window, here):
130+ """Create a GTKUI to run a TestRepository command under subunit_window.
131+
132+ :param subunit_window: A SubunitWindow from tribunal-subunit.
133+ :param here: What should the 'here' be for the UI.
134+ """
135+ self.window = subunit_window
136+ self.here = here
137+
138+ def _check_cmd(self):
139+ # TODO: prompt for options and arguments here.
140+ # Options are available on self.cmd.options
141+ options = []
142+ self.options = optparse.Values()
143+ seen_options = set()
144+ for option, value in options:
145+ setattr(self.options, option, value)
146+ seen_options.add(option)
147+ if not 'quiet' in seen_options:
148+ setattr(self.options, 'quiet', False)
149+ for option in self.cmd.options:
150+ if not option.dest in seen_options:
151+ setattr(self.options, option.dest, option.default)
152+ # Arguments on self.cmd.args
153+ parsed_args = {}
154+ unparsed = []
155+ failed = False
156+ for arg in self.cmd.args:
157+ try:
158+ parsed_args[arg.name] = arg.parse(unparsed)
159+ except ValueError:
160+ failed = True
161+ break
162+ self.arguments = parsed_args
163+ return unparsed == [] and not failed
164+
165+ def output_error(self, error_tuple):
166+ # Shows the error in a dialog. We could instead have a 'console' on
167+ # the SubunitWindow and write the error into the console.
168+ import traceback
169+ exctype, value, tb = error_tuple
170+ as_string = ''.join(traceback.format_exception(exctype, value, tb))
171+ dialog = gtk.MessageDialog(self.window.subunit_window,
172+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
173+ gtk.MESSAGE_ERROR,
174+ gtk.BUTTONS_CLOSE,
175+ as_string)
176+ dialog.run()
177+ dialog.destroy()
178+
179+ def output_rest(self, rest_string):
180+ # TODO: format as HTML, embed a browser?
181+ raise NotImplementedError(self.output_rest)
182+
183+ def output_results(self, suite_or_test):
184+ # TODO: feed to the main window
185+ raise NotImplementedError(self.output_results)
186+
187+ def output_stream(self, stream):
188+ # TODO: ask for a filename to save to.
189+ raise NotImplementedError(self.output_stream)
190+
191+ def output_table(self, table):
192+ raise NotImplementedError(self.output_table)
193+
194+ def output_values(self, values):
195+ # TODO: log this to the GUI somehow.
196+ outputs = []
197+ for label, value in values:
198+ outputs.append('%s: %s' % (label, value))
199+ print '%s\n' % ' '.join(outputs)
200+
201+ def subprocess_Popen(self, *args, **kwargs):
202+ # TODO: use a GTK GIO thingy
203+ import subprocess
204+ return subprocess.Popen(*args, **kwargs)

Subscribers

People subscribed via source and target branches

to all changes: