update_crate_tests: Add documentation
Test: pydoc update_crate_tests Change-Id: Ice2c3a8ab901e38b2cf77433ec1ba6aac2360236
This commit is contained in:
@@ -13,22 +13,41 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# 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 json
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
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_tests_digest_tests": [{"test-timeout": "600000"}],
|
||||||
"ring_device_test_src_lib": [{"test-timeout": "100000"}],
|
"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_client",
|
||||||
"aidl_test_rust_service"
|
"aidl_test_rust_service"
|
||||||
]
|
]
|
||||||
exclude_paths = [
|
|
||||||
|
# Excluded modules.
|
||||||
|
EXCLUDE_PATHS = [
|
||||||
"//external/adhd",
|
"//external/adhd",
|
||||||
"//external/crosvm",
|
"//external/crosvm",
|
||||||
"//external/libchromeos-rs",
|
"//external/libchromeos-rs",
|
||||||
@@ -37,10 +56,18 @@ exclude_paths = [
|
|||||||
|
|
||||||
|
|
||||||
class UpdaterException(Exception):
|
class UpdaterException(Exception):
|
||||||
pass
|
"""Exception generated by this script."""
|
||||||
|
|
||||||
|
|
||||||
class Env(object):
|
class Env(object):
|
||||||
|
"""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):
|
def __init__(self):
|
||||||
try:
|
try:
|
||||||
self.ANDROID_BUILD_TOP = os.environ['ANDROID_BUILD_TOP']
|
self.ANDROID_BUILD_TOP = os.environ['ANDROID_BUILD_TOP']
|
||||||
@@ -51,8 +78,25 @@ class Env(object):
|
|||||||
|
|
||||||
|
|
||||||
class Bazel(object):
|
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):
|
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':
|
if platform.system() != 'Linux':
|
||||||
raise UpdaterException('This script has only been tested on Linux.')
|
raise UpdaterException('This script has only been tested on Linux.')
|
||||||
self.path = os.path.join(env.ANDROID_BUILD_TOP, "tools", "bazel")
|
self.path = os.path.join(env.ANDROID_BUILD_TOP, "tools", "bazel")
|
||||||
@@ -74,8 +118,8 @@ class Bazel(object):
|
|||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
raise UpdaterException('Unable to update TEST_MAPPING: ' + e.output)
|
raise UpdaterException('Unable to update TEST_MAPPING: ' + e.output)
|
||||||
|
|
||||||
# Return all modules for a given path.
|
|
||||||
def query_modules(self, path):
|
def query_modules(self, path):
|
||||||
|
"""Returns all modules for a given path."""
|
||||||
cmd = self.path + " query --config=queryview /" + path + ":all"
|
cmd = self.path + " query --config=queryview /" + path + ":all"
|
||||||
out = subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL, text=True).strip().split("\n")
|
out = subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL, text=True).strip().split("\n")
|
||||||
modules = set()
|
modules = set()
|
||||||
@@ -86,8 +130,8 @@ class Bazel(object):
|
|||||||
modules.add(line)
|
modules.add(line)
|
||||||
return modules
|
return modules
|
||||||
|
|
||||||
# Return all reverse dependencies for a single module.
|
|
||||||
def query_rdeps(self, module):
|
def query_rdeps(self, module):
|
||||||
|
"""Returns all reverse dependencies for a single module."""
|
||||||
cmd = (self.path + " query --config=queryview \'rdeps(//..., " +
|
cmd = (self.path + " query --config=queryview \'rdeps(//..., " +
|
||||||
module + ")\' --output=label_kind")
|
module + ")\' --output=label_kind")
|
||||||
out = (subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL, text=True)
|
out = (subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL, text=True)
|
||||||
@@ -97,13 +141,13 @@ class Bazel(object):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
def exclude_module(self, module):
|
def exclude_module(self, module):
|
||||||
for path in exclude_paths:
|
for path in EXCLUDE_PATHS:
|
||||||
if module.startswith(path):
|
if module.startswith(path):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Return all reverse dependency tests for modules in this package.
|
|
||||||
def query_rdep_tests(self, modules):
|
def query_rdep_tests(self, modules):
|
||||||
|
"""Returns all reverse dependency tests for modules in this package."""
|
||||||
rdep_tests = set()
|
rdep_tests = set()
|
||||||
for module in modules:
|
for module in modules:
|
||||||
for rdep in self.query_rdeps(module):
|
for rdep in self.query_rdeps(module):
|
||||||
@@ -115,7 +159,28 @@ class Bazel(object):
|
|||||||
|
|
||||||
|
|
||||||
class Package(object):
|
class Package(object):
|
||||||
|
"""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):
|
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:
|
if path == None:
|
||||||
self.dir = os.getcwd()
|
self.dir = os.getcwd()
|
||||||
else:
|
else:
|
||||||
@@ -138,12 +203,23 @@ class Package(object):
|
|||||||
|
|
||||||
|
|
||||||
class TestMapping(object):
|
class TestMapping(object):
|
||||||
|
"""A TEST_MAPPING file.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
package: The package associated with this TEST_MAPPING file.
|
||||||
|
"""
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
|
"""Constructor.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: The absolute path to the package.
|
||||||
|
"""
|
||||||
env = Env()
|
env = Env()
|
||||||
bazel = Bazel(env)
|
bazel = Bazel(env)
|
||||||
self.package = Package(path, env, bazel)
|
self.package = Package(path, env, bazel)
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
|
"""Generates the TEST_MAPPING file."""
|
||||||
tests = self.package.get_rdep_tests()
|
tests = self.package.get_rdep_tests()
|
||||||
if not bool(tests):
|
if not bool(tests):
|
||||||
return
|
return
|
||||||
@@ -151,18 +227,20 @@ class TestMapping(object):
|
|||||||
self.write_test_mapping(test_mapping)
|
self.write_test_mapping(test_mapping)
|
||||||
|
|
||||||
def tests_to_mapping(self, tests):
|
def tests_to_mapping(self, tests):
|
||||||
|
"""Translate the test list into a dictionary."""
|
||||||
test_mapping = {"presubmit": []}
|
test_mapping = {"presubmit": []}
|
||||||
for test in tests:
|
for test in tests:
|
||||||
if test in test_exclude:
|
if test in TEST_EXCLUDE:
|
||||||
continue
|
continue
|
||||||
if test in test_options:
|
if test in TEST_OPTIONS:
|
||||||
test_mapping["presubmit"].append({"name": test, "options": test_options[test]})
|
test_mapping["presubmit"].append({"name": test, "options": TEST_OPTIONS[test]})
|
||||||
else:
|
else:
|
||||||
test_mapping["presubmit"].append({"name": test})
|
test_mapping["presubmit"].append({"name": test})
|
||||||
test_mapping["presubmit"] = sorted(test_mapping["presubmit"], key=lambda t: t["name"])
|
test_mapping["presubmit"] = sorted(test_mapping["presubmit"], key=lambda t: t["name"])
|
||||||
return test_mapping
|
return test_mapping
|
||||||
|
|
||||||
def write_test_mapping(self, test_mapping):
|
def write_test_mapping(self, test_mapping):
|
||||||
|
"""Writes the TEST_MAPPING file."""
|
||||||
with open("TEST_MAPPING", "w") as json_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_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.dump(test_mapping, json_file, indent=2, separators=(',', ': '), sort_keys=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user