Index: mod_for_factory_scripts/factory_ui |
diff --git a/mod_for_factory_scripts/factory_ui b/mod_for_factory_scripts/factory_ui |
deleted file mode 100644 |
index 35b8d72d6d03647384ca6348870089dbb5113eb1..0000000000000000000000000000000000000000 |
--- a/mod_for_factory_scripts/factory_ui |
+++ /dev/null |
@@ -1,431 +0,0 @@ |
-#!/usr/bin/python |
-# |
-# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
- |
- |
-# DESCRIPTION : |
-# |
-# This UI is intended to be used by the factory autotest suite to |
-# provide factory operators feedback on test status and control over |
-# execution order. |
-# |
-# In short, the UI is composed of a 'console' panel on the bottom of |
-# the screen which displays the autotest log, and there is also a |
-# 'test list' panel on the right hand side of the screen. The |
-# majority of the screen is dedicated to tests, which are executed in |
-# 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. |
- |
- |
-import gobject |
-import gtk |
-import os |
-import pango |
-import subprocess |
-import sys |
-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')} |
- |
-_LABEL_EN_SIZE = (160, 35) |
-_LABEL_EN_FONT = pango.FontDescription('courier new extra-condensed 16') |
-_LABEL_ZW_SIZE = (70, 35) |
-_LABEL_ZW_FONT = pango.FontDescription('normal 12') |
-_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 |
- xterm at a strategic location, and running tail against the log.''' |
- |
- def __init__(self, allocation, log_file_path): |
- xterm_coords = '135x14+%d+%d' % (allocation.x, allocation.y) |
- XXX_log('xterm_coords = %s' % xterm_coords) |
- xterm_cmd = ('xterm --geometry %s -bw 0 -e ' % xterm_coords + |
- 'tail -f %s' % log_file_path) |
- self._proc = subprocess.Popen(xterm_cmd.split()) |
- |
- def __del__(self): |
- XXX_log('console_proc __del__') |
- 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): |
- XXX_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): |
- 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 |
-# tests, and hence this should not be called. |
- |
-def handle_key_release_event(_, event): |
- XXX_log('base ui key event (%s)' % event.keyval) |
- return True |
- |
- |
-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): |
- 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]: |
- 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 set_active_test(test): |
- test.count += 1 |
- test.label_box.update_status(_ACTIVE) |
- control_send_target_test_update(test) |
- |
- |
-def make_hsep(width=1): |
- frame = gtk.EventBox() |
- frame.set_size_request(-1, width) |
- frame.modify_bg(gtk.STATE_NORMAL, _SEP_COLOR) |
- return frame |
- |
- |
-def make_vsep(width=1): |
- frame = gtk.EventBox() |
- frame.set_size_request(width, -1) |
- frame.modify_bg(gtk.STATE_NORMAL, _SEP_COLOR) |
- 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, _LIGHT_GREEN) |
- box = gtk.EventBox() |
- box.modify_bg(gtk.STATE_NORMAL, _BLACK) |
- box.add(label) |
- 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(): |
- window = gtk.Window(gtk.WINDOW_TOPLEVEL) |
- window.connect('destroy', lambda _: gtk.main_quit()) |
- window.modify_bg(gtk.STATE_NORMAL, _BLACK) |
- |
- screen = window.get_screen() |
- screen_size = (screen.get_width(), screen.get_height()) |
- window.set_size_request(*screen_size) |
- |
- label_trough = gtk.VBox() |
- label_trough.set_spacing(0) |
- |
- rhs_box = gtk.EventBox() |
- rhs_box.modify_bg(gtk.STATE_NORMAL, _LABEL_TROUGH_COLOR) |
- rhs_box.add(label_trough) |
- |
- console_box = gtk.EventBox() |
- console_box.set_size_request(-1, 180) |
- console_box.modify_bg(gtk.STATE_NORMAL, _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) |
- |
- lhs_box = gtk.VBox() |
- lhs_box.pack_end(console_box, False, False) |
- lhs_box.pack_start(test_widget_box) |
- lhs_box.pack_start(make_hsep(3), False, False) |
- |
- base_box = gtk.HBox() |
- base_box.pack_end(rhs_box, False, False) |
- base_box.pack_end(make_vsep(3), False, False) |
- base_box.pack_start(lhs_box) |
- |
- 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. |
- XXX_log('pulling control info') |
- test_list = control_recv() |
- status_file_path = control_recv() |
- log_file_path = control_recv() |
- |
- for test in test_list: |
- test.status = None |
- 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) |
- window.show_all() |
- |
- test_widget_allocation = test_widget_box.get_allocation() |
- test_widget_size = (test_widget_allocation.width, |
- test_widget_allocation.height) |
- XXX_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) |
- |
- refresh_test_status(status_file_path, test_list) |
- remaining_tests_queue = [x for x in reversed(test_list) |
- 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) |
- |
- console = console_proc(console_box.get_allocation(), log_file_path) |
- |
- XXX_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 remaining_tests_queue: |
- 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') |
- |
-if __name__ == '__main__': |
- |
- # In global scope, get the test_data class description from the |
- # control program -- this allows a convenient single point of |
- # definition for this class. |
- test_data_class_def = control_recv() |
- exec(test_data_class_def) |
- |
- main() |