blob: 3f4bb92d72fc6f6c0dd45b8c82ee072644517fb8 [file] [log] [blame]
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +01001#!/usr/bin/env python3
2#
3# Copyright (C) 2020 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"""Add tests to TEST_MAPPING. Include tests for reverse dependencies."""
17import json
18import os
19import platform
20import subprocess
21import sys
22
Joel Galensona0d4c5e2021-04-06 09:36:47 -070023test_options = {
24 "ring_device_test_tests_digest_tests": [{"test-timeout": "600000"}],
25 "ring_device_test_src_lib": [{"test-timeout": "100000"}],
26}
Jeff Vander Stoep0b0e24f2021-01-24 20:50:26 +010027test_exclude = [
28 "aidl_test_rust_client",
29 "aidl_test_rust_service"
30 ]
31exclude_paths = [
32 "//external/adhd",
33 "//external/crosvm",
34 "//external/libchromeos-rs",
35 "//external/vm_tools"
36 ]
37
Thiébaud Weksteen5212f8a2021-06-10 08:18:32 +020038
39class UpdaterException(Exception):
40 pass
41
42
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010043class Env(object):
Thiébaud Weksteenfc485b22021-06-10 13:30:20 +020044 def __init__(self):
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010045 try:
46 self.ANDROID_BUILD_TOP = os.environ['ANDROID_BUILD_TOP']
Thiébaud Weksteen5212f8a2021-06-10 08:18:32 +020047 except KeyError:
48 raise UpdaterException('$ANDROID_BUILD_TOP is not defined; you '
49 'must first source build/envsetup.sh and '
50 'select a target.')
Thiébaud Weksteen5212f8a2021-06-10 08:18:32 +020051
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010052
53class Bazel(object):
54 # set up the Bazel queryview
55 def __init__(self, env):
Thiébaud Weksteen3e32afc2021-06-10 07:56:06 +020056 if platform.system() != 'Linux':
Thiébaud Weksteen5212f8a2021-06-10 08:18:32 +020057 raise UpdaterException('This script has only been tested on Linux.')
Thiébaud Weksteen3e32afc2021-06-10 07:56:06 +020058 self.path = os.path.join(env.ANDROID_BUILD_TOP, "tools", "bazel")
Thiébaud Weksteendf132d62021-06-10 08:45:37 +020059 soong_ui = os.path.join(env.ANDROID_BUILD_TOP, "build", "soong", "soong_ui.bash")
Thiébaud Weksteenfc485b22021-06-10 13:30:20 +020060
61 # soong_ui requires to be at the root of the repository.
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010062 os.chdir(env.ANDROID_BUILD_TOP)
Thiébaud Weksteendf132d62021-06-10 08:45:37 +020063 print("Generating Bazel files...")
64 cmd = [soong_ui, "--make-mode", "GENERATE_BAZEL_FILES=1", "nothing"]
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +010065 try:
Thiébaud Weksteendf132d62021-06-10 08:45:37 +020066 subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True)
67 except subprocess.CalledProcessError as e:
68 raise UpdaterException('Unable to generate bazel workspace: ' + e.output)
69
70 print("Building Bazel Queryview. This can take a couple of minutes...")
71 cmd = [soong_ui, "--build-mode", "--all-modules", "--dir=.", "queryview"]
72 try:
73 subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True)
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +010074 except subprocess.CalledProcessError as e:
Thiébaud Weksteen5212f8a2021-06-10 08:18:32 +020075 raise UpdaterException('Unable to update TEST_MAPPING: ' + e.output)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010076
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010077 # Return all modules for a given path.
78 def query_modules(self, path):
Thiébaud Weksteen3e32afc2021-06-10 07:56:06 +020079 cmd = self.path + " query --config=queryview /" + path + ":all"
Thiébaud Weksteen76c4e232021-06-10 07:35:19 +020080 out = subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL, text=True).strip().split("\n")
81 modules = set()
82 for line in out:
83 # speed up by excluding unused modules.
84 if "windows_x86" in line:
85 continue
86 modules.add(line)
87 return modules
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010088
89 # Return all reverse dependencies for a single module.
90 def query_rdeps(self, module):
Thiébaud Weksteen3e32afc2021-06-10 07:56:06 +020091 cmd = (self.path + " query --config=queryview \'rdeps(//..., " +
Thiébaud Weksteen76c4e232021-06-10 07:35:19 +020092 module + ")\' --output=label_kind")
93 out = (subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL, text=True)
94 .strip().split("\n"))
95 if '' in out:
96 out.remove('')
97 return out
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010098
Jeff Vander Stoep0b0e24f2021-01-24 20:50:26 +010099 def exclude_module(self, module):
100 for path in exclude_paths:
101 if module.startswith(path):
102 return True
103 return False
104
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100105 # Return all reverse dependency tests for modules in this package.
106 def query_rdep_tests(self, modules):
107 rdep_tests = set()
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100108 for module in modules:
109 for rdep in self.query_rdeps(module):
Thiébaud Weksteen2e532bb2021-06-10 09:01:34 +0200110 rule_type, _, mod = rdep.split(" ")
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100111 if rule_type == "rust_test_" or rule_type == "rust_test":
Jeff Vander Stoep0b0e24f2021-01-24 20:50:26 +0100112 if self.exclude_module(mod) == False:
113 rdep_tests.add(mod.split(":")[1].split("--")[0])
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100114 return rdep_tests
115
116
Thiébaud Weksteen2e532bb2021-06-10 09:01:34 +0200117class Package(object):
Thiébaud Weksteenfc485b22021-06-10 13:30:20 +0200118 def __init__(self, path, env, bazel):
119 if path == None:
120 self.dir = os.getcwd()
121 else:
122 self.dir = path
123 try:
124 self.dir_rel = self.dir.split(env.ANDROID_BUILD_TOP)[1]
125 except IndexError:
126 raise UpdaterException('The path ' + self.dir + ' is not under ' +
127 env.ANDROID_BUILD_TOP + '; You must be in the '
128 'directory of a crate or pass its absolute path '
129 'as first argument.')
130
131 # Move to the package_directory.
132 os.chdir(self.dir)
133 modules = bazel.query_modules(self.dir_rel)
Thiébaud Weksteen2e532bb2021-06-10 09:01:34 +0200134 self.rdep_tests = bazel.query_rdep_tests(modules)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100135
136 def get_rdep_tests(self):
137 return self.rdep_tests
138
139
140class TestMapping(object):
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +0100141 def __init__(self, path):
Thiébaud Weksteenfc485b22021-06-10 13:30:20 +0200142 env = Env()
143 bazel = Bazel(env)
144 self.package = Package(path, env, bazel)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100145
Thiébaud Weksteen2e532bb2021-06-10 09:01:34 +0200146 def create(self):
Thiébaud Weksteenfc485b22021-06-10 13:30:20 +0200147 tests = self.package.get_rdep_tests()
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100148 if not bool(tests):
149 return
150 test_mapping = self.tests_to_mapping(tests)
151 self.write_test_mapping(test_mapping)
152
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100153 def tests_to_mapping(self, tests):
154 test_mapping = {"presubmit": []}
155 for test in tests:
Jeff Vander Stoep0b0e24f2021-01-24 20:50:26 +0100156 if test in test_exclude:
157 continue
158 if test in test_options:
159 test_mapping["presubmit"].append({"name": test, "options": test_options[test]})
160 else:
161 test_mapping["presubmit"].append({"name": test})
Joel Galenson4f9d11f2021-04-06 10:18:21 -0700162 test_mapping["presubmit"] = sorted(test_mapping["presubmit"], key=lambda t: t["name"])
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100163 return test_mapping
164
165 def write_test_mapping(self, test_mapping):
166 with open("TEST_MAPPING", "w") as json_file:
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +0100167 json_file.write("// Generated by update_crate_tests.py for tests that depend on this crate.\n")
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100168 json.dump(test_mapping, json_file, indent=2, separators=(',', ': '), sort_keys=True)
169 json_file.write("\n")
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +0100170 print("TEST_MAPPING successfully updated!")
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100171
Thiébaud Weksteenfc485b22021-06-10 13:30:20 +0200172
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100173def main():
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +0100174 if len(sys.argv) == 2:
175 path = sys.argv[1]
176 else:
177 path = None
Thiébaud Weksteen5212f8a2021-06-10 08:18:32 +0200178 try:
179 test_mapping = TestMapping(path)
180 except UpdaterException as err:
181 sys.exit("Error: " + str(err))
Thiébaud Weksteen2e532bb2021-06-10 09:01:34 +0200182 test_mapping.create()
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100183
184if __name__ == '__main__':
185 main()