1#!/usr/bin/python3
2
3# Copyright (C) 2023 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Script to automatically populate TEST_MAPPING"""
18
19if __name__ != "__main__":
20    print("This is a script, not a library.")
21    exit(1)
22
23import argparse
24import json
25import os
26import textwrap
27
28from pathlib import Path
29
30# some other things we might consider adding:
31# - support for other classes of tests, besides 'presubmit'
32# - use bpmodify to add tests to 'general-tests' or 'device-tests' automatically
33
34parser = argparse.ArgumentParser(
35     epilog=textwrap.dedent("""For example,
36             `map_tests --dir system/libhidl --tests-in system/libhidl/transport -w` would add the
37             tests in the `transport` subdirectory into system/libhidl/TEST_MAPPING`. In general,
38             it's expected to be used by `map_tests.py -w` in the directory where you want to add
39             tests."""))
40parser.add_argument("-i", "--module-info", action="store", help="Default is $ANDROID_PRODUCT_OUT/module-info.json. If this is out of date, run `refreshmod`.")
41parser.add_argument("-w", "--write", action="store_true", help="Write over the TEST_MAPPING file.")
42parser.add_argument("-d", "--dir", action="store", help="Directory where TEST_MAPPING file should exist, defaults to current directory.")
43parser.add_argument("-t", "--tests-in", action="store", help="Directory to pull tests from, defaults to test mapping '--dir'")
44parser.add_argument("-p", "--print", action="store", help="Also print the module-info.json entry for this module, or '-' to print everything")
45args = parser.parse_args()
46
47INFO_PATH = args.module_info or (os.environ["ANDROID_PRODUCT_OUT"] + "/module-info.json")
48MAP_DIR = args.dir or os.getcwd()
49TESTS_IN_DIR = args.tests_in or MAP_DIR
50PRINT = args.print
51WRITE = args.write
52del args
53
54MAP_PATH = MAP_DIR + "/TEST_MAPPING"
55TOP = os.environ["ANDROID_BUILD_TOP"]
56
57################################################################################
58# READ THE CURRENT TEST MAPPING
59################################################################################
60if os.path.exists(MAP_PATH):
61    with open(MAP_PATH, "r", encoding="utf-8") as f:
62        test_mapping = json.loads("".join(f.readlines()))
63else:
64    test_mapping = {}
65
66################################################################################
67# READ THE MODULE INFO
68################################################################################
69with open(INFO_PATH, "r", encoding="utf-8") as module_info_file:
70    info = json.load(module_info_file)
71
72################################################################################
73# UPDATE TEST MAPPING BASED ON MODULE INFO
74################################################################################
75tests_dir = os.path.relpath(TESTS_IN_DIR, TOP)
76
77for name,k in info.items():
78    if PRINT == '-' or name == PRINT: print(name, k)
79
80    # skip 32-bit tests, which show up in module-info.json, but they aren't present
81    # at the atest level
82    if name.endswith("_32"): continue
83
84    is_in_path = any(Path(p).is_relative_to(tests_dir) for p in k['path'])
85    if not is_in_path: continue
86
87    # these are the test_suites that TEST_MAPPING can currently pull tests from
88    is_built = any(ts in k['compatibility_suites'] for ts in ['device-tests', 'general-tests'])
89    if not is_built: continue
90
91    # automatically runs using other infrastructure
92    if k['is_unit_test'] == 'true': continue
93
94    has_test_config = len(k['test_config'])
95    is_native_test = 'NATIVE_TESTS' in k['class']
96    if not has_test_config and not is_native_test: continue
97
98    if "presubmit" not in test_mapping: test_mapping["presubmit"] = []
99
100    already_there = any(i["name"] == name for i in test_mapping["presubmit"])
101    if already_there: continue
102
103    test_mapping["presubmit"] += [{ "name": name }]
104
105out = json.dumps(test_mapping, indent=2)
106
107################################################################################
108# WRITE THE OUTPUT
109################################################################################
110if WRITE:
111    with open(MAP_PATH, "w+", encoding="utf-8") as f:
112        f.write(out + "\n")
113else:
114    print(out)
115