Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(270)

Side by Side Diff: src/scripts/mod_for_factory_scripts/factory_ui

Issue 1937002: Launch X server from upstart. New factory UI. Misc script tweaks. (Closed) Base URL: ssh://git@chromiumos-git/chromeos
Patch Set: response to feedback Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/scripts/mod_for_factory_scripts/factory_startx.sh ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
2 #
3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7
8 # DESCRIPTION :
9 #
10 # This UI is intended to be used by the factory autotest suite to
11 # provide factory operators feedback on test status and control over
12 # execution order.
13 #
14 # In short, the UI is composed of a 'console' panel on the bottom of
15 # the screen which displays the autotest log, and there is also a
16 # 'test list' panel on the right hand side of the screen. The
17 # majority of the screen is dedicated to tests, which are executed in
18 # seperate processes, but instructed to display their own UIs in this
19 # dedicated area whenever possible. Tests in the test list are
20 # executed in order by default, but can be activated on demand via
21 # associated keyboard shortcuts (triggers). As tests are run, their
22 # status is color-indicated to the operator -- greyed out means
23 # untested, yellow means active, green passed and red failed.
24
25
26 import gobject
27 import gtk
28 import os
29 import pango
30 import subprocess
31 import sys
32 import time
33
34
35 def XXX_log(s):
36 print >> sys.stderr, '--- XXX : ' + s
37
38
39 _LABEL_COLORS = {
40 'active': gtk.gdk.color_parse('light goldenrod'),
41 'passed': gtk.gdk.color_parse('pale green'),
42 'failed': gtk.gdk.color_parse('tomato'),
43 'untested': gtk.gdk.color_parse('dark slate grey')}
44
45 _LABEL_EN_SIZE = (160, 35)
46 _LABEL_EN_FONT = pango.FontDescription('courier new extra-condensed 16')
47 _LABEL_ZW_SIZE = (70, 35)
48 _LABEL_ZW_FONT = pango.FontDescription('normal 12')
49 _LABEL_T_SIZE = (30, 35)
50 _LABEL_T_FONT = pango.FontDescription('courier new italic ultra-condensed 10')
51 _LABEL_UNTESTED_FG = gtk.gdk.color_parse('grey40')
52 _LABEL_TROUGH_COLOR = gtk.gdk.color_parse('grey20')
53 _SEP_COLOR = gtk.gdk.color_parse('grey50')
54 _BLACK = gtk.gdk.color_parse('black')
55 _LIGHT_GREEN = gtk.gdk.color_parse('light green')
56
57
58 class console_proc:
59 '''Display a progress log. Implemented by launching an borderless
60 xterm at a strategic location, and running tail against the log.'''
61
62 def __init__(self, allocation, log_file_path):
63 xterm_coords = '135x14+%d+%d' % (allocation.x, allocation.y)
64 XXX_log('xterm_coords = %s' % xterm_coords)
65 xterm_cmd = ('xterm --geometry %s -bw 0 -e ' % xterm_coords +
66 'tail -f %s' % log_file_path)
67 self._proc = subprocess.Popen(xterm_cmd.split())
68
69 def __del__(self):
70 XXX_log('console_proc __del__')
71 self._proc.kill()
72
73
74 # Routines to communicate with the autotest control file, using python
75 # expressions. The stdin_callback assures notification for any new
76 # messages.
77
78 def stdin_callback(s, c):
79 XXX_log('stdin_callback, quitting gtk main')
80 gtk.main_quit()
81 return True
82
83 def control_recv():
84 return eval(sys.stdin.readline().rstrip())
85
86 def control_send(x):
87 print repr(x)
88 sys.stdout.flush()
89
90
91 # Capture keyboard events here for debugging -- under normal
92 # circumstances, all keyboard events should be captured by executing
93 # tests, and hence this should not be called.
94
95 def handle_key_release_event(_, event):
96 XXX_log('base ui key event (%s)' % event.keyval)
97 return True
98
99
100 def update_label_status(test, status):
101 if status != 'untested':
102 test.label_box.modify_fg(gtk.STATE_NORMAL, _BLACK)
103 for label in test.label_list:
104 label.modify_fg(gtk.STATE_NORMAL, _BLACK)
105 test.label_box.modify_bg(gtk.STATE_NORMAL, _LABEL_COLORS[status])
106 test.label_box.queue_draw()
107
108
109 def refresh_test_status(status_file_path, test_list):
110 result_dict = {}
111 with open(status_file_path) as file:
112 for line in file:
113 columns = line.split('\t')
114 if len(columns) >= 8 and not columns[0] and not columns[1]:
115 result_state = columns[2]
116 full_name = columns[3]
117 result_dict[full_name] = result_state
118 for test in test_list:
119 full_name = '%s.%d' % (test.formal_name, test.count)
120 result_state = result_dict.get(full_name, None)
121 if result_state is None:
122 status = 'untested'
123 elif result_state == 'GOOD':
124 status = 'passed'
125 else:
126 status = 'failed'
127 if test.status != status:
128 XXX_log('status change for %s : %s -> %s' %
129 (test.label_en, test.status, status))
130 test.status = status
131 update_label_status(test, status)
132
133
134 def select_active_test(test_list, remaining_tests_queue,
135 test_counters, trigger):
136 active_test = None
137 if trigger is not None:
138 trigger_dict = dict((test.trigger, test) for test in test_list)
139 active_test = trigger_dict.get(trigger, None)
140 if active_test in remaining_tests_queue:
141 remaining_tests_queue.remove(active_test)
142 if active_test is None:
143 active_test = remaining_tests_queue.pop()
144 count = test_counters[active_test.formal_name]
145 count += 1
146 active_test.count = count
147 test_counters[active_test.formal_name] = count
148 update_label_status(active_test, 'active')
149 XXX_log('select_active_test %s.%d' %
150 (active_test.formal_name, active_test.count))
151 return (active_test.label_en, active_test.count)
152
153
154 def make_test_label(test):
155 label_en = gtk.Label(test.label_en)
156 label_en.set_size_request(*_LABEL_EN_SIZE)
157 label_en.modify_font(_LABEL_EN_FONT)
158 label_en.set_alignment(0.8, 0.5)
159 label_en.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG)
160 label_zw = gtk.Label(test.label_zw)
161 label_zw.set_size_request(*_LABEL_ZW_SIZE)
162 label_zw.modify_font(_LABEL_ZW_FONT)
163 label_zw.set_alignment(0.2, 0.5)
164 label_zw.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG)
165 label_t = gtk.Label('C-' + test.trigger)
166 label_t.set_size_request(*_LABEL_T_SIZE)
167 label_t.modify_font(_LABEL_T_FONT)
168 label_t.set_alignment(0.5, 0.5)
169 label_t.modify_fg(gtk.STATE_NORMAL, _BLACK)
170 hbox = gtk.HBox()
171 hbox.pack_start(label_en, False, False)
172 hbox.pack_start(label_zw, False, False)
173 hbox.pack_start(label_t, False, False)
174 label_box = gtk.EventBox()
175 label_box.add(hbox)
176 test.label_box = label_box
177 test.label_list = [label_en, label_zw]
178 return label_box
179
180
181 def make_hsep(width=1):
182 frame = gtk.EventBox()
183 frame.set_size_request(-1, width)
184 frame.modify_bg(gtk.STATE_NORMAL, _SEP_COLOR)
185 return frame
186
187
188 def make_vsep(width=1):
189 frame = gtk.EventBox()
190 frame.set_size_request(width, -1)
191 frame.modify_bg(gtk.STATE_NORMAL, _SEP_COLOR)
192 return frame
193
194
195 def make_test_widget_box():
196 label = gtk.Label('no active test')
197 font = pango.FontDescription('courier new condensed 20')
198 label.modify_font(font)
199 label.set_alignment(0.5, 0.5)
200 label.modify_fg(gtk.STATE_NORMAL, _LIGHT_GREEN)
201 box = gtk.EventBox()
202 box.modify_bg(gtk.STATE_NORMAL, _BLACK)
203 box.add(label)
204 align = gtk.Alignment(xalign=0.5, yalign=0.5)
205 align.set_size_request(-1, -1)
206 align.add(box)
207 return align
208
209
210 def main():
211 window = gtk.Window(gtk.WINDOW_TOPLEVEL)
212 window.connect('destroy', lambda _: gtk.main_quit())
213 window.modify_bg(gtk.STATE_NORMAL, _BLACK)
214
215 screen = window.get_screen()
216 screen_size = (screen.get_width(), screen.get_height())
217 window.set_size_request(*screen_size)
218
219 label_trough = gtk.VBox()
220 label_trough.set_spacing(0)
221
222 rhs_box = gtk.EventBox()
223 rhs_box.modify_bg(gtk.STATE_NORMAL, _LABEL_TROUGH_COLOR)
224 rhs_box.add(label_trough)
225
226 console_box = gtk.EventBox()
227 console_box.set_size_request(-1, 180)
228 console_box.modify_bg(gtk.STATE_NORMAL, _BLACK)
229
230 test_widget_box = make_test_widget_box()
231
232 lhs_box = gtk.VBox()
233 lhs_box.pack_end(console_box, False, False)
234 lhs_box.pack_start(test_widget_box)
235 lhs_box.pack_start(make_hsep(3), False, False)
236
237 base_box = gtk.HBox()
238 base_box.pack_end(rhs_box, False, False)
239 base_box.pack_end(make_vsep(3), False, False)
240 base_box.pack_start(lhs_box)
241
242 window.connect('key-release-event', handle_key_release_event)
243 window.add_events(gtk.gdk.KEY_RELEASE_MASK)
244
245 # On startup, get general configuration data from the autotest
246 # control program, specifically the list of tests to run (in
247 # order) and some filenames.
248 XXX_log('pulling control info')
249 test_list = control_recv()
250 status_file_path = control_recv()
251 log_file_path = control_recv()
252
253 for test in test_list:
254 test.status = None
255 label = make_test_label(test)
256 label_trough.pack_start(label, False, False)
257 label_trough.pack_start(make_hsep(), False, False)
258
259 window.add(base_box)
260 window.show_all()
261
262 test_widget_allocation = test_widget_box.get_allocation()
263 test_widget_size = (test_widget_allocation.width,
264 test_widget_allocation.height)
265 XXX_log('test_widget_size = %s' % repr(test_widget_size))
266 control_send(test_widget_size)
267
268 # Use a common datastructure for counters to allow multiple tests
269 # to share the same formal name.
270 test_counters = dict((test.formal_name, 0) for test in test_list)
271 for test in test_list:
272 test.count = 0
273
274 refresh_test_status(status_file_path, test_list)
275 remaining_tests_queue = [x for x in reversed(test_list)
276 if test.status != 'passed']
277
278 gobject.io_add_watch(sys.stdin, gobject.IO_IN, stdin_callback)
279
280 console = console_proc(console_box.get_allocation(), log_file_path)
281
282 XXX_log('finished ui setup')
283
284 # Test selection is driven either by triggers or by the
285 # remaining_tests_queue. If a trigger was seen, explicitly run
286 # the corresponding test. Otherwise choose the next test from the
287 # queue. Tests are removed from the queue as they are run,
288 # regarless of the outcome. Tests that are interrupted by trigger
289 # are treated as having failed.
290 #
291 # Iterations in the main loop here are driven by data availability
292 # on stdin, which is used to communicate with the autotest control
293 # program. On each step through the loop, a trigger is received
294 # (possibly None) to indicate how the next test should be selected.
295
296 while remaining_tests_queue:
297 trigger = control_recv()
298 XXX_log('ui received trigger (%s)' % trigger)
299 active_test_name, count = select_active_test(
300 test_list, remaining_tests_queue,
301 test_counters, trigger)
302 control_send((active_test_name, count))
303 gtk.main()
304 refresh_test_status(status_file_path, test_list)
305
306 control_send((None, 0))
307
308 XXX_log('exiting ui')
309
310 if __name__ == '__main__':
311
312 # In global scope, get the test_data class description from the
313 # control program -- this allows a convenient single point of
314 # definition for this class.
315 test_data_class_def = control_recv()
316 exec(test_data_class_def)
317
318 main()
OLDNEW
« no previous file with comments | « src/scripts/mod_for_factory_scripts/factory_startx.sh ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698