diff --git a/mod_for_factory_scripts/factory_ui b/mod_for_factory_scripts/factory_ui index 8b40199f82..35b8d72d6d 100644 --- a/mod_for_factory_scripts/factory_ui +++ b/mod_for_factory_scripts/factory_ui @@ -35,12 +35,16 @@ import time def XXX_log(s): print >> sys.stderr, '--- XXX : ' + s +_ACTIVE = 'ACTIVE' +_PASSED = 'PASS' +_FAILED = 'FAIL' +_UNTESTED = 'UNTESTED' _LABEL_COLORS = { - 'active': gtk.gdk.color_parse('light goldenrod'), - 'passed': gtk.gdk.color_parse('pale green'), - 'failed': gtk.gdk.color_parse('tomato'), - 'untested': gtk.gdk.color_parse('dark slate grey')} + _ACTIVE: gtk.gdk.color_parse('light goldenrod'), + _PASSED: gtk.gdk.color_parse('pale green'), + _FAILED: gtk.gdk.color_parse('tomato'), + _UNTESTED: gtk.gdk.color_parse('dark slate grey')} _LABEL_EN_SIZE = (160, 35) _LABEL_EN_FONT = pango.FontDescription('courier new extra-condensed 16') @@ -50,10 +54,13 @@ _LABEL_T_SIZE = (30, 35) _LABEL_T_FONT = pango.FontDescription('courier new italic ultra-condensed 10') _LABEL_UNTESTED_FG = gtk.gdk.color_parse('grey40') _LABEL_TROUGH_COLOR = gtk.gdk.color_parse('grey20') +_LABEL_STATUS_SIZE = (140, 30) +_LABEL_STATUS_FONT = pango.FontDescription( + 'courier new bold extra-condensed 16') _SEP_COLOR = gtk.gdk.color_parse('grey50') _BLACK = gtk.gdk.color_parse('black') _LIGHT_GREEN = gtk.gdk.color_parse('light green') - +_OTHER_LABEL_FONT = pango.FontDescription('courier new condensed 20') class console_proc: '''Display a progress log. Implemented by launching an borderless @@ -87,6 +94,11 @@ def control_send(x): print repr(x) sys.stdout.flush() +def control_send_target_test_update(test): + XXX_log('ui send_target_test_update %s.%s_%s' % + (test.formal_name, test.tag_prefix, test.count)) + control_send((test.formal_name, test.tag_prefix, test.count)) + # Capture keyboard events here for debugging -- under normal # circumstances, all keyboard events should be captured by executing @@ -97,85 +109,142 @@ def handle_key_release_event(_, event): return True -def update_label_status(test, status): - if status != 'untested': - test.label_box.modify_fg(gtk.STATE_NORMAL, _BLACK) - for label in test.label_list: - label.modify_fg(gtk.STATE_NORMAL, _BLACK) - test.label_box.modify_bg(gtk.STATE_NORMAL, _LABEL_COLORS[status]) - test.label_box.queue_draw() +class test_label_box(gtk.EventBox): + + def __init__(self, test): + gtk.EventBox.__init__(self) + 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.8, 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.2, 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, _BLACK) + hbox = gtk.HBox() + hbox.pack_start(label_en, False, False) + hbox.pack_start(label_zw, False, False) + hbox.pack_start(label_t, False, False) + self.add(hbox) + self.label_list = [label_en, label_zw] + + def update_status(self, status): + if status != _UNTESTED: + self.modify_fg(gtk.STATE_NORMAL, _BLACK) + for label in self.label_list: + label.modify_fg(gtk.STATE_NORMAL, _BLACK) + self.modify_bg(gtk.STATE_NORMAL, _LABEL_COLORS[status]) + self.queue_draw() + + +class subtest_label_box(gtk.EventBox): + + def __init__(self, test): + gtk.EventBox.__init__(self) + self.modify_bg(gtk.STATE_NORMAL, _BLACK) + label_status = gtk.Label(_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_en = gtk.Label(test.label_en) + label_en.set_alignment(1, 0.5) + label_en.modify_font(_LABEL_EN_FONT) + label_en.modify_fg(gtk.STATE_NORMAL, _LIGHT_GREEN) + label_zw = gtk.Label(test.label_zw) + label_zw.set_alignment(1, 0.5) + label_zw.modify_font(_LABEL_ZW_FONT) + label_zw.modify_fg(gtk.STATE_NORMAL, _LIGHT_GREEN) + 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, _LIGHT_GREEN) + hbox = gtk.HBox() + hbox.pack_end(label_status, False, False) + hbox.pack_end(label_sep, False, False) + hbox.pack_end(label_zw, False, False) + hbox.pack_end(label_en, False, False) + self.add(hbox) + self.label_status = label_status + + def update_status(self, status): + if status != _UNTESTED: + self.label_status.set_text(status) + self.label_status.modify_fg(gtk.STATE_NORMAL, _LABEL_COLORS[status]) + self.queue_draw() + + +class status_map(): + + def __init__(self): + self.status_dict = {} + + def index(self, formal_name, tag_prefix): + return '%s.%s' % (formal_name, tag_prefix) + + def lookup(self, formal_name, tag_prefix): + return self.status_dict.setdefault( + self.index(formal_name, tag_prefix), + (_UNTESTED, 0)) + + def update(self, formal_name, tag_prefix, status, count): + _, existing_count = self.lookup(formal_name, tag_prefix) + if count > existing_count: + index = self.index(formal_name, tag_prefix) + self.status_dict[index] = (status, count) + + def get_subtest_status(self, test): + map(self.set_test_status, test.automated_seq) + sub_status_set = set(st.status for st in test.automated_seq) + min_count = min([st.count for st in test.automated_seq]) + max_count = max([st.count for st in test.automated_seq]) + if len(sub_status_set) == 1: + return (sub_status_set.pop(), max_count) + if test.count > min_count: + return (_ACTIVE, max_count) + return (_FAILED, max_count) + + def set_test_status(self, test): + status, count = ( + test.automated_seq + and self.get_subtest_status(test) + or self.lookup(test.formal_name, test.tag_prefix)) + status = test.count > count and _ACTIVE or status + max_count = max(test.count, count) + if test.status != status or test.count != max_count: + XXX_log('status change for %s : %s/%s -> %s/%s' % + (self.index(test.formal_name, test.tag_prefix), + test.count, test.status, max_count, status)) + test.status = status + test.count = max_count + test.label_box.update_status(status) def refresh_test_status(status_file_path, test_list): - result_dict = {} + smap = status_map() with open(status_file_path) as file: for line in file: columns = line.split('\t') if len(columns) >= 8 and not columns[0] and not columns[1]: - result_state = columns[2] - full_name = columns[3] - result_dict[full_name] = result_state - for test in test_list: - full_name = '%s.%d' % (test.formal_name, test.count) - result_state = result_dict.get(full_name, None) - if result_state is None: - status = 'untested' - elif result_state == 'GOOD': - status = 'passed' - else: - status = 'failed' - if test.status != status: - XXX_log('status change for %s : %s -> %s' % - (test.label_en, test.status, status)) - test.status = status - update_label_status(test, status) + status = columns[2] == 'GOOD' and _PASSED or _FAILED + formal_name, _, tag = columns[3].rpartition('.') + tag_prefix, _, count = tag.rpartition('_') + count = int(count) + smap.update(formal_name, tag_prefix, status, count) + map(smap.set_test_status, test_list) -def select_active_test(test_list, remaining_tests_queue, - test_counters, trigger): - active_test = None - if trigger is not None: - trigger_dict = dict((test.trigger, test) for test in test_list) - active_test = trigger_dict.get(trigger, None) - if active_test in remaining_tests_queue: - remaining_tests_queue.remove(active_test) - if active_test is None: - active_test = remaining_tests_queue.pop() - count = test_counters[active_test.formal_name] - count += 1 - active_test.count = count - test_counters[active_test.formal_name] = count - update_label_status(active_test, 'active') - XXX_log('select_active_test %s.%d' % - (active_test.formal_name, active_test.count)) - return (active_test.label_en, active_test.count) - - -def make_test_label(test): - 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.8, 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.2, 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, _BLACK) - hbox = gtk.HBox() - hbox.pack_start(label_en, False, False) - hbox.pack_start(label_zw, False, False) - hbox.pack_start(label_t, False, False) - label_box = gtk.EventBox() - label_box.add(hbox) - test.label_box = label_box - test.label_list = [label_en, label_zw] - return label_box +def set_active_test(test): + test.count += 1 + test.label_box.update_status(_ACTIVE) + control_send_target_test_update(test) def make_hsep(width=1): @@ -192,19 +261,23 @@ def make_vsep(width=1): return frame -def make_test_widget_box(): +def make_notest_label(): label = gtk.Label('no active test') - font = pango.FontDescription('courier new condensed 20') - label.modify_font(font) + label.modify_font(_OTHER_LABEL_FONT) label.set_alignment(0.5, 0.5) label.modify_fg(gtk.STATE_NORMAL, _LIGHT_GREEN) box = gtk.EventBox() box.modify_bg(gtk.STATE_NORMAL, _BLACK) box.add(label) - align = gtk.Alignment(xalign=0.5, yalign=0.5) - align.set_size_request(-1, -1) - align.add(box) - return align + return box + + +def make_automated_seq_widget(as_test): + vbox = gtk.VBox() + vbox.set_spacing(0) + map(lambda st: vbox.pack_start(st.label_box, False, False), + as_test.automated_seq) + return vbox def main(): @@ -227,7 +300,11 @@ def main(): console_box.set_size_request(-1, 180) console_box.modify_bg(gtk.STATE_NORMAL, _BLACK) - test_widget_box = make_test_widget_box() + 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) lhs_box = gtk.VBox() lhs_box.pack_end(console_box, False, False) @@ -252,8 +329,15 @@ def main(): for test in test_list: test.status = None - label = make_test_label(test) - label_trough.pack_start(label, False, False) + test.count = 0 + test.tag_prefix = test.trigger + test.label_box = test_label_box(test) + for subtest in test.automated_seq: + subtest.status = None + subtest.count = 0 + subtest.tag_prefix = test.formal_name + subtest.label_box = subtest_label_box(subtest) + label_trough.pack_start(test.label_box, False, False) label_trough.pack_start(make_hsep(), False, False) window.add(base_box) @@ -265,15 +349,15 @@ def main(): XXX_log('test_widget_size = %s' % repr(test_widget_size)) control_send(test_widget_size) - # Use a common datastructure for counters to allow multiple tests - # to share the same formal name. - test_counters = dict((test.formal_name, 0) for test in test_list) - for test in test_list: - test.count = 0 + trigger_dict = dict((test.trigger, test) for test in test_list) refresh_test_status(status_file_path, test_list) remaining_tests_queue = [x for x in reversed(test_list) - if test.status != 'passed'] + if x.status == _UNTESTED] + XXX_log('remaining_tests_queue = %s' % + repr([x.label_en for x in remaining_tests_queue])) + + active_test = None gobject.io_add_watch(sys.stdin, gobject.IO_IN, stdin_callback) @@ -294,15 +378,44 @@ def main(): # (possibly None) to indicate how the next test should be selected. while remaining_tests_queue: - trigger = control_recv() - XXX_log('ui received trigger (%s)' % trigger) - active_test_name, count = select_active_test( - test_list, remaining_tests_queue, - test_counters, trigger) - control_send((active_test_name, count)) - gtk.main() - refresh_test_status(status_file_path, test_list) + command, arg = control_recv() + XXX_log('ui received command %s(%s)' % (command, arg)) + if command == 'switch_to': + active_test = trigger_dict.get(arg, None) + if active_test in remaining_tests_queue: + remaining_tests_queue.remove(active_test) + set_active_test(active_test) + elif command == 'next_test': + active_test = remaining_tests_queue.pop() + set_active_test(active_test) + else: + XXX_log('ui command unknown, exiting...') + break + if active_test.automated_seq: + XXX_log('ui starting automated_seq') + subtest_queue = [x for x in reversed(active_test.automated_seq)] + test_widget_box.remove(notest_label) + as_widget = make_automated_seq_widget(active_test) + test_widget_box.add(as_widget) + window.show_all() + command = None + while command != 'quit_automated_seq': + active_subtest = subtest_queue.pop() + active_subtest.label_box.update_status(_ACTIVE) + gtk.main() + command = control_recv() + XXX_log('ui automated_seq step (%s)' % command) + refresh_test_status(status_file_path, test_list) + test_widget_box.queue_draw() + test_widget_box.remove(as_widget) + test_widget_box.add(notest_label) + window.show_all() + XXX_log('ui exiting automated_seq') + else: + gtk.main() + refresh_test_status(status_file_path, test_list) + # Tell the control process we are done. control_send((None, 0)) XXX_log('exiting ui')