xref: /aosp_15_r20/external/ltp/android/tools/compare_ltp_projects.py (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
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