blob: 3a4ea3851f8026a3aa00adb56243e0feafb47421 [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.
Thiébaud Weksteen3604b752021-06-10 14:22:00 +020016"""Add or update tests to TEST_MAPPING.
17
18This script uses Bazel to find reverse dependencies on a crate and generates a
19TEST_MAPPING file. It accepts the absolute path to a crate as argument. If no
20argument is provided, it assumes the crate is the current directory.
21
22 Usage:
23 $ . build/envsetup.sh
24 $ lunch aosp_arm64-eng
25 $ update_crate_tests.py $ANDROID_BUILD_TOP/external/rust/crates/libc
26
27This script is automatically called by external_updater.
Ivan Lozanod7438cf2022-04-26 12:57:08 -040028
29A test_mapping_config.json file can be defined in the project directory to
30configure the generated TEST_MAPPING file, for example:
31
32 {
33 // Run tests in postsubmit instead of presubmit.
34 "postsubmit_tests":["foo"]
35 }
36
Thiébaud Weksteen3604b752021-06-10 14:22:00 +020037"""
38
Joel Galenson08352442021-08-20 11:39:48 -070039import argparse
40import glob
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010041import json
42import os
43import platform
Joel Galenson4a08c642021-10-14 13:25:57 -070044import re
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010045import subprocess
46import sys
Joel Galenson4a2a3a82021-08-20 12:26:37 -070047from datetime import datetime
Joel Galenson08352442021-08-20 11:39:48 -070048from pathlib import Path
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +010049
Thiébaud Weksteen3604b752021-06-10 14:22:00 +020050# Some tests requires specific options. Consider fixing the upstream crate
51# before updating this dictionary.
52TEST_OPTIONS = {
Joel Galenson54d65532021-08-31 14:08:05 -070053 "ring_test_tests_digest_tests": [{"test-timeout": "600000"}],
54 "ring_test_src_lib": [{"test-timeout": "100000"}],
Joel Galensona0d4c5e2021-04-06 09:36:47 -070055}
Thiébaud Weksteen3604b752021-06-10 14:22:00 +020056
Matthew Maurer24758f82021-12-10 18:51:22 +000057# Groups to add tests to. "presubmit" runs x86_64 device tests+host tests, and
58# "presubmit-rust" runs arm64 device tests on physical devices.
59TEST_GROUPS = [
60 "presubmit",
Ivan Lozanod7438cf2022-04-26 12:57:08 -040061 "presubmit-rust",
62 "postsubmit",
Matthew Maurer24758f82021-12-10 18:51:22 +000063]
64
Thiébaud Weksteen3604b752021-06-10 14:22:00 +020065# Excluded tests. These tests will be ignored by this script.
66TEST_EXCLUDE = [
Joel Galenson54d65532021-08-31 14:08:05 -070067 "ash_test_src_lib",
68 "ash_test_tests_constant_size_arrays",
69 "ash_test_tests_display",
70 "shared_library_test_src_lib",
Ivan Lozano69030b32022-03-04 14:03:47 -050071 "vulkano_test_src_lib",
72
73 # These are helper binaries for aidl_integration_test
74 # and aren't actually meant to run as individual tests.
75 "aidl_test_rust_client",
76 "aidl_test_rust_service",
77 "aidl_test_rust_service_async",
78
David LeGared3bbf8c2022-03-15 16:01:49 +000079 # This is a helper binary for AuthFsHostTest and shouldn't
80 # be run directly.
81 "open_then_run",
82
Ivan Lozano69030b32022-03-04 14:03:47 -050083 # TODO: Remove when b/198197213 is closed.
84 "diced_client_test",
Matthew Maurer037b4452022-06-14 14:30:15 -070085
86 "CoverageRustSmokeTest",
87 "libtrusty-rs-tests",
88 "terminal-size_test_src_lib",
Thiébaud Weksteen3604b752021-06-10 14:22:00 +020089]
90
91# Excluded modules.
92EXCLUDE_PATHS = [
Jeff Vander Stoep0b0e24f2021-01-24 20:50:26 +010093 "//external/adhd",
94 "//external/crosvm",
95 "//external/libchromeos-rs",
96 "//external/vm_tools"
Thiébaud Weksteen3604b752021-06-10 14:22:00 +020097]
Jeff Vander Stoep0b0e24f2021-01-24 20:50:26 +010098
Joel Galenson4a08c642021-10-14 13:25:57 -070099LABEL_PAT = re.compile('^//(.*):.*$')
100EXTERNAL_PAT = re.compile('^//external/rust/')
101
Thiébaud Weksteen5212f8a2021-06-10 08:18:32 +0200102
103class UpdaterException(Exception):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200104 """Exception generated by this script."""
Thiébaud Weksteen5212f8a2021-06-10 08:18:32 +0200105
106
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100107class Env(object):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200108 """Env captures the execution environment.
109
110 It ensures this script is executed within an AOSP repository.
111
112 Attributes:
113 ANDROID_BUILD_TOP: A string representing the absolute path to the top
114 of the repository.
115 """
Thiébaud Weksteenfc485b22021-06-10 13:30:20 +0200116 def __init__(self):
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100117 try:
118 self.ANDROID_BUILD_TOP = os.environ['ANDROID_BUILD_TOP']
Thiébaud Weksteen5212f8a2021-06-10 08:18:32 +0200119 except KeyError:
120 raise UpdaterException('$ANDROID_BUILD_TOP is not defined; you '
121 'must first source build/envsetup.sh and '
122 'select a target.')
Thiébaud Weksteen5212f8a2021-06-10 08:18:32 +0200123
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100124
125class Bazel(object):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200126 """Bazel wrapper.
127
128 The wrapper is used to call bazel queryview and generate the list of
129 reverse dependencies.
130
131 Attributes:
132 path: The path to the bazel executable.
133 """
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100134 def __init__(self, env):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200135 """Constructor.
136
137 Note that the current directory is changed to ANDROID_BUILD_TOP.
138
139 Args:
140 env: An instance of Env.
141
142 Raises:
143 UpdaterException: an error occurred while calling soong_ui.
144 """
Thiébaud Weksteen3e32afc2021-06-10 07:56:06 +0200145 if platform.system() != 'Linux':
Thiébaud Weksteen5212f8a2021-06-10 08:18:32 +0200146 raise UpdaterException('This script has only been tested on Linux.')
Jeff Vander Stoep2f3afc22022-12-13 15:43:05 +0100147 self.path = os.path.join(env.ANDROID_BUILD_TOP, "build", "bazel", "bin", "bazel")
Thiébaud Weksteendf132d62021-06-10 08:45:37 +0200148 soong_ui = os.path.join(env.ANDROID_BUILD_TOP, "build", "soong", "soong_ui.bash")
Thiébaud Weksteenfc485b22021-06-10 13:30:20 +0200149
150 # soong_ui requires to be at the root of the repository.
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100151 os.chdir(env.ANDROID_BUILD_TOP)
Thiébaud Weksteendf132d62021-06-10 08:45:37 +0200152 print("Generating Bazel files...")
Lukacs T. Berki9206f1c2021-09-02 17:51:13 +0200153 cmd = [soong_ui, "--make-mode", "bp2build"]
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +0100154 try:
Thiébaud Weksteendf132d62021-06-10 08:45:37 +0200155 subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True)
156 except subprocess.CalledProcessError as e:
157 raise UpdaterException('Unable to generate bazel workspace: ' + e.output)
158
159 print("Building Bazel Queryview. This can take a couple of minutes...")
160 cmd = [soong_ui, "--build-mode", "--all-modules", "--dir=.", "queryview"]
161 try:
162 subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True)
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +0100163 except subprocess.CalledProcessError as e:
Thiébaud Weksteen5212f8a2021-06-10 08:18:32 +0200164 raise UpdaterException('Unable to update TEST_MAPPING: ' + e.output)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100165
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100166 def query_modules(self, path):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200167 """Returns all modules for a given path."""
Thiébaud Weksteen3e32afc2021-06-10 07:56:06 +0200168 cmd = self.path + " query --config=queryview /" + path + ":all"
Thiébaud Weksteen76c4e232021-06-10 07:35:19 +0200169 out = subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL, text=True).strip().split("\n")
170 modules = set()
171 for line in out:
172 # speed up by excluding unused modules.
173 if "windows_x86" in line:
174 continue
175 modules.add(line)
176 return modules
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100177
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100178 def query_rdeps(self, module):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200179 """Returns all reverse dependencies for a single module."""
Thiébaud Weksteen3e32afc2021-06-10 07:56:06 +0200180 cmd = (self.path + " query --config=queryview \'rdeps(//..., " +
Thiébaud Weksteen76c4e232021-06-10 07:35:19 +0200181 module + ")\' --output=label_kind")
182 out = (subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL, text=True)
183 .strip().split("\n"))
184 if '' in out:
185 out.remove('')
186 return out
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100187
Jeff Vander Stoep0b0e24f2021-01-24 20:50:26 +0100188 def exclude_module(self, module):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200189 for path in EXCLUDE_PATHS:
Jeff Vander Stoep0b0e24f2021-01-24 20:50:26 +0100190 if module.startswith(path):
191 return True
192 return False
193
Jeff Vander Stoep408e5db2023-01-30 12:26:28 +0100194 # Return all the TEST_MAPPING files within a given path.
195 def find_all_test_mapping_files(self, path):
196 result = []
197 for root, dirs, files in os.walk(path):
198 if "TEST_MAPPING" in files:
199 result.append(os.path.join(root, "TEST_MAPPING"))
200 return result
201
202 # For a given test, return the TEST_MAPPING file where the test is mapped.
203 # This limits the search to the directory specified in "path" along with its subdirs.
204 def test_to_test_mapping(self, env, path, test):
205 test_mapping_files = self.find_all_test_mapping_files(env.ANDROID_BUILD_TOP + path)
206 for file in test_mapping_files:
207 with open(file) as fd:
208 if "\""+ test + "\"" in fd.read():
209 mapping_path = file.split("/TEST_MAPPING")[0].split("//")[1]
210 return mapping_path
211
212 return None
213
214 # Returns:
215 # rdep_test: for tests specified locally.
216 # rdep_dirs: for paths to TEST_MAPPING files for reverse dependencies.
217 #
218 # We import directories for non-local tests because including tests directly has proven to be
219 # fragile and burdensome. For example, whenever a project removes or renames a test, all the
220 # TEST_MAPPING files for its reverse dependencies must be updated or we get test breakages.
221 # That can be many tens of projects that must updated to prevent the reported breakage of tests
222 # that no longer exist. Similarly when a test is added, it won't be run when the reverse
223 # dependencies change unless/until update_crate_tests.py is run for its depenencies.
224 # Importing TEST_MAPPING files instead of tests solves both of these problems. When tests are
225 # removed, renamed, or added, only files local to the project need to be modified.
226 # The downside is that we potentially miss some tests. But this seems like a reasonable
227 # tradeoff.
228 def query_rdep_tests_dirs(self, env, modules, path, exclude_dir):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200229 """Returns all reverse dependency tests for modules in this package."""
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100230 rdep_tests = set()
Joel Galenson4a08c642021-10-14 13:25:57 -0700231 rdep_dirs = set()
232 path_pat = re.compile("^/%s:.*$" % path)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100233 for module in modules:
234 for rdep in self.query_rdeps(module):
Thiébaud Weksteen2e532bb2021-06-10 09:01:34 +0200235 rule_type, _, mod = rdep.split(" ")
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100236 if rule_type == "rust_test_" or rule_type == "rust_test":
Joel Galenson4a08c642021-10-14 13:25:57 -0700237 if self.exclude_module(mod):
238 continue
239 path_match = path_pat.match(mod)
240 if path_match or not EXTERNAL_PAT.match(mod):
Jeff Vander Stoep408e5db2023-01-30 12:26:28 +0100241 rdep_path = mod.split(":")[0]
242 rdep_test = mod.split(":")[1].split("--")[0]
243 mapping_path = self.test_to_test_mapping(env, rdep_path, rdep_test)
244 # Only include tests directly if they're local to the project.
245 if (mapping_path is not None) and exclude_dir.endswith(mapping_path):
246 rdep_tests.add(rdep_test)
247 # All other tests are included by path.
248 elif mapping_path is not None:
249 rdep_dirs.add(mapping_path)
Joel Galenson4a08c642021-10-14 13:25:57 -0700250 else:
251 label_match = LABEL_PAT.match(mod)
252 if label_match:
253 rdep_dirs.add(label_match.group(1))
254 return (rdep_tests, rdep_dirs)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100255
256
Thiébaud Weksteen2e532bb2021-06-10 09:01:34 +0200257class Package(object):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200258 """A Bazel package.
259
260 Attributes:
261 dir: The absolute path to this package.
262 dir_rel: The relative path to this package.
263 rdep_tests: The list of computed reverse dependencies.
Joel Galenson4a08c642021-10-14 13:25:57 -0700264 rdep_dirs: The list of computed reverse dependency directories.
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200265 """
Thiébaud Weksteenfc485b22021-06-10 13:30:20 +0200266 def __init__(self, path, env, bazel):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200267 """Constructor.
268
269 Note that the current directory is changed to the package location when
270 called.
271
272 Args:
273 path: Path to the package.
274 env: An instance of Env.
275 bazel: An instance of Bazel.
276
277 Raises:
278 UpdaterException: the package does not appear to belong to the
279 current repository.
280 """
Joel Galenson17791042021-06-17 14:59:15 -0700281 self.dir = path
Thiébaud Weksteenfc485b22021-06-10 13:30:20 +0200282 try:
283 self.dir_rel = self.dir.split(env.ANDROID_BUILD_TOP)[1]
284 except IndexError:
285 raise UpdaterException('The path ' + self.dir + ' is not under ' +
286 env.ANDROID_BUILD_TOP + '; You must be in the '
287 'directory of a crate or pass its absolute path '
Joel Galenson17791042021-06-17 14:59:15 -0700288 'as the argument.')
Thiébaud Weksteenfc485b22021-06-10 13:30:20 +0200289
290 # Move to the package_directory.
291 os.chdir(self.dir)
292 modules = bazel.query_modules(self.dir_rel)
Jeff Vander Stoep408e5db2023-01-30 12:26:28 +0100293 (self.rdep_tests, self.rdep_dirs) = bazel.query_rdep_tests_dirs(env, modules,
294 self.dir_rel, self.dir)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100295
Joel Galenson4a08c642021-10-14 13:25:57 -0700296 def get_rdep_tests_dirs(self):
297 return (self.rdep_tests, self.rdep_dirs)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100298
299
300class TestMapping(object):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200301 """A TEST_MAPPING file.
302
303 Attributes:
304 package: The package associated with this TEST_MAPPING file.
305 """
Joel Galenson17791042021-06-17 14:59:15 -0700306 def __init__(self, env, bazel, path):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200307 """Constructor.
308
309 Args:
Joel Galenson17791042021-06-17 14:59:15 -0700310 env: An instance of Env.
311 bazel: An instance of Bazel.
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200312 path: The absolute path to the package.
313 """
Thiébaud Weksteenfc485b22021-06-10 13:30:20 +0200314 self.package = Package(path, env, bazel)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100315
Thiébaud Weksteen2e532bb2021-06-10 09:01:34 +0200316 def create(self):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200317 """Generates the TEST_MAPPING file."""
Joel Galenson4a08c642021-10-14 13:25:57 -0700318 (tests, dirs) = self.package.get_rdep_tests_dirs()
319 if not bool(tests) and not bool(dirs):
320 if os.path.isfile('TEST_MAPPING'):
321 os.remove('TEST_MAPPING')
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100322 return
Joel Galenson4a08c642021-10-14 13:25:57 -0700323 test_mapping = self.tests_dirs_to_mapping(tests, dirs)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100324 self.write_test_mapping(test_mapping)
325
Joel Galenson4a08c642021-10-14 13:25:57 -0700326 def tests_dirs_to_mapping(self, tests, dirs):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200327 """Translate the test list into a dictionary."""
Matthew Maurer24758f82021-12-10 18:51:22 +0000328 test_mapping = {"imports": []}
Ivan Lozanod7438cf2022-04-26 12:57:08 -0400329 config = None
330 if os.path.isfile(os.path.join(self.package.dir, "test_mapping_config.json")):
331 with open(os.path.join(self.package.dir, "test_mapping_config.json"), 'r') as fd:
332 config = json.load(fd)
333
Matthew Maurer24758f82021-12-10 18:51:22 +0000334 for test_group in TEST_GROUPS:
335 test_mapping[test_group] = []
336 for test in tests:
337 if test in TEST_EXCLUDE:
338 continue
Ivan Lozanod7438cf2022-04-26 12:57:08 -0400339 if config and 'postsubmit_tests' in config:
340 if test in config['postsubmit_tests'] and 'postsubmit' not in test_group:
341 continue
342 if test not in config['postsubmit_tests'] and 'postsubmit' in test_group:
343 continue
Matthew Maurere1d07ba2022-06-14 14:38:51 -0700344 else:
345 if 'postsubmit' in test_group:
346 # If postsubmit_tests is not configured, do not place
347 # anything in postsubmit - presubmit groups are
348 # automatically included in postsubmit in CI.
349 continue
Matthew Maurer24758f82021-12-10 18:51:22 +0000350 if test in TEST_OPTIONS:
351 test_mapping[test_group].append({"name": test, "options": TEST_OPTIONS[test]})
352 else:
353 test_mapping[test_group].append({"name": test})
354 test_mapping[test_group] = sorted(test_mapping[test_group], key=lambda t: t["name"])
Ivan Lozanod7438cf2022-04-26 12:57:08 -0400355
Joel Galenson4a08c642021-10-14 13:25:57 -0700356 for dir in dirs:
357 test_mapping["imports"].append({"path": dir})
Joel Galenson4a08c642021-10-14 13:25:57 -0700358 test_mapping["imports"] = sorted(test_mapping["imports"], key=lambda t: t["path"])
Matthew Maurer24758f82021-12-10 18:51:22 +0000359 test_mapping = {section: entry for (section, entry) in test_mapping.items() if entry}
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100360 return test_mapping
361
362 def write_test_mapping(self, test_mapping):
Thiébaud Weksteen3604b752021-06-10 14:22:00 +0200363 """Writes the TEST_MAPPING file."""
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100364 with open("TEST_MAPPING", "w") as json_file:
Jeff Vander Stoep1b24dc32021-02-03 18:52:42 +0100365 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 +0100366 json.dump(test_mapping, json_file, indent=2, separators=(',', ': '), sort_keys=True)
367 json_file.write("\n")
Joel Galenson17791042021-06-17 14:59:15 -0700368 print("TEST_MAPPING successfully updated for %s!" % self.package.dir_rel)
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100369
Thiébaud Weksteenfc485b22021-06-10 13:30:20 +0200370
Joel Galenson08352442021-08-20 11:39:48 -0700371def parse_args():
372 parser = argparse.ArgumentParser('update_crate_tests')
Joel Galenson4a2a3a82021-08-20 12:26:37 -0700373 parser.add_argument('paths',
374 nargs='*',
375 help='Absolute or relative paths of the projects as globs.')
376 parser.add_argument('--branch_and_commit',
377 action='store_true',
378 help='Starts a new branch and commit changes.')
379 parser.add_argument('--push_change',
380 action='store_true',
381 help='Pushes change to Gerrit.')
Joel Galenson08352442021-08-20 11:39:48 -0700382 return parser.parse_args()
383
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100384def main():
Joel Galenson08352442021-08-20 11:39:48 -0700385 args = parse_args()
386 paths = args.paths if len(args.paths) > 0 else [os.getcwd()]
387 # We want to use glob to get all the paths, so we first convert to absolute.
388 paths = [Path(path).resolve() for path in paths]
389 paths = sorted([path for abs_path in paths
390 for path in glob.glob(str(abs_path))])
391
Joel Galenson17791042021-06-17 14:59:15 -0700392 env = Env()
393 bazel = Bazel(env)
394 for path in paths:
395 try:
396 test_mapping = TestMapping(env, bazel, path)
Joel Galenson4a2a3a82021-08-20 12:26:37 -0700397 test_mapping.create()
398 changed = (subprocess.call(['git', 'diff', '--quiet']) == 1)
Joel Galensonbdf3ab42021-08-30 08:57:18 -0700399 untracked = (os.path.isfile('TEST_MAPPING') and
400 (subprocess.run(['git', 'ls-files', '--error-unmatch', 'TEST_MAPPING'],
401 stderr=subprocess.DEVNULL,
402 stdout=subprocess.DEVNULL).returncode == 1))
403 if args.branch_and_commit and (changed or untracked):
Joel Galenson4a2a3a82021-08-20 12:26:37 -0700404 subprocess.check_output(['repo', 'start',
405 'tmp_auto_test_mapping', '.'])
406 subprocess.check_output(['git', 'add', 'TEST_MAPPING'])
Matthew Maurer3f7f7e22022-06-14 14:28:31 -0700407 # test_mapping_config.json is not always present
408 subprocess.call(['git', 'add', 'test_mapping_config.json'],
409 stderr=subprocess.DEVNULL,
410 stdout=subprocess.DEVNULL)
Joel Galenson4a2a3a82021-08-20 12:26:37 -0700411 subprocess.check_output(['git', 'commit', '-m',
412 'Update TEST_MAPPING\n\nTest: None'])
Joel Galensonbdf3ab42021-08-30 08:57:18 -0700413 if args.push_change and (changed or untracked):
Joel Galenson4a2a3a82021-08-20 12:26:37 -0700414 date = datetime.today().strftime('%m-%d')
415 subprocess.check_output(['git', 'push', 'aosp', 'HEAD:refs/for/master',
416 '-o', 'topic=test-mapping-%s' % date])
417 except (UpdaterException, subprocess.CalledProcessError) as err:
Joel Galenson17791042021-06-17 14:59:15 -0700418 sys.exit("Error: " + str(err))
Jeff Vander Stoepcec8bac2021-01-15 14:15:37 +0100419
420if __name__ == '__main__':
421 main()