| OLD | NEW |
| 1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
| 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 library provides convenience routines to launch factory tests. | 10 # This library provides convenience routines to launch factory tests. |
| 11 # This includes support for identifying keyboard test switching | 11 # This includes support for identifying keyboard test switching |
| 12 # triggers, grabbing control of the keyboard and mouse, and making the | 12 # triggers, grabbing control of the keyboard and mouse, and making the |
| 13 # mouse cursor disappear. It also manages communication of any found | 13 # mouse cursor disappear. It also manages communication of any found |
| 14 # keyboard triggers to the control process, via writing data to | 14 # keyboard triggers to the control process, via writing data to |
| 15 # factory.RESULT_FILE_PATH. | 15 # factory.RESULT_FILE_PATH. |
| 16 | 16 |
| 17 | 17 |
| 18 from autotest_lib.client.bin import factory | 18 from autotest_lib.client.bin import factory |
| 19 from autotest_lib.client.common_lib import error | 19 from autotest_lib.client.common_lib import error |
| 20 | 20 |
| 21 from factory import ACTIVE, PASSED, FAILED, UNTESTED, STATUS_CODE_MAP |
| 22 |
| 21 import cairo | 23 import cairo |
| 22 import gtk | 24 import gtk |
| 23 import pango | 25 import pango |
| 24 import sys | 26 import sys |
| 25 | 27 |
| 26 | 28 |
| 27 BLACK = gtk.gdk.Color() | 29 BLACK = gtk.gdk.Color() |
| 28 RED = gtk.gdk.Color(0xFFFF, 0, 0) | 30 RED = gtk.gdk.Color(0xFFFF, 0, 0) |
| 29 GREEN = gtk.gdk.Color(0, 0xFFFF, 0) | 31 GREEN = gtk.gdk.Color(0, 0xFFFF, 0) |
| 30 BLUE = gtk.gdk.Color(0, 0, 0xFFFF) | 32 BLUE = gtk.gdk.Color(0, 0, 0xFFFF) |
| 31 WHITE = gtk.gdk.Color(0xFFFF, 0xFFFF, 0xFFFF) | 33 WHITE = gtk.gdk.Color(0xFFFF, 0xFFFF, 0xFFFF) |
| 32 | 34 |
| 33 LIGHT_GREEN = gtk.gdk.color_parse('light green') | 35 LIGHT_GREEN = gtk.gdk.color_parse('light green') |
| 34 | 36 |
| 35 RGBA_GREEN_OVERLAY = (0, 0.5, 0, 0.6) | 37 RGBA_GREEN_OVERLAY = (0, 0.5, 0, 0.6) |
| 36 RGBA_YELLOW_OVERLAY = (0.6, 0.6, 0, 0.6) | 38 RGBA_YELLOW_OVERLAY = (0.6, 0.6, 0, 0.6) |
| 37 | 39 |
| 38 ACTIVE = 'ACTIVE' | |
| 39 PASSED = 'PASS' | |
| 40 FAILED = 'FAIL' | |
| 41 UNTESTED = 'UNTESTED' | |
| 42 | |
| 43 STATUS_CODE_MAP = { | |
| 44 'START': ACTIVE, | |
| 45 'GOOD': PASSED, | |
| 46 'FAIL': FAILED, | |
| 47 'ERROR': FAILED} | |
| 48 | |
| 49 LABEL_COLORS = { | 40 LABEL_COLORS = { |
| 50 ACTIVE: gtk.gdk.color_parse('light goldenrod'), | 41 ACTIVE: gtk.gdk.color_parse('light goldenrod'), |
| 51 PASSED: gtk.gdk.color_parse('pale green'), | 42 PASSED: gtk.gdk.color_parse('pale green'), |
| 52 FAILED: gtk.gdk.color_parse('tomato'), | 43 FAILED: gtk.gdk.color_parse('tomato'), |
| 53 UNTESTED: gtk.gdk.color_parse('dark slate grey')} | 44 UNTESTED: gtk.gdk.color_parse('dark slate grey')} |
| 54 | 45 |
| 55 LABEL_FONT = pango.FontDescription('courier new condensed 16') | 46 LABEL_FONT = pango.FontDescription('courier new condensed 16') |
| 56 | 47 |
| 57 FAIL_TIMEOUT = 30 | 48 FAIL_TIMEOUT = 30 |
| 58 | 49 |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 137 factory.log('factory_test running gtk.main') | 128 factory.log('factory_test running gtk.main') |
| 138 gtk.main() | 129 gtk.main() |
| 139 factory.log('factory_test quit gtk.main') | 130 factory.log('factory_test quit gtk.main') |
| 140 | 131 |
| 141 if cleanup_callback is not None: | 132 if cleanup_callback is not None: |
| 142 cleanup_callback() | 133 cleanup_callback() |
| 143 | 134 |
| 144 gtk.gdk.pointer_ungrab() | 135 gtk.gdk.pointer_ungrab() |
| 145 gtk.gdk.keyboard_ungrab() | 136 gtk.gdk.keyboard_ungrab() |
| 146 | 137 |
| 147 if self._got_trigger is None: | 138 if self._got_trigger is not None: |
| 148 return | 139 factory.log('run_test_widget returning kbd_shortcut "%s"' % |
| 149 with open(factory.RESULT_FILE_PATH, 'w') as file: | 140 self._got_trigger) |
| 150 file.write('%s\n' % repr(self._got_trigger)) | 141 factory.log_shared_data('activated_kbd_shortcut', self._got_trigger) |
| 151 raise error.TestFail('explicit test switch triggered (%s)' % | |
| 152 self._got_trigger) | |
| 153 | |
| 154 | |
| 155 class StatusMap(): | |
| 156 | |
| 157 def __init__(self, status_file_path, test_list): | |
| 158 self._test_queue = [t for t in reversed(test_list)] | |
| 159 self._as_test_set = set(t for t in test_list if t.automated_seq) | |
| 160 self._status_map = {} | |
| 161 for test in test_list: | |
| 162 test_index = self.index(test.formal_name, test.tag_prefix) | |
| 163 self._status_map[test_index] = (test, UNTESTED, 0, None, None) | |
| 164 for subtest in test.automated_seq: | |
| 165 st_index = self.index(subtest.formal_name, subtest.tag_prefix) | |
| 166 self._status_map[st_index] = (subtest, UNTESTED, 0, None, None) | |
| 167 self._status_file_path = status_file_path | |
| 168 self._status_file_pos = 0 | |
| 169 self.read_new_data() | |
| 170 | |
| 171 def index(self, formal_name, tag_prefix): | |
| 172 return '%s.%s' % (formal_name, tag_prefix) | |
| 173 | |
| 174 def filter(self, target_status): | |
| 175 comp = (isinstance(target_status, list) and | |
| 176 (lambda s: s in target_status) or | |
| 177 (lambda s: s == target_status)) | |
| 178 return [t for t in self._test_queue if comp(self.lookup_status(t))] | |
| 179 | |
| 180 def next_untested(self): | |
| 181 remaining = self.filter(UNTESTED) | |
| 182 factory.log('remaining untested = [%s]' % | |
| 183 ', '.join([self.index(t.formal_name, t.tag_prefix) | |
| 184 for t in remaining])) | |
| 185 if not remaining: return None | |
| 186 return remaining.pop() | |
| 187 | |
| 188 def read_new_data(self): | |
| 189 with open(self._status_file_path) as file: | |
| 190 file.seek(self._status_file_pos) | |
| 191 for line in file: | |
| 192 cols = line.strip().split('\t') + [''] | |
| 193 code = cols[0] | |
| 194 test_id = cols[1] | |
| 195 if code not in STATUS_CODE_MAP or test_id == '----': | |
| 196 continue | |
| 197 status = STATUS_CODE_MAP[code] | |
| 198 error = status == FAILED and cols[len(cols) - 2] or None | |
| 199 factory.log('reading code = %s, test_id = %s, error_msg = "%s"' | |
| 200 % (code, test_id, error)) | |
| 201 formal_name, _, tag = test_id.rpartition('.') | |
| 202 tag_prefix, _, count = tag.rpartition('_') | |
| 203 self.update(formal_name, tag_prefix, status, int(count), error) | |
| 204 self._status_file_pos = file.tell() | |
| 205 map(self.update_as_test, self._as_test_set) | |
| 206 return True | |
| 207 | |
| 208 def update(self, formal_name, tag_prefix, status, count, error): | |
| 209 test_index = self.index(formal_name, tag_prefix) | |
| 210 if test_index not in self._status_map: | |
| 211 factory.log('ignoring status update (%s) for test %s' % | |
| 212 (status, test_index)) | |
| 213 return | |
| 214 test, old_status, old_count, label, _ = self._status_map[test_index] | |
| 215 if count < old_count: | |
| 216 factory.log('ERROR: count regression for %s (%d-%d)' % | |
| 217 (test_index, old_count, count)) | |
| 218 if test.repeat_forever and status in [PASSED, FAILED]: | |
| 219 status = UNTESTED | |
| 220 if status != old_status: | |
| 221 factory.log('status change for %s : %s/%s -> %s/%s' % | |
| 222 (test_index, old_status, old_count, status, count)) | |
| 223 if label is not None: | |
| 224 label.update(status) | |
| 225 self._status_map[test_index] = (test, status, count, label, error) | |
| 226 | |
| 227 def update_as_test(self, test): | |
| 228 st_status_set = set(map(self.lookup_status, test.automated_seq)) | |
| 229 max_count = max(map(self.lookup_count, test.automated_seq)) | |
| 230 if len(st_status_set) == 1: | |
| 231 status = st_status_set.pop() | |
| 232 else: | |
| 233 status = ACTIVE in st_status_set and ACTIVE or FAILED | |
| 234 self.update(test.formal_name, test.tag_prefix, status, max_count, None) | |
| 235 | |
| 236 def set_label(self, test, label): | |
| 237 test_index = self.index(test.formal_name, test.tag_prefix) | |
| 238 test, status, count, _, error = self._status_map[test_index] | |
| 239 label.update(status) | |
| 240 self._status_map[test_index] = test, status, count, label, error | |
| 241 | |
| 242 def lookup_status(self, test): | |
| 243 test_index = self.index(test.formal_name, test.tag_prefix) | |
| 244 return self._status_map[test_index][1] | |
| 245 | |
| 246 def lookup_count(self, test): | |
| 247 test_index = self.index(test.formal_name, test.tag_prefix) | |
| 248 return self._status_map[test_index][2] | |
| 249 | |
| 250 def lookup_label(self, test): | |
| 251 test_index = self.index(test.formal_name, test.tag_prefix) | |
| 252 return self._status_map[test_index][3] | |
| 253 | |
| 254 def lookup_error(self, test): | |
| 255 test_index = self.index(test.formal_name, test.tag_prefix) | |
| 256 return self._status_map[test_index][4] | |
| OLD | NEW |