OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # | 2 # |
3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 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 | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 | 7 |
8 # DESCRIPTION : | 8 # DESCRIPTION : |
9 # | 9 # |
10 # This UI is intended to be used by the factory autotest suite to | 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 | 11 # provide factory operators feedback on test status and control over |
12 # execution order. | 12 # execution order. |
13 # | 13 # |
14 # In short, the UI is composed of a 'console' panel on the bottom of | 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 | 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 | 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 | 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 | 18 # seperate processes, but instructed to display their own UIs in this |
19 # dedicated area whenever possible. Tests in the test list are | 19 # dedicated area whenever possible. Tests in the test list are |
20 # executed in order by default, but can be activated on demand via | 20 # executed in order by default, but can be activated on demand via |
21 # associated keyboard shortcuts (triggers). As tests are run, their | 21 # associated keyboard shortcuts (triggers). As tests are run, their |
22 # status is color-indicated to the operator -- greyed out means | 22 # status is color-indicated to the operator -- greyed out means |
23 # untested, yellow means active, green passed and red failed. | 23 # untested, yellow means active, green passed and red failed. |
24 | 24 |
25 | 25 |
26 import gobject | 26 import gobject |
27 import gtk | 27 import gtk |
| 28 import imp |
28 import os | 29 import os |
29 import pango | 30 import pango |
30 import subprocess | 31 import subprocess |
31 import sys | 32 import sys |
32 import time | 33 import time |
33 | 34 |
| 35 import common |
| 36 import factory |
| 37 import factory_ui_lib as ful |
34 | 38 |
35 def XXX_log(s): | 39 from factory import TestData |
36 print >> sys.stderr, 'FACTORY: ' + s | |
37 | 40 |
38 _ACTIVE = 'ACTIVE' | |
39 _PASSED = 'PASS' | |
40 _FAILED = 'FAIL' | |
41 _UNTESTED = 'UNTESTED' | |
42 | 41 |
43 _STATUS_CODE_MAP = { | 42 _SEP_COLOR = gtk.gdk.color_parse('grey50') |
44 'START': _ACTIVE, | |
45 'GOOD': _PASSED, | |
46 'FAIL': _FAILED, | |
47 'ERROR': _FAILED} | |
48 | |
49 _LABEL_COLORS = { | |
50 _ACTIVE: gtk.gdk.color_parse('light goldenrod'), | |
51 _PASSED: gtk.gdk.color_parse('pale green'), | |
52 _FAILED: gtk.gdk.color_parse('tomato'), | |
53 _UNTESTED: gtk.gdk.color_parse('dark slate grey')} | |
54 | 43 |
55 _LABEL_EN_SIZE = (160, 35) | 44 _LABEL_EN_SIZE = (160, 35) |
56 _LABEL_EN_FONT = pango.FontDescription('courier new extra-condensed 16') | 45 _LABEL_EN_FONT = pango.FontDescription('courier new extra-condensed 16') |
57 _LABEL_ZW_SIZE = (70, 35) | 46 _LABEL_ZW_SIZE = (70, 35) |
58 _LABEL_ZW_FONT = pango.FontDescription('normal 12') | 47 _LABEL_ZW_FONT = pango.FontDescription('normal 12') |
59 _LABEL_T_SIZE = (30, 35) | 48 _LABEL_T_SIZE = (30, 35) |
60 _LABEL_T_FONT = pango.FontDescription('courier new italic ultra-condensed 10') | 49 _LABEL_T_FONT = pango.FontDescription('courier new italic ultra-condensed 10') |
61 _LABEL_UNTESTED_FG = gtk.gdk.color_parse('grey40') | 50 _LABEL_UNTESTED_FG = gtk.gdk.color_parse('grey40') |
62 _LABEL_TROUGH_COLOR = gtk.gdk.color_parse('grey20') | 51 _LABEL_TROUGH_COLOR = gtk.gdk.color_parse('grey20') |
63 _LABEL_STATUS_SIZE = (140, 30) | 52 _LABEL_STATUS_SIZE = (140, 30) |
64 _LABEL_STATUS_FONT = pango.FontDescription( | 53 _LABEL_STATUS_FONT = pango.FontDescription( |
65 'courier new bold extra-condensed 16') | 54 'courier new bold extra-condensed 16') |
66 _SEP_COLOR = gtk.gdk.color_parse('grey50') | |
67 _BLACK = gtk.gdk.color_parse('black') | |
68 _LIGHT_GREEN = gtk.gdk.color_parse('light green') | |
69 _OTHER_LABEL_FONT = pango.FontDescription('courier new condensed 20') | 55 _OTHER_LABEL_FONT = pango.FontDescription('courier new condensed 20') |
70 | 56 |
71 class console_proc: | 57 |
| 58 class Console: |
72 '''Display a progress log. Implemented by launching an borderless | 59 '''Display a progress log. Implemented by launching an borderless |
73 xterm at a strategic location, and running tail against the log.''' | 60 xterm at a strategic location, and running tail against the log.''' |
74 | 61 |
75 def __init__(self, allocation, log_file_path): | 62 def __init__(self, allocation): |
76 xterm_coords = '135x14+%d+%d' % (allocation.x, allocation.y) | 63 xterm_coords = '135x14+%d+%d' % (allocation.x, allocation.y) |
77 XXX_log('xterm_coords = %s' % xterm_coords) | 64 factory.log('xterm_coords = %s' % xterm_coords) |
78 xterm_cmd = (('aterm --geometry %s -bw 0 -e bash -c ' % | 65 xterm_cmd = (('aterm --geometry %s -bw 0 -e bash -c ' % |
79 xterm_coords).split() + | 66 xterm_coords).split() + |
80 ['tail -f %s | grep FACTORY' % log_file_path]) | 67 ['tail -f %s | grep FACTORY' % factory.LOG_PATH]) |
81 XXX_log('xterm_cmd = %s' % xterm_cmd) | 68 factory.log('xterm_cmd = %s' % xterm_cmd) |
82 self._proc = subprocess.Popen(xterm_cmd) | 69 self._proc = subprocess.Popen(xterm_cmd) |
83 | 70 |
84 def __del__(self): | 71 def __del__(self): |
85 XXX_log('console_proc __del__') | 72 factory.log('console_proc __del__') |
86 self._proc.kill() | 73 self._proc.kill() |
87 | 74 |
88 | 75 |
89 # Routines to communicate with the autotest control file, using python | 76 # Routines to communicate with the autotest control file, using python |
90 # expressions. The stdin_callback assures notification for any new | 77 # expressions. The stdin_callback assures notification for any new |
91 # messages. | 78 # messages. |
92 | 79 |
93 def stdin_callback(s, c): | 80 def stdin_callback(s, c): |
94 XXX_log('stdin_callback, quitting gtk main') | 81 factory.log('stdin_callback, quitting gtk main') |
95 gtk.main_quit() | 82 gtk.main_quit() |
96 return True | 83 return True |
97 | 84 |
98 def control_recv(): | 85 def control_recv(): |
99 return eval(sys.stdin.readline().rstrip()) | 86 return eval(sys.stdin.readline().rstrip()) |
100 | 87 |
101 def control_send(x): | 88 def control_send(x): |
102 print repr(x) | 89 print repr(x) |
103 sys.stdout.flush() | 90 sys.stdout.flush() |
104 | 91 |
105 def control_send_target_test_update(test, count): | 92 def control_send_target_test_update(test, count): |
106 XXX_log('ui send_target_test_update %s.%s_%s' % | 93 factory.log('ui send_target_test_update %s.%s_%s' % |
107 (test.formal_name, test.tag_prefix, count)) | 94 (test.formal_name, test.tag_prefix, count)) |
108 control_send((test.formal_name, test.tag_prefix, count)) | 95 control_send((test.formal_name, test.tag_prefix, count)) |
109 | 96 |
110 | 97 |
111 # Capture keyboard events here for debugging -- under normal | 98 # Capture keyboard events here for debugging -- under normal |
112 # circumstances, all keyboard events should be captured by executing | 99 # circumstances, all keyboard events should be captured by executing |
113 # tests, and hence this should not be called. | 100 # tests, and hence this should not be called. |
114 | 101 |
115 def handle_key_release_event(_, event): | 102 def handle_key_release_event(_, event): |
116 XXX_log('base ui key event (%s)' % event.keyval) | 103 factory.log('base ui key event (%s)' % event.keyval) |
117 return True | 104 return True |
118 | 105 |
119 | 106 |
120 class TestLabelBox(gtk.EventBox): | 107 class TestLabelBox(gtk.EventBox): |
121 | 108 |
122 def __init__(self, test): | 109 def __init__(self, test): |
123 gtk.EventBox.__init__(self) | 110 gtk.EventBox.__init__(self) |
124 self.modify_bg(gtk.STATE_NORMAL, _LABEL_COLORS[_UNTESTED]) | 111 self.modify_bg(gtk.STATE_NORMAL, ft.LABEL_COLORS[ft.UNTESTED]) |
125 label_en = gtk.Label(test.label_en) | 112 label_en = gtk.Label(test.label_en) |
126 label_en.set_size_request(*_LABEL_EN_SIZE) | 113 label_en.set_size_request(*_LABEL_EN_SIZE) |
127 label_en.modify_font(_LABEL_EN_FONT) | 114 label_en.modify_font(_LABEL_EN_FONT) |
128 label_en.set_alignment(0.5, 0.5) | 115 label_en.set_alignment(0.5, 0.5) |
129 label_en.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG) | 116 label_en.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG) |
130 label_zw = gtk.Label(test.label_zw) | 117 label_zw = gtk.Label(test.label_zw) |
131 label_zw.set_size_request(*_LABEL_ZW_SIZE) | 118 label_zw.set_size_request(*_LABEL_ZW_SIZE) |
132 label_zw.modify_font(_LABEL_ZW_FONT) | 119 label_zw.modify_font(_LABEL_ZW_FONT) |
133 label_zw.set_alignment(0.5, 0.5) | 120 label_zw.set_alignment(0.5, 0.5) |
134 label_zw.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG) | 121 label_zw.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG) |
135 label_t = gtk.Label('C-' + test.trigger) | 122 label_t = gtk.Label('C-' + test.trigger) |
136 label_t.set_size_request(*_LABEL_T_SIZE) | 123 label_t.set_size_request(*_LABEL_T_SIZE) |
137 label_t.modify_font(_LABEL_T_FONT) | 124 label_t.modify_font(_LABEL_T_FONT) |
138 label_t.set_alignment(0.5, 0.5) | 125 label_t.set_alignment(0.5, 0.5) |
139 label_t.modify_fg(gtk.STATE_NORMAL, _BLACK) | 126 label_t.modify_fg(gtk.STATE_NORMAL, ft.BLACK) |
140 hbox = gtk.HBox() | 127 hbox = gtk.HBox() |
141 hbox.pack_start(label_en, False, False) | 128 hbox.pack_start(label_en, False, False) |
142 hbox.pack_start(label_zw, False, False) | 129 hbox.pack_start(label_zw, False, False) |
143 hbox.pack_start(label_t, False, False) | 130 hbox.pack_start(label_t, False, False) |
144 self.add(hbox) | 131 self.add(hbox) |
145 self.label_list = [label_en, label_zw] | 132 self.label_list = [label_en, label_zw] |
146 | 133 |
147 def update(self, status): | 134 def update(self, status): |
148 if status == _UNTESTED: | 135 if status == ft.UNTESTED: |
149 return | 136 return |
150 self.modify_fg(gtk.STATE_NORMAL, _BLACK) | 137 self.modify_fg(gtk.STATE_NORMAL, ft.BLACK) |
151 for label in self.label_list: | 138 for label in self.label_list: |
152 label.modify_fg(gtk.STATE_NORMAL, _BLACK) | 139 label.modify_fg(gtk.STATE_NORMAL, ft.BLACK) |
153 self.modify_bg(gtk.STATE_NORMAL, _LABEL_COLORS[status]) | 140 self.modify_bg(gtk.STATE_NORMAL, ft.LABEL_COLORS[status]) |
154 self.queue_draw() | 141 self.queue_draw() |
155 | 142 |
156 | 143 |
157 class SubTestLabelBox(gtk.EventBox): | 144 class SubTestLabelBox(gtk.EventBox): |
158 | 145 |
159 def __init__(self, test): | 146 def __init__(self, test): |
160 gtk.EventBox.__init__(self) | 147 gtk.EventBox.__init__(self) |
161 self.modify_bg(gtk.STATE_NORMAL, _BLACK) | 148 self.modify_bg(gtk.STATE_NORMAL, ft.BLACK) |
162 label_status = gtk.Label(_UNTESTED) | 149 label_status = gtk.Label(ft.UNTESTED) |
163 label_status.set_size_request(*_LABEL_STATUS_SIZE) | 150 label_status.set_size_request(*_LABEL_STATUS_SIZE) |
164 label_status.set_alignment(0, 0.5) | 151 label_status.set_alignment(0, 0.5) |
165 label_status.modify_font(_LABEL_STATUS_FONT) | 152 label_status.modify_font(_LABEL_STATUS_FONT) |
166 label_status.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG) | 153 label_status.modify_fg(gtk.STATE_NORMAL, _LABEL_UNTESTED_FG) |
167 label_en = gtk.Label(test.label_en) | 154 label_en = gtk.Label(test.label_en) |
168 label_en.set_alignment(1, 0.5) | 155 label_en.set_alignment(1, 0.5) |
169 label_en.modify_font(_LABEL_EN_FONT) | 156 label_en.modify_font(_LABEL_EN_FONT) |
170 label_en.modify_fg(gtk.STATE_NORMAL, _LIGHT_GREEN) | 157 label_en.modify_fg(gtk.STATE_NORMAL, ft.LIGHT_GREEN) |
171 label_zw = gtk.Label(test.label_zw) | 158 label_zw = gtk.Label(test.label_zw) |
172 label_zw.set_alignment(1, 0.5) | 159 label_zw.set_alignment(1, 0.5) |
173 label_zw.modify_font(_LABEL_ZW_FONT) | 160 label_zw.modify_font(_LABEL_ZW_FONT) |
174 label_zw.modify_fg(gtk.STATE_NORMAL, _LIGHT_GREEN) | 161 label_zw.modify_fg(gtk.STATE_NORMAL, ft.LIGHT_GREEN) |
175 label_sep = gtk.Label(' : ') | 162 label_sep = gtk.Label(' : ') |
176 label_sep.set_alignment(0.5, 0.5) | 163 label_sep.set_alignment(0.5, 0.5) |
177 label_sep.modify_font(_LABEL_EN_FONT) | 164 label_sep.modify_font(_LABEL_EN_FONT) |
178 label_sep.modify_fg(gtk.STATE_NORMAL, _LIGHT_GREEN) | 165 label_sep.modify_fg(gtk.STATE_NORMAL, ft.LIGHT_GREEN) |
179 hbox = gtk.HBox() | 166 hbox = gtk.HBox() |
180 hbox.pack_end(label_status, False, False) | 167 hbox.pack_end(label_status, False, False) |
181 hbox.pack_end(label_sep, False, False) | 168 hbox.pack_end(label_sep, False, False) |
182 hbox.pack_end(label_zw, False, False) | 169 hbox.pack_end(label_zw, False, False) |
183 hbox.pack_end(label_en, False, False) | 170 hbox.pack_end(label_en, False, False) |
184 self.add(hbox) | 171 self.add(hbox) |
185 self.label_status = label_status | 172 self.label_status = label_status |
186 | 173 |
187 def update(self, status): | 174 def update(self, status): |
188 if status == _UNTESTED: | 175 if status == ft.UNTESTED: |
189 return | 176 return |
190 self.label_status.set_text(status) | 177 self.label_status.set_text(status) |
191 self.label_status.modify_fg(gtk.STATE_NORMAL, _LABEL_COLORS[status]) | 178 self.label_status.modify_fg(gtk.STATE_NORMAL, ft.LABEL_COLORS[status]) |
192 self.queue_draw() | 179 self.queue_draw() |
193 | 180 |
194 | 181 |
195 class StatusMap(): | 182 class StatusMap(): |
196 | 183 |
197 def __init__(self, status_file_path, test_list): | 184 def __init__(self, status_file_path, test_list): |
198 self._test_queue = [t for t in reversed(test_list)] | 185 self._test_queue = [t for t in reversed(test_list)] |
199 self._as_test_set = set(t for t in test_list if t.automated_seq) | 186 self._as_test_set = set(t for t in test_list if t.automated_seq) |
200 self._status_dict = {} | 187 self._status_dict = {} |
201 for test in test_list: | 188 for test in test_list: |
202 test_index = self.index(test.formal_name, test.tag_prefix) | 189 test_index = self.index(test.formal_name, test.tag_prefix) |
203 self._status_dict[test_index] = (test, _UNTESTED, 0, None) | 190 self._status_dict[test_index] = (test, ft.UNTESTED, 0, None) |
204 for subtest in test.automated_seq: | 191 for subtest in test.automated_seq: |
205 st_index = self.index(subtest.formal_name, subtest.tag_prefix) | 192 st_index = self.index(subtest.formal_name, subtest.tag_prefix) |
206 self._status_dict[st_index] = (subtest, _UNTESTED, 0, None) | 193 self._status_dict[st_index] = (subtest, ft.UNTESTED, 0, None) |
207 self._status_file_path = status_file_path | 194 self._status_file_path = status_file_path |
208 self._status_file_pos = 0 | 195 self._status_file_pos = 0 |
209 self.read_new_data() | 196 self.read_new_data() |
210 gobject.timeout_add(200, self.read_new_data) | 197 gobject.timeout_add(200, self.read_new_data) |
211 | 198 |
212 def index(self, formal_name, tag_prefix): | 199 def index(self, formal_name, tag_prefix): |
213 return '%s.%s' % (formal_name, tag_prefix) | 200 return '%s.%s' % (formal_name, tag_prefix) |
214 | 201 |
215 def next_untested(self): | 202 def next_untested(self): |
216 remaining = [t for t in self._test_queue | 203 remaining = [t for t in self._test_queue |
217 if self.lookup_status(t) == _UNTESTED] | 204 if self.lookup_status(t) == ft.UNTESTED] |
218 XXX_log('remaining untested = [%s]' % | 205 factory.log('remaining untested = [%s]' % |
219 ', '.join([self.index(t.formal_name, t.tag_prefix) | 206 ', '.join([self.index(t.formal_name, t.tag_prefix) |
220 for t in remaining])) | 207 for t in remaining])) |
221 if not remaining: return None | 208 if not remaining: return None |
222 return remaining.pop() | 209 return remaining.pop() |
223 | 210 |
224 def read_new_data(self): | 211 def read_new_data(self): |
225 with open(self._status_file_path) as file: | 212 with open(self._status_file_path) as file: |
226 file.seek(self._status_file_pos) | 213 file.seek(self._status_file_pos) |
227 for line in file: | 214 for line in file: |
228 cols = line.lstrip().split('\t') + [''] | 215 cols = line.lstrip().split('\t') + [''] |
229 code = cols[0] | 216 code = cols[0] |
230 test_id = cols[1] | 217 test_id = cols[1] |
231 if code not in _STATUS_CODE_MAP or test_id == '----': | 218 if code not in ft.STATUS_CODE_MAP or test_id == '----': |
232 continue | 219 continue |
233 status = _STATUS_CODE_MAP[code] | 220 status = ft.STATUS_CODE_MAP[code] |
234 XXX_log('reading code = %s, test_id = %s' % (code, test_id)) | 221 factory.log('reading code = %s, test_id = %s' % (code, test_id)) |
235 formal_name, _, tag = test_id.rpartition('.') | 222 formal_name, _, tag = test_id.rpartition('.') |
236 tag_prefix, _, count = tag.rpartition('_') | 223 tag_prefix, _, count = tag.rpartition('_') |
237 self.update(formal_name, tag_prefix, status, int(count)) | 224 self.update(formal_name, tag_prefix, status, int(count)) |
238 self._status_file_pos = file.tell() | 225 self._status_file_pos = file.tell() |
239 map(self.update_as_test, self._as_test_set) | 226 map(self.update_as_test, self._as_test_set) |
240 return True | 227 return True |
241 | 228 |
242 def update(self, formal_name, tag_prefix, status, count): | 229 def update(self, formal_name, tag_prefix, status, count): |
243 test_index = self.index(formal_name, tag_prefix) | 230 test_index = self.index(formal_name, tag_prefix) |
244 if test_index not in self._status_dict: | 231 if test_index not in self._status_dict: |
245 XXX_log('ignoring status update (%s) for test %s' % | 232 factory.log('ignoring status update (%s) for test %s' % |
246 (status, test_index)) | 233 (status, test_index)) |
247 return | 234 return |
248 test, old_status, old_count, label = self._status_dict[test_index] | 235 test, old_status, old_count, label = self._status_dict[test_index] |
249 if count < old_count: | 236 if count < old_count: |
250 XXX_log('ERROR: count regression for %s (%d-%d)' % | 237 factory.log('ERROR: count regression for %s (%d-%d)' % |
251 (test_index, old_count, count)) | 238 (test_index, old_count, count)) |
252 if status != old_status: | 239 if status != old_status: |
253 XXX_log('status change for %s : %s/%s -> %s/%s' % | 240 factory.log('status change for %s : %s/%s -> %s/%s' % |
254 (test_index, old_status, old_count, status, count)) | 241 (test_index, old_status, old_count, status, count)) |
255 if label is not None: | 242 if label is not None: |
256 label.update(status) | 243 label.update(status) |
257 self._status_dict[test_index] = (test, status, count, label) | 244 self._status_dict[test_index] = (test, status, count, label) |
258 | 245 |
259 def update_as_test(self, test): | 246 def update_as_test(self, test): |
260 st_status_set = set(map(self.lookup_status, test.automated_seq)) | 247 st_status_set = set(map(self.lookup_status, test.automated_seq)) |
261 max_count = max(map(self.lookup_count, test.automated_seq)) | 248 max_count = max(map(self.lookup_count, test.automated_seq)) |
262 if len(st_status_set) == 1: | 249 if len(st_status_set) == 1: |
263 status = st_status_set.pop() | 250 status = st_status_set.pop() |
264 elif _ACTIVE in st_status_set: | |
265 status = _ACTIVE | |
266 else: | 251 else: |
267 status = _FAILED | 252 status = ft.ACTIVE in st_status_set and ft.ACTIVE or ft.FAILED |
268 self.update(test.formal_name, test.tag_prefix, status, max_count) | 253 self.update(test.formal_name, test.tag_prefix, status, max_count) |
269 | 254 |
270 def set_label(self, test, label): | 255 def set_label(self, test, label): |
271 test_index = self.index(test.formal_name, test.tag_prefix) | 256 test_index = self.index(test.formal_name, test.tag_prefix) |
272 test, status, count, _ = self._status_dict[test_index] | 257 test, status, count, _ = self._status_dict[test_index] |
273 label.update(status) | 258 label.update(status) |
274 self._status_dict[test_index] = test, status, count, label | 259 self._status_dict[test_index] = test, status, count, label |
275 | 260 |
276 def lookup_status(self, test): | 261 def lookup_status(self, test): |
277 test_index = self.index(test.formal_name, test.tag_prefix) | 262 test_index = self.index(test.formal_name, test.tag_prefix) |
(...skipping 19 matching lines...) Expand all Loading... |
297 frame = gtk.EventBox() | 282 frame = gtk.EventBox() |
298 frame.set_size_request(width, -1) | 283 frame.set_size_request(width, -1) |
299 frame.modify_bg(gtk.STATE_NORMAL, _SEP_COLOR) | 284 frame.modify_bg(gtk.STATE_NORMAL, _SEP_COLOR) |
300 return frame | 285 return frame |
301 | 286 |
302 | 287 |
303 def make_notest_label(): | 288 def make_notest_label(): |
304 label = gtk.Label('no active test') | 289 label = gtk.Label('no active test') |
305 label.modify_font(_OTHER_LABEL_FONT) | 290 label.modify_font(_OTHER_LABEL_FONT) |
306 label.set_alignment(0.5, 0.5) | 291 label.set_alignment(0.5, 0.5) |
307 label.modify_fg(gtk.STATE_NORMAL, _LIGHT_GREEN) | 292 label.modify_fg(gtk.STATE_NORMAL, ft.LIGHT_GREEN) |
308 box = gtk.EventBox() | 293 box = gtk.EventBox() |
309 box.modify_bg(gtk.STATE_NORMAL, _BLACK) | 294 box.modify_bg(gtk.STATE_NORMAL, ft.BLACK) |
310 box.add(label) | 295 box.add(label) |
311 return box | 296 return box |
312 | 297 |
313 | 298 |
314 def make_automated_seq_widget(as_test, status_map): | 299 def make_automated_seq_widget(as_test, status_map): |
315 vbox = gtk.VBox() | 300 vbox = gtk.VBox() |
316 vbox.set_spacing(0) | 301 vbox.set_spacing(0) |
317 map(lambda st: vbox.pack_start(status_map.lookup_label(st), False, False), | 302 map(lambda st: vbox.pack_start(status_map.lookup_label(st), False, False), |
318 as_test.automated_seq) | 303 as_test.automated_seq) |
319 return vbox | 304 return vbox |
320 | 305 |
321 | 306 |
322 def main(): | 307 def main(): |
323 window = gtk.Window(gtk.WINDOW_TOPLEVEL) | 308 window = gtk.Window(gtk.WINDOW_TOPLEVEL) |
324 window.connect('destroy', lambda _: gtk.main_quit()) | 309 window.connect('destroy', lambda _: gtk.main_quit()) |
325 window.modify_bg(gtk.STATE_NORMAL, _BLACK) | 310 window.modify_bg(gtk.STATE_NORMAL, ft.BLACK) |
326 | 311 |
327 screen = window.get_screen() | 312 screen = window.get_screen() |
328 screen_size = (screen.get_width(), screen.get_height()) | 313 screen_size = (screen.get_width(), screen.get_height()) |
329 window.set_size_request(*screen_size) | 314 window.set_size_request(*screen_size) |
330 | 315 |
331 label_trough = gtk.VBox() | 316 label_trough = gtk.VBox() |
332 label_trough.set_spacing(0) | 317 label_trough.set_spacing(0) |
333 | 318 |
334 rhs_box = gtk.EventBox() | 319 rhs_box = gtk.EventBox() |
335 rhs_box.modify_bg(gtk.STATE_NORMAL, _LABEL_TROUGH_COLOR) | 320 rhs_box.modify_bg(gtk.STATE_NORMAL, _LABEL_TROUGH_COLOR) |
336 rhs_box.add(label_trough) | 321 rhs_box.add(label_trough) |
337 | 322 |
338 console_box = gtk.EventBox() | 323 console_box = gtk.EventBox() |
339 console_box.set_size_request(-1, 180) | 324 console_box.set_size_request(-1, 180) |
340 console_box.modify_bg(gtk.STATE_NORMAL, _BLACK) | 325 console_box.modify_bg(gtk.STATE_NORMAL, ft.BLACK) |
341 | 326 |
342 notest_label = make_notest_label() | 327 notest_label = make_notest_label() |
343 | 328 |
344 test_widget_box = gtk.Alignment(xalign=0.5, yalign=0.5) | 329 test_widget_box = gtk.Alignment(xalign=0.5, yalign=0.5) |
345 test_widget_box.set_size_request(-1, -1) | 330 test_widget_box.set_size_request(-1, -1) |
346 test_widget_box.add(notest_label) | 331 test_widget_box.add(notest_label) |
347 | 332 |
348 lhs_box = gtk.VBox() | 333 lhs_box = gtk.VBox() |
349 lhs_box.pack_end(console_box, False, False) | 334 lhs_box.pack_end(console_box, False, False) |
350 lhs_box.pack_start(test_widget_box) | 335 lhs_box.pack_start(test_widget_box) |
351 lhs_box.pack_start(make_hsep(3), False, False) | 336 lhs_box.pack_start(make_hsep(3), False, False) |
352 | 337 |
353 base_box = gtk.HBox() | 338 base_box = gtk.HBox() |
354 base_box.pack_end(rhs_box, False, False) | 339 base_box.pack_end(rhs_box, False, False) |
355 base_box.pack_end(make_vsep(3), False, False) | 340 base_box.pack_end(make_vsep(3), False, False) |
356 base_box.pack_start(lhs_box) | 341 base_box.pack_start(lhs_box) |
357 | 342 |
358 window.connect('key-release-event', handle_key_release_event) | 343 window.connect('key-release-event', handle_key_release_event) |
359 window.add_events(gtk.gdk.KEY_RELEASE_MASK) | 344 window.add_events(gtk.gdk.KEY_RELEASE_MASK) |
360 | 345 |
361 # On startup, get general configuration data from the autotest | 346 # On startup, get general configuration data from the autotest |
362 # control program, specifically the list of tests to run (in | 347 # control program, specifically the list of tests to run (in |
363 # order) and some filenames. | 348 # order) and some filenames. |
364 XXX_log('pulling control info') | 349 factory.log('pulling control info') |
365 test_list = control_recv() | 350 test_list = control_recv() |
366 status_file_path = control_recv() | 351 status_file_path = control_recv() |
367 log_file_path = control_recv() | |
368 | 352 |
369 status_map = StatusMap(status_file_path, test_list) | 353 status_map = StatusMap(status_file_path, test_list) |
370 | 354 |
371 for test in test_list: | 355 for test in test_list: |
372 tlb = TestLabelBox(test) | 356 tlb = TestLabelBox(test) |
373 status_map.set_label(test, tlb) | 357 status_map.set_label(test, tlb) |
374 label_trough.pack_start(tlb, False, False) | 358 label_trough.pack_start(tlb, False, False) |
375 label_trough.pack_start(make_hsep(), False, False) | 359 label_trough.pack_start(make_hsep(), False, False) |
376 for subtest in test.automated_seq: | 360 for subtest in test.automated_seq: |
377 stlb = SubTestLabelBox(subtest) | 361 stlb = SubTestLabelBox(subtest) |
378 status_map.set_label(subtest, stlb) | 362 status_map.set_label(subtest, stlb) |
379 | 363 |
380 window.add(base_box) | 364 window.add(base_box) |
381 window.show_all() | 365 window.show_all() |
382 | 366 |
383 test_widget_allocation = test_widget_box.get_allocation() | 367 test_widget_allocation = test_widget_box.get_allocation() |
384 test_widget_size = (test_widget_allocation.width, | 368 test_widget_size = (test_widget_allocation.width, |
385 test_widget_allocation.height) | 369 test_widget_allocation.height) |
386 XXX_log('test_widget_size = %s' % repr(test_widget_size)) | 370 factory.log('test_widget_size = %s' % repr(test_widget_size)) |
387 control_send(test_widget_size) | 371 control_send(test_widget_size) |
388 | 372 |
389 trigger_dict = dict((test.trigger, test) for test in test_list) | 373 trigger_dict = dict((test.trigger, test) for test in test_list) |
390 | 374 |
391 gobject.io_add_watch(sys.stdin, gobject.IO_IN, stdin_callback) | 375 gobject.io_add_watch(sys.stdin, gobject.IO_IN, stdin_callback) |
392 | 376 |
393 console = console_proc(console_box.get_allocation(), log_file_path) | 377 console = Console(console_box.get_allocation()) |
394 | 378 |
395 XXX_log('finished ui setup') | 379 factory.log('finished ui setup') |
396 | 380 |
397 # Test selection is driven either by triggers or by the | 381 # Test selection is driven either by triggers or by the |
398 # remaining_tests_queue. If a trigger was seen, explicitly run | 382 # remaining_tests_queue. If a trigger was seen, explicitly run |
399 # the corresponding test. Otherwise choose the next test from the | 383 # the corresponding test. Otherwise choose the next test from the |
400 # queue. Tests are removed from the queue as they are run, | 384 # queue. Tests are removed from the queue as they are run, |
401 # regarless of the outcome. Tests that are interrupted by trigger | 385 # regarless of the outcome. Tests that are interrupted by trigger |
402 # are treated as having failed. | 386 # are treated as having failed. |
403 # | 387 # |
404 # Iterations in the main loop here are driven by data availability | 388 # Iterations in the main loop here are driven by data availability |
405 # on stdin, which is used to communicate with the autotest control | 389 # on stdin, which is used to communicate with the autotest control |
406 # program. On each step through the loop, a trigger is received | 390 # program. On each step through the loop, a trigger is received |
407 # (possibly None) to indicate how the next test should be selected. | 391 # (possibly None) to indicate how the next test should be selected. |
408 | 392 |
409 while True: | 393 while True: |
410 command, arg = control_recv() | 394 command, arg = control_recv() |
411 XXX_log('ui received command %s(%s)' % (command, arg)) | 395 factory.log('ui received command %s(%s)' % (command, arg)) |
412 if command == 'switch_to': | 396 if command == 'switch_to': |
413 next_test = trigger_dict.get(arg, None) | 397 next_test = trigger_dict.get(arg, None) |
414 elif command == 'next_test': | 398 elif command == 'next_test': |
415 next_test = status_map.next_untested() | 399 next_test = status_map.next_untested() |
416 else: | 400 else: |
417 XXX_log('ui command unknown, exiting...') | 401 factory.log('ui command unknown, exiting...') |
418 break | 402 break |
419 control_send_target_test_update( | 403 control_send_target_test_update( |
420 next_test, status_map.lookup_count(next_test) + 1) | 404 next_test, status_map.lookup_count(next_test) + 1) |
421 if next_test.automated_seq: | 405 if next_test.automated_seq: |
422 XXX_log('ui starting automated_seq') | 406 factory.log('ui starting automated_seq') |
423 test_widget_box.remove(notest_label) | 407 test_widget_box.remove(notest_label) |
424 as_widget = make_automated_seq_widget(next_test, status_map) | 408 as_widget = make_automated_seq_widget(next_test, status_map) |
425 test_widget_box.add(as_widget) | 409 test_widget_box.add(as_widget) |
426 window.show_all() | 410 window.show_all() |
427 gtk.main() | 411 gtk.main() |
428 command = control_recv() | 412 command = control_recv() |
429 XXX_log('ui automated_seq cmd (%s)' % command) | 413 factory.log('ui automated_seq cmd (%s)' % command) |
430 test_widget_box.remove(as_widget) | 414 test_widget_box.remove(as_widget) |
431 test_widget_box.add(notest_label) | 415 test_widget_box.add(notest_label) |
432 window.show_all() | 416 window.show_all() |
433 XXX_log('ui exiting automated_seq') | 417 factory.log('ui exiting automated_seq') |
434 else: | 418 else: |
435 gtk.main() | 419 gtk.main() |
436 | 420 |
437 # Tell the control process we are done. | 421 # Tell the control process we are done. |
438 control_send((None, 0)) | 422 control_send((None, 0)) |
439 | 423 |
440 XXX_log('exiting ui') | 424 factory.log('exiting ui') |
441 | 425 |
442 if __name__ == '__main__': | 426 if __name__ == '__main__': |
443 | |
444 # In global scope, get the test_data class description from the | |
445 # control program -- this allows a convenient single point of | |
446 # definition for this class. | |
447 test_data_class_def = control_recv() | |
448 exec(test_data_class_def) | |
449 | |
450 main() | 427 main() |
OLD | NEW |