| 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 |