| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # | 2 # |
| 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 | 7 |
| 8 # DESCRIPTION : | 8 # DESCRIPTION : |
| 9 # | 9 # |
| 10 # This UI is intended to be used by the factory autotest suite to | 10 # This UI is intended to be used by the factory autotest suite to |
| 11 # provide factory operators feedback on test status and control over | 11 # provide factory operators feedback on test status and control over |
| 12 # execution order. | 12 # execution order. |
| 13 # | 13 # |
| 14 # In short, the UI is composed of a 'console' panel on the bottom of | 14 # In short, the UI is composed of a 'console' panel on the bottom of |
| 15 # the screen which displays the autotest log, and there is also a | 15 # the screen which displays the autotest log, and there is also a |
| 16 # 'test list' panel on the right hand side of the screen. The | 16 # 'test list' panel on the right hand side of the screen. The |
| 17 # majority of the screen is dedicated to tests, which are executed in | 17 # majority of the screen is dedicated to tests, which are executed in |
| 18 # seperate processes, but instructed to display their own UIs in this | 18 # seperate processes, but instructed to display their own UIs in this |
| 19 # dedicated area whenever possible. Tests in the test list are | 19 # dedicated area whenever possible. Tests in the test list are |
| 20 # executed in order by default, but can be activated on demand via | 20 # executed in order by default, but can be activated on demand via |
| 21 # associated keyboard shortcuts (triggers). As tests are run, their | 21 # associated keyboard shortcuts. As tests are run, their status is |
| 22 # status is color-indicated to the operator -- greyed out means | 22 # color-indicated to the operator -- greyed out means untested, yellow |
| 23 # untested, yellow means active, green passed and red failed. | 23 # means active, green passed and red failed. |
| 24 | 24 |
| 25 | 25 |
| 26 import gobject | 26 import gobject |
| 27 import gtk | 27 import gtk |
| 28 import imp | 28 import imp |
| 29 import os | 29 import os |
| 30 import pango | 30 import pango |
| 31 import subprocess | 31 import subprocess |
| 32 import sys | 32 import sys |
| 33 import time | 33 import time |
| 34 | 34 |
| 35 import common | 35 import common |
| 36 import factory | 36 import factory |
| 37 import factory_ui_lib as ful | 37 import factory_ui_lib as ful |
| 38 | 38 |
| 39 from factory import TestData | 39 |
| 40 # These definitions are expose these classes directly into this |
| 41 # namespace, so that the test_list that is sent from the control file |
| 42 # can have cleaner syntax. These are done in this fashion, as opposed |
| 43 # to "from factory import <class>" to work-around Python namespace |
| 44 # wackiness -- the from syntax does not work, creating new classes. |
| 45 OperatorTest = factory.OperatorTest |
| 46 InformationScreen = factory.InformationScreen |
| 47 AutomatedSequence = factory.AutomatedSequence |
| 48 AutomatedSubTest = factory.AutomatedSubTest |
| 49 AutomatedRebootSubTest = factory.AutomatedRebootSubTest |
| 40 | 50 |
| 41 | 51 |
| 42 _SEP_COLOR = gtk.gdk.color_parse('grey50') | 52 _SEP_COLOR = gtk.gdk.color_parse('grey50') |
| 43 | 53 |
| 44 _LABEL_EN_SIZE = (160, 35) | 54 _LABEL_EN_SIZE = (170, 35) |
| 45 _LABEL_ZW_SIZE = (70, 35) | 55 _LABEL_ZW_SIZE = (70, 35) |
| 46 _LABEL_EN_FONT = pango.FontDescription('courier new extra-condensed 16') | 56 _LABEL_EN_FONT = pango.FontDescription('courier new extra-condensed 16') |
| 47 _LABEL_ZW_FONT = pango.FontDescription('normal 12') | 57 _LABEL_ZW_FONT = pango.FontDescription('normal 12') |
| 48 _LABEL_T_SIZE = (30, 35) | 58 _LABEL_T_SIZE = (30, 35) |
| 49 _LABEL_T_FONT = pango.FontDescription('courier new italic ultra-condensed 10') | 59 _LABEL_T_FONT = pango.FontDescription('courier new italic ultra-condensed 10') |
| 50 _LABEL_UNTESTED_FG = gtk.gdk.color_parse('grey40') | 60 _LABEL_UNTESTED_FG = gtk.gdk.color_parse('grey40') |
| 51 _LABEL_TROUGH_COLOR = gtk.gdk.color_parse('grey20') | 61 _LABEL_TROUGH_COLOR = gtk.gdk.color_parse('grey20') |
| 52 _LABEL_STATUS_SIZE = (140, 30) | 62 _LABEL_STATUS_SIZE = (140, 30) |
| 53 _LABEL_STATUS_FONT = pango.FontDescription( | 63 _LABEL_STATUS_FONT = pango.FontDescription( |
| 54 'courier new bold extra-condensed 16') | 64 'courier new bold extra-condensed 16') |
| 55 _OTHER_LABEL_FONT = pango.FontDescription('courier new condensed 20') | 65 _OTHER_LABEL_FONT = pango.FontDescription('courier new condensed 20') |
| 56 | 66 |
| 57 _ST_LABEL_EN_SIZE = (250, 35) | 67 _ST_LABEL_EN_SIZE = (250, 35) |
| 58 _ST_LABEL_ZW_SIZE = (150, 35) | 68 _ST_LABEL_ZW_SIZE = (150, 35) |
| 59 | 69 |
| 60 _STATUS_REFRESH_MS = 200 | 70 _STATUS_REFRESH_MS = 100 |
| 61 | 71 |
| 62 class Console: | 72 class Console: |
| 63 '''Display a progress log. Implemented by launching an borderless | 73 '''Display a progress log. Implemented by launching an borderless |
| 64 xterm at a strategic location, and running tail against the log.''' | 74 xterm at a strategic location, and running tail against the log.''' |
| 65 | 75 |
| 66 def __init__(self, allocation): | 76 def __init__(self, allocation): |
| 67 xterm_coords = '135x14+%d+%d' % (allocation.x, allocation.y) | 77 xterm_coords = '135x14+%d+%d' % (allocation.x, allocation.y) |
| 68 factory.log('xterm_coords = %s' % xterm_coords) | 78 factory.log('xterm_coords = %s' % xterm_coords) |
| 69 xterm_cmd = (('aterm --geometry %s -bw 0 -e bash -c ' % | 79 xterm_cmd = (('aterm --geometry %s -bw 0 -e bash -c ' % |
| 70 xterm_coords).split() + | 80 xterm_coords).split() + |
| 71 ['tail -f %s | grep FACTORY' % factory.LOG_PATH]) | 81 ['tail -f %s | grep FACTORY' % factory.LOG_PATH]) |
| 72 factory.log('xterm_cmd = %s' % xterm_cmd) | 82 factory.log('xterm_cmd = %s' % xterm_cmd) |
| 73 self._proc = subprocess.Popen(xterm_cmd) | 83 self._proc = subprocess.Popen(xterm_cmd) |
| 74 | 84 |
| 75 def __del__(self): | 85 def __del__(self): |
| 76 factory.log('console_proc __del__') | 86 factory.log('console_proc __del__') |
| 77 self._proc.kill() | 87 self._proc.kill() |
| 78 | 88 |
| 79 | 89 |
| 80 # Routines to communicate with the autotest control file, using python | |
| 81 # expressions. The stdin_callback assures notification for any new | |
| 82 # messages. | |
| 83 | |
| 84 def stdin_callback(s, c): | |
| 85 factory.log('stdin_callback, quitting gtk main') | |
| 86 gtk.main_quit() | |
| 87 return True | |
| 88 | |
| 89 def control_recv(): | |
| 90 return eval(sys.stdin.readline().rstrip()) | |
| 91 | |
| 92 def control_send(x): | |
| 93 print repr(x) | |
| 94 sys.stdout.flush() | |
| 95 | |
| 96 def control_send_target_test_update(test, count): | |
| 97 factory.log('ui send_target_test_update %s.%s_%s' % | |
| 98 (test.formal_name, test.tag_prefix, count)) | |
| 99 control_send((test.formal_name, test.tag_prefix, count)) | |
| 100 | |
| 101 | |
| 102 # Capture keyboard events here for debugging -- under normal | 90 # Capture keyboard events here for debugging -- under normal |
| 103 # circumstances, all keyboard events should be captured by executing | 91 # circumstances, all keyboard events should be captured by executing |
| 104 # tests, and hence this should not be called. | 92 # tests, and hence this should not be called. |
| 105 | 93 |
| 106 def handle_key_release_event(_, event): | 94 def handle_key_release_event(_, event): |
| 107 factory.log('base ui key event (%s)' % event.keyval) | 95 factory.log('base ui key event (%s)' % event.keyval) |
| 108 return True | 96 return True |
| 109 | 97 |
| 110 | 98 |
| 111 class TestLabelBox(gtk.EventBox): | 99 class TestLabelBox(gtk.EventBox): |
| 112 | 100 |
| 113 def __init__(self, test): | 101 def __init__(self, test): |
| 114 gtk.EventBox.__init__(self) | 102 gtk.EventBox.__init__(self) |
| 115 self.modify_bg(gtk.STATE_NORMAL, ful.LABEL_COLORS[ful.UNTESTED]) | 103 self.modify_bg(gtk.STATE_NORMAL, ful.LABEL_COLORS[ful.UNTESTED]) |
| 116 label_en = gtk.Label(test.label_en) | 104 |
| 117 label_en.set_size_request(*_LABEL_EN_SIZE) | 105 label_en = ful.make_label(test.label_en, size=_LABEL_EN_SIZE, |
| 118 label_en.modify_font(_LABEL_EN_FONT) | 106 font=_LABEL_EN_FONT, alignment=(0.5, 0.5), |
| 119 label_en.set_alignment(0.5, 0.5) | 107 fg=_LABEL_UNTESTED_FG) |
| 120 label_en.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG) | 108 label_zw = ful.make_label(test.label_zw, size=_LABEL_ZW_SIZE, |
| 121 label_zw = gtk.Label(test.label_zw) | 109 font=_LABEL_ZW_FONT, alignment=(0.5, 0.5), |
| 122 label_zw.set_size_request(*_LABEL_ZW_SIZE) | 110 fg=_LABEL_UNTESTED_FG) |
| 123 label_zw.modify_font(_LABEL_ZW_FONT) | 111 label_t = ful.make_label('C-' + test.kbd_shortcut, size=_LABEL_T_SIZE, |
| 124 label_zw.set_alignment(0.5, 0.5) | 112 font=_LABEL_T_FONT, alignment=(0.5, 0.5), |
| 125 label_zw.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG) | 113 fg=ful.BLACK) |
| 126 label_t = gtk.Label('C-' + test.trigger) | |
| 127 label_t.set_size_request(*_LABEL_T_SIZE) | |
| 128 label_t.modify_font(_LABEL_T_FONT) | |
| 129 label_t.set_alignment(0.5, 0.5) | |
| 130 label_t.modify_fg(gtk.STATE_NORMAL, ful.BLACK) | |
| 131 hbox = gtk.HBox() | 114 hbox = gtk.HBox() |
| 132 hbox.pack_start(label_en, False, False) | 115 hbox.pack_start(label_en, False, False) |
| 133 hbox.pack_start(label_zw, False, False) | 116 hbox.pack_start(label_zw, False, False) |
| 134 hbox.pack_start(label_t, False, False) | 117 hbox.pack_start(label_t, False, False) |
| 135 self.add(hbox) | 118 self.add(hbox) |
| 136 self.label_list = [label_en, label_zw] | 119 self.label_list = [label_en, label_zw] |
| 137 | 120 |
| 138 def update(self, status): | 121 def update(self, status): |
| 139 label_fg = status == ful.UNTESTED and _LABEL_UNTESTED_FG or ful.BLACK | 122 label_fg = status == ful.UNTESTED and _LABEL_UNTESTED_FG or ful.BLACK |
| 140 for label in self.label_list: | 123 for label in self.label_list: |
| 141 label.modify_fg(gtk.STATE_NORMAL, label_fg) | 124 label.modify_fg(gtk.STATE_NORMAL, label_fg) |
| 142 self.modify_bg(gtk.STATE_NORMAL, ful.LABEL_COLORS[status]) | 125 self.modify_bg(gtk.STATE_NORMAL, ful.LABEL_COLORS[status]) |
| 143 self.queue_draw() | 126 self.queue_draw() |
| 144 | 127 |
| 145 | 128 |
| 146 class SubTestLabelBox(gtk.EventBox): | 129 class SubTestLabelBox(gtk.EventBox): |
| 147 | 130 |
| 148 def __init__(self, test): | 131 def __init__(self, test): |
| 149 gtk.EventBox.__init__(self) | 132 gtk.EventBox.__init__(self) |
| 150 self.modify_bg(gtk.STATE_NORMAL, ful.BLACK) | 133 self.modify_bg(gtk.STATE_NORMAL, ful.BLACK) |
| 151 label_status = gtk.Label(ful.UNTESTED) | 134 label_status = ful.make_label(ful.UNTESTED, size=_LABEL_STATUS_SIZE, |
| 152 label_status.set_size_request(*_LABEL_STATUS_SIZE) | 135 alignment=(0, 0.5), |
| 153 label_status.set_alignment(0, 0.5) | 136 font=_LABEL_STATUS_FONT, |
| 154 label_status.modify_font(_LABEL_STATUS_FONT) | 137 fg=_LABEL_UNTESTED_FG) |
| 155 label_status.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG) | 138 label_sep = ful.make_label(' : ', alignment=(0.5, 0.5), |
| 156 label_sep = gtk.Label(' : ') | 139 font=_LABEL_EN_FONT) |
| 157 label_sep.set_alignment(0.5, 0.5) | 140 label_en = ful.make_label(test.label_en, size=_ST_LABEL_EN_SIZE, |
| 158 label_sep.modify_font(_LABEL_EN_FONT) | 141 alignment=(0.5, 0.5), |
| 159 label_sep.modify_fg(gtk.STATE_NORMAL, ful.LIGHT_GREEN) | 142 font=_LABEL_EN_FONT) |
| 160 label_en = gtk.Label(test.label_en) | 143 label_zw = ful.make_label(test.label_zw, size=_ST_LABEL_ZW_SIZE, |
| 161 label_en.set_size_request(*_ST_LABEL_EN_SIZE) | 144 alignment=(0.5, 0.5), font=_LABEL_ZW_FONT) |
| 162 label_en.set_alignment(0.5, 0.5) | |
| 163 label_en.modify_font(_LABEL_EN_FONT) | |
| 164 label_en.modify_fg(gtk.STATE_NORMAL, ful.LIGHT_GREEN) | |
| 165 label_zw = gtk.Label(test.label_zw) | |
| 166 label_zw.set_size_request(*_ST_LABEL_ZW_SIZE) | |
| 167 label_zw.set_alignment(0.5, 0.5) | |
| 168 label_zw.modify_font(_LABEL_ZW_FONT) | |
| 169 label_zw.modify_fg(gtk.STATE_NORMAL, ful.LIGHT_GREEN) | |
| 170 hbox = gtk.HBox() | 145 hbox = gtk.HBox() |
| 171 hbox.pack_end(label_status, False, False) | 146 hbox.pack_end(label_status, False, False) |
| 172 hbox.pack_end(label_sep, False, False) | 147 hbox.pack_end(label_sep, False, False) |
| 173 hbox.pack_end(label_zw, False, False) | 148 hbox.pack_end(label_zw, False, False) |
| 174 hbox.pack_end(label_en, False, False) | 149 hbox.pack_end(label_en, False, False) |
| 175 self.add(hbox) | 150 self.add(hbox) |
| 176 self.label_status = label_status | 151 self.label_status = label_status |
| 177 | 152 |
| 178 def update(self, status): | 153 def update(self, status): |
| 179 if status == ful.UNTESTED: | 154 if status == ful.UNTESTED: |
| 180 return | 155 return |
| 181 self.label_status.set_text(status) | 156 self.label_status.set_text(status) |
| 182 self.label_status.modify_fg(gtk.STATE_NORMAL, ful.LABEL_COLORS[status]) | 157 self.label_status.modify_fg(gtk.STATE_NORMAL, ful.LABEL_COLORS[status]) |
| 183 self.queue_draw() | 158 self.queue_draw() |
| 184 | 159 |
| 185 | 160 |
| 161 class UiState(): |
| 162 |
| 163 def __init__(self, window, status_map, test_widget_box): |
| 164 |
| 165 def make_empty_test_label_widget(): |
| 166 label_box = gtk.EventBox() |
| 167 label_box.modify_bg(gtk.STATE_NORMAL, ful.BLACK) |
| 168 label = ful.make_label('no active test', font=_OTHER_LABEL_FONT, |
| 169 alignment=(0.5, 0.5)) |
| 170 label_box.add(label) |
| 171 return label_box |
| 172 |
| 173 def make_automated_seq_label_widget(subtest_list): |
| 174 vbox = gtk.VBox() |
| 175 vbox.set_spacing(0) |
| 176 for subtest in subtest_list: |
| 177 label_box = SubTestLabelBox(subtest) |
| 178 status_map.set_label_box(subtest, label_box) |
| 179 vbox.pack_start(status_map.lookup_label_box(subtest), |
| 180 False, False) |
| 181 return vbox |
| 182 |
| 183 self._window = window |
| 184 self._status_map = status_map |
| 185 self._test_widget_box = test_widget_box |
| 186 self._empty_test_widget = make_empty_test_label_widget() |
| 187 self._active_test_widget = self._empty_test_widget |
| 188 self.active_test = None |
| 189 |
| 190 self._test_widget_box.add(self._empty_test_widget) |
| 191 |
| 192 self._automated_seq_widget_map = dict( |
| 193 (t, make_automated_seq_label_widget(t.subtest_list)) |
| 194 for t in self._status_map.test_db.seq_test_set) |
| 195 |
| 196 def set_active_test(self, test): |
| 197 '''Control what kind of widget is shown in the testing area of |
| 198 the screen. For normal operator tests, this is just a label |
| 199 saying there is no active test. The expectation is that the |
| 200 operator test itself has a window obscuring this message. For |
| 201 automated sequences, since there is no other window, the no |
| 202 active test message is replaced with an updated list of |
| 203 subtest status.''' |
| 204 if test == self.active_test: |
| 205 return |
| 206 self.active_test = test |
| 207 self._test_widget_box.remove(self._active_test_widget) |
| 208 active_widget = (test in self._status_map.test_db.seq_test_set |
| 209 and self._automated_seq_widget_map[test] |
| 210 or self._empty_test_widget) |
| 211 self._test_widget_box.add(active_widget) |
| 212 self._active_test_widget = active_widget |
| 213 self._window.show_all() |
| 214 |
| 215 |
| 186 def make_hsep(width=1): | 216 def make_hsep(width=1): |
| 187 frame = gtk.EventBox() | 217 frame = gtk.EventBox() |
| 188 frame.set_size_request(-1, width) | 218 frame.set_size_request(-1, width) |
| 189 frame.modify_bg(gtk.STATE_NORMAL, _SEP_COLOR) | 219 frame.modify_bg(gtk.STATE_NORMAL, _SEP_COLOR) |
| 190 return frame | 220 return frame |
| 191 | 221 |
| 192 | 222 |
| 193 def make_vsep(width=1): | 223 def make_vsep(width=1): |
| 194 frame = gtk.EventBox() | 224 frame = gtk.EventBox() |
| 195 frame.set_size_request(width, -1) | 225 frame.set_size_request(width, -1) |
| 196 frame.modify_bg(gtk.STATE_NORMAL, _SEP_COLOR) | 226 frame.modify_bg(gtk.STATE_NORMAL, _SEP_COLOR) |
| 197 return frame | 227 return frame |
| 198 | 228 |
| 199 | 229 |
| 200 def make_notest_label(): | 230 def refresh_status(status_map, ui_state): |
| 201 label = gtk.Label('no active test') | 231 status_map.read_new_data() |
| 202 label.modify_font(_OTHER_LABEL_FONT) | 232 active_test = status_map.get_active_top_level_test() |
| 203 label.set_alignment(0.5, 0.5) | 233 ui_state.set_active_test(active_test) |
| 204 label.modify_fg(gtk.STATE_NORMAL, ful.LIGHT_GREEN) | 234 return True |
| 205 box = gtk.EventBox() | |
| 206 box.modify_bg(gtk.STATE_NORMAL, ful.BLACK) | |
| 207 box.add(label) | |
| 208 return box | |
| 209 | |
| 210 | |
| 211 def make_automated_seq_widget(as_test, status_map): | |
| 212 vbox = gtk.VBox() | |
| 213 vbox.set_spacing(0) | |
| 214 map(lambda st: vbox.pack_start(status_map.lookup_label(st), False, False), | |
| 215 as_test.automated_seq) | |
| 216 return vbox | |
| 217 | 235 |
| 218 | 236 |
| 219 def main(): | 237 def main(): |
| 238 '''This process is launched by the autotest suite_Factory control |
| 239 process. Communication with this process is an exchange of well |
| 240 formed python expressions over stdin and stdout. Basically |
| 241 sending wraps arguments in a call to repr() and recv calls eval() |
| 242 to re-generate the python data.''' |
| 243 |
| 244 def control_recv(): |
| 245 return eval(sys.stdin.readline().rstrip()) |
| 246 |
| 247 def control_send(x): |
| 248 print repr(x) |
| 249 sys.stdout.flush() |
| 250 |
| 251 # On startup, get the list of tests to run (in order) and the |
| 252 # autotest status file path. |
| 253 factory.log('pulling control info') |
| 254 test_list = control_recv() |
| 255 status_file_path = control_recv() |
| 256 |
| 257 status_map = factory.StatusMap(test_list, status_file_path) |
| 258 |
| 220 window = gtk.Window(gtk.WINDOW_TOPLEVEL) | 259 window = gtk.Window(gtk.WINDOW_TOPLEVEL) |
| 221 window.connect('destroy', lambda _: gtk.main_quit()) | 260 window.connect('destroy', lambda _: gtk.main_quit()) |
| 222 window.modify_bg(gtk.STATE_NORMAL, ful.BLACK) | 261 window.modify_bg(gtk.STATE_NORMAL, ful.BLACK) |
| 223 | 262 |
| 224 screen = window.get_screen() | 263 screen = window.get_screen() |
| 225 screen_size = (screen.get_width(), screen.get_height()) | 264 screen_size = (screen.get_width(), screen.get_height()) |
| 226 window.set_size_request(*screen_size) | 265 window.set_size_request(*screen_size) |
| 227 | 266 |
| 228 label_trough = gtk.VBox() | 267 label_trough = gtk.VBox() |
| 229 label_trough.set_spacing(0) | 268 label_trough.set_spacing(0) |
| 230 | 269 |
| 231 rhs_box = gtk.EventBox() | 270 rhs_box = gtk.EventBox() |
| 232 rhs_box.modify_bg(gtk.STATE_NORMAL, _LABEL_TROUGH_COLOR) | 271 rhs_box.modify_bg(gtk.STATE_NORMAL, _LABEL_TROUGH_COLOR) |
| 233 rhs_box.add(label_trough) | 272 rhs_box.add(label_trough) |
| 234 | 273 |
| 235 console_box = gtk.EventBox() | 274 console_box = gtk.EventBox() |
| 236 console_box.set_size_request(-1, 180) | 275 console_box.set_size_request(-1, 180) |
| 237 console_box.modify_bg(gtk.STATE_NORMAL, ful.BLACK) | 276 console_box.modify_bg(gtk.STATE_NORMAL, ful.BLACK) |
| 238 | 277 |
| 239 notest_label = make_notest_label() | |
| 240 | |
| 241 test_widget_box = gtk.Alignment(xalign=0.5, yalign=0.5) | 278 test_widget_box = gtk.Alignment(xalign=0.5, yalign=0.5) |
| 242 test_widget_box.set_size_request(-1, -1) | 279 test_widget_box.set_size_request(-1, -1) |
| 243 test_widget_box.add(notest_label) | 280 |
| 281 ui_state = UiState(window, status_map, test_widget_box) |
| 244 | 282 |
| 245 lhs_box = gtk.VBox() | 283 lhs_box = gtk.VBox() |
| 246 lhs_box.pack_end(console_box, False, False) | 284 lhs_box.pack_end(console_box, False, False) |
| 247 lhs_box.pack_start(test_widget_box) | 285 lhs_box.pack_start(test_widget_box) |
| 248 lhs_box.pack_start(make_hsep(3), False, False) | 286 lhs_box.pack_start(make_hsep(3), False, False) |
| 249 | 287 |
| 250 base_box = gtk.HBox() | 288 base_box = gtk.HBox() |
| 251 base_box.pack_end(rhs_box, False, False) | 289 base_box.pack_end(rhs_box, False, False) |
| 252 base_box.pack_end(make_vsep(3), False, False) | 290 base_box.pack_end(make_vsep(3), False, False) |
| 253 base_box.pack_start(lhs_box) | 291 base_box.pack_start(lhs_box) |
| 254 | 292 |
| 255 window.connect('key-release-event', handle_key_release_event) | 293 window.connect('key-release-event', handle_key_release_event) |
| 256 window.add_events(gtk.gdk.KEY_RELEASE_MASK) | 294 window.add_events(gtk.gdk.KEY_RELEASE_MASK) |
| 257 | 295 |
| 258 # On startup, get general configuration data from the autotest | 296 gobject.timeout_add(_STATUS_REFRESH_MS, refresh_status, |
| 259 # control program, specifically the list of tests to run (in | 297 status_map, ui_state) |
| 260 # order) and some filenames. | |
| 261 factory.log('pulling control info') | |
| 262 test_list = control_recv() | |
| 263 status_file_path = control_recv() | |
| 264 | |
| 265 status_map = ful.StatusMap(status_file_path, test_list) | |
| 266 gobject.timeout_add(_STATUS_REFRESH_MS, status_map.read_new_data) | |
| 267 | 298 |
| 268 for test in test_list: | 299 for test in test_list: |
| 269 tlb = TestLabelBox(test) | 300 label_box = TestLabelBox(test) |
| 270 status_map.set_label(test, tlb) | 301 status_map.set_label_box(test, label_box) |
| 271 label_trough.pack_start(tlb, False, False) | 302 label_trough.pack_start(label_box, False, False) |
| 272 label_trough.pack_start(make_hsep(), False, False) | 303 label_trough.pack_start(make_hsep(), False, False) |
| 273 for subtest in test.automated_seq: | |
| 274 stlb = SubTestLabelBox(subtest) | |
| 275 status_map.set_label(subtest, stlb) | |
| 276 | 304 |
| 277 window.add(base_box) | 305 window.add(base_box) |
| 278 window.show_all() | 306 window.show_all() |
| 279 | 307 |
| 280 ful.hide_cursor(window.window) | 308 ful.hide_cursor(window.window) |
| 281 | 309 |
| 282 test_widget_allocation = test_widget_box.get_allocation() | 310 test_widget_allocation = test_widget_box.get_allocation() |
| 283 test_widget_size = (test_widget_allocation.width, | 311 test_widget_size = (test_widget_allocation.width, |
| 284 test_widget_allocation.height) | 312 test_widget_allocation.height) |
| 285 factory.log('test_widget_size = %s' % repr(test_widget_size)) | 313 factory.log('test_widget_size = %s' % repr(test_widget_size)) |
| 286 control_send(test_widget_size) | 314 control_send(test_widget_size) |
| 287 | 315 |
| 288 trigger_dict = dict((test.trigger, test) for test in test_list) | |
| 289 | |
| 290 gobject.io_add_watch(sys.stdin, gobject.IO_IN, stdin_callback) | |
| 291 | |
| 292 console = Console(console_box.get_allocation()) | 316 console = Console(console_box.get_allocation()) |
| 293 | 317 |
| 294 factory.log('finished ui setup') | 318 factory.log('factory_ui setup done, starting gtk.main()...') |
| 295 | 319 |
| 296 # Test selection is driven either by triggers or by the | 320 gtk.main() |
| 297 # remaining_tests_queue. If a trigger was seen, explicitly run | |
| 298 # the corresponding test. Otherwise choose the next test from the | |
| 299 # queue. Tests are removed from the queue as they are run, | |
| 300 # regarless of the outcome. Tests that are interrupted by trigger | |
| 301 # are treated as having failed. | |
| 302 # | |
| 303 # Iterations in the main loop here are driven by data availability | |
| 304 # on stdin, which is used to communicate with the autotest control | |
| 305 # program. On each step through the loop, a trigger is received | |
| 306 # (possibly None) to indicate how the next test should be selected. | |
| 307 | 321 |
| 308 while True: | 322 factory.log('factory_ui gtk.main() finished, exiting.') |
| 309 command, arg = control_recv() | |
| 310 factory.log('ui received command %s(%s)' % (command, arg)) | |
| 311 if command == 'switch_to': | |
| 312 next_test = trigger_dict.get(arg, None) | |
| 313 elif command == 'next_test': | |
| 314 next_test = status_map.next_untested() | |
| 315 else: | |
| 316 factory.log('ui command unknown, exiting...') | |
| 317 break | |
| 318 control_send_target_test_update( | |
| 319 next_test, status_map.lookup_count(next_test) + 1) | |
| 320 if next_test.automated_seq: | |
| 321 factory.log('ui starting automated_seq') | |
| 322 test_widget_box.remove(notest_label) | |
| 323 as_widget = make_automated_seq_widget(next_test, status_map) | |
| 324 test_widget_box.add(as_widget) | |
| 325 window.show_all() | |
| 326 gtk.main() | |
| 327 command = control_recv() | |
| 328 factory.log('ui automated_seq cmd (%s)' % command) | |
| 329 test_widget_box.remove(as_widget) | |
| 330 test_widget_box.add(notest_label) | |
| 331 window.show_all() | |
| 332 factory.log('ui exiting automated_seq') | |
| 333 else: | |
| 334 gtk.main() | |
| 335 | |
| 336 # Tell the control process we are done. | |
| 337 control_send((None, 0)) | |
| 338 | |
| 339 factory.log('exiting ui') | |
| 340 | 323 |
| 341 if __name__ == '__main__': | 324 if __name__ == '__main__': |
| 342 main() | 325 main() |
| OLD | NEW |