| Index: client/bin/factory_ui
|
| diff --git a/client/bin/factory_ui b/client/bin/factory_ui
|
| index 6768db78bd1e7b645f49bd7acd23871e3c948dfa..0c1c89bf0357cc94a84daf0758f60c09b5da964c 100755
|
| --- a/client/bin/factory_ui
|
| +++ b/client/bin/factory_ui
|
| @@ -18,9 +18,9 @@
|
| # seperate processes, but instructed to display their own UIs in this
|
| # dedicated area whenever possible. Tests in the test list are
|
| # executed in order by default, but can be activated on demand via
|
| -# associated keyboard shortcuts (triggers). As tests are run, their
|
| -# status is color-indicated to the operator -- greyed out means
|
| -# untested, yellow means active, green passed and red failed.
|
| +# associated keyboard shortcuts. As tests are run, their status is
|
| +# color-indicated to the operator -- greyed out means untested, yellow
|
| +# means active, green passed and red failed.
|
|
|
|
|
| import gobject
|
| @@ -36,12 +36,22 @@ import common
|
| import factory
|
| import factory_ui_lib as ful
|
|
|
| -from factory import TestData
|
| +
|
| +# These definitions are expose these classes directly into this
|
| +# namespace, so that the test_list that is sent from the control file
|
| +# can have cleaner syntax. These are done in this fashion, as opposed
|
| +# to "from factory import <class>" to work-around Python namespace
|
| +# wackiness -- the from syntax does not work, creating new classes.
|
| +OperatorTest = factory.OperatorTest
|
| +InformationScreen = factory.InformationScreen
|
| +AutomatedSequence = factory.AutomatedSequence
|
| +AutomatedSubTest = factory.AutomatedSubTest
|
| +AutomatedRebootSubTest = factory.AutomatedRebootSubTest
|
|
|
|
|
| _SEP_COLOR = gtk.gdk.color_parse('grey50')
|
|
|
| -_LABEL_EN_SIZE = (160, 35)
|
| +_LABEL_EN_SIZE = (170, 35)
|
| _LABEL_ZW_SIZE = (70, 35)
|
| _LABEL_EN_FONT = pango.FontDescription('courier new extra-condensed 16')
|
| _LABEL_ZW_FONT = pango.FontDescription('normal 12')
|
| @@ -57,7 +67,7 @@ _OTHER_LABEL_FONT = pango.FontDescription('courier new condensed 20')
|
| _ST_LABEL_EN_SIZE = (250, 35)
|
| _ST_LABEL_ZW_SIZE = (150, 35)
|
|
|
| -_STATUS_REFRESH_MS = 200
|
| +_STATUS_REFRESH_MS = 100
|
|
|
| class Console:
|
| '''Display a progress log. Implemented by launching an borderless
|
| @@ -77,28 +87,6 @@ class Console:
|
| self._proc.kill()
|
|
|
|
|
| -# Routines to communicate with the autotest control file, using python
|
| -# expressions. The stdin_callback assures notification for any new
|
| -# messages.
|
| -
|
| -def stdin_callback(s, c):
|
| - factory.log('stdin_callback, quitting gtk main')
|
| - gtk.main_quit()
|
| - return True
|
| -
|
| -def control_recv():
|
| - return eval(sys.stdin.readline().rstrip())
|
| -
|
| -def control_send(x):
|
| - print repr(x)
|
| - sys.stdout.flush()
|
| -
|
| -def control_send_target_test_update(test, count):
|
| - factory.log('ui send_target_test_update %s.%s_%s' %
|
| - (test.formal_name, test.tag_prefix, count))
|
| - control_send((test.formal_name, test.tag_prefix, count))
|
| -
|
| -
|
| # Capture keyboard events here for debugging -- under normal
|
| # circumstances, all keyboard events should be captured by executing
|
| # tests, and hence this should not be called.
|
| @@ -113,21 +101,16 @@ class TestLabelBox(gtk.EventBox):
|
| def __init__(self, test):
|
| gtk.EventBox.__init__(self)
|
| self.modify_bg(gtk.STATE_NORMAL, ful.LABEL_COLORS[ful.UNTESTED])
|
| - label_en = gtk.Label(test.label_en)
|
| - label_en.set_size_request(*_LABEL_EN_SIZE)
|
| - label_en.modify_font(_LABEL_EN_FONT)
|
| - label_en.set_alignment(0.5, 0.5)
|
| - label_en.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG)
|
| - label_zw = gtk.Label(test.label_zw)
|
| - label_zw.set_size_request(*_LABEL_ZW_SIZE)
|
| - label_zw.modify_font(_LABEL_ZW_FONT)
|
| - label_zw.set_alignment(0.5, 0.5)
|
| - label_zw.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG)
|
| - label_t = gtk.Label('C-' + test.trigger)
|
| - label_t.set_size_request(*_LABEL_T_SIZE)
|
| - label_t.modify_font(_LABEL_T_FONT)
|
| - label_t.set_alignment(0.5, 0.5)
|
| - label_t.modify_fg(gtk.STATE_NORMAL, ful.BLACK)
|
| +
|
| + label_en = ful.make_label(test.label_en, size=_LABEL_EN_SIZE,
|
| + font=_LABEL_EN_FONT, alignment=(0.5, 0.5),
|
| + fg=_LABEL_UNTESTED_FG)
|
| + label_zw = ful.make_label(test.label_zw, size=_LABEL_ZW_SIZE,
|
| + font=_LABEL_ZW_FONT, alignment=(0.5, 0.5),
|
| + fg=_LABEL_UNTESTED_FG)
|
| + label_t = ful.make_label('C-' + test.kbd_shortcut, size=_LABEL_T_SIZE,
|
| + font=_LABEL_T_FONT, alignment=(0.5, 0.5),
|
| + fg=ful.BLACK)
|
| hbox = gtk.HBox()
|
| hbox.pack_start(label_en, False, False)
|
| hbox.pack_start(label_zw, False, False)
|
| @@ -148,25 +131,17 @@ class SubTestLabelBox(gtk.EventBox):
|
| def __init__(self, test):
|
| gtk.EventBox.__init__(self)
|
| self.modify_bg(gtk.STATE_NORMAL, ful.BLACK)
|
| - label_status = gtk.Label(ful.UNTESTED)
|
| - label_status.set_size_request(*_LABEL_STATUS_SIZE)
|
| - label_status.set_alignment(0, 0.5)
|
| - label_status.modify_font(_LABEL_STATUS_FONT)
|
| - label_status.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG)
|
| - label_sep = gtk.Label(' : ')
|
| - label_sep.set_alignment(0.5, 0.5)
|
| - label_sep.modify_font(_LABEL_EN_FONT)
|
| - label_sep.modify_fg(gtk.STATE_NORMAL, ful.LIGHT_GREEN)
|
| - label_en = gtk.Label(test.label_en)
|
| - label_en.set_size_request(*_ST_LABEL_EN_SIZE)
|
| - label_en.set_alignment(0.5, 0.5)
|
| - label_en.modify_font(_LABEL_EN_FONT)
|
| - label_en.modify_fg(gtk.STATE_NORMAL, ful.LIGHT_GREEN)
|
| - label_zw = gtk.Label(test.label_zw)
|
| - label_zw.set_size_request(*_ST_LABEL_ZW_SIZE)
|
| - label_zw.set_alignment(0.5, 0.5)
|
| - label_zw.modify_font(_LABEL_ZW_FONT)
|
| - label_zw.modify_fg(gtk.STATE_NORMAL, ful.LIGHT_GREEN)
|
| + label_status = ful.make_label(ful.UNTESTED, size=_LABEL_STATUS_SIZE,
|
| + alignment=(0, 0.5),
|
| + font=_LABEL_STATUS_FONT,
|
| + fg=_LABEL_UNTESTED_FG)
|
| + label_sep = ful.make_label(' : ', alignment=(0.5, 0.5),
|
| + font=_LABEL_EN_FONT)
|
| + label_en = ful.make_label(test.label_en, size=_ST_LABEL_EN_SIZE,
|
| + alignment=(0.5, 0.5),
|
| + font=_LABEL_EN_FONT)
|
| + label_zw = ful.make_label(test.label_zw, size=_ST_LABEL_ZW_SIZE,
|
| + alignment=(0.5, 0.5), font=_LABEL_ZW_FONT)
|
| hbox = gtk.HBox()
|
| hbox.pack_end(label_status, False, False)
|
| hbox.pack_end(label_sep, False, False)
|
| @@ -183,6 +158,61 @@ class SubTestLabelBox(gtk.EventBox):
|
| self.queue_draw()
|
|
|
|
|
| +class UiState():
|
| +
|
| + def __init__(self, window, status_map, test_widget_box):
|
| +
|
| + def make_empty_test_label_widget():
|
| + label_box = gtk.EventBox()
|
| + label_box.modify_bg(gtk.STATE_NORMAL, ful.BLACK)
|
| + label = ful.make_label('no active test', font=_OTHER_LABEL_FONT,
|
| + alignment=(0.5, 0.5))
|
| + label_box.add(label)
|
| + return label_box
|
| +
|
| + def make_automated_seq_label_widget(subtest_list):
|
| + vbox = gtk.VBox()
|
| + vbox.set_spacing(0)
|
| + for subtest in subtest_list:
|
| + label_box = SubTestLabelBox(subtest)
|
| + status_map.set_label_box(subtest, label_box)
|
| + vbox.pack_start(status_map.lookup_label_box(subtest),
|
| + False, False)
|
| + return vbox
|
| +
|
| + self._window = window
|
| + self._status_map = status_map
|
| + self._test_widget_box = test_widget_box
|
| + self._empty_test_widget = make_empty_test_label_widget()
|
| + self._active_test_widget = self._empty_test_widget
|
| + self.active_test = None
|
| +
|
| + self._test_widget_box.add(self._empty_test_widget)
|
| +
|
| + self._automated_seq_widget_map = dict(
|
| + (t, make_automated_seq_label_widget(t.subtest_list))
|
| + for t in self._status_map.test_db.seq_test_set)
|
| +
|
| + def set_active_test(self, test):
|
| + '''Control what kind of widget is shown in the testing area of
|
| + the screen. For normal operator tests, this is just a label
|
| + saying there is no active test. The expectation is that the
|
| + operator test itself has a window obscuring this message. For
|
| + automated sequences, since there is no other window, the no
|
| + active test message is replaced with an updated list of
|
| + subtest status.'''
|
| + if test == self.active_test:
|
| + return
|
| + self.active_test = test
|
| + self._test_widget_box.remove(self._active_test_widget)
|
| + active_widget = (test in self._status_map.test_db.seq_test_set
|
| + and self._automated_seq_widget_map[test]
|
| + or self._empty_test_widget)
|
| + self._test_widget_box.add(active_widget)
|
| + self._active_test_widget = active_widget
|
| + self._window.show_all()
|
| +
|
| +
|
| def make_hsep(width=1):
|
| frame = gtk.EventBox()
|
| frame.set_size_request(-1, width)
|
| @@ -197,26 +227,35 @@ def make_vsep(width=1):
|
| return frame
|
|
|
|
|
| -def make_notest_label():
|
| - label = gtk.Label('no active test')
|
| - label.modify_font(_OTHER_LABEL_FONT)
|
| - label.set_alignment(0.5, 0.5)
|
| - label.modify_fg(gtk.STATE_NORMAL, ful.LIGHT_GREEN)
|
| - box = gtk.EventBox()
|
| - box.modify_bg(gtk.STATE_NORMAL, ful.BLACK)
|
| - box.add(label)
|
| - return box
|
| +def refresh_status(status_map, ui_state):
|
| + status_map.read_new_data()
|
| + active_test = status_map.get_active_top_level_test()
|
| + ui_state.set_active_test(active_test)
|
| + return True
|
|
|
|
|
| -def make_automated_seq_widget(as_test, status_map):
|
| - vbox = gtk.VBox()
|
| - vbox.set_spacing(0)
|
| - map(lambda st: vbox.pack_start(status_map.lookup_label(st), False, False),
|
| - as_test.automated_seq)
|
| - return vbox
|
| +def main():
|
| + '''This process is launched by the autotest suite_Factory control
|
| + process. Communication with this process is an exchange of well
|
| + formed python expressions over stdin and stdout. Basically
|
| + sending wraps arguments in a call to repr() and recv calls eval()
|
| + to re-generate the python data.'''
|
|
|
| + def control_recv():
|
| + return eval(sys.stdin.readline().rstrip())
|
| +
|
| + def control_send(x):
|
| + print repr(x)
|
| + sys.stdout.flush()
|
| +
|
| + # On startup, get the list of tests to run (in order) and the
|
| + # autotest status file path.
|
| + factory.log('pulling control info')
|
| + test_list = control_recv()
|
| + status_file_path = control_recv()
|
| +
|
| + status_map = factory.StatusMap(test_list, status_file_path)
|
|
|
| -def main():
|
| window = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
| window.connect('destroy', lambda _: gtk.main_quit())
|
| window.modify_bg(gtk.STATE_NORMAL, ful.BLACK)
|
| @@ -236,11 +275,10 @@ def main():
|
| console_box.set_size_request(-1, 180)
|
| console_box.modify_bg(gtk.STATE_NORMAL, ful.BLACK)
|
|
|
| - notest_label = make_notest_label()
|
| -
|
| test_widget_box = gtk.Alignment(xalign=0.5, yalign=0.5)
|
| test_widget_box.set_size_request(-1, -1)
|
| - test_widget_box.add(notest_label)
|
| +
|
| + ui_state = UiState(window, status_map, test_widget_box)
|
|
|
| lhs_box = gtk.VBox()
|
| lhs_box.pack_end(console_box, False, False)
|
| @@ -255,24 +293,14 @@ def main():
|
| window.connect('key-release-event', handle_key_release_event)
|
| window.add_events(gtk.gdk.KEY_RELEASE_MASK)
|
|
|
| - # On startup, get general configuration data from the autotest
|
| - # control program, specifically the list of tests to run (in
|
| - # order) and some filenames.
|
| - factory.log('pulling control info')
|
| - test_list = control_recv()
|
| - status_file_path = control_recv()
|
| -
|
| - status_map = ful.StatusMap(status_file_path, test_list)
|
| - gobject.timeout_add(_STATUS_REFRESH_MS, status_map.read_new_data)
|
| + gobject.timeout_add(_STATUS_REFRESH_MS, refresh_status,
|
| + status_map, ui_state)
|
|
|
| for test in test_list:
|
| - tlb = TestLabelBox(test)
|
| - status_map.set_label(test, tlb)
|
| - label_trough.pack_start(tlb, False, False)
|
| + label_box = TestLabelBox(test)
|
| + status_map.set_label_box(test, label_box)
|
| + label_trough.pack_start(label_box, False, False)
|
| label_trough.pack_start(make_hsep(), False, False)
|
| - for subtest in test.automated_seq:
|
| - stlb = SubTestLabelBox(subtest)
|
| - status_map.set_label(subtest, stlb)
|
|
|
| window.add(base_box)
|
| window.show_all()
|
| @@ -285,58 +313,13 @@ def main():
|
| factory.log('test_widget_size = %s' % repr(test_widget_size))
|
| control_send(test_widget_size)
|
|
|
| - trigger_dict = dict((test.trigger, test) for test in test_list)
|
| + console = Console(console_box.get_allocation())
|
|
|
| - gobject.io_add_watch(sys.stdin, gobject.IO_IN, stdin_callback)
|
| + factory.log('factory_ui setup done, starting gtk.main()...')
|
|
|
| - console = Console(console_box.get_allocation())
|
| + gtk.main()
|
|
|
| - factory.log('finished ui setup')
|
| -
|
| - # Test selection is driven either by triggers or by the
|
| - # remaining_tests_queue. If a trigger was seen, explicitly run
|
| - # the corresponding test. Otherwise choose the next test from the
|
| - # queue. Tests are removed from the queue as they are run,
|
| - # regarless of the outcome. Tests that are interrupted by trigger
|
| - # are treated as having failed.
|
| - #
|
| - # Iterations in the main loop here are driven by data availability
|
| - # on stdin, which is used to communicate with the autotest control
|
| - # program. On each step through the loop, a trigger is received
|
| - # (possibly None) to indicate how the next test should be selected.
|
| -
|
| - while True:
|
| - command, arg = control_recv()
|
| - factory.log('ui received command %s(%s)' % (command, arg))
|
| - if command == 'switch_to':
|
| - next_test = trigger_dict.get(arg, None)
|
| - elif command == 'next_test':
|
| - next_test = status_map.next_untested()
|
| - else:
|
| - factory.log('ui command unknown, exiting...')
|
| - break
|
| - control_send_target_test_update(
|
| - next_test, status_map.lookup_count(next_test) + 1)
|
| - if next_test.automated_seq:
|
| - factory.log('ui starting automated_seq')
|
| - test_widget_box.remove(notest_label)
|
| - as_widget = make_automated_seq_widget(next_test, status_map)
|
| - test_widget_box.add(as_widget)
|
| - window.show_all()
|
| - gtk.main()
|
| - command = control_recv()
|
| - factory.log('ui automated_seq cmd (%s)' % command)
|
| - test_widget_box.remove(as_widget)
|
| - test_widget_box.add(notest_label)
|
| - window.show_all()
|
| - factory.log('ui exiting automated_seq')
|
| - else:
|
| - gtk.main()
|
| -
|
| - # Tell the control process we are done.
|
| - control_send((None, 0))
|
| -
|
| - factory.log('exiting ui')
|
| + factory.log('factory_ui gtk.main() finished, exiting.')
|
|
|
| if __name__ == '__main__':
|
| main()
|
|
|