1 | #--
|
---|
2 | #
|
---|
3 | # Author:: Nathaniel Talbott.
|
---|
4 | # Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
|
---|
5 | # License:: Ruby license.
|
---|
6 |
|
---|
7 | require 'gtk'
|
---|
8 | require 'test/unit/ui/testrunnermediator'
|
---|
9 | require 'test/unit/ui/testrunnerutilities'
|
---|
10 |
|
---|
11 | module Test
|
---|
12 | module Unit
|
---|
13 | module UI
|
---|
14 | module GTK
|
---|
15 |
|
---|
16 | # Runs a Test::Unit::TestSuite in a Gtk UI. Obviously,
|
---|
17 | # this one requires you to have Gtk
|
---|
18 | # (http://www.gtk.org/) and the Ruby Gtk extension
|
---|
19 | # (http://ruby-gnome.sourceforge.net/) installed.
|
---|
20 | class TestRunner
|
---|
21 | extend TestRunnerUtilities
|
---|
22 |
|
---|
23 | # Creates a new TestRunner for running the passed
|
---|
24 | # suite.
|
---|
25 | def initialize(suite, output_level = NORMAL)
|
---|
26 | if (suite.respond_to?(:suite))
|
---|
27 | @suite = suite.suite
|
---|
28 | else
|
---|
29 | @suite = suite
|
---|
30 | end
|
---|
31 | @result = nil
|
---|
32 |
|
---|
33 | @runner = Thread.current
|
---|
34 | @restart_signal = Class.new(Exception)
|
---|
35 | @viewer = Thread.start do
|
---|
36 | @runner.join rescue @runner.run
|
---|
37 | Gtk.main
|
---|
38 | end
|
---|
39 | @viewer.join rescue nil # wait deadlock to handshake
|
---|
40 | end
|
---|
41 |
|
---|
42 | # Begins the test run.
|
---|
43 | def start
|
---|
44 | setup_mediator
|
---|
45 | setup_ui
|
---|
46 | attach_to_mediator
|
---|
47 | start_ui
|
---|
48 | @result
|
---|
49 | end
|
---|
50 |
|
---|
51 | private
|
---|
52 | def setup_mediator
|
---|
53 | @mediator = TestRunnerMediator.new(@suite)
|
---|
54 | suite_name = @suite.to_s
|
---|
55 | if ( @suite.kind_of?(Module) )
|
---|
56 | suite_name = @suite.name
|
---|
57 | end
|
---|
58 | suite_name_entry.set_text(suite_name)
|
---|
59 | end
|
---|
60 |
|
---|
61 | def attach_to_mediator
|
---|
62 | run_button.signal_connect("clicked", nil, &method(:run_test))
|
---|
63 | @mediator.add_listener(TestRunnerMediator::RESET, &method(:reset_ui))
|
---|
64 | @mediator.add_listener(TestResult::FAULT, &method(:add_fault))
|
---|
65 | @mediator.add_listener(TestResult::CHANGED, &method(:result_changed))
|
---|
66 | @mediator.add_listener(TestRunnerMediator::STARTED, &method(:started))
|
---|
67 | @mediator.add_listener(TestCase::STARTED, &method(:test_started))
|
---|
68 | @mediator.add_listener(TestCase::FINISHED, &method(:test_finished))
|
---|
69 | @mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished))
|
---|
70 | end
|
---|
71 |
|
---|
72 | def run_test(*)
|
---|
73 | @runner.raise(@restart_signal)
|
---|
74 | end
|
---|
75 |
|
---|
76 | def start_ui
|
---|
77 | @viewer.run
|
---|
78 | running = false
|
---|
79 | begin
|
---|
80 | loop do
|
---|
81 | if (running ^= true)
|
---|
82 | run_button.child.text = "Stop"
|
---|
83 | @mediator.run_suite
|
---|
84 | else
|
---|
85 | run_button.child.text = "Run"
|
---|
86 | @viewer.join
|
---|
87 | break
|
---|
88 | end
|
---|
89 | end
|
---|
90 | rescue @restart_signal
|
---|
91 | retry
|
---|
92 | rescue
|
---|
93 | end
|
---|
94 | end
|
---|
95 |
|
---|
96 | def stop(*)
|
---|
97 | Gtk.main_quit
|
---|
98 | end
|
---|
99 |
|
---|
100 | def reset_ui(count)
|
---|
101 | test_progress_bar.set_style(green_style)
|
---|
102 | test_progress_bar.configure(0, 0, count)
|
---|
103 | @red = false
|
---|
104 |
|
---|
105 | run_count_label.set_text("0")
|
---|
106 | assertion_count_label.set_text("0")
|
---|
107 | failure_count_label.set_text("0")
|
---|
108 | error_count_label.set_text("0")
|
---|
109 |
|
---|
110 | fault_list.remove_items(fault_list.children)
|
---|
111 | end
|
---|
112 |
|
---|
113 | def add_fault(fault)
|
---|
114 | if ( ! @red )
|
---|
115 | test_progress_bar.set_style(red_style)
|
---|
116 | @red = true
|
---|
117 | end
|
---|
118 | item = FaultListItem.new(fault)
|
---|
119 | item.show
|
---|
120 | fault_list.append_items([item])
|
---|
121 | end
|
---|
122 |
|
---|
123 | def show_fault(fault)
|
---|
124 | raw_show_fault(fault.long_display)
|
---|
125 | end
|
---|
126 |
|
---|
127 | def raw_show_fault(string)
|
---|
128 | fault_detail_label.set_text(string)
|
---|
129 | outer_detail_sub_panel.queue_resize
|
---|
130 | end
|
---|
131 |
|
---|
132 | def clear_fault
|
---|
133 | raw_show_fault("")
|
---|
134 | end
|
---|
135 |
|
---|
136 | def result_changed(result)
|
---|
137 | run_count_label.set_text(result.run_count.to_s)
|
---|
138 | assertion_count_label.set_text(result.assertion_count.to_s)
|
---|
139 | failure_count_label.set_text(result.failure_count.to_s)
|
---|
140 | error_count_label.set_text(result.error_count.to_s)
|
---|
141 | end
|
---|
142 |
|
---|
143 | def started(result)
|
---|
144 | @result = result
|
---|
145 | output_status("Started...")
|
---|
146 | end
|
---|
147 |
|
---|
148 | def test_started(test_name)
|
---|
149 | output_status("Running #{test_name}...")
|
---|
150 | end
|
---|
151 |
|
---|
152 | def test_finished(test_name)
|
---|
153 | test_progress_bar.set_value(test_progress_bar.get_value + 1)
|
---|
154 | end
|
---|
155 |
|
---|
156 | def finished(elapsed_time)
|
---|
157 | output_status("Finished in #{elapsed_time} seconds")
|
---|
158 | end
|
---|
159 |
|
---|
160 | def output_status(string)
|
---|
161 | status_entry.set_text(string)
|
---|
162 | end
|
---|
163 |
|
---|
164 | def setup_ui
|
---|
165 | main_window.signal_connect("destroy", nil, &method(:stop))
|
---|
166 | main_window.show_all
|
---|
167 | fault_list.signal_connect("select-child", nil) {
|
---|
168 | | list, item, data |
|
---|
169 | show_fault(item.fault)
|
---|
170 | }
|
---|
171 | fault_list.signal_connect("unselect-child", nil) {
|
---|
172 | clear_fault
|
---|
173 | }
|
---|
174 | @red = false
|
---|
175 | end
|
---|
176 |
|
---|
177 | def main_window
|
---|
178 | lazy_initialize(:main_window) {
|
---|
179 | @main_window = Gtk::Window.new(Gtk::WINDOW_TOPLEVEL)
|
---|
180 | @main_window.set_title("Test::Unit TestRunner")
|
---|
181 | @main_window.set_usize(800, 600)
|
---|
182 | @main_window.set_uposition(20, 20)
|
---|
183 | @main_window.set_policy(true, true, false)
|
---|
184 | @main_window.add(main_panel)
|
---|
185 | }
|
---|
186 | end
|
---|
187 |
|
---|
188 | def main_panel
|
---|
189 | lazy_initialize(:main_panel) {
|
---|
190 | @main_panel = Gtk::VBox.new(false, 0)
|
---|
191 | @main_panel.pack_start(suite_panel, false, false, 0)
|
---|
192 | @main_panel.pack_start(progress_panel, false, false, 0)
|
---|
193 | @main_panel.pack_start(info_panel, false, false, 0)
|
---|
194 | @main_panel.pack_start(list_panel, false, false, 0)
|
---|
195 | @main_panel.pack_start(detail_panel, true, true, 0)
|
---|
196 | @main_panel.pack_start(status_panel, false, false, 0)
|
---|
197 | }
|
---|
198 | end
|
---|
199 |
|
---|
200 | def suite_panel
|
---|
201 | lazy_initialize(:suite_panel) {
|
---|
202 | @suite_panel = Gtk::HBox.new(false, 10)
|
---|
203 | @suite_panel.border_width(10)
|
---|
204 | @suite_panel.pack_start(Gtk::Label.new("Suite:"), false, false, 0)
|
---|
205 | @suite_panel.pack_start(suite_name_entry, true, true, 0)
|
---|
206 | @suite_panel.pack_start(run_button, false, false, 0)
|
---|
207 | }
|
---|
208 | end
|
---|
209 |
|
---|
210 | def suite_name_entry
|
---|
211 | lazy_initialize(:suite_name_entry) {
|
---|
212 | @suite_name_entry = Gtk::Entry.new
|
---|
213 | @suite_name_entry.set_editable(false)
|
---|
214 | }
|
---|
215 | end
|
---|
216 |
|
---|
217 | def run_button
|
---|
218 | lazy_initialize(:run_button) {
|
---|
219 | @run_button = Gtk::Button.new("Run")
|
---|
220 | }
|
---|
221 | end
|
---|
222 |
|
---|
223 | def progress_panel
|
---|
224 | lazy_initialize(:progress_panel) {
|
---|
225 | @progress_panel = Gtk::HBox.new(false, 10)
|
---|
226 | @progress_panel.border_width(10)
|
---|
227 | @progress_panel.pack_start(test_progress_bar, true, true, 0)
|
---|
228 | }
|
---|
229 | end
|
---|
230 |
|
---|
231 | def test_progress_bar
|
---|
232 | lazy_initialize(:test_progress_bar) {
|
---|
233 | @test_progress_bar = EnhancedProgressBar.new
|
---|
234 | @test_progress_bar.set_usize(@test_progress_bar.allocation.width,
|
---|
235 | info_panel.size_request.height)
|
---|
236 | @test_progress_bar.set_style(green_style)
|
---|
237 | }
|
---|
238 | end
|
---|
239 |
|
---|
240 | def green_style
|
---|
241 | lazy_initialize(:green_style) {
|
---|
242 | @green_style = Gtk::Style.new
|
---|
243 | @green_style.set_bg(Gtk::STATE_PRELIGHT, 0x0000, 0xFFFF, 0x0000)
|
---|
244 | }
|
---|
245 | end
|
---|
246 |
|
---|
247 | def red_style
|
---|
248 | lazy_initialize(:red_style) {
|
---|
249 | @red_style = Gtk::Style.new
|
---|
250 | @red_style.set_bg(Gtk::STATE_PRELIGHT, 0xFFFF, 0x0000, 0x0000)
|
---|
251 | }
|
---|
252 | end
|
---|
253 |
|
---|
254 | def info_panel
|
---|
255 | lazy_initialize(:info_panel) {
|
---|
256 | @info_panel = Gtk::HBox.new(false, 0)
|
---|
257 | @info_panel.border_width(10)
|
---|
258 | @info_panel.pack_start(Gtk::Label.new("Runs:"), false, false, 0)
|
---|
259 | @info_panel.pack_start(run_count_label, true, false, 0)
|
---|
260 | @info_panel.pack_start(Gtk::Label.new("Assertions:"), false, false, 0)
|
---|
261 | @info_panel.pack_start(assertion_count_label, true, false, 0)
|
---|
262 | @info_panel.pack_start(Gtk::Label.new("Failures:"), false, false, 0)
|
---|
263 | @info_panel.pack_start(failure_count_label, true, false, 0)
|
---|
264 | @info_panel.pack_start(Gtk::Label.new("Errors:"), false, false, 0)
|
---|
265 | @info_panel.pack_start(error_count_label, true, false, 0)
|
---|
266 | }
|
---|
267 | end
|
---|
268 |
|
---|
269 | def run_count_label
|
---|
270 | lazy_initialize(:run_count_label) {
|
---|
271 | @run_count_label = Gtk::Label.new("0")
|
---|
272 | @run_count_label.set_justify(Gtk::JUSTIFY_LEFT)
|
---|
273 | }
|
---|
274 | end
|
---|
275 |
|
---|
276 | def assertion_count_label
|
---|
277 | lazy_initialize(:assertion_count_label) {
|
---|
278 | @assertion_count_label = Gtk::Label.new("0")
|
---|
279 | @assertion_count_label.set_justify(Gtk::JUSTIFY_LEFT)
|
---|
280 | }
|
---|
281 | end
|
---|
282 |
|
---|
283 | def failure_count_label
|
---|
284 | lazy_initialize(:failure_count_label) {
|
---|
285 | @failure_count_label = Gtk::Label.new("0")
|
---|
286 | @failure_count_label.set_justify(Gtk::JUSTIFY_LEFT)
|
---|
287 | }
|
---|
288 | end
|
---|
289 |
|
---|
290 | def error_count_label
|
---|
291 | lazy_initialize(:error_count_label) {
|
---|
292 | @error_count_label = Gtk::Label.new("0")
|
---|
293 | @error_count_label.set_justify(Gtk::JUSTIFY_LEFT)
|
---|
294 | }
|
---|
295 | end
|
---|
296 |
|
---|
297 | def list_panel
|
---|
298 | lazy_initialize(:list_panel) {
|
---|
299 | @list_panel = Gtk::HBox.new
|
---|
300 | @list_panel.border_width(10)
|
---|
301 | @list_panel.pack_start(list_scrolled_window, true, true, 0)
|
---|
302 | }
|
---|
303 | end
|
---|
304 |
|
---|
305 | def list_scrolled_window
|
---|
306 | lazy_initialize(:list_scrolled_window) {
|
---|
307 | @list_scrolled_window = Gtk::ScrolledWindow.new
|
---|
308 | @list_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
|
---|
309 | @list_scrolled_window.set_usize(@list_scrolled_window.allocation.width, 150)
|
---|
310 | @list_scrolled_window.add_with_viewport(fault_list)
|
---|
311 | }
|
---|
312 | end
|
---|
313 |
|
---|
314 | def fault_list
|
---|
315 | lazy_initialize(:fault_list) {
|
---|
316 | @fault_list = Gtk::List.new
|
---|
317 | }
|
---|
318 | end
|
---|
319 |
|
---|
320 | def detail_panel
|
---|
321 | lazy_initialize(:detail_panel) {
|
---|
322 | @detail_panel = Gtk::HBox.new
|
---|
323 | @detail_panel.border_width(10)
|
---|
324 | @detail_panel.pack_start(detail_scrolled_window, true, true, 0)
|
---|
325 | }
|
---|
326 | end
|
---|
327 |
|
---|
328 | def detail_scrolled_window
|
---|
329 | lazy_initialize(:detail_scrolled_window) {
|
---|
330 | @detail_scrolled_window = Gtk::ScrolledWindow.new
|
---|
331 | @detail_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
|
---|
332 | @detail_scrolled_window.set_usize(400, @detail_scrolled_window.allocation.height)
|
---|
333 | @detail_scrolled_window.add_with_viewport(outer_detail_sub_panel)
|
---|
334 | }
|
---|
335 | end
|
---|
336 |
|
---|
337 | def outer_detail_sub_panel
|
---|
338 | lazy_initialize(:outer_detail_sub_panel) {
|
---|
339 | @outer_detail_sub_panel = Gtk::VBox.new
|
---|
340 | @outer_detail_sub_panel.pack_start(inner_detail_sub_panel, false, false, 0)
|
---|
341 | }
|
---|
342 | end
|
---|
343 |
|
---|
344 | def inner_detail_sub_panel
|
---|
345 | lazy_initialize(:inner_detail_sub_panel) {
|
---|
346 | @inner_detail_sub_panel = Gtk::HBox.new
|
---|
347 | @inner_detail_sub_panel.pack_start(fault_detail_label, false, false, 0)
|
---|
348 | }
|
---|
349 | end
|
---|
350 |
|
---|
351 | def fault_detail_label
|
---|
352 | lazy_initialize(:fault_detail_label) {
|
---|
353 | @fault_detail_label = EnhancedLabel.new("")
|
---|
354 | style = Gtk::Style.new
|
---|
355 | font = Gdk::Font.font_load("-*-Courier New-medium-r-normal--*-120-*-*-*-*-*-*")
|
---|
356 | begin
|
---|
357 | style.set_font(font)
|
---|
358 | rescue ArgumentError; end
|
---|
359 | @fault_detail_label.set_style(style)
|
---|
360 | @fault_detail_label.set_justify(Gtk::JUSTIFY_LEFT)
|
---|
361 | @fault_detail_label.set_line_wrap(false)
|
---|
362 | }
|
---|
363 | end
|
---|
364 |
|
---|
365 | def status_panel
|
---|
366 | lazy_initialize(:status_panel) {
|
---|
367 | @status_panel = Gtk::HBox.new
|
---|
368 | @status_panel.border_width(10)
|
---|
369 | @status_panel.pack_start(status_entry, true, true, 0)
|
---|
370 | }
|
---|
371 | end
|
---|
372 |
|
---|
373 | def status_entry
|
---|
374 | lazy_initialize(:status_entry) {
|
---|
375 | @status_entry = Gtk::Entry.new
|
---|
376 | @status_entry.set_editable(false)
|
---|
377 | }
|
---|
378 | end
|
---|
379 |
|
---|
380 | def lazy_initialize(symbol)
|
---|
381 | if (!instance_eval("defined?(@#{symbol.to_s})"))
|
---|
382 | yield
|
---|
383 | end
|
---|
384 | return instance_eval("@" + symbol.to_s)
|
---|
385 | end
|
---|
386 | end
|
---|
387 |
|
---|
388 | class EnhancedProgressBar < Gtk::ProgressBar
|
---|
389 | def set_style(style)
|
---|
390 | super
|
---|
391 | hide
|
---|
392 | show
|
---|
393 | end
|
---|
394 | end
|
---|
395 |
|
---|
396 | class EnhancedLabel < Gtk::Label
|
---|
397 | def set_text(text)
|
---|
398 | super(text.gsub(/\n\t/, "\n" + (" " * 4)))
|
---|
399 | end
|
---|
400 | end
|
---|
401 |
|
---|
402 | class FaultListItem < Gtk::ListItem
|
---|
403 | attr_reader(:fault)
|
---|
404 | def initialize(fault)
|
---|
405 | super(fault.short_display)
|
---|
406 | @fault = fault
|
---|
407 | end
|
---|
408 | end
|
---|
409 | end
|
---|
410 | end
|
---|
411 | end
|
---|
412 | end
|
---|
413 |
|
---|
414 | if __FILE__ == $0
|
---|
415 | Test::Unit::UI::GTK::TestRunner.start_command_line_test
|
---|
416 | end
|
---|