| 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 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 # associated keyboard shortcuts. As tests are run, their status is | 21 # associated keyboard shortcuts. As tests are run, their status is |
| 22 # color-indicated to the operator -- greyed out means untested, yellow | 22 # color-indicated to the operator -- greyed out means untested, yellow |
| 23 # 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 signal |
| 31 import subprocess | 32 import subprocess |
| 32 import sys | 33 import sys |
| 33 import time | 34 import time |
| 34 | 35 |
| 35 import common | 36 import common |
| 36 import factory | 37 import factory |
| 37 import factory_ui_lib as ful | 38 import factory_ui_lib as ful |
| 38 | 39 |
| 40 from gtk import gdk |
| 41 from Xlib import X |
| 42 from Xlib.display import Display as X_Display |
| 43 |
| 39 | 44 |
| 40 # These definitions are expose these classes directly into this | 45 # These definitions are expose these classes directly into this |
| 41 # namespace, so that the test_list that is sent from the control file | 46 # 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 | 47 # can have cleaner syntax. These are done in this fashion, as opposed |
| 43 # to "from factory import <class>" to work-around Python namespace | 48 # to "from factory import <class>" to work-around Python namespace |
| 44 # wackiness -- the from syntax does not work, creating new classes. | 49 # wackiness -- the from syntax does not work, creating new classes. |
| 45 OperatorTest = factory.OperatorTest | 50 OperatorTest = factory.OperatorTest |
| 46 InformationScreen = factory.InformationScreen | 51 InformationScreen = factory.InformationScreen |
| 47 AutomatedSequence = factory.AutomatedSequence | 52 AutomatedSequence = factory.AutomatedSequence |
| 48 AutomatedSubTest = factory.AutomatedSubTest | 53 AutomatedSubTest = factory.AutomatedSubTest |
| 49 AutomatedRebootSubTest = factory.AutomatedRebootSubTest | 54 AutomatedRebootSubTest = factory.AutomatedRebootSubTest |
| 50 | 55 |
| 51 | 56 |
| 52 _SEP_COLOR = gtk.gdk.color_parse('grey50') | |
| 53 | |
| 54 _LABEL_EN_SIZE = (170, 35) | 57 _LABEL_EN_SIZE = (170, 35) |
| 55 _LABEL_ZW_SIZE = (70, 35) | 58 _LABEL_ZW_SIZE = (70, 35) |
| 56 _LABEL_EN_FONT = pango.FontDescription('courier new extra-condensed 16') | 59 _LABEL_EN_FONT = pango.FontDescription('courier new extra-condensed 16') |
| 57 _LABEL_ZW_FONT = pango.FontDescription('normal 12') | 60 _LABEL_ZW_FONT = pango.FontDescription('normal 12') |
| 58 _LABEL_T_SIZE = (30, 35) | 61 _LABEL_T_SIZE = (30, 35) |
| 59 _LABEL_T_FONT = pango.FontDescription('courier new italic ultra-condensed 10') | 62 _LABEL_T_FONT = pango.FontDescription('courier new italic ultra-condensed 10') |
| 60 _LABEL_UNTESTED_FG = gtk.gdk.color_parse('grey40') | 63 _LABEL_UNTESTED_FG = gtk.gdk.color_parse('grey40') |
| 61 _LABEL_TROUGH_COLOR = gtk.gdk.color_parse('grey20') | 64 _LABEL_TROUGH_COLOR = gtk.gdk.color_parse('grey20') |
| 62 _LABEL_STATUS_SIZE = (140, 30) | 65 _LABEL_STATUS_SIZE = (140, 30) |
| 63 _LABEL_STATUS_FONT = pango.FontDescription( | 66 _LABEL_STATUS_FONT = pango.FontDescription( |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 153 def update(self, status): | 156 def update(self, status): |
| 154 if status == ful.UNTESTED: | 157 if status == ful.UNTESTED: |
| 155 return | 158 return |
| 156 self.label_status.set_text(status) | 159 self.label_status.set_text(status) |
| 157 self.label_status.modify_fg(gtk.STATE_NORMAL, ful.LABEL_COLORS[status]) | 160 self.label_status.modify_fg(gtk.STATE_NORMAL, ful.LABEL_COLORS[status]) |
| 158 self.queue_draw() | 161 self.queue_draw() |
| 159 | 162 |
| 160 | 163 |
| 161 class UiState(): | 164 class UiState(): |
| 162 | 165 |
| 163 def __init__(self, window, status_map, test_widget_box): | 166 def __init__(self, status_map, test_widget_box): |
| 164 | 167 |
| 165 def make_empty_test_label_widget(): | 168 def make_empty_test_label_widget(): |
| 166 label_box = gtk.EventBox() | 169 label_box = gtk.EventBox() |
| 167 label_box.modify_bg(gtk.STATE_NORMAL, ful.BLACK) | 170 label_box.modify_bg(gtk.STATE_NORMAL, ful.BLACK) |
| 168 label = ful.make_label('no active test', font=_OTHER_LABEL_FONT, | 171 label = ful.make_label('no active test', font=_OTHER_LABEL_FONT, |
| 169 alignment=(0.5, 0.5)) | 172 alignment=(0.5, 0.5)) |
| 170 label_box.add(label) | 173 label_box.add(label) |
| 171 return label_box | 174 return label_box |
| 172 | 175 |
| 173 def make_automated_seq_label_widget(subtest_list): | 176 def make_automated_seq_label_widget(subtest_list): |
| 174 vbox = gtk.VBox() | 177 vbox = gtk.VBox() |
| 175 vbox.set_spacing(0) | 178 vbox.set_spacing(0) |
| 176 for subtest in subtest_list: | 179 for subtest in subtest_list: |
| 177 label_box = SubTestLabelBox(subtest) | 180 label_box = SubTestLabelBox(subtest) |
| 178 status_map.set_label_box(subtest, label_box) | 181 status_map.set_label_box(subtest, label_box) |
| 179 vbox.pack_start(status_map.lookup_label_box(subtest), | 182 vbox.pack_start(status_map.lookup_label_box(subtest), |
| 180 False, False) | 183 False, False) |
| 181 return vbox | 184 return vbox |
| 182 | 185 |
| 183 self._window = window | |
| 184 self._status_map = status_map | 186 self._status_map = status_map |
| 185 self._test_widget_box = test_widget_box | 187 self._test_widget_box = test_widget_box |
| 186 self._empty_test_widget = make_empty_test_label_widget() | 188 self._empty_test_widget = make_empty_test_label_widget() |
| 187 self._active_test_widget = self._empty_test_widget | 189 self._active_test_widget = self._empty_test_widget |
| 188 self.active_test = None | 190 self.active_test = None |
| 189 | 191 |
| 190 self._test_widget_box.add(self._empty_test_widget) | 192 self._test_widget_box.add(self._empty_test_widget) |
| 191 | 193 |
| 192 self._automated_seq_widget_map = dict( | 194 self._automated_seq_widget_map = dict( |
| 193 (t, make_automated_seq_label_widget(t.subtest_list)) | 195 (t, make_automated_seq_label_widget(t.subtest_list)) |
| (...skipping 11 matching lines...) Expand all Loading... |
| 205 return | 207 return |
| 206 factory.log('UI active test -> %s' % | 208 factory.log('UI active test -> %s' % |
| 207 self._status_map.test_db.get_unique_details(test)) | 209 self._status_map.test_db.get_unique_details(test)) |
| 208 self.active_test = test | 210 self.active_test = test |
| 209 self._test_widget_box.remove(self._active_test_widget) | 211 self._test_widget_box.remove(self._active_test_widget) |
| 210 active_widget = (test in self._status_map.test_db.seq_test_set | 212 active_widget = (test in self._status_map.test_db.seq_test_set |
| 211 and self._automated_seq_widget_map[test] | 213 and self._automated_seq_widget_map[test] |
| 212 or self._empty_test_widget) | 214 or self._empty_test_widget) |
| 213 self._test_widget_box.add(active_widget) | 215 self._test_widget_box.add(active_widget) |
| 214 self._active_test_widget = active_widget | 216 self._active_test_widget = active_widget |
| 215 self._window.show_all() | 217 self._test_widget_box.show_all() |
| 216 | |
| 217 | |
| 218 def make_hsep(width=1): | |
| 219 frame = gtk.EventBox() | |
| 220 frame.set_size_request(-1, width) | |
| 221 frame.modify_bg(gtk.STATE_NORMAL, _SEP_COLOR) | |
| 222 return frame | |
| 223 | |
| 224 | |
| 225 def make_vsep(width=1): | |
| 226 frame = gtk.EventBox() | |
| 227 frame.set_size_request(width, -1) | |
| 228 frame.modify_bg(gtk.STATE_NORMAL, _SEP_COLOR) | |
| 229 return frame | |
| 230 | 218 |
| 231 | 219 |
| 232 def refresh_status(status_map, ui_state): | 220 def refresh_status(status_map, ui_state): |
| 233 status_map.read_new_data() | 221 status_map.read_new_data() |
| 234 active_test = status_map.get_active_top_level_test() | 222 active_test = status_map.get_active_top_level_test() |
| 235 ui_state.set_active_test(active_test) | 223 ui_state.set_active_test(active_test) |
| 236 return True | 224 return True |
| 237 | 225 |
| 238 | 226 |
| 239 def main(): | 227 def grab_shortcut_keys(kbd_shortcut_set, control_pid): |
| 228 disp = X_Display() |
| 229 root = disp.screen().root |
| 230 |
| 231 # We want to receive KeyPress events |
| 232 root.change_attributes(event_mask = X.KeyPressMask) |
| 233 |
| 234 keycode_map = {} |
| 235 for shortcut in kbd_shortcut_set: |
| 236 keysym = gdk.keyval_from_name(shortcut) |
| 237 keycode = disp.keysym_to_keycode(keysym) |
| 238 keycode_map[keycode] = shortcut |
| 239 root.grab_key(keycode, X.ControlMask, 1, |
| 240 X.GrabModeAsync, X.GrabModeAsync) |
| 241 |
| 242 # This flushes the XGrabKey calls to the server. |
| 243 for x in range(0, root.display.pending_events()): |
| 244 root.display.next_event() |
| 245 |
| 246 def handle_xevent(src, cond, xhandle=root.display, |
| 247 keycode_map=keycode_map, |
| 248 control_pid=control_pid): |
| 249 for i in range(0, xhandle.pending_events()): |
| 250 xevent = xhandle.next_event() |
| 251 if xevent.type == X.KeyPress: |
| 252 keycode = xevent.detail |
| 253 factory.log_shared_data('activated_kbd_shortcut', |
| 254 keycode_map[keycode]) |
| 255 os.kill(control_pid, signal.SIGUSR1) |
| 256 return True |
| 257 |
| 258 gobject.io_add_watch(root.display, gobject.IO_IN, handle_xevent) |
| 259 |
| 260 |
| 261 def main(test_list, status_file_path, control_pid): |
| 240 '''This process is launched by the autotest suite_Factory control | 262 '''This process is launched by the autotest suite_Factory control |
| 241 process. Communication with this process is an exchange of well | 263 process, which should be identified by the <control pid> cmdline |
| 242 formed python expressions over stdin and stdout. Basically | 264 argument. When operators press keyboard shortcuts, the shortcut |
| 243 sending wraps arguments in a call to repr() and recv calls eval() | 265 value is logged with log_shared_data() and a SIGUSR1 is sent to |
| 244 to re-generate the python data.''' | 266 the control program.''' |
| 245 | |
| 246 def control_recv(): | |
| 247 return eval(sys.stdin.readline().rstrip()) | |
| 248 | |
| 249 def control_send(x): | |
| 250 print repr(x) | |
| 251 sys.stdout.flush() | |
| 252 | |
| 253 # On startup, get the list of tests to run (in order) and the | |
| 254 # autotest status file path. | |
| 255 factory.log('pulling control info') | |
| 256 test_list = control_recv() | |
| 257 status_file_path = control_recv() | |
| 258 | 267 |
| 259 status_map = factory.StatusMap(test_list, status_file_path) | 268 status_map = factory.StatusMap(test_list, status_file_path) |
| 260 | 269 |
| 261 window = gtk.Window(gtk.WINDOW_TOPLEVEL) | 270 window = gtk.Window(gtk.WINDOW_TOPLEVEL) |
| 262 window.connect('destroy', lambda _: gtk.main_quit()) | 271 window.connect('destroy', lambda _: gtk.main_quit()) |
| 263 window.modify_bg(gtk.STATE_NORMAL, ful.BLACK) | 272 window.modify_bg(gtk.STATE_NORMAL, ful.BLACK) |
| 264 | 273 |
| 265 screen = window.get_screen() | 274 screen = window.get_screen() |
| 275 if (screen is None): |
| 276 log('ERROR: communication with the X server is not working, ' + |
| 277 'could not find a working screen. UI exiting.') |
| 278 sys.exit(1) |
| 279 |
| 266 screen_size = (screen.get_width(), screen.get_height()) | 280 screen_size = (screen.get_width(), screen.get_height()) |
| 267 window.set_size_request(*screen_size) | 281 window.set_size_request(*screen_size) |
| 268 | 282 |
| 269 label_trough = gtk.VBox() | 283 label_trough = gtk.VBox() |
| 270 label_trough.set_spacing(0) | 284 label_trough.set_spacing(0) |
| 271 | 285 |
| 272 rhs_box = gtk.EventBox() | 286 rhs_box = gtk.EventBox() |
| 273 rhs_box.modify_bg(gtk.STATE_NORMAL, _LABEL_TROUGH_COLOR) | 287 rhs_box.modify_bg(gtk.STATE_NORMAL, _LABEL_TROUGH_COLOR) |
| 274 rhs_box.add(label_trough) | 288 rhs_box.add(label_trough) |
| 275 | 289 |
| 276 console_box = gtk.EventBox() | 290 console_box = gtk.EventBox() |
| 277 console_box.set_size_request(-1, 180) | 291 console_box.set_size_request(-1, 180) |
| 278 console_box.modify_bg(gtk.STATE_NORMAL, ful.BLACK) | 292 console_box.modify_bg(gtk.STATE_NORMAL, ful.BLACK) |
| 279 | 293 |
| 280 test_widget_box = gtk.Alignment(xalign=0.5, yalign=0.5) | 294 test_widget_box = gtk.Alignment(xalign=0.5, yalign=0.5) |
| 281 test_widget_box.set_size_request(-1, -1) | 295 test_widget_box.set_size_request(-1, -1) |
| 282 | 296 |
| 283 ui_state = UiState(window, status_map, test_widget_box) | 297 ui_state = UiState(status_map, test_widget_box) |
| 284 | 298 |
| 285 lhs_box = gtk.VBox() | 299 lhs_box = gtk.VBox() |
| 286 lhs_box.pack_end(console_box, False, False) | 300 lhs_box.pack_end(console_box, False, False) |
| 287 lhs_box.pack_start(test_widget_box) | 301 lhs_box.pack_start(test_widget_box) |
| 288 lhs_box.pack_start(make_hsep(3), False, False) | 302 lhs_box.pack_start(ful.make_hsep(3), False, False) |
| 289 | 303 |
| 290 base_box = gtk.HBox() | 304 base_box = gtk.HBox() |
| 291 base_box.pack_end(rhs_box, False, False) | 305 base_box.pack_end(rhs_box, False, False) |
| 292 base_box.pack_end(make_vsep(3), False, False) | 306 base_box.pack_end(ful.make_vsep(3), False, False) |
| 293 base_box.pack_start(lhs_box) | 307 base_box.pack_start(lhs_box) |
| 294 | 308 |
| 295 window.connect('key-release-event', handle_key_release_event) | 309 window.connect('key-release-event', handle_key_release_event) |
| 296 window.add_events(gtk.gdk.KEY_RELEASE_MASK) | 310 window.add_events(gtk.gdk.KEY_RELEASE_MASK) |
| 297 | 311 |
| 298 gobject.timeout_add(_STATUS_REFRESH_MS, refresh_status, | 312 gobject.timeout_add(_STATUS_REFRESH_MS, refresh_status, |
| 299 status_map, ui_state) | 313 status_map, ui_state) |
| 300 | 314 |
| 301 for test in test_list: | 315 for test in test_list: |
| 302 label_box = TestLabelBox(test) | 316 label_box = TestLabelBox(test) |
| 303 status_map.set_label_box(test, label_box) | 317 status_map.set_label_box(test, label_box) |
| 304 label_trough.pack_start(label_box, False, False) | 318 label_trough.pack_start(label_box, False, False) |
| 305 label_trough.pack_start(make_hsep(), False, False) | 319 label_trough.pack_start(ful.make_hsep(), False, False) |
| 306 | 320 |
| 307 window.add(base_box) | 321 window.add(base_box) |
| 308 window.show_all() | 322 window.show_all() |
| 309 | 323 |
| 324 grab_shortcut_keys(status_map.test_db.kbd_shortcut_set, control_pid) |
| 325 |
| 310 ful.hide_cursor(window.window) | 326 ful.hide_cursor(window.window) |
| 311 | 327 |
| 312 test_widget_allocation = test_widget_box.get_allocation() | 328 test_widget_allocation = test_widget_box.get_allocation() |
| 313 test_widget_size = (test_widget_allocation.width, | 329 test_widget_size = (test_widget_allocation.width, |
| 314 test_widget_allocation.height) | 330 test_widget_allocation.height) |
| 315 factory.log('test_widget_size = %s' % repr(test_widget_size)) | 331 factory.log_shared_data('test_widget_size', test_widget_size) |
| 316 control_send(test_widget_size) | |
| 317 | 332 |
| 318 console = Console(console_box.get_allocation()) | 333 console = Console(console_box.get_allocation()) |
| 319 | 334 |
| 320 factory.log('factory_ui setup done, starting gtk.main()...') | 335 factory.log('factory_ui setup done, starting gtk.main()...') |
| 321 | 336 |
| 322 gtk.main() | 337 gtk.main() |
| 323 | 338 |
| 324 factory.log('factory_ui gtk.main() finished, exiting.') | 339 factory.log('factory_ui gtk.main() finished, exiting.') |
| 325 | 340 |
| 326 if __name__ == '__main__': | 341 if __name__ == '__main__': |
| 327 main() | 342 |
| 343 if len(sys.argv) != 4: |
| 344 print ('usage: %s <test list path> <status file path> <control pid>' % |
| 345 sys.argv[0]) |
| 346 test_list_path, status_file_path, control_pid_str = sys.argv[1:] |
| 347 control_pid = int(control_pid_str) |
| 348 |
| 349 execfile(test_list_path) |
| 350 |
| 351 main(TEST_LIST, status_file_path, control_pid) |
| OLD | NEW |