Class: Syskit::GUI::Testing
- Defined in:
- lib/syskit/gui/testing.rb
Overview
GUI to interface with testing
Defined Under Namespace
Classes: Stats, SubprocessItem
Instance Attribute Summary collapse
-
#app ⇒ Roby::Application
readonly
The roby application we're working on.
-
#discovery_count ⇒ Object
readonly
The count of slaves that are doing discovery.
-
#exception_rendering ⇒ Object
readonly
Returns the value of attribute exception_rendering.
-
#item_model ⇒ Object
readonly
The item model that represents the subprocess state.
-
#manager ⇒ Autorespawn::Manager
readonly
The test manager.
-
#pid_to_slave ⇒ Hash<Integer,Autorespawn::Slave>
readonly
PID-to-slave mapping.
-
#poll_timer ⇒ Object
readonly
The timer used to call #manager.poll periodically.
-
#process_lock ⇒ Object
readonly
Synchronization primitive between the DRb incoming thread and the Qt thread.
-
#process_sync ⇒ Object
readonly
Synchronization primitive between the DRb incoming thread and the Qt thread.
-
#selected_item ⇒ Object
readonly
The currently selected item.
-
#server ⇒ Roby::App::TestServer
readonly
The test server that allow us to communicate with the tests.
-
#slaves ⇒ Hash<Numeric,(Autorespawn::Slave,Qt::StandardItem)>
readonly
Registered slaves.
-
#test_count ⇒ Object
readonly
The count of slaves that are doing discovery.
-
#test_list_ui ⇒ Object
readonly
Returns the value of attribute test_list_ui.
-
#test_result_page ⇒ Object
readonly
Returns the value of attribute test_result_page.
-
#test_result_ui ⇒ Object
readonly
Returns the value of attribute test_result_ui.
-
#work_queue ⇒ Object
readonly
Synchronization primitive between the DRb incoming thread and the Qt thread.
Instance Method Summary collapse
- #add_hooks ⇒ Object
- #add_test_slaves ⇒ Object
- #create_status_bar_ui ⇒ Object
- #create_ui ⇒ Object
-
#deregister_slave_pid(pid) ⇒ Object
Deregister a PID-to-slave mapping.
- #discover_exceptions_from_failure(failure) ⇒ Object
- #display_item_details(item) ⇒ Object
-
#initialize(parent = nil, app: Roby.app, poll_period: 0.1) ⇒ Testing
constructor
A new instance of Testing.
-
#item_from_pid(pid) ⇒ Object
Resolves an item from the slave PID.
-
#item_from_slave(slave) ⇒ Object
Resolves a slave item from its object.
- #process(&block) ⇒ Object
- #process_pending_work ⇒ Object
- #queue_work(&block) ⇒ Object
-
#register_slave(slave) ⇒ Object
Register a new slave and add it to the item model.
-
#register_slave_pid(slave) ⇒ Object
Register a PID-to-slave mapping.
-
#reloaded ⇒ Object
Call this after reloading the app so that the list of tests gets refreshed as well.
- #restore_from_settings(settings) ⇒ Object
- #running? ⇒ Boolean
- #save_to_settings(settings) ⇒ Object
-
#slave_from_pid(pid) ⇒ Object
Resolves a slave from its PID.
- #start ⇒ Object
- #stats ⇒ Object
- #stop ⇒ Object
- #update_item_details ⇒ Object
- #update_selected_item_state ⇒ Object
- #update_status_label(status_label) ⇒ Object
Constructor Details
#initialize(parent = nil, app: Roby.app, poll_period: 0.1) ⇒ Testing
Returns a new instance of Testing
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 |
# File 'lib/syskit/gui/testing.rb', line 61 def initialize(parent = nil, app: Roby.app, poll_period: 0.1) super(parent) @app = app @slaves = Hash.new @pid_to_slave = Hash.new @work_queue = Array.new @process_lock = Mutex.new @process_sync = ConditionVariable.new @discovery_count = 0 @test_count = 0 @running = false @manager = Autorespawn::Manager.new(name: Hash[models: ['syskit-ide']]) @server = Roby::App::TestServer.start(Process.pid) @item_model = Qt::StandardItemModel.new(self) create_ui @test_result_page = MetaRuby::GUI::HTML::Page.new(test_result_ui.page) @exception_rendering = Roby::GUI::ExceptionRendering.new(test_result_page) test_result_page.enable_exception_rendering(exception_rendering) exception_rendering.add_excluded_pattern(/\/lib\/minitest(?:\.rb:|\/)/) exception_rendering.add_excluded_pattern(/\/lib\/autorespawn(?:\.rb:|\/)/) connect test_result_page, SIGNAL('fileOpenClicked(const QUrl&)'), self, SIGNAL('fileOpenClicked(const QUrl&)') test_list_ui.connect(SIGNAL('clicked(const QModelIndex&)')) do |index| item = item_model.item_from_index(index) display_item_details(item) end test_list_ui.connect(SIGNAL('doubleClicked(const QModelIndex&)')) do |index| item = item_model.item_from_index(index) manager.queue(item.slave) end add_hooks @poll_timer = Qt::Timer.new poll_timer.connect(SIGNAL('timeout()')) do manager.poll(autospawn: running?) process_pending_work if item = @selected_item runtime = @selected_item.runtime if runtime != @selected_item_runtime update_selected_item_state @selected_item_runtime = runtime end end end poll_timer.start(Integer(poll_period * 1000)) add_test_slaves emit statsChanged end |
Instance Attribute Details
#app ⇒ Roby::Application (readonly)
Returns the roby application we're working on
11 12 13 |
# File 'lib/syskit/gui/testing.rb', line 11 def app @app end |
#discovery_count ⇒ Object (readonly)
The count of slaves that are doing discovery
53 54 55 |
# File 'lib/syskit/gui/testing.rb', line 53 def discovery_count @discovery_count end |
#exception_rendering ⇒ Object (readonly)
Returns the value of attribute exception_rendering
35 36 37 |
# File 'lib/syskit/gui/testing.rb', line 35 def exception_rendering @exception_rendering end |
#item_model ⇒ Object (readonly)
The item model that represents the subprocess state
31 32 33 |
# File 'lib/syskit/gui/testing.rb', line 31 def item_model @item_model end |
#manager ⇒ Autorespawn::Manager (readonly)
Returns the test manager
14 15 16 |
# File 'lib/syskit/gui/testing.rb', line 14 def manager @manager end |
#pid_to_slave ⇒ Hash<Integer,Autorespawn::Slave> (readonly)
PID-to-slave mapping
28 29 30 |
# File 'lib/syskit/gui/testing.rb', line 28 def pid_to_slave @pid_to_slave end |
#poll_timer ⇒ Object (readonly)
The timer used to call #manager.poll periodically
38 39 40 |
# File 'lib/syskit/gui/testing.rb', line 38 def poll_timer @poll_timer end |
#process_lock ⇒ Object (readonly)
Synchronization primitive between the DRb incoming thread and the Qt thread
46 47 48 |
# File 'lib/syskit/gui/testing.rb', line 46 def process_lock @process_lock end |
#process_sync ⇒ Object (readonly)
Synchronization primitive between the DRb incoming thread and the Qt thread
50 51 52 |
# File 'lib/syskit/gui/testing.rb', line 50 def process_sync @process_sync end |
#selected_item ⇒ Object (readonly)
The currently selected item
59 60 61 |
# File 'lib/syskit/gui/testing.rb', line 59 def selected_item @selected_item end |
#server ⇒ Roby::App::TestServer (readonly)
Returns the test server that allow us to communicate with the tests
18 19 20 |
# File 'lib/syskit/gui/testing.rb', line 18 def server @server end |
#slaves ⇒ Hash<Numeric,(Autorespawn::Slave,Qt::StandardItem)> (readonly)
Registered slaves
23 24 25 |
# File 'lib/syskit/gui/testing.rb', line 23 def slaves @slaves end |
#test_count ⇒ Object (readonly)
The count of slaves that are doing discovery
56 57 58 |
# File 'lib/syskit/gui/testing.rb', line 56 def test_count @test_count end |
#test_list_ui ⇒ Object (readonly)
Returns the value of attribute test_list_ui
32 33 34 |
# File 'lib/syskit/gui/testing.rb', line 32 def test_list_ui @test_list_ui end |
#test_result_page ⇒ Object (readonly)
Returns the value of attribute test_result_page
34 35 36 |
# File 'lib/syskit/gui/testing.rb', line 34 def test_result_page @test_result_page end |
#test_result_ui ⇒ Object (readonly)
Returns the value of attribute test_result_ui
33 34 35 |
# File 'lib/syskit/gui/testing.rb', line 33 def test_result_ui @test_result_ui end |
#work_queue ⇒ Object (readonly)
Synchronization primitive between the DRb incoming thread and the Qt thread
42 43 44 |
# File 'lib/syskit/gui/testing.rb', line 42 def work_queue @work_queue end |
Instance Method Details
#add_hooks ⇒ Object
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 |
# File 'lib/syskit/gui/testing.rb', line 607 def add_hooks manager.on_slave_new do |slave| queue_work do register_slave(slave) emit statsChanged end end manager.on_slave_start do |slave| queue_work do register_slave_pid(slave) item = item_from_slave(slave) item.start if selected_item == item update_item_details end emit statsChanged end end manager.on_slave_finished do |slave| queue_work do deregister_slave_pid(slave.pid) item = item_from_slave(slave) item.finished(slave.status) if selected_item == item update_item_details end end end server.on_exception do |pid, exception| queue_work do item = item_from_pid(pid) item.add_exception(exception) if selected_item == item update_item_details end end end server.on_discovery_start do |pid| queue_work do @discovery_count += 1 item_from_pid(pid).discovery_start end end server.on_discovery_finished do |pid| queue_work do @discovery_count -= 1 item_from_pid(pid).discovery_finished end end server.on_test_start do |pid| queue_work do @test_count += 1 item_from_pid(pid).test_start end end server.on_test_result do |pid, file, test_case_name, test_name, failures, assertions, time| queue_work do item = item_from_pid(pid) item.add_test_result(file, test_case_name, test_name, failures, assertions, time) if !selected_item || (selected_item == item) update_item_details end emit statsChanged end end server.on_test_finished do |pid| queue_work do @test_count -= 1 item_from_pid(pid).test_finished end end end |
#add_test_slaves ⇒ Object
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 |
# File 'lib/syskit/gui/testing.rb', line 680 def add_test_slaves tests = app.discover_test_files.map do |path, models| process_id = Hash[path: path] if !models.empty? process_id[:models] = models.map(&:name).sort end [path, process_id] end argv_set = app.argv_set.flat_map do |string| ['--set', string] end tests.sort_by(&:first).each do |path, process_id| slave = manager.add_slave( Gem.ruby, '-S', 'roby', 'autotest', '--server', server.server_id.to_s, path, '-r', app.robot_name, *argv_set, name: process_id) slave.register_files([Pathname.new(path)]) end end |
#create_status_bar_ui ⇒ Object
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 |
# File 'lib/syskit/gui/testing.rb', line 126 def = Qt::HBoxLayout.new .( = Qt::PushButton.new("Start Tests", self)) connect SIGNAL('started()') do .text = "Stop" end connect SIGNAL('stopped()') do .text = "Start" end .connect(SIGNAL('clicked()')) do if running? stop else start end end .(status_label = StateLabel.new(parent: self), 1) status_label.declare_state("STOPPED", :blue) status_label.declare_state("RUNNING", :green) connect SIGNAL('statsChanged()') do update_status_label(status_label) end return end |
#create_ui ⇒ Object
154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/syskit/gui/testing.rb', line 154 def create_ui layout = Qt::VBoxLayout.new(self) = layout.add_layout() splitter = Qt::Splitter.new(self) layout.(splitter, 1) splitter.(@test_list_ui = Qt::ListView.new(self)) test_list_ui.model = item_model test_list_ui.edit_triggers = Qt::AbstractItemView::NoEditTriggers splitter.(@test_result_ui = Qt::WebView.new(self)) end |
#deregister_slave_pid(pid) ⇒ Object
Deregister a PID-to-slave mapping
576 577 578 579 580 |
# File 'lib/syskit/gui/testing.rb', line 576 def deregister_slave_pid(pid) if !(slave = pid_to_slave.delete(pid)) Roby.warn "no slave registered for PID #{pid}" end end |
#discover_exceptions_from_failure(failure) ⇒ Object
245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/syskit/gui/testing.rb', line 245 def discover_exceptions_from_failure(failure) if failure.kind_of?(Minitest::UnexpectedError) return discover_exceptions_from_failure(failure.exception) end result = [failure] if failure.respond_to?(:original_exceptions) result.concat failure.original_exceptions.flat_map { |e| discover_exceptions_from_failure(e) } end result.uniq end |
#display_item_details(item) ⇒ Object
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 |
# File 'lib/syskit/gui/testing.rb', line 182 def display_item_details(item) @selected_item = item @selected_item_runtime = nil test_result_page.clear if test_file_path = item.slave.name[:path] items = [] items << ['title', 'Test file'] items << ['', test_result_page.link_to(Pathname.new(test_file_path), test_file_path)] models = (item.slave.name[:models] || Array.new) if !models.empty? items << ['title', 'Models'] models.sort.each do |model_name| begin model_object = constant(model_name) rescue NameError items << ['', model_name] next end if definition_file = Roby.app.definition_file_for(model_object) link = test_result_page.link_to(Pathname.new(definition_file), "Open File") items << ['', "#{model_name} [#{link}]"] else items << ['', model_name] end end end items = items.map do |li_class, text| if li_class.empty? "<li>#{text}</li>" else "<li class=\"#{li_class}\">#{text}</li>" end end test_result_page.push nil, "<ul class='body-header-list'>#{items.join("")}</ul>" end update_selected_item_state item.exceptions.each do |e| test_result_page.push_exception(nil, e) end item.each_test_result do |r| name = "#{r.test_case_name}::#{r.test_name}" info = "#{r.skip_count} skips, #{r.failure_count} failures and #{r.assertions} assertions executed in %.3fs" % [r.time] color = if r.failure_count > 0 then :red elsif r.skip_count > 0 then :orange else :green end color = SubprocessItem.html_color(color) style = "padding: .1em; background-color: #{color}" test_result_page.push(nil, "<div class=\"test_result\" style=\"#{style}\">#{MetaRuby::GUI::HTML.escape_html(name)}: #{MetaRuby::GUI::HTML.escape_html(info)}</div>") all_exceptions = r.failures.flat_map do |e| discover_exceptions_from_failure(e) end.uniq all_exceptions.each do |e| test_result_page.push_exception(nil, e) end end end |
#item_from_pid(pid) ⇒ Object
Resolves an item from the slave PID
552 553 554 |
# File 'lib/syskit/gui/testing.rb', line 552 def item_from_pid(pid) item_from_slave(slave_from_pid(pid)) end |
#item_from_slave(slave) ⇒ Object
Resolves a slave item from its object
530 531 532 533 534 535 536 |
# File 'lib/syskit/gui/testing.rb', line 530 def item_from_slave(slave) if info = slaves[slave.object_id] return info[1] else Kernel.raise ArgumentError, "#{slave} is not registered" end end |
#process(&block) ⇒ Object
598 599 600 601 602 603 |
# File 'lib/syskit/gui/testing.rb', line 598 def process(&block) process_lock.synchronize do work_queue << block process_sync.wait(process_lock) end end |
#process_pending_work ⇒ Object
582 583 584 585 586 587 588 589 |
# File 'lib/syskit/gui/testing.rb', line 582 def process_pending_work process_lock.synchronize do while !work_queue.empty? work_queue.shift.call end process_sync.signal end end |
#queue_work(&block) ⇒ Object
592 593 594 595 596 |
# File 'lib/syskit/gui/testing.rb', line 592 def queue_work(&block) process_lock.synchronize do work_queue << block end end |
#register_slave(slave) ⇒ Object
Register a new slave and add it to the item model
557 558 559 560 561 |
# File 'lib/syskit/gui/testing.rb', line 557 def register_slave(slave) item = SubprocessItem.new(app, slave) slaves[slave.object_id] = [slave, item] item_model.append_row(item) end |
#register_slave_pid(slave) ⇒ Object
Register a PID-to-slave mapping
567 568 569 570 571 |
# File 'lib/syskit/gui/testing.rb', line 567 def register_slave_pid(slave) item = item_from_slave(slave) pid_to_slave[slave.pid] = slave item.slave_pid = slave.pid end |
#reloaded ⇒ Object
Call this after reloading the app so that the list of tests gets refreshed as well
312 313 314 315 316 317 318 |
# File 'lib/syskit/gui/testing.rb', line 312 def reloaded slaves.clear pid_to_slave.clear item_model.clear manager.clear add_test_slaves end |
#restore_from_settings(settings) ⇒ Object
119 120 121 122 123 124 |
# File 'lib/syskit/gui/testing.rb', line 119 def restore_from_settings(settings) parallel = settings.value('parallel_level') if !parallel.null? manager.parallel_level = parallel.to_int end end |
#running? ⇒ Boolean
263 264 265 |
# File 'lib/syskit/gui/testing.rb', line 263 def running? @running end |
#save_to_settings(settings) ⇒ Object
116 117 |
# File 'lib/syskit/gui/testing.rb', line 116 def save_to_settings(settings) end |
#slave_from_pid(pid) ⇒ Object
Resolves a slave from its PID
541 542 543 544 545 546 547 |
# File 'lib/syskit/gui/testing.rb', line 541 def slave_from_pid(pid) if slave = pid_to_slave[pid] return slave else Kernel.raise ArgumentError, "no slave registered for PID #{pid}" end end |
#start ⇒ Object
267 268 269 270 271 272 |
# File 'lib/syskit/gui/testing.rb', line 267 def start return if running? @running = true emit statsChanged emit started end |
#stats ⇒ Object
286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/syskit/gui/testing.rb', line 286 def stats stats = Stats.new(manager.slave_count, 0, 0, 0, 0, 0, 0) slaves.each_value do |_, slave| stats.executed_test_count += 1 if slave.has_tested? stats.executed_count += 1 if slave.executed? stats.run_count += slave.run_count stats.failure_count += slave.failure_count stats.assertions_count += slave.assertions_count stats.skip_count += slave.skip_count end # Remove the "self" slave stats.executed_count -= 1 stats end |
#stop ⇒ Object
274 275 276 277 278 279 280 |
# File 'lib/syskit/gui/testing.rb', line 274 def stop manager.kill process_pending_work @running = false emit statsChanged emit stopped end |
#update_item_details ⇒ Object
257 258 259 260 261 |
# File 'lib/syskit/gui/testing.rb', line 257 def update_item_details if selected_item display_item_details(selected_item) end end |
#update_selected_item_state ⇒ Object
168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/syskit/gui/testing.rb', line 168 def update_selected_item_state item = @selected_item if runtime = item.runtime status_text = item.status_text.join("<br/>") if item.finished? test_result_page.push nil, "Run %i ran for %.01fs: %s" % [item.total_run_count, runtime, status_text], id: 'status' elsif runtime = item.runtime test_result_page.push nil, "Run %i currently running %.01fs: %s" % [item.total_run_count, runtime, status_text], id: 'status' end else test_result_page.push nil, "Never ran", id: 'status' end end |
#update_status_label(status_label) ⇒ Object
301 302 303 304 305 306 307 308 |
# File 'lib/syskit/gui/testing.rb', line 301 def update_status_label(status_label) stats = self.stats state_name = if running? then 'RUNNING' else 'STOPPED' end status_label.update_state( state_name, text: "#{stats.executed_count} of #{stats.test_count} test files executed, #{stats.run_count} runs, #{stats.skip_count} skips, #{stats.failure_count} failures and #{stats.assertions_count} assertions") end |