1# Copyright 2017 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import glob 6import json 7import os 8 9import logging 10 11from autotest_lib.site_utils.sponge_lib import autotest_job_info 12 13 14UNKNOWN_EFFORT_NAME = 'UNKNOWN_BUILD' 15UNKNOWN_ENV_NAME = 'UNKNOWN_BOARD' 16 17 18class ACTSSummaryEnums(object): 19 """A class contains the attribute names used in a ACTS summary.""" 20 21 Requested = 'Requested' 22 Failed = 'Failed' 23 Unknown = 'Unknown' 24 25 26class ACTSRecordEnums(object): 27 """A class contains the attribute names used in an ACTS record.""" 28 29 BeginTime = 'Begin Time' 30 Details = 'Details' 31 EndTime = 'End Time' 32 Extras = 'Extras' 33 ExtraErrors = 'Extra Errors' 34 Result = 'Result' 35 TestClass = 'Test Class' 36 TestName = 'Test Name' 37 UID = 'UID' 38 39 40class ACTSTaskInfo(autotest_job_info.AutotestTaskInfo): 41 """Task info for an ACTS test.""" 42 43 tags = autotest_job_info.AutotestTaskInfo.tags + ['acts', 'testtracker'] 44 logs = autotest_job_info.AutotestTaskInfo.logs + ['results'] 45 46 def __init__(self, test, job): 47 """ 48 @param test: The autotest test for this ACTS test. 49 @param job: The job info that is the parent ot this task. 50 """ 51 super(ACTSTaskInfo, self).__init__(test, job) 52 53 summary_location = os.path.join( 54 self.results_dir, 'results/latest/test_run_summary.json') 55 56 build_info_location = os.path.join(self.results_dir, 57 'results/BUILD_INFO-*') 58 build_info_files = glob.iglob(build_info_location) 59 60 try: 61 build_info_file = next(build_info_files) 62 logging.info('Using build info file: %s', build_info_file) 63 with open(build_info_file) as fd: 64 self.build_info = json.load(fd) 65 except Exception as e: 66 logging.exception(e) 67 logging.error('Bad build info file.') 68 self.build_info = {} 69 70 try: 71 build_prop_str = self.build_info['build_prop'] 72 prop_dict = {} 73 self.build_info['build_prop'] = prop_dict 74 lines = build_prop_str.splitlines() 75 for line in lines: 76 parts = line.split('=') 77 78 if len(parts) != 2: 79 continue 80 81 prop_dict[parts[0]] = parts[1] 82 except Exception as e: 83 logging.exception(e) 84 logging.error('Bad build prop data, using default empty dict') 85 self.build_info['build_prop'] = {} 86 87 try: 88 with open(summary_location) as fd: 89 self._acts_summary = json.load(fd) 90 91 self._summary_block = self._acts_summary['Summary'] 92 93 record_block = self._acts_summary['Results'] 94 self._records = list(ACTSRecord(record) for record in record_block) 95 self.is_valid = True 96 except Exception as e: 97 logging.exception(e) 98 logging.error('Bad acts data, reverting to autotest only.') 99 self.is_valid = False 100 self.tags = autotest_job_info.AutotestTaskInfo.tags 101 102 @property 103 def test_case_count(self): 104 """The number of test cases run.""" 105 return self._summary_block[ACTSSummaryEnums.Requested] 106 107 @property 108 def failed_case_count(self): 109 """The number of failed test cases.""" 110 return self._summary_block[ACTSSummaryEnums.Failed] 111 112 @property 113 def error_case_count(self): 114 """The number of errored test cases.""" 115 return self._summary_block[ACTSSummaryEnums.Unknown] 116 117 @property 118 def records(self): 119 """All records of test cases in the ACTS tests.""" 120 return self._records 121 122 @property 123 def owner(self): 124 """The owner of the task.""" 125 if 'param-testtracker_owner' in self.keyvals: 126 return self.keyvals['param-testtracker_owner'].strip("'").strip('"') 127 elif 'param-test_tracker_owner' in self.keyvals: 128 return self.keyvals['param-testtracker_owner'].strip("'").strip('"') 129 else: 130 return self._job.user.strip("'").strip('"') 131 132 @property 133 def effort_name(self): 134 """The test tracker effort name.""" 135 build_id = self.build_info.get('build_prop', {}).get('ro.build.id') 136 if build_id and any(c.isdigit() for c in build_id): 137 return build_id 138 else: 139 build_version = self.build_info.get('build_prop', {}).get( 140 'ro.build.version.incremental', UNKNOWN_EFFORT_NAME) 141 return build_version 142 143 144 @property 145 def project_id(self): 146 """The test tracker project id.""" 147 if 'param-testtracker_project_id' in self.keyvals: 148 return self.keyvals.get('param-testtracker_project_id') 149 else: 150 return self.keyvals.get('param-test_tracker_project_id') 151 152 @property 153 def environment(self): 154 """The name of the enviroment for test tracker.""" 155 build_props = self.build_info.get('build_prop', {}) 156 157 if 'ro.product.board' in build_props: 158 board = build_props['ro.product.board'] 159 elif 'ro.build.product' in build_props: 160 board = build_props['ro.build.product'] 161 else: 162 board = UNKNOWN_ENV_NAME 163 164 return board 165 166 @property 167 def extra_environment(self): 168 """Extra environment info about the task.""" 169 if 'param-testtracker_extra_env' in self.keyvals: 170 extra = self.keyvals.get('param-testtracker_extra_env', []) 171 else: 172 extra = self.keyvals.get('param-test_tracker_extra_env', []) 173 174 if not isinstance(extra, list): 175 extra = [extra] 176 177 return extra 178 179 180class ACTSRecord(object): 181 """A single record of a test case in an ACTS test.""" 182 183 tags = ['acts', 'testtracker'] 184 185 def __init__(self, json_record): 186 """ 187 @param json_record: The json info for this record 188 """ 189 self._json_record = json_record 190 191 @property 192 def test_class(self): 193 """The test class that was run.""" 194 return self._json_record[ACTSRecordEnums.TestClass] 195 196 @property 197 def test_case(self): 198 """The test case that was run. None implies all in the class.""" 199 return self._json_record.get(ACTSRecordEnums.TestName) 200 201 @property 202 def uid(self): 203 """The uid of the test case.""" 204 return self._json_record.get(ACTSRecordEnums.UID) 205 206 @property 207 def status(self): 208 """The status of the test case.""" 209 return self._json_record[ACTSRecordEnums.Result] 210 211 @property 212 def start_time(self): 213 """The start time of the test case.""" 214 return self._json_record[ACTSRecordEnums.BeginTime] / 1000.0 215 216 @property 217 def end_time(self): 218 """The end time of the test case.""" 219 return self._json_record[ACTSRecordEnums.EndTime] / 1000.0 220 221 @property 222 def details(self): 223 """Details about the test case.""" 224 return self._json_record.get(ACTSRecordEnums.Details) 225 226 @property 227 def extras(self): 228 """Extra info about the test case.""" 229 return self._json_record.get(ACTSRecordEnums.Extras) 230 231 @property 232 def extra_errors(self): 233 """Extra errors about the test case.""" 234 return self._json_record.get(ACTSRecordEnums.ExtraErrors) 235 236 @property 237 def extra_environment(self): 238 """Extra details about the environment for this test.""" 239 extras = self.extras 240 if not extras: 241 return None 242 243 test_tracker_info = self.extras.get('test_tracker_info') 244 if not test_tracker_info: 245 return self.extras.get('test_tracker_environment_info') 246 247 return test_tracker_info.get('extra_environment') 248 249 @property 250 def uuid(self): 251 """The test tracker uuid of the test case.""" 252 extras = self.extras 253 if not extras: 254 return None 255 256 test_tracker_info = self.extras.get('test_tracker_info') 257 if not test_tracker_info: 258 return self.extras.get('test_tracker_uuid') 259 260 return test_tracker_info.get('test_tracker_uuid') 261