1*49cdfc7eSAndroid Build Coastguard Worker#! /usr/bin/env python3 2*49cdfc7eSAndroid Build Coastguard Worker# 3*49cdfc7eSAndroid Build Coastguard Worker# Copyright 2018 - The Android Open Source Project 4*49cdfc7eSAndroid Build Coastguard Worker# 5*49cdfc7eSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*49cdfc7eSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*49cdfc7eSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*49cdfc7eSAndroid Build Coastguard Worker# 9*49cdfc7eSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*49cdfc7eSAndroid Build Coastguard Worker# 11*49cdfc7eSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*49cdfc7eSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*49cdfc7eSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*49cdfc7eSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*49cdfc7eSAndroid Build Coastguard Worker# limitations under the License. 16*49cdfc7eSAndroid Build Coastguard Worker 17*49cdfc7eSAndroid Build Coastguard Workerfrom __future__ import print_function 18*49cdfc7eSAndroid Build Coastguard Worker 19*49cdfc7eSAndroid Build Coastguard Worker"""Tool for comparing 2 LTP projects to find added / removed tests & testsuites. 20*49cdfc7eSAndroid Build Coastguard Worker 21*49cdfc7eSAndroid Build Coastguard WorkerThe tool can be run in two separate steps by dumping the list of tests as a json 22*49cdfc7eSAndroid Build Coastguard Workerfile, i.e.: 23*49cdfc7eSAndroid Build Coastguard Worker 24*49cdfc7eSAndroid Build Coastguard Worker$ git checkout master 25*49cdfc7eSAndroid Build Coastguard Worker$ compare_ltp_projects.py --ltp-old . --ltp-old-json ./ltp_old.json 26*49cdfc7eSAndroid Build Coastguard Worker$ git checkout mymergebranch 27*49cdfc7eSAndroid Build Coastguard Worker$ compare_ltp_projects.py --ltp-old-json ./ltp_old.json --ltp-new . 28*49cdfc7eSAndroid Build Coastguard Worker""" 29*49cdfc7eSAndroid Build Coastguard Worker 30*49cdfc7eSAndroid Build Coastguard Workerimport os 31*49cdfc7eSAndroid Build Coastguard Workerimport argparse 32*49cdfc7eSAndroid Build Coastguard Workerimport os.path 33*49cdfc7eSAndroid Build Coastguard Workerimport sys 34*49cdfc7eSAndroid Build Coastguard Workerimport json 35*49cdfc7eSAndroid Build Coastguard Worker 36*49cdfc7eSAndroid Build Coastguard Workerdef scan_tests(ltp_root, suite): 37*49cdfc7eSAndroid Build Coastguard Worker ''' Find all tests that are run as part of given test suite in LTP. 38*49cdfc7eSAndroid Build Coastguard Worker 39*49cdfc7eSAndroid Build Coastguard Worker Args: 40*49cdfc7eSAndroid Build Coastguard Worker ltp_root: Path ot the LTP project. 41*49cdfc7eSAndroid Build Coastguard Worker suite: Name of testsuite. 42*49cdfc7eSAndroid Build Coastguard Worker 43*49cdfc7eSAndroid Build Coastguard Worker Returns: 44*49cdfc7eSAndroid Build Coastguard Worker List of tests names that are run as part of the given test suite. 45*49cdfc7eSAndroid Build Coastguard Worker ''' 46*49cdfc7eSAndroid Build Coastguard Worker 47*49cdfc7eSAndroid Build Coastguard Worker tests = [] 48*49cdfc7eSAndroid Build Coastguard Worker if not suite: 49*49cdfc7eSAndroid Build Coastguard Worker return tests 50*49cdfc7eSAndroid Build Coastguard Worker 51*49cdfc7eSAndroid Build Coastguard Worker runtest_dir = ltp_root + os.path.sep + 'runtest' 52*49cdfc7eSAndroid Build Coastguard Worker test_suiteFile = runtest_dir + os.path.sep + suite 53*49cdfc7eSAndroid Build Coastguard Worker if not os.path.isfile(test_suiteFile): 54*49cdfc7eSAndroid Build Coastguard Worker print('No tests defined for suite {}',format(suite)) 55*49cdfc7eSAndroid Build Coastguard Worker return tests 56*49cdfc7eSAndroid Build Coastguard Worker 57*49cdfc7eSAndroid Build Coastguard Worker with open(test_suiteFile) as f: 58*49cdfc7eSAndroid Build Coastguard Worker for line in f: 59*49cdfc7eSAndroid Build Coastguard Worker line = line.strip() 60*49cdfc7eSAndroid Build Coastguard Worker if line and not line.startswith('#'): 61*49cdfc7eSAndroid Build Coastguard Worker tests.append(line.split()[0]) 62*49cdfc7eSAndroid Build Coastguard Worker f.close() 63*49cdfc7eSAndroid Build Coastguard Worker return tests 64*49cdfc7eSAndroid Build Coastguard Worker 65*49cdfc7eSAndroid Build Coastguard Workerdef scan_test_suites(ltp_root, scenario): 66*49cdfc7eSAndroid Build Coastguard Worker ''' Find all testuites and tests run as part of given LTP scenario 67*49cdfc7eSAndroid Build Coastguard Worker 68*49cdfc7eSAndroid Build Coastguard Worker Args: 69*49cdfc7eSAndroid Build Coastguard Worker ltp_root: Path to the LTP project. 70*49cdfc7eSAndroid Build Coastguard Worker scenario: name of the scenario (found in ltp_root/scenario_groups). E.g. "vts" 71*49cdfc7eSAndroid Build Coastguard Worker 72*49cdfc7eSAndroid Build Coastguard Worker Returns: 73*49cdfc7eSAndroid Build Coastguard Worker List of testsuite names that are run as part of given scenario (e.g. vts). 74*49cdfc7eSAndroid Build Coastguard Worker If scenario is not specified, return all testsuites. 75*49cdfc7eSAndroid Build Coastguard Worker ''' 76*49cdfc7eSAndroid Build Coastguard Worker 77*49cdfc7eSAndroid Build Coastguard Worker runtest_dir = ltp_root + os.path.sep + 'runtest' 78*49cdfc7eSAndroid Build Coastguard Worker if not os.path.isdir(runtest_dir): 79*49cdfc7eSAndroid Build Coastguard Worker print('Invalid ltp_root {}, runtest directory doesnt exist'.format(ltp_root)) 80*49cdfc7eSAndroid Build Coastguard Worker sys.exit(2) 81*49cdfc7eSAndroid Build Coastguard Worker 82*49cdfc7eSAndroid Build Coastguard Worker test_suites = [] 83*49cdfc7eSAndroid Build Coastguard Worker if scenario: 84*49cdfc7eSAndroid Build Coastguard Worker scenarioFile = ltp_root + os.path.sep + 'scenario_groups' + os.path.sep + scenario 85*49cdfc7eSAndroid Build Coastguard Worker if not os.path.isfile(scenarioFile): 86*49cdfc7eSAndroid Build Coastguard Worker return test_suites 87*49cdfc7eSAndroid Build Coastguard Worker with open(scenarioFile) as f: 88*49cdfc7eSAndroid Build Coastguard Worker for line in f: 89*49cdfc7eSAndroid Build Coastguard Worker line = line.strip() 90*49cdfc7eSAndroid Build Coastguard Worker if not line.startswith('#'): 91*49cdfc7eSAndroid Build Coastguard Worker test_suites.append(line) 92*49cdfc7eSAndroid Build Coastguard Worker return test_suites 93*49cdfc7eSAndroid Build Coastguard Worker 94*49cdfc7eSAndroid Build Coastguard Worker runtest_dir = ltp_root + os.path.sep + 'runtest' 95*49cdfc7eSAndroid Build Coastguard Worker test_suites = [f for f in os.listdir(runtest_dir) if os.path.isfile(runtest_dir + os.path.sep + f)] 96*49cdfc7eSAndroid Build Coastguard Worker 97*49cdfc7eSAndroid Build Coastguard Worker return test_suites 98*49cdfc7eSAndroid Build Coastguard Worker 99*49cdfc7eSAndroid Build Coastguard Workerdef scan_ltp(ltp_root, scenario): 100*49cdfc7eSAndroid Build Coastguard Worker ''' scan LTP project and return all tests and testuites present. 101*49cdfc7eSAndroid Build Coastguard Worker 102*49cdfc7eSAndroid Build Coastguard Worker Args: 103*49cdfc7eSAndroid Build Coastguard Worker ltp_root: Path to the LTP project. 104*49cdfc7eSAndroid Build Coastguard Worker scenario: specific scenario we want to scan. e.g. vts 105*49cdfc7eSAndroid Build Coastguard Worker 106*49cdfc7eSAndroid Build Coastguard Worker Returns: 107*49cdfc7eSAndroid Build Coastguard Worker Dictionary of all LTP test names keyed by testsuite they are run as part of. 108*49cdfc7eSAndroid Build Coastguard Worker E.g. 109*49cdfc7eSAndroid Build Coastguard Worker { 110*49cdfc7eSAndroid Build Coastguard Worker 'mem': ['oom01', 'mem02',...], 111*49cdfc7eSAndroid Build Coastguard Worker 'syscalls': ['open01', 'read02',...], 112*49cdfc7eSAndroid Build Coastguard Worker ... 113*49cdfc7eSAndroid Build Coastguard Worker } 114*49cdfc7eSAndroid Build Coastguard Worker ''' 115*49cdfc7eSAndroid Build Coastguard Worker 116*49cdfc7eSAndroid Build Coastguard Worker if not os.path.isdir(ltp_root): 117*49cdfc7eSAndroid Build Coastguard Worker print('ltp_root {} does not exist'.format(ltp_root)) 118*49cdfc7eSAndroid Build Coastguard Worker sys.exit(1) 119*49cdfc7eSAndroid Build Coastguard Worker 120*49cdfc7eSAndroid Build Coastguard Worker test_suites = scan_test_suites(ltp_root, scenario) 121*49cdfc7eSAndroid Build Coastguard Worker if not test_suites: 122*49cdfc7eSAndroid Build Coastguard Worker print('No Testsuites found for scenario {}'.format(scenario)) 123*49cdfc7eSAndroid Build Coastguard Worker sys.exit(3) 124*49cdfc7eSAndroid Build Coastguard Worker 125*49cdfc7eSAndroid Build Coastguard Worker ltp_tests = {} 126*49cdfc7eSAndroid Build Coastguard Worker for suite in test_suites: 127*49cdfc7eSAndroid Build Coastguard Worker ltp_tests[suite] = scan_tests(ltp_root, suite) 128*49cdfc7eSAndroid Build Coastguard Worker return ltp_tests 129*49cdfc7eSAndroid Build Coastguard Worker 130*49cdfc7eSAndroid Build Coastguard Workerdef show_diff(ltp_tests_1, ltp_tests_2): 131*49cdfc7eSAndroid Build Coastguard Worker ''' Find and print diff between testcases in 2 LTP project checkouts. 132*49cdfc7eSAndroid Build Coastguard Worker 133*49cdfc7eSAndroid Build Coastguard Worker Args: 134*49cdfc7eSAndroid Build Coastguard Worker ltp_tests_1: dictionary of tests keyed by test suite names 135*49cdfc7eSAndroid Build Coastguard Worker ltp_tests_2: dictionary of tests keyed by test suite names 136*49cdfc7eSAndroid Build Coastguard Worker ''' 137*49cdfc7eSAndroid Build Coastguard Worker test_suites1 = set(ltp_tests_1.keys()) 138*49cdfc7eSAndroid Build Coastguard Worker test_suites2 = set(ltp_tests_2.keys()) 139*49cdfc7eSAndroid Build Coastguard Worker 140*49cdfc7eSAndroid Build Coastguard Worker # Generate lists of deleted, added and common test suites between 141*49cdfc7eSAndroid Build Coastguard Worker # LTP1 & LTP2 142*49cdfc7eSAndroid Build Coastguard Worker deleted_test_suites = sorted(test_suites1.difference(test_suites2)) 143*49cdfc7eSAndroid Build Coastguard Worker added_test_suites = sorted(test_suites2.difference(test_suites1)) 144*49cdfc7eSAndroid Build Coastguard Worker common_test_suites = test_suites1.intersection(test_suites2) 145*49cdfc7eSAndroid Build Coastguard Worker 146*49cdfc7eSAndroid Build Coastguard Worker deleted_tests = [] 147*49cdfc7eSAndroid Build Coastguard Worker added_tests = [] 148*49cdfc7eSAndroid Build Coastguard Worker for suite in common_test_suites: 149*49cdfc7eSAndroid Build Coastguard Worker tests1 = set(ltp_tests_1[suite]) 150*49cdfc7eSAndroid Build Coastguard Worker tests2 = set(ltp_tests_2[suite]) 151*49cdfc7eSAndroid Build Coastguard Worker 152*49cdfc7eSAndroid Build Coastguard Worker exclusive_test1 = tests1.difference(tests2) 153*49cdfc7eSAndroid Build Coastguard Worker exclusive_test2 = tests2.difference(tests1) 154*49cdfc7eSAndroid Build Coastguard Worker for e in exclusive_test1: 155*49cdfc7eSAndroid Build Coastguard Worker deleted_tests.append(suite + '.' + e) 156*49cdfc7eSAndroid Build Coastguard Worker for e in exclusive_test2: 157*49cdfc7eSAndroid Build Coastguard Worker added_tests.append(suite + '.' + e) 158*49cdfc7eSAndroid Build Coastguard Worker deleted_tests = sorted(deleted_tests) 159*49cdfc7eSAndroid Build Coastguard Worker added_tests = sorted(added_tests) 160*49cdfc7eSAndroid Build Coastguard Worker print_columns(added_test_suites, deleted_test_suites, added_tests, deleted_tests) 161*49cdfc7eSAndroid Build Coastguard Worker 162*49cdfc7eSAndroid Build Coastguard Worker 163*49cdfc7eSAndroid Build Coastguard Workerdef print_columns(added_test_suites, deleted_test_suites, added_tests, deleted_tests): 164*49cdfc7eSAndroid Build Coastguard Worker DEFAULT_WIDTH = 8 165*49cdfc7eSAndroid Build Coastguard Worker # find the maximum width of a test suite name or test name 166*49cdfc7eSAndroid Build Coastguard Worker # we have to print to decide the alignment. 167*49cdfc7eSAndroid Build Coastguard Worker if not deleted_test_suites: 168*49cdfc7eSAndroid Build Coastguard Worker width_suites = DEFAULT_WIDTH 169*49cdfc7eSAndroid Build Coastguard Worker else: 170*49cdfc7eSAndroid Build Coastguard Worker width_suites = max([len(x) for x in deleted_test_suites]) 171*49cdfc7eSAndroid Build Coastguard Worker 172*49cdfc7eSAndroid Build Coastguard Worker if not deleted_tests: 173*49cdfc7eSAndroid Build Coastguard Worker width_tests = DEFAULT_WIDTH 174*49cdfc7eSAndroid Build Coastguard Worker else: 175*49cdfc7eSAndroid Build Coastguard Worker width_tests = max([len(x) for x in deleted_tests]) 176*49cdfc7eSAndroid Build Coastguard Worker width = max(width_suites, width_tests); 177*49cdfc7eSAndroid Build Coastguard Worker 178*49cdfc7eSAndroid Build Coastguard Worker # total rows we have to print 179*49cdfc7eSAndroid Build Coastguard Worker total_rows = max(len(deleted_test_suites), len(added_test_suites)) 180*49cdfc7eSAndroid Build Coastguard Worker added_text = 'Added ({})'.format(len(added_test_suites)) 181*49cdfc7eSAndroid Build Coastguard Worker deleted_text = 'Deleted ({})'.format(len(deleted_test_suites)) 182*49cdfc7eSAndroid Build Coastguard Worker if total_rows > 0: 183*49cdfc7eSAndroid Build Coastguard Worker print('{:*^{len}}'.format(' Tests Suites ', len=width*2+2)) 184*49cdfc7eSAndroid Build Coastguard Worker print('{:<{len}} {:<{len}}'.format(added_text, deleted_text, len=width)) 185*49cdfc7eSAndroid Build Coastguard Worker for i in range(total_rows): 186*49cdfc7eSAndroid Build Coastguard Worker print('{:<{len}} {:<{len}}'.format('' if i >= len(added_test_suites) else str(added_test_suites[i]), 187*49cdfc7eSAndroid Build Coastguard Worker '' if i >= len(deleted_test_suites) else str(deleted_test_suites[i]), len=width)) 188*49cdfc7eSAndroid Build Coastguard Worker 189*49cdfc7eSAndroid Build Coastguard Worker print('') 190*49cdfc7eSAndroid Build Coastguard Worker # total rows we have to print 191*49cdfc7eSAndroid Build Coastguard Worker total_rows = max(len(deleted_tests), len(added_tests)) 192*49cdfc7eSAndroid Build Coastguard Worker added_text = 'Added ({})'.format(len(added_tests)) 193*49cdfc7eSAndroid Build Coastguard Worker deleted_text = 'Deleted ({})'.format(len(deleted_tests)) 194*49cdfc7eSAndroid Build Coastguard Worker if total_rows: 195*49cdfc7eSAndroid Build Coastguard Worker print('{:*^{len}}'.format(' Tests ', len=width*2+2)) 196*49cdfc7eSAndroid Build Coastguard Worker print('{:^{len}} {:^{len}}'.format(added_text, deleted_text, len=width)) 197*49cdfc7eSAndroid Build Coastguard Worker for i in range(total_rows): 198*49cdfc7eSAndroid Build Coastguard Worker print('{:<{len}} {:<{len}}'.format('' if i >= len(added_tests) else str(added_tests[i]), 199*49cdfc7eSAndroid Build Coastguard Worker '' if i >= len(deleted_tests) else str(deleted_tests[i]), len=width)) 200*49cdfc7eSAndroid Build Coastguard Workerdef main(): 201*49cdfc7eSAndroid Build Coastguard Worker arg_parser = argparse.ArgumentParser( 202*49cdfc7eSAndroid Build Coastguard Worker description='Diff 2 LTP projects for supported test cases') 203*49cdfc7eSAndroid Build Coastguard Worker arg_parser.add_argument('-o', '--ltp-old', 204*49cdfc7eSAndroid Build Coastguard Worker dest='ltp_old', 205*49cdfc7eSAndroid Build Coastguard Worker help="LTP Root Directory before merge") 206*49cdfc7eSAndroid Build Coastguard Worker arg_parser.add_argument('-oj', '--ltp-old-json', 207*49cdfc7eSAndroid Build Coastguard Worker dest='ltp_old_json', 208*49cdfc7eSAndroid Build Coastguard Worker default='ltp_old.json', 209*49cdfc7eSAndroid Build Coastguard Worker help="Old LTP parsed directory in json format") 210*49cdfc7eSAndroid Build Coastguard Worker arg_parser.add_argument('-n', '--ltp-new', 211*49cdfc7eSAndroid Build Coastguard Worker dest='ltp_new', 212*49cdfc7eSAndroid Build Coastguard Worker help="LTP Root Directory after merge") 213*49cdfc7eSAndroid Build Coastguard Worker arg_parser.add_argument('-nj', '--ltp-new-json', 214*49cdfc7eSAndroid Build Coastguard Worker dest='ltp_new_json', 215*49cdfc7eSAndroid Build Coastguard Worker default='ltp_new.json', 216*49cdfc7eSAndroid Build Coastguard Worker help="New LTP parsed directory in json format") 217*49cdfc7eSAndroid Build Coastguard Worker arg_parser.add_argument('--scenario', default=None, 218*49cdfc7eSAndroid Build Coastguard Worker dest='scenario', 219*49cdfc7eSAndroid Build Coastguard Worker help="LTP scenario to list tests for") 220*49cdfc7eSAndroid Build Coastguard Worker args = arg_parser.parse_args() 221*49cdfc7eSAndroid Build Coastguard Worker 222*49cdfc7eSAndroid Build Coastguard Worker # Find tests in the "old" directory or read the json output from a prior run 223*49cdfc7eSAndroid Build Coastguard Worker if args.ltp_old: 224*49cdfc7eSAndroid Build Coastguard Worker ltp_tests1 = scan_ltp(args.ltp_old, args.scenario) 225*49cdfc7eSAndroid Build Coastguard Worker elif args.ltp_old_json and os.path.isfile(args.ltp_old_json): 226*49cdfc7eSAndroid Build Coastguard Worker with open(args.ltp_old_json) as f: 227*49cdfc7eSAndroid Build Coastguard Worker ltp_tests1 = json.load(f) 228*49cdfc7eSAndroid Build Coastguard Worker else: 229*49cdfc7eSAndroid Build Coastguard Worker ltp_tests1 = None 230*49cdfc7eSAndroid Build Coastguard Worker 231*49cdfc7eSAndroid Build Coastguard Worker # Do the same for the "new" directory 232*49cdfc7eSAndroid Build Coastguard Worker if args.ltp_new: 233*49cdfc7eSAndroid Build Coastguard Worker ltp_tests2 = scan_ltp(args.ltp_new, args.scenario) 234*49cdfc7eSAndroid Build Coastguard Worker elif args.ltp_new_json and os.path.isfile(args.ltp_new_json): 235*49cdfc7eSAndroid Build Coastguard Worker with open(args.ltp_new_json) as f: 236*49cdfc7eSAndroid Build Coastguard Worker ltp_tests2 = json.load(f) 237*49cdfc7eSAndroid Build Coastguard Worker else: 238*49cdfc7eSAndroid Build Coastguard Worker ltp_tests2 = None 239*49cdfc7eSAndroid Build Coastguard Worker 240*49cdfc7eSAndroid Build Coastguard Worker if ltp_tests1 and ltp_tests2: 241*49cdfc7eSAndroid Build Coastguard Worker # Show the difference of the two directories if both are present 242*49cdfc7eSAndroid Build Coastguard Worker show_diff(ltp_tests1, ltp_tests2) 243*49cdfc7eSAndroid Build Coastguard Worker elif ltp_tests1: 244*49cdfc7eSAndroid Build Coastguard Worker # If just the old directory was read, dump it as json 245*49cdfc7eSAndroid Build Coastguard Worker with open(args.ltp_old_json, 'w') as f: 246*49cdfc7eSAndroid Build Coastguard Worker json.dump(ltp_tests1, f) 247*49cdfc7eSAndroid Build Coastguard Worker elif ltp_tests2: 248*49cdfc7eSAndroid Build Coastguard Worker # If just the new directory was read, dump it as json 249*49cdfc7eSAndroid Build Coastguard Worker with open(args.ltp_new_json, 'w') as f: 250*49cdfc7eSAndroid Build Coastguard Worker json.dump(ltp_tests2, f) 251*49cdfc7eSAndroid Build Coastguard Worker 252*49cdfc7eSAndroid Build Coastguard Workerif __name__ == '__main__': 253*49cdfc7eSAndroid Build Coastguard Worker main() 254