mirror of
https://github.com/flatcar/scripts.git
synced 2025-08-10 06:26:57 +02:00
Add support for sequences of fully automated tests.
Paired with CL: http://codereview.chromium.org/2322004 Review URL: http://codereview.chromium.org/2367001
This commit is contained in:
parent
7e12aadbbd
commit
8d43b4bf8c
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user