summaryrefslogtreecommitdiff
path: root/lib/test/unit/ui/gtk2/testrunner.rb
blob: 418faa6b2ac9781399841b742c368a22a7dbb3c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
# Author:: Kenta MURATA.
# Copyright:: Copyright (c) 2000-2002 Kenta MURATA. All rights reserved.
# License:: Ruby license.

require "gtk2"
require "test/unit/ui/testrunnermediator"
require "test/unit/ui/testrunnerutilities"

module Test
  module Unit
    module UI
      module GTK2

        Gtk.init

        class EnhancedLabel < Gtk::Label
          def set_text(text)
            super(text.gsub(/\n\t/, "\n    "))
          end
        end

        class FaultList < Gtk::TreeView
          def initialize
            @faults = []
            @model = Gtk::ListStore.new(String, String)
            super(@model)
            column = Gtk::TreeViewColumn.new
            column.visible = false
            append_column(column)
            renderer = Gtk::CellRendererText.new
            column = Gtk::TreeViewColumn.new("Failures", renderer, {:text => 1})
            append_column(column)
            selection.mode = Gtk::SELECTION_SINGLE
            set_rules_hint(true)
            set_headers_visible(false)
          end # def initialize

          def add_fault(fault)
            @faults.push(fault)
            iter = @model.append
            iter.set_value(0, (@faults.length - 1).to_s)
            iter.set_value(1, fault.short_display)
          end # def add_fault(fault)

          def get_fault(iter)
            @faults[iter.get_value(0).to_i]
          end # def get_fault

          def clear
            model.clear
          end # def clear
        end

        class TestRunner
          extend TestRunnerUtilities

          def lazy_initialize(symbol)
            if !instance_eval("defined?(@#{symbol})") then
              yield
            end
            return instance_eval("@#{symbol}")
          end
          private :lazy_initialize

          def status_entry
            lazy_initialize(:status_entry) do
              @status_entry = Gtk::Entry.new
              @status_entry.editable = false
            end
          end
          private :status_entry

          def status_panel
            lazy_initialize(:status_panel) do
              @status_panel = Gtk::HBox.new
              @status_panel.border_width = 10
              @status_panel.pack_start(status_entry, true, true, 0)
            end
          end
          private :status_panel

          def fault_detail_label
            lazy_initialize(:fault_detail_label) do
              @fault_detail_label = EnhancedLabel.new("")
#              style = Gtk::Style.new
#              font = Gdk::Font.
#               font_load("-*-Courier 10 Pitch-medium-r-normal--*-120-*-*-*-*-*-*")
#              style.set_font(font)
#              @fault_detail_label.style = style
              @fault_detail_label.justify = Gtk::JUSTIFY_LEFT
              @fault_detail_label.wrap = false
            end
          end
          private :fault_detail_label

          def inner_detail_sub_panel
            lazy_initialize(:inner_detail_sub_panel) do
              @inner_detail_sub_panel = Gtk::HBox.new
              @inner_detail_sub_panel.pack_start(fault_detail_label, false, false, 0)
            end
          end
          private :inner_detail_sub_panel

          def outer_detail_sub_panel
            lazy_initialize(:outer_detail_sub_panel) do
              @outer_detail_sub_panel = Gtk::VBox.new
              @outer_detail_sub_panel.pack_start(inner_detail_sub_panel, false, false, 0)
            end
          end
          private :outer_detail_sub_panel

          def detail_scrolled_window
            lazy_initialize(:detail_scrolled_window) do
              @detail_scrolled_window = Gtk::ScrolledWindow.new
              @detail_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
              @detail_scrolled_window.
                set_size_request(400, @detail_scrolled_window.allocation.height)
              @detail_scrolled_window.add_with_viewport(outer_detail_sub_panel)
            end
          end
          private :detail_scrolled_window

          def detail_panel
            lazy_initialize(:detail_panel) do
              @detail_panel = Gtk::HBox.new
              @detail_panel.border_width = 10
              @detail_panel.pack_start(detail_scrolled_window, true, true, 0)
            end
          end
          private :detail_panel

          def fault_list
            lazy_initialize(:fault_list) do
              @fault_list = FaultList.new
            end
          end
          private :fault_list

          def list_scrolled_window
            lazy_initialize(:list_scrolled_window) do
              @list_scrolled_window = Gtk::ScrolledWindow.new
              @list_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
              @list_scrolled_window.
                set_size_request(@list_scrolled_window.allocation.width, 150)
              @list_scrolled_window.add_with_viewport(fault_list)
            end
          end
          private :list_scrolled_window

          def list_panel
            lazy_initialize(:list_panel) do
              @list_panel = Gtk::HBox.new
              @list_panel.border_width = 10
              @list_panel.pack_start(list_scrolled_window, true, true, 0)
            end
          end
          private :list_panel

          def error_count_label
            lazy_initialize(:error_count_label) do
              @error_count_label = Gtk::Label.new("0")
              @error_count_label.justify = Gtk::JUSTIFY_LEFT
            end
          end
          private :error_count_label

          def failure_count_label
            lazy_initialize(:failure_count_label) do
              @failure_count_label = Gtk::Label.new("0")
              @failure_count_label.justify = Gtk::JUSTIFY_LEFT
            end
          end
          private :failure_count_label

          def assertion_count_label
            lazy_initialize(:assertion_count_label) do
              @assertion_count_label = Gtk::Label.new("0")
              @assertion_count_label.justify = Gtk::JUSTIFY_LEFT
            end
          end
          private :assertion_count_label

          def run_count_label
            lazy_initialize(:run_count_label) do
              @run_count_label = Gtk::Label.new("0")
              @run_count_label.justify = Gtk::JUSTIFY_LEFT
            end
          end
          private :run_count_label
          
          def info_panel
            lazy_initialize(:info_panel) do
              @info_panel = Gtk::HBox.new(false, 0)
              @info_panel.border_width = 10
              @info_panel.pack_start(Gtk::Label.new("Runs:"), false, false, 0)
              @info_panel.pack_start(run_count_label, true, false, 0)
              @info_panel.pack_start(Gtk::Label.new("Assertions:"), false, false, 0)
              @info_panel.pack_start(assertion_count_label, true, false, 0)
              @info_panel.pack_start(Gtk::Label.new("Failures:"), false, false, 0)
              @info_panel.pack_start(failure_count_label, true, false, 0)
              @info_panel.pack_start(Gtk::Label.new("Errors:"), false, false, 0)
              @info_panel.pack_start(error_count_label, true, false, 0)
            end
          end # def info_panel
          private :info_panel

          def green_style
            lazy_initialize(:green_style) do
              @green_style = Gtk::Style.new
              @green_style.set_bg(Gtk::STATE_PRELIGHT, 0x0000, 0xFFFF, 0x0000)
            end
          end # def green_style
          private :green_style
          
          def red_style
            lazy_initialize(:red_style) do
              @red_style = Gtk::Style.new
              @red_style.set_bg(Gtk::STATE_PRELIGHT, 0xFFFF, 0x0000, 0x0000)
            end
          end # def red_style
          private :red_style
          
          def test_progress_bar
            lazy_initialize(:test_progress_bar) {
              @test_progress_bar = Gtk::ProgressBar.new
              @test_progress_bar.fraction = 0.0
              @test_progress_bar.
                set_size_request(@test_progress_bar.allocation.width,
                                 info_panel.size_request[1])
              @test_progress_bar.style = green_style
            }
          end # def test_progress_bar
          private :test_progress_bar
          
          def progress_panel
            lazy_initialize(:progress_panel) do
              @progress_panel = Gtk::HBox.new(false, 10)
              @progress_panel.border_width = 10
              @progress_panel.pack_start(test_progress_bar, true, true, 0)
            end
          end # def progress_panel

          def run_button
            lazy_initialize(:run_button) do
              @run_button = Gtk::Button.new("Run")
            end
          end # def run_button

          def suite_name_entry
            lazy_initialize(:suite_name_entry) do
              @suite_name_entry = Gtk::Entry.new
              @suite_name_entry.editable = false
            end
          end # def suite_name_entry
          private :suite_name_entry

          def suite_panel
            lazy_initialize(:suite_panel) do
              @suite_panel = Gtk::HBox.new(false, 10)
              @suite_panel.border_width = 10
              @suite_panel.pack_start(Gtk::Label.new("Suite:"), false, false, 0)
              @suite_panel.pack_start(suite_name_entry, true, true, 0)
              @suite_panel.pack_start(run_button, false, false, 0)
            end
          end # def suite_panel
          private :suite_panel

          def main_panel
            lazy_initialize(:main_panel) do
              @main_panel = Gtk::VBox.new(false, 0)
              @main_panel.pack_start(suite_panel, false, false, 0)
              @main_panel.pack_start(progress_panel, false, false, 0)
              @main_panel.pack_start(info_panel, false, false, 0)
              @main_panel.pack_start(list_panel, false, false, 0)
              @main_panel.pack_start(detail_panel, true, true, 0)
              @main_panel.pack_start(status_panel, false, false, 0)
            end
          end # def main_panel
          private :main_panel

          def main_window
            lazy_initialize(:main_window) do
              @main_window = Gtk::Window.new(Gtk::Window::TOPLEVEL)
              @main_window.set_title("Test::Unit TestRunner")
              @main_window.set_default_size(800, 600)
              @main_window.set_resizable(true)
              @main_window.add(main_panel)
            end
          end # def main_window
          private :main_window

          def setup_ui
            main_window.signal_connect("destroy", nil) { stop }
            main_window.show_all
            fault_list.selection.signal_connect("changed", nil) do
              |selection, data|
              if selection.selected then
                show_fault(fault_list.get_fault(selection.selected))
              else
                clear_fault
              end
            end
          end # def setup_ui
          private :setup_ui

          def output_status(string)
            status_entry.set_text(string)
          end # def output_status(string)
          private :output_status

          def finished(elapsed_time)
            test_progress_bar.fraction = 1.0
            output_status("Finished in #{elapsed_time} seconds")
          end # def finished(elapsed_time)
          private :finished

          def test_started(test_name)
            output_status("Running #{test_name}...")
          end # def test_started(test_name)
          private :test_started

          def started(result)
            @result = result
            output_status("Started...")
          end # def started(result)
          private :started

          def test_finished(result)
            test_progress_bar.fraction += 1.0 / @count
          end # def test_finished(result)

          def result_changed(result)
            run_count_label.label = result.run_count.to_s
            assertion_count_label.label = result.assertion_count.to_s
            failure_count_label.label = result.failure_count.to_s
            error_count_label.label = result.error_count.to_s
          end # def result_changed(result)
          private :result_changed

          def clear_fault
            raw_show_fault("")
          end # def clear_fault
          private :clear_fault

          def raw_show_fault(string)
            fault_detail_label.set_text(string)
            outer_detail_sub_panel.queue_resize
          end # def raw_show_fault(string)
          private :raw_show_fault

          def show_fault(fault)
            raw_show_fault(fault.long_display)
          end # def show_fault(fault)
          private :show_fault

          def add_fault(fault)
            if not @red then
              test_progress_bar.style = red_style
              @red = true
            end
            fault_list.add_fault(fault)
          end # def add_fault(fault)
          private :add_fault

          def reset_ui(count)
            test_progress_bar.style = green_style
            test_progress_bar.fraction = 0.0
            @count = count + 1
            @red = false

            run_count_label.set_text("0")
            assertion_count_label.set_text("0")
            failure_count_label.set_text("0")
            error_count_label.set_text("0")

            fault_list.clear
          end # def reset_ui(count)
          private :reset_ui

          def stop
            Gtk.main_quit
          end # def stop
          private :stop

          def run_test
            @runner.raise(@restart_signal)
          end
          private :run_test

          def start_ui
            @viewer.run
            running = false
            begin
              loop do
                if (running ^= true)
                  run_button.child.text = "Stop"
                  @mediator.run_suite
                else
                  run_button.child.text = "Run"
                  @viewer.join
                  break
                end
              end
            rescue @restart_signal
              retry
            rescue
            end
          end # def start_ui
          private :start_ui

          def attach_to_mediator
            run_button.signal_connect("clicked", nil) { run_test }
            @mediator.add_listener(TestRunnerMediator::RESET, &method(:reset_ui))
            @mediator.add_listener(TestRunnerMediator::STARTED, &method(:started))
            @mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished))
            @mediator.add_listener(TestResult::FAULT, &method(:add_fault))
            @mediator.add_listener(TestResult::CHANGED, &method(:result_changed))
            @mediator.add_listener(TestCase::STARTED, &method(:test_started))
            @mediator.add_listener(TestCase::FINISHED, &method(:test_finished))
          end # def attach_to_mediator
          private :attach_to_mediator

          def setup_mediator
            @mediator = TestRunnerMediator.new(@suite)
            suite_name = @suite.to_s
            if @suite.kind_of?(Module) then
              suite_name = @suite.name
            end
            suite_name_entry.set_text(suite_name)
          end # def setup_mediator
          private :setup_mediator

          def start
            setup_mediator
            setup_ui
            attach_to_mediator
            start_ui
            @result
          end # def start

          def initialize(suite, output_level = NORMAL)
            if suite.respond_to?(:suite) then
              @suite = suite.suite
            else
              @suite = suite
            end
            @result = nil

            @runner = Thread.current
            @restart_signal = Class.new(Exception)
            @viewer = Thread.start do
              @runner.join rescue @runner.run
              Gtk.main
            end
            @viewer.join rescue nil # wait deadlock to handshake
          end # def initialize(suite)

        end # class TestRunner

      end # module GTK2
    end # module UI
  end # module Unit
end # module Test