1*c2e18aaaSAndroid Build Coastguard Worker# Copyright 2019, The Android Open Source Project 2*c2e18aaaSAndroid Build Coastguard Worker# 3*c2e18aaaSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*c2e18aaaSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*c2e18aaaSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*c2e18aaaSAndroid Build Coastguard Worker# 7*c2e18aaaSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*c2e18aaaSAndroid Build Coastguard Worker# 9*c2e18aaaSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*c2e18aaaSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*c2e18aaaSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*c2e18aaaSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*c2e18aaaSAndroid Build Coastguard Worker# limitations under the License. 14*c2e18aaaSAndroid Build Coastguard Worker 15*c2e18aaaSAndroid Build Coastguard Worker"""Classes for bug events history.""" 16*c2e18aaaSAndroid Build Coastguard Worker 17*c2e18aaaSAndroid Build Coastguard Workerimport datetime 18*c2e18aaaSAndroid Build Coastguard Workerimport json 19*c2e18aaaSAndroid Build Coastguard Workerimport logging 20*c2e18aaaSAndroid Build Coastguard Workerimport os 21*c2e18aaaSAndroid Build Coastguard Worker 22*c2e18aaaSAndroid Build Coastguard Workerfrom atest import atest_utils 23*c2e18aaaSAndroid Build Coastguard Workerfrom atest import constants 24*c2e18aaaSAndroid Build Coastguard Workerfrom atest.metrics import metrics_utils 25*c2e18aaaSAndroid Build Coastguard Worker 26*c2e18aaaSAndroid Build Coastguard Worker_META_FILE = os.path.join( 27*c2e18aaaSAndroid Build Coastguard Worker atest_utils.get_misc_dir(), '.config', 'asuite', 'atest_history.json' 28*c2e18aaaSAndroid Build Coastguard Worker) 29*c2e18aaaSAndroid Build Coastguard Worker_DETECT_OPTION_FILTER = ['-v', '--verbose'] 30*c2e18aaaSAndroid Build Coastguard Worker_DETECTED_SUCCESS = 1 31*c2e18aaaSAndroid Build Coastguard Worker_DETECTED_FAIL = 0 32*c2e18aaaSAndroid Build Coastguard Worker# constants of history key 33*c2e18aaaSAndroid Build Coastguard Worker_LATEST_EXIT_CODE = 'latest_exit_code' 34*c2e18aaaSAndroid Build Coastguard Worker_UPDATED_AT = 'updated_at' 35*c2e18aaaSAndroid Build Coastguard Worker 36*c2e18aaaSAndroid Build Coastguard Worker 37*c2e18aaaSAndroid Build Coastguard Workerclass BugDetector: 38*c2e18aaaSAndroid Build Coastguard Worker """Class for handling if a bug is detected by comparing test history.""" 39*c2e18aaaSAndroid Build Coastguard Worker 40*c2e18aaaSAndroid Build Coastguard Worker def __init__(self, argv, exit_code, history_file=None): 41*c2e18aaaSAndroid Build Coastguard Worker """BugDetector constructor 42*c2e18aaaSAndroid Build Coastguard Worker 43*c2e18aaaSAndroid Build Coastguard Worker Args: 44*c2e18aaaSAndroid Build Coastguard Worker argv: A list of arguments. 45*c2e18aaaSAndroid Build Coastguard Worker exit_code: An integer of exit code. 46*c2e18aaaSAndroid Build Coastguard Worker history_file: A string of a given history file path. 47*c2e18aaaSAndroid Build Coastguard Worker """ 48*c2e18aaaSAndroid Build Coastguard Worker self.detect_key = self.get_detect_key(argv) 49*c2e18aaaSAndroid Build Coastguard Worker self.exit_code = exit_code 50*c2e18aaaSAndroid Build Coastguard Worker self.file = history_file if history_file else _META_FILE 51*c2e18aaaSAndroid Build Coastguard Worker self.history = self.get_history() 52*c2e18aaaSAndroid Build Coastguard Worker self.caught_result = self.detect_bug_caught() 53*c2e18aaaSAndroid Build Coastguard Worker self.update_history() 54*c2e18aaaSAndroid Build Coastguard Worker 55*c2e18aaaSAndroid Build Coastguard Worker def get_detect_key(self, argv): 56*c2e18aaaSAndroid Build Coastguard Worker """Get the key for history searching. 57*c2e18aaaSAndroid Build Coastguard Worker 58*c2e18aaaSAndroid Build Coastguard Worker 1. remove '-v' in argv to argv_no_verbose 59*c2e18aaaSAndroid Build Coastguard Worker 2. sort the argv_no_verbose 60*c2e18aaaSAndroid Build Coastguard Worker 61*c2e18aaaSAndroid Build Coastguard Worker Args: 62*c2e18aaaSAndroid Build Coastguard Worker argv: A list of arguments. 63*c2e18aaaSAndroid Build Coastguard Worker 64*c2e18aaaSAndroid Build Coastguard Worker Returns: 65*c2e18aaaSAndroid Build Coastguard Worker A string of ordered command line. 66*c2e18aaaSAndroid Build Coastguard Worker """ 67*c2e18aaaSAndroid Build Coastguard Worker argv_without_option = [x for x in argv if x not in _DETECT_OPTION_FILTER] 68*c2e18aaaSAndroid Build Coastguard Worker argv_without_option.sort() 69*c2e18aaaSAndroid Build Coastguard Worker return ' '.join(argv_without_option) 70*c2e18aaaSAndroid Build Coastguard Worker 71*c2e18aaaSAndroid Build Coastguard Worker def get_history(self): 72*c2e18aaaSAndroid Build Coastguard Worker """Get a history object from a history file. 73*c2e18aaaSAndroid Build Coastguard Worker 74*c2e18aaaSAndroid Build Coastguard Worker e.g. 75*c2e18aaaSAndroid Build Coastguard Worker { 76*c2e18aaaSAndroid Build Coastguard Worker "SystemUITests:.ScrimControllerTest":{ 77*c2e18aaaSAndroid Build Coastguard Worker "latest_exit_code": 5, "updated_at": "2019-01-26T15:33:08.305026"}, 78*c2e18aaaSAndroid Build Coastguard Worker "--host hello_world_test ":{ 79*c2e18aaaSAndroid Build Coastguard Worker "latest_exit_code": 0, "updated_at": "2019-02-26T15:33:08.305026"}, 80*c2e18aaaSAndroid Build Coastguard Worker } 81*c2e18aaaSAndroid Build Coastguard Worker 82*c2e18aaaSAndroid Build Coastguard Worker Returns: 83*c2e18aaaSAndroid Build Coastguard Worker An object of loading from a history. 84*c2e18aaaSAndroid Build Coastguard Worker """ 85*c2e18aaaSAndroid Build Coastguard Worker history = atest_utils.load_json_safely(self.file) 86*c2e18aaaSAndroid Build Coastguard Worker return history 87*c2e18aaaSAndroid Build Coastguard Worker 88*c2e18aaaSAndroid Build Coastguard Worker def detect_bug_caught(self): 89*c2e18aaaSAndroid Build Coastguard Worker """Detection of catching bugs. 90*c2e18aaaSAndroid Build Coastguard Worker 91*c2e18aaaSAndroid Build Coastguard Worker When latest_exit_code and current exit_code are different, treat it 92*c2e18aaaSAndroid Build Coastguard Worker as a bug caught. 93*c2e18aaaSAndroid Build Coastguard Worker 94*c2e18aaaSAndroid Build Coastguard Worker Returns: 95*c2e18aaaSAndroid Build Coastguard Worker A integer of detecting result, e.g. 96*c2e18aaaSAndroid Build Coastguard Worker 1: success 97*c2e18aaaSAndroid Build Coastguard Worker 0: fail 98*c2e18aaaSAndroid Build Coastguard Worker """ 99*c2e18aaaSAndroid Build Coastguard Worker if not self.history: 100*c2e18aaaSAndroid Build Coastguard Worker return _DETECTED_FAIL 101*c2e18aaaSAndroid Build Coastguard Worker latest = self.history.get(self.detect_key, {}) 102*c2e18aaaSAndroid Build Coastguard Worker if latest.get(_LATEST_EXIT_CODE, self.exit_code) == self.exit_code: 103*c2e18aaaSAndroid Build Coastguard Worker return _DETECTED_FAIL 104*c2e18aaaSAndroid Build Coastguard Worker return _DETECTED_SUCCESS 105*c2e18aaaSAndroid Build Coastguard Worker 106*c2e18aaaSAndroid Build Coastguard Worker def update_history(self): 107*c2e18aaaSAndroid Build Coastguard Worker """Update the history file. 108*c2e18aaaSAndroid Build Coastguard Worker 109*c2e18aaaSAndroid Build Coastguard Worker 1. update latest_bug result to history cache. 110*c2e18aaaSAndroid Build Coastguard Worker 2. trim history cache to size from oldest updated time. 111*c2e18aaaSAndroid Build Coastguard Worker 3. write to the file. 112*c2e18aaaSAndroid Build Coastguard Worker """ 113*c2e18aaaSAndroid Build Coastguard Worker latest_bug = { 114*c2e18aaaSAndroid Build Coastguard Worker self.detect_key: { 115*c2e18aaaSAndroid Build Coastguard Worker _LATEST_EXIT_CODE: self.exit_code, 116*c2e18aaaSAndroid Build Coastguard Worker _UPDATED_AT: datetime.datetime.now().isoformat(), 117*c2e18aaaSAndroid Build Coastguard Worker } 118*c2e18aaaSAndroid Build Coastguard Worker } 119*c2e18aaaSAndroid Build Coastguard Worker self.history.update(latest_bug) 120*c2e18aaaSAndroid Build Coastguard Worker num_history = len(self.history) 121*c2e18aaaSAndroid Build Coastguard Worker if num_history > constants.UPPER_LIMIT: 122*c2e18aaaSAndroid Build Coastguard Worker sorted_history = sorted( 123*c2e18aaaSAndroid Build Coastguard Worker self.history.items(), key=lambda kv: kv[1][_UPDATED_AT] 124*c2e18aaaSAndroid Build Coastguard Worker ) 125*c2e18aaaSAndroid Build Coastguard Worker self.history = dict( 126*c2e18aaaSAndroid Build Coastguard Worker sorted_history[(num_history - constants.TRIM_TO_SIZE) :] 127*c2e18aaaSAndroid Build Coastguard Worker ) 128*c2e18aaaSAndroid Build Coastguard Worker if not os.path.exists(os.path.dirname(self.file)): 129*c2e18aaaSAndroid Build Coastguard Worker os.makedirs(os.path.dirname(self.file)) 130*c2e18aaaSAndroid Build Coastguard Worker with open(self.file, 'w') as outfile: 131*c2e18aaaSAndroid Build Coastguard Worker try: 132*c2e18aaaSAndroid Build Coastguard Worker json.dump(self.history, outfile, indent=0) 133*c2e18aaaSAndroid Build Coastguard Worker except ValueError as e: 134*c2e18aaaSAndroid Build Coastguard Worker logging.debug(e) 135*c2e18aaaSAndroid Build Coastguard Worker metrics_utils.handle_exc_and_send_exit_event( 136*c2e18aaaSAndroid Build Coastguard Worker constants.ACCESS_HISTORY_FAILURE 137*c2e18aaaSAndroid Build Coastguard Worker ) 138