# Copyright 2017 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import glob import json import os import logging from autotest_lib.site_utils.sponge_lib import autotest_job_info UNKNOWN_EFFORT_NAME = 'UNKNOWN_BUILD' UNKNOWN_ENV_NAME = 'UNKNOWN_BOARD' class ACTSSummaryEnums(object): """A class contains the attribute names used in a ACTS summary.""" Requested = 'Requested' Failed = 'Failed' Unknown = 'Unknown' class ACTSRecordEnums(object): """A class contains the attribute names used in an ACTS record.""" BeginTime = 'Begin Time' Details = 'Details' EndTime = 'End Time' Extras = 'Extras' ExtraErrors = 'Extra Errors' Result = 'Result' TestClass = 'Test Class' TestName = 'Test Name' UID = 'UID' class ACTSTaskInfo(autotest_job_info.AutotestTaskInfo): """Task info for an ACTS test.""" tags = autotest_job_info.AutotestTaskInfo.tags + ['acts', 'testtracker'] logs = autotest_job_info.AutotestTaskInfo.logs + ['results'] def __init__(self, test, job): """ @param test: The autotest test for this ACTS test. @param job: The job info that is the parent ot this task. """ super(ACTSTaskInfo, self).__init__(test, job) summary_location = os.path.join( self.results_dir, 'results/latest/test_run_summary.json') build_info_location = os.path.join(self.results_dir, 'results/BUILD_INFO-*') build_info_files = glob.iglob(build_info_location) try: build_info_file = next(build_info_files) logging.info('Using build info file: %s', build_info_file) with open(build_info_file) as fd: self.build_info = json.load(fd) except Exception as e: logging.exception(e) logging.error('Bad build info file.') self.build_info = {} try: build_prop_str = self.build_info['build_prop'] prop_dict = {} self.build_info['build_prop'] = prop_dict lines = build_prop_str.splitlines() for line in lines: parts = line.split('=') if len(parts) != 2: continue prop_dict[parts[0]] = parts[1] except Exception as e: logging.exception(e) logging.error('Bad build prop data, using default empty dict') self.build_info['build_prop'] = {} try: with open(summary_location) as fd: self._acts_summary = json.load(fd) self._summary_block = self._acts_summary['Summary'] record_block = self._acts_summary['Results'] self._records = list(ACTSRecord(record) for record in record_block) self.is_valid = True except Exception as e: logging.exception(e) logging.error('Bad acts data, reverting to autotest only.') self.is_valid = False self.tags = autotest_job_info.AutotestTaskInfo.tags @property def test_case_count(self): """The number of test cases run.""" return self._summary_block[ACTSSummaryEnums.Requested] @property def failed_case_count(self): """The number of failed test cases.""" return self._summary_block[ACTSSummaryEnums.Failed] @property def error_case_count(self): """The number of errored test cases.""" return self._summary_block[ACTSSummaryEnums.Unknown] @property def records(self): """All records of test cases in the ACTS tests.""" return self._records @property def owner(self): """The owner of the task.""" if 'param-testtracker_owner' in self.keyvals: return self.keyvals['param-testtracker_owner'].strip("'").strip('"') elif 'param-test_tracker_owner' in self.keyvals: return self.keyvals['param-testtracker_owner'].strip("'").strip('"') else: return self._job.user.strip("'").strip('"') @property def effort_name(self): """The test tracker effort name.""" build_id = self.build_info.get('build_prop', {}).get('ro.build.id') if build_id and any(c.isdigit() for c in build_id): return build_id else: build_version = self.build_info.get('build_prop', {}).get( 'ro.build.version.incremental', UNKNOWN_EFFORT_NAME) return build_version @property def project_id(self): """The test tracker project id.""" if 'param-testtracker_project_id' in self.keyvals: return self.keyvals.get('param-testtracker_project_id') else: return self.keyvals.get('param-test_tracker_project_id') @property def environment(self): """The name of the enviroment for test tracker.""" build_props = self.build_info.get('build_prop', {}) if 'ro.product.board' in build_props: board = build_props['ro.product.board'] elif 'ro.build.product' in build_props: board = build_props['ro.build.product'] else: board = UNKNOWN_ENV_NAME return board @property def extra_environment(self): """Extra environment info about the task.""" if 'param-testtracker_extra_env' in self.keyvals: extra = self.keyvals.get('param-testtracker_extra_env', []) else: extra = self.keyvals.get('param-test_tracker_extra_env', []) if not isinstance(extra, list): extra = [extra] return extra class ACTSRecord(object): """A single record of a test case in an ACTS test.""" tags = ['acts', 'testtracker'] def __init__(self, json_record): """ @param json_record: The json info for this record """ self._json_record = json_record @property def test_class(self): """The test class that was run.""" return self._json_record[ACTSRecordEnums.TestClass] @property def test_case(self): """The test case that was run. None implies all in the class.""" return self._json_record.get(ACTSRecordEnums.TestName) @property def uid(self): """The uid of the test case.""" return self._json_record.get(ACTSRecordEnums.UID) @property def status(self): """The status of the test case.""" return self._json_record[ACTSRecordEnums.Result] @property def start_time(self): """The start time of the test case.""" return self._json_record[ACTSRecordEnums.BeginTime] / 1000.0 @property def end_time(self): """The end time of the test case.""" return self._json_record[ACTSRecordEnums.EndTime] / 1000.0 @property def details(self): """Details about the test case.""" return self._json_record.get(ACTSRecordEnums.Details) @property def extras(self): """Extra info about the test case.""" return self._json_record.get(ACTSRecordEnums.Extras) @property def extra_errors(self): """Extra errors about the test case.""" return self._json_record.get(ACTSRecordEnums.ExtraErrors) @property def extra_environment(self): """Extra details about the environment for this test.""" extras = self.extras if not extras: return None test_tracker_info = self.extras.get('test_tracker_info') if not test_tracker_info: return self.extras.get('test_tracker_environment_info') return test_tracker_info.get('extra_environment') @property def uuid(self): """The test tracker uuid of the test case.""" extras = self.extras if not extras: return None test_tracker_info = self.extras.get('test_tracker_info') if not test_tracker_info: return self.extras.get('test_tracker_uuid') return test_tracker_info.get('test_tracker_uuid')