OLD | NEW |
1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 | 5 |
6 # DESCRIPTION : | 6 # DESCRIPTION : |
7 # | 7 # |
8 # This library provides common types and routines for the factory ui | 8 # This library provides common types and routines for the factory ui |
9 # infrastructure. This library explicitly does not import gtk, to | 9 # infrastructure. This library explicitly does not import gtk, to |
10 # allow its use by the autotest control process. | 10 # allow its use by the autotest control process. |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
44 if l != 'self'] | 44 if l != 'self'] |
45 c = ('%s' % self.__class__).rpartition('.')[2] | 45 c = ('%s' % self.__class__).rpartition('.')[2] |
46 return '%s(%s)' % (c, ','.join(d)) | 46 return '%s(%s)' % (c, ','.join(d)) |
47 | 47 |
48 class FactoryAutotestTest(FactoryTest): | 48 class FactoryAutotestTest(FactoryTest): |
49 # Placeholder parent for tests with autotest_name fields. | 49 # Placeholder parent for tests with autotest_name fields. |
50 pass | 50 pass |
51 | 51 |
52 class OperatorTest(FactoryAutotestTest): | 52 class OperatorTest(FactoryAutotestTest): |
53 def __init__(self, label_en='', label_zw='', autotest_name=None, | 53 def __init__(self, label_en='', label_zw='', autotest_name=None, |
54 kbd_shortcut=None, dargs={}, drop_caches=False): | 54 kbd_shortcut=None, dargs={}, drop_caches=False, |
| 55 unique_name=None): |
55 self.__dict__.update(vars()) | 56 self.__dict__.update(vars()) |
56 | 57 |
57 class InformationScreen(OperatorTest): | 58 class InformationScreen(OperatorTest): |
58 # These tests never pass or fail, just return to untested state. | 59 # These tests never pass or fail, just return to untested state. |
59 pass | 60 pass |
60 | 61 |
61 class AutomatedSequence(FactoryTest): | 62 class AutomatedSequence(FactoryTest): |
62 def __init__(self, label_en='', label_zw='', subtest_tag_prefix=None, | 63 def __init__(self, label_en='', label_zw='', subtest_tag_prefix=None, |
63 kbd_shortcut=None, subtest_list=[]): | 64 kbd_shortcut=None, subtest_list=[], unique_name=None): |
64 self.__dict__.update(vars()) | 65 self.__dict__.update(vars()) |
65 | 66 |
66 class AutomatedSubTest(FactoryAutotestTest): | 67 class AutomatedSubTest(FactoryAutotestTest): |
67 def __init__(self, label_en='', label_zw='', autotest_name=None, | 68 def __init__(self, label_en='', label_zw='', autotest_name=None, |
68 dargs={}, drop_caches=False): | 69 dargs={}, drop_caches=False, unique_name=None): |
69 self.__dict__.update(vars()) | 70 self.__dict__.update(vars()) |
70 | 71 |
71 class AutomatedRebootSubTest(FactoryAutotestTest): | 72 class AutomatedRebootSubTest(FactoryAutotestTest): |
72 def __init__(self, label_en='', label_zw='', iterations=None, | 73 def __init__(self, label_en='', label_zw='', iterations=None, |
73 autotest_name='factory_RebootStub', dargs={}, | 74 autotest_name='factory_RebootStub', dargs={}, |
74 drop_caches=False): | 75 drop_caches=False, unique_name=None): |
75 self.__dict__.update(vars()) | 76 self.__dict__.update(vars()) |
76 | 77 |
77 | 78 |
78 class TestDatabase: | 79 class TestDatabase: |
79 | 80 |
80 def __init__(self, test_list): | 81 def __init__(self, test_list): |
81 self.test_queue = [t for t in reversed(test_list)] | 82 self.test_queue = [t for t in reversed(test_list)] |
82 self._subtest_parent_map = {} | 83 self._subtest_parent_map = {} |
83 self._tag_prefix_map = {} | 84 self._tag_prefix_map = {} |
84 for test in test_list: | 85 for test in test_list: |
85 if not isinstance(test, AutomatedSequence): | 86 if not isinstance(test, AutomatedSequence): |
86 self._tag_prefix_map[test] = test.kbd_shortcut | 87 self._tag_prefix_map[test] = test.kbd_shortcut |
87 continue | 88 continue |
88 step_count = 1 | 89 step_count = 1 |
89 for subtest in test.subtest_list: | 90 for subtest in test.subtest_list: |
90 self._subtest_parent_map[subtest] = test | 91 self._subtest_parent_map[subtest] = test |
91 if not isinstance(subtest, FactoryAutotestTest): | 92 if not isinstance(subtest, FactoryAutotestTest): |
92 continue | 93 continue |
93 tag_prefix = ('%s_s%d' % (test.subtest_tag_prefix, step_count)) | 94 tag_prefix = ('%s_s%d' % (test.subtest_tag_prefix, step_count)) |
94 self._tag_prefix_map[subtest] = tag_prefix | 95 self._tag_prefix_map[subtest] = tag_prefix |
95 step_count += 1 | 96 step_count += 1 |
96 self.seq_test_set = set(test for test in test_list | 97 self.seq_test_set = set(test for test in test_list |
97 if isinstance(test, AutomatedSequence)) | 98 if isinstance(test, AutomatedSequence)) |
98 self.subtest_set = set(reduce(lambda x, y: x + y, | 99 self.subtest_set = set(reduce(lambda x, y: x + y, |
99 [test.subtest_list for test in | 100 [test.subtest_list for test in |
100 self.seq_test_set], [])) | 101 self.seq_test_set], [])) |
101 self._subtest_map = dict((self._tag_prefix_map[st], st) | 102 self._subtest_map = dict((self._tag_prefix_map[st], st) |
102 for st in self.subtest_set) | 103 for st in self.subtest_set) |
103 self._unique_name_map = dict((self.get_unique_name(t), t) | 104 self.all_tests = set(test_list) | self.subtest_set |
104 for t in self._tag_prefix_map) | 105 self._unique_name_map = dict((t.unique_name, t) for t in self.all_tests |
| 106 if isinstance(t, FactoryAutotestTest) |
| 107 and t.unique_name is not None) |
| 108 self._unique_details_map = dict((self.get_unique_details(t), t) |
| 109 for t in self.all_tests) |
105 self._kbd_shortcut_map = dict((test.kbd_shortcut, test) | 110 self._kbd_shortcut_map = dict((test.kbd_shortcut, test) |
106 for test in test_list) | 111 for test in test_list) |
107 self.kbd_shortcut_set = set(self._kbd_shortcut_map) | 112 self.kbd_shortcut_set = set(self._kbd_shortcut_map) |
108 | 113 |
109 # Validate keyboard shortcut uniqueness. | 114 # Validate keyboard shortcut uniqueness. |
110 assert(None not in self.kbd_shortcut_set) | 115 assert(None not in self.kbd_shortcut_set) |
111 delta = set(test_list) - set(self._kbd_shortcut_map.values()) | 116 delta = set(test_list) - set(self._kbd_shortcut_map.values()) |
112 for test in delta: | 117 for test in delta: |
113 collision = kbd_shortcut_map[test.kbd_shortcut] | 118 collision = kbd_shortcut_map[test.kbd_shortcut] |
114 log('ERROR: tests %s and %s both have kbd_shortcut %s' % | 119 log('ERROR: tests %s and %s both have kbd_shortcut %s' % |
115 (test.label_en, collision.label_en, test.kbd_shortcut)) | 120 (test.label_en, collision.label_en, test.kbd_shortcut)) |
116 assert not delta | 121 assert not delta |
117 | 122 |
118 def get_test_by_details(self, autotest_name, tag_prefix): | 123 def get_tag_prefix(self, test): |
119 unique_name = '%s.%s' % (autotest_name, tag_prefix) | 124 return self._tag_prefix_map[test] |
120 return self._unique_name_map.get(unique_name) | 125 |
| 126 def get_unique_details(self, test): |
| 127 if isinstance(test, AutomatedSequence): |
| 128 return test.subtest_tag_prefix |
| 129 return '%s.%s' % (test.autotest_name, self.get_tag_prefix(test)) |
| 130 |
| 131 def get_test_by_unique_details(self, autotest_name, tag_prefix): |
| 132 unique_details = '%s.%s' % (autotest_name, tag_prefix) |
| 133 return self._unique_details_map.get(unique_details) |
121 | 134 |
122 def get_test_by_kbd_shortcut(self, kbd_shortcut): | 135 def get_test_by_kbd_shortcut(self, kbd_shortcut): |
123 return self._kbd_shortcut_map.get(kbd_shortcut) | 136 return self._kbd_shortcut_map.get(kbd_shortcut) |
124 | 137 |
125 def get_unique_name(self, test): | 138 def get_test_by_unique_name(self, unique_name): |
126 if isinstance(test, AutomatedSequence): | 139 return self._unique_name_map.get(unique_name) |
127 return test.subtest_tag_prefix | |
128 return '%s.%s' % (test.autotest_name, self._tag_prefix_map[test]) | |
129 | |
130 def get_tag_prefix(self, test): | |
131 return self._tag_prefix_map[test] | |
132 | |
133 def get_all_tests(self): | |
134 return set(self.test_queue) | self.subtest_set | |
135 | 140 |
136 def get_subtest_parent(self, test): | 141 def get_subtest_parent(self, test): |
137 return self._subtest_parent_map.get(test) | 142 return self._subtest_parent_map.get(test) |
138 | 143 |
139 def get_subtest_by_tag_prefix(self, tag_prefix): | 144 def get_subtest_by_tag_prefix(self, tag_prefix): |
140 return self._subtest_map.get(tag_prefix) | 145 return self._subtest_map.get(tag_prefix) |
141 | 146 |
142 | 147 |
143 class StatusMap: | 148 class StatusMap: |
144 | 149 |
145 class Entry: | 150 class Entry: |
146 | 151 |
147 def __init__(self): | 152 def __init__(self): |
148 self.status = UNTESTED | 153 self.status = UNTESTED |
149 self.count = 0 | 154 self.count = 0 |
150 self.label_box = None | 155 self.label_box = None |
151 self.error_msg = None | 156 self.error_msg = None |
152 | 157 |
153 def __init__(self, test_list, status_file_path): | 158 def __init__(self, test_list, status_file_path): |
154 self.test_db = TestDatabase(test_list) | 159 self.test_db = TestDatabase(test_list) |
155 all_tests = self.test_db.get_all_tests() | 160 all_tests = self.test_db.all_tests |
156 self._status_map = dict((t, StatusMap.Entry()) for t in all_tests) | 161 self._status_map = dict((t, StatusMap.Entry()) for t in all_tests) |
157 self._status_file_path = status_file_path | 162 self._status_file_path = status_file_path |
158 self._status_file_pos = 0 | 163 self._status_file_pos = 0 |
159 self.read_new_data() | 164 self.read_new_data() |
160 | 165 |
161 def lookup_status(self, test): | 166 def lookup_status(self, test): |
162 return self._status_map[test].status | 167 return self._status_map[test].status |
163 | 168 |
164 def lookup_count(self, test): | 169 def lookup_count(self, test): |
165 return self._status_map[test].count | 170 return self._status_map[test].count |
(...skipping 14 matching lines...) Expand all Loading... |
180 | 185 |
181 def filter(self, target_status): | 186 def filter(self, target_status): |
182 comp = (isinstance(target_status, list) and | 187 comp = (isinstance(target_status, list) and |
183 (lambda s: s in target_status) or | 188 (lambda s: s in target_status) or |
184 (lambda s: s == target_status)) | 189 (lambda s: s == target_status)) |
185 return [t for t in self.test_db.test_queue | 190 return [t for t in self.test_db.test_queue |
186 if comp(self.lookup_status(t))] | 191 if comp(self.lookup_status(t))] |
187 | 192 |
188 def next_untested(self): | 193 def next_untested(self): |
189 remaining = self.filter(UNTESTED) | 194 remaining = self.filter(UNTESTED) |
190 unique_names = [self.test_db.get_unique_name(t) for t in remaining] | 195 unique_details = [self.test_db.get_unique_details(t) for t in remaining] |
191 log('remaining untested = [%s]' % ', '.join(unique_names)) | 196 log('remaining untested = [%s]' % ', '.join(unique_details)) |
192 return remaining is not [] and remaining.pop() or None | 197 return remaining is not [] and remaining.pop() or None |
193 | 198 |
194 def read_new_data(self): | 199 def read_new_data(self): |
195 with open(self._status_file_path) as file: | 200 with open(self._status_file_path) as file: |
196 file.seek(self._status_file_pos) | 201 file.seek(self._status_file_pos) |
197 for line in file: | 202 for line in file: |
198 cols = line.strip().split('\t') + [''] | 203 cols = line.strip().split('\t') + [''] |
199 code = cols[0] | 204 code = cols[0] |
200 test_id = cols[1] | 205 test_id = cols[1] |
201 if code not in STATUS_CODE_MAP or test_id == '----': | 206 if code not in STATUS_CODE_MAP or test_id == '----': |
202 continue | 207 continue |
203 status = STATUS_CODE_MAP[code] | 208 status = STATUS_CODE_MAP[code] |
204 error_msg = status == FAILED and cols[len(cols) - 2] or None | 209 error_msg = status == FAILED and cols[len(cols) - 2] or None |
205 log('reading code = %s, test_id = %s, error_msg = "%s"' | 210 log('reading code = %s, test_id = %s, error_msg = "%s"' |
206 % (code, test_id, error_msg)) | 211 % (code, test_id, error_msg)) |
207 autotest_name, _, tag = test_id.rpartition('.') | 212 autotest_name, _, tag = test_id.rpartition('.') |
208 tag_prefix, _, count = tag.rpartition('_') | 213 tag_prefix, _, count = tag.rpartition('_') |
209 test = self.test_db.get_test_by_details( | 214 test = self.test_db.get_test_by_unique_details( |
210 autotest_name, tag_prefix) | 215 autotest_name, tag_prefix) |
211 if test is None: | 216 if test is None: |
212 log('ignoring update (%s) for test "%s" "%s"' % | 217 log('ignoring update (%s) for test "%s" "%s"' % |
213 (status, autotest_name, tag_prefix)) | 218 (status, autotest_name, tag_prefix)) |
214 continue | 219 continue |
215 self.update(test, status, int(count), error_msg) | 220 self.update(test, status, int(count), error_msg) |
216 map(self.update_seq_test, self.test_db.seq_test_set) | 221 map(self.update_seq_test, self.test_db.seq_test_set) |
217 self._status_file_pos = file.tell() | 222 self._status_file_pos = file.tell() |
218 | 223 |
219 def get_active_top_level_test(self): | 224 def get_active_top_level_test(self): |
220 active_tests = set(self.filter(ACTIVE)) - self.test_db.subtest_set | 225 active_tests = set(self.filter(ACTIVE)) - self.test_db.subtest_set |
221 return active_tests and active_tests.pop() or None | 226 return active_tests and active_tests.pop() or None |
222 | 227 |
223 def get_active_subtest(self): | 228 def get_active_subtest(self): |
224 active_subtests = set(self.filter(ACTIVE)) & self.test_db.subtest_set | 229 active_subtests = set(self.filter(ACTIVE)) & self.test_db.subtest_set |
225 return active_subtests and active_subtests.pop() or None | 230 return active_subtests and active_subtests.pop() or None |
226 | 231 |
227 def register_active(self, test): | 232 def register_active(self, test): |
228 active_tests = set(self.filter(ACTIVE)) | 233 active_tests = set(self.filter(ACTIVE)) |
229 assert(test not in active_tests) | 234 assert(test not in active_tests) |
230 if test in self.test_db.subtest_set: | 235 if test in self.test_db.subtest_set: |
231 parent_seq_test = self.test_db.get_subtest_parent(test) | 236 parent_seq_test = self.test_db.get_subtest_parent(test) |
232 active_tests -= set([parent_seq_test]) | 237 active_tests -= set([parent_seq_test]) |
233 for bad_test in active_tests: | 238 for bad_test in active_tests: |
234 unique_name = self.test_db.get_unique_name(bad_test) | 239 unique_details = self.test_db.get_unique_details(bad_test) |
235 log('WARNING: assuming test %s FAILED (status log has no data)' % | 240 log('WARNING: assuming test %s FAILED (status log has no data)' % |
236 unique_name) | 241 unique_details) |
237 self.update(bad_test, FAILED, self.lookup_count(bad_test), | 242 self.update(bad_test, FAILED, self.lookup_count(bad_test), |
238 'assumed FAILED (status log has no data)') | 243 'assumed FAILED (status log has no data)') |
239 | 244 |
240 def update(self, test, status, count, error_msg): | 245 def update(self, test, status, count, error_msg): |
241 entry = self._status_map[test] | 246 entry = self._status_map[test] |
242 unique_name = self.test_db.get_unique_name(test) | 247 unique_details = self.test_db.get_unique_details(test) |
243 if count < entry.count: | 248 if count < entry.count: |
244 log('ERROR: count regression for %s (%d -> %d)' % | 249 log('ERROR: count regression for %s (%d -> %d)' % |
245 (unique_name, entry.count, count)) | 250 (unique_details, entry.count, count)) |
246 if isinstance(test, InformationScreen) and status in [PASSED, FAILED]: | 251 if isinstance(test, InformationScreen) and status in [PASSED, FAILED]: |
247 status = UNTESTED | 252 status = UNTESTED |
248 if status != entry.status: | 253 if status != entry.status: |
249 log('status change for %s : %s/%s -> %s/%s' % | 254 log('status change for %s : %s/%s -> %s/%s' % |
250 (unique_name, entry.status, entry.count, status, count)) | 255 (unique_details, entry.status, entry.count, status, count)) |
251 if entry.label_box is not None: | 256 if entry.label_box is not None: |
252 entry.label_box.update(status) | 257 entry.label_box.update(status) |
253 if status == ACTIVE: | 258 if status == ACTIVE: |
254 self.register_active(test) | 259 self.register_active(test) |
255 entry.status = status | 260 entry.status = status |
256 entry.count = count | 261 entry.count = count |
257 entry.error_msg = error_msg | 262 entry.error_msg = error_msg |
258 log('%s new status = %s' % (unique_name, self._status_map[test].status)) | 263 log('%s new status = %s' % |
| 264 (unique_details, self._status_map[test].status)) |
259 | 265 |
260 def update_seq_test(self, test): | 266 def update_seq_test(self, test): |
261 subtest_status_set = set(map(self.lookup_status, test.subtest_list)) | 267 subtest_status_set = set(map(self.lookup_status, test.subtest_list)) |
262 max_count = max(map(self.lookup_count, test.subtest_list)) | 268 max_count = max(map(self.lookup_count, test.subtest_list)) |
263 if len(subtest_status_set) == 1: | 269 if len(subtest_status_set) == 1: |
264 status = subtest_status_set.pop() | 270 status = subtest_status_set.pop() |
| 271 elif subtest_status_set == set([PASSED, FAILED]): |
| 272 status = FAILED |
265 else: | 273 else: |
266 status = ACTIVE in subtest_status_set and ACTIVE or FAILED | 274 status = ACTIVE |
267 self.update(test, status, max_count, None) | 275 self.update(test, status, max_count, None) |
268 | 276 |
269 def set_label_box(self, test, label_box): | 277 def set_label_box(self, test, label_box): |
270 entry = self._status_map[test] | 278 entry = self._status_map[test] |
271 entry.label_box = label_box | 279 entry.label_box = label_box |
272 label_box.update(entry.status) | 280 label_box.update(entry.status) |
273 | 281 |
274 | 282 |
275 class LogData: | 283 class LogData: |
276 | 284 |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
346 if test.drop_caches: | 354 if test.drop_caches: |
347 self._job.drop_caches_between_iterations = True | 355 self._job.drop_caches_between_iterations = True |
348 self._job.run_test(test.autotest_name, **dargs) | 356 self._job.run_test(test.autotest_name, **dargs) |
349 self._job.drop_caches_between_iterations = False | 357 self._job.drop_caches_between_iterations = False |
350 self._log_data.read_new_data() | 358 self._log_data.read_new_data() |
351 activated_ks = self._log_data.shared_dict.pop( | 359 activated_ks = self._log_data.shared_dict.pop( |
352 'activated_kbd_shortcut', None) | 360 'activated_kbd_shortcut', None) |
353 lookup = self._status_map.test_db.get_test_by_kbd_shortcut | 361 lookup = self._status_map.test_db.get_test_by_kbd_shortcut |
354 self.activated_kbd_shortcut_test = ( | 362 self.activated_kbd_shortcut_test = ( |
355 activated_ks and lookup(activated_ks) or None) | 363 activated_ks and lookup(activated_ks) or None) |
OLD | NEW |