Merge changes Ice2c3a8a,I5c4f16af am: 53f2408738 am: 108713b948 am: ccbe204fe5 am: 4cca01cb22

Original change: https://android-review.googlesource.com/c/platform/development/+/1733553

Change-Id: Ic075e476b023b718ea7b1ffddeba70352348f674
This commit is contained in:
Treehugger Robot
2021-06-14 08:50:33 +00:00
committed by Automerger Merge Worker

View File

@@ -13,61 +13,96 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Add tests to TEST_MAPPING. Include tests for reverse dependencies."""
"""Add or update tests to TEST_MAPPING.
This script uses Bazel to find reverse dependencies on a crate and generates a
TEST_MAPPING file. It accepts the absolute path to a crate as argument. If no
argument is provided, it assumes the crate is the current directory.
Usage:
$ . build/envsetup.sh
$ lunch aosp_arm64-eng
$ update_crate_tests.py $ANDROID_BUILD_TOP/external/rust/crates/libc
This script is automatically called by external_updater.
"""
import json
import os
import platform
import subprocess
import sys
test_options = {
# Some tests requires specific options. Consider fixing the upstream crate
# before updating this dictionary.
TEST_OPTIONS = {
"ring_device_test_tests_digest_tests": [{"test-timeout": "600000"}],
"ring_device_test_src_lib": [{"test-timeout": "100000"}],
}
test_exclude = [
# Excluded tests. These tests will be ignored by this script.
TEST_EXCLUDE = [
"aidl_test_rust_client",
"aidl_test_rust_service"
]
exclude_paths = [
]
# Excluded modules.
EXCLUDE_PATHS = [
"//external/adhd",
"//external/crosvm",
"//external/libchromeos-rs",
"//external/vm_tools"
]
]
class UpdaterException(Exception):
pass
"""Exception generated by this script."""
class Env(object):
def __init__(self, path):
"""Env captures the execution environment.
It ensures this script is executed within an AOSP repository.
Attributes:
ANDROID_BUILD_TOP: A string representing the absolute path to the top
of the repository.
"""
def __init__(self):
try:
self.ANDROID_BUILD_TOP = os.environ['ANDROID_BUILD_TOP']
except KeyError:
raise UpdaterException('$ANDROID_BUILD_TOP is not defined; you '
'must first source build/envsetup.sh and '
'select a target.')
if path == None:
self.cwd = os.getcwd()
else:
self.cwd = path
try:
self.cwd_relative = self.cwd.split(self.ANDROID_BUILD_TOP)[1]
except IndexError:
raise UpdaterException('The path ' + self.cwd + ' is not under ' +
self.ANDROID_BUILD_TOP + '; You must be in the '
'directory of a crate or pass its absolute path '
'as first argument.')
class Bazel(object):
# set up the Bazel queryview
"""Bazel wrapper.
The wrapper is used to call bazel queryview and generate the list of
reverse dependencies.
Attributes:
path: The path to the bazel executable.
"""
def __init__(self, env):
"""Constructor.
Note that the current directory is changed to ANDROID_BUILD_TOP.
Args:
env: An instance of Env.
Raises:
UpdaterException: an error occurred while calling soong_ui.
"""
if platform.system() != 'Linux':
raise UpdaterException('This script has only been tested on Linux.')
self.path = os.path.join(env.ANDROID_BUILD_TOP, "tools", "bazel")
soong_ui = os.path.join(env.ANDROID_BUILD_TOP, "build", "soong", "soong_ui.bash")
# soong_ui requires to be at the root of the repository.
os.chdir(env.ANDROID_BUILD_TOP)
print("Generating Bazel files...")
cmd = [soong_ui, "--make-mode", "GENERATE_BAZEL_FILES=1", "nothing"]
@@ -82,10 +117,9 @@ class Bazel(object):
subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True)
except subprocess.CalledProcessError as e:
raise UpdaterException('Unable to update TEST_MAPPING: ' + e.output)
os.chdir(env.cwd)
# Return all modules for a given path.
def query_modules(self, path):
"""Returns all modules for a given path."""
cmd = self.path + " query --config=queryview /" + path + ":all"
out = subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL, text=True).strip().split("\n")
modules = set()
@@ -96,8 +130,8 @@ class Bazel(object):
modules.add(line)
return modules
# Return all reverse dependencies for a single module.
def query_rdeps(self, module):
"""Returns all reverse dependencies for a single module."""
cmd = (self.path + " query --config=queryview \'rdeps(//..., " +
module + ")\' --output=label_kind")
out = (subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL, text=True)
@@ -107,13 +141,13 @@ class Bazel(object):
return out
def exclude_module(self, module):
for path in exclude_paths:
for path in EXCLUDE_PATHS:
if module.startswith(path):
return True
return False
# Return all reverse dependency tests for modules in this package.
def query_rdep_tests(self, modules):
"""Returns all reverse dependency tests for modules in this package."""
rdep_tests = set()
for module in modules:
for rdep in self.query_rdeps(module):
@@ -125,8 +159,43 @@ class Bazel(object):
class Package(object):
def __init__(self, path, bazel):
modules = bazel.query_modules(path)
"""A Bazel package.
Attributes:
dir: The absolute path to this package.
dir_rel: The relative path to this package.
rdep_tests: The list of computed reverse dependencies.
"""
def __init__(self, path, env, bazel):
"""Constructor.
Note that the current directory is changed to the package location when
called.
Args:
path: Path to the package.
env: An instance of Env.
bazel: An instance of Bazel.
Raises:
UpdaterException: the package does not appear to belong to the
current repository.
"""
if path == None:
self.dir = os.getcwd()
else:
self.dir = path
try:
self.dir_rel = self.dir.split(env.ANDROID_BUILD_TOP)[1]
except IndexError:
raise UpdaterException('The path ' + self.dir + ' is not under ' +
env.ANDROID_BUILD_TOP + '; You must be in the '
'directory of a crate or pass its absolute path '
'as first argument.')
# Move to the package_directory.
os.chdir(self.dir)
modules = bazel.query_modules(self.dir_rel)
self.rdep_tests = bazel.query_rdep_tests(modules)
def get_rdep_tests(self):
@@ -134,36 +203,51 @@ class Package(object):
class TestMapping(object):
"""A TEST_MAPPING file.
Attributes:
package: The package associated with this TEST_MAPPING file.
"""
def __init__(self, path):
self.env = Env(path)
self.bazel = Bazel(self.env)
"""Constructor.
Args:
path: The absolute path to the package.
"""
env = Env()
bazel = Bazel(env)
self.package = Package(path, env, bazel)
def create(self):
tests = Package(self.env.cwd_relative, self.bazel).get_rdep_tests()
"""Generates the TEST_MAPPING file."""
tests = self.package.get_rdep_tests()
if not bool(tests):
return
test_mapping = self.tests_to_mapping(tests)
self.write_test_mapping(test_mapping)
def tests_to_mapping(self, tests):
"""Translate the test list into a dictionary."""
test_mapping = {"presubmit": []}
for test in tests:
if test in test_exclude:
if test in TEST_EXCLUDE:
continue
if test in test_options:
test_mapping["presubmit"].append({"name": test, "options": test_options[test]})
if test in TEST_OPTIONS:
test_mapping["presubmit"].append({"name": test, "options": TEST_OPTIONS[test]})
else:
test_mapping["presubmit"].append({"name": test})
test_mapping["presubmit"] = sorted(test_mapping["presubmit"], key=lambda t: t["name"])
return test_mapping
def write_test_mapping(self, test_mapping):
"""Writes the TEST_MAPPING file."""
with open("TEST_MAPPING", "w") as json_file:
json_file.write("// Generated by update_crate_tests.py for tests that depend on this crate.\n")
json.dump(test_mapping, json_file, indent=2, separators=(',', ': '), sort_keys=True)
json_file.write("\n")
print("TEST_MAPPING successfully updated!")
def main():
if len(sys.argv) == 2:
path = sys.argv[1]