| 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()
|
|
|