diff options
author | 2024-11-14 08:48:30 +0000 | |
---|---|---|
committer | 2024-11-15 20:25:26 +0000 | |
commit | 9fbfe26b27a7ff9068018e3c4808a545f577212c (patch) | |
tree | 93e1ab737145f4f88165d96f84c1c5694a503501 | |
parent | b881f3015e1cae94232d4925b2f4c855ea32a404 (diff) |
run-test: Create boot-image-compilation script
Add script which compiles the boot image to the run-test zip file.
Also add json file with all the test rules.
Each rule can adb-push data from the zip file,
and adb-shell execute any pushed bash script.
The boot-image compilation is just another rule,
and the other rules have a dependency on it.
Therefore it will execute first and only once.
Test: Install ART test APEX on device and manually perform the commands
Change-Id: I70bad11b11f81a427bf16eecd773dc10c34ff664
-rw-r--r-- | test/Android.run-test.bp | 21 | ||||
-rwxr-xr-x | test/Android.run-test.bp.py | 7 | ||||
-rwxr-xr-x | test/default_run.py | 21 | ||||
-rw-r--r-- | test/globals.py | 24 | ||||
-rwxr-xr-x | test/run-test | 33 | ||||
-rwxr-xr-x | test/run_test_build.py | 102 |
6 files changed, 138 insertions, 70 deletions
diff --git a/test/Android.run-test.bp b/test/Android.run-test.bp index a7b4ae6929..f119ec75b4 100644 --- a/test/Android.run-test.bp +++ b/test/Android.run-test.bp @@ -2639,16 +2639,13 @@ prebuilt_etc_host { genrule_defaults { name: "art-run-test-host-data-defaults", - tool_files: [ - "run_test_build.py", - ":art-run-test-bootclasspath", - ], srcs: [ // Since genrules are sandboxed, all the sources they use must be listed in // the Android.bp file. Some tests have symlinks to files from other tests, and // those must also be listed to avoid a dangling symlink in the sandbox. "jvmti-common/*.java", "utils/python/**/*.py", + ":art-run-test-bootclasspath", ":development_docs", ":asm-9.6-filegroup", ":ojluni-AbstractCollection", @@ -2663,7 +2660,9 @@ genrule_defaults { "testrunner/*.py", "knownfailures.json", "default_run.py", + "globals.py", "run-test", + "run_test_build.py", ], tools: [ "android-smali", @@ -5760,16 +5759,13 @@ prebuilt_etc_host { genrule_defaults { name: "art-run-test-target-data-defaults", - tool_files: [ - "run_test_build.py", - ":art-run-test-bootclasspath", - ], srcs: [ // Since genrules are sandboxed, all the sources they use must be listed in // the Android.bp file. Some tests have symlinks to files from other tests, and // those must also be listed to avoid a dangling symlink in the sandbox. "jvmti-common/*.java", "utils/python/**/*.py", + ":art-run-test-bootclasspath", ":development_docs", ":asm-9.6-filegroup", ":ojluni-AbstractCollection", @@ -5784,7 +5780,9 @@ genrule_defaults { "testrunner/*.py", "knownfailures.json", "default_run.py", + "globals.py", "run-test", + "run_test_build.py", ], tools: [ "android-smali", @@ -8881,16 +8879,13 @@ prebuilt_etc_host { genrule_defaults { name: "art-run-test-jvm-data-defaults", - tool_files: [ - "run_test_build.py", - ":art-run-test-bootclasspath", - ], srcs: [ // Since genrules are sandboxed, all the sources they use must be listed in // the Android.bp file. Some tests have symlinks to files from other tests, and // those must also be listed to avoid a dangling symlink in the sandbox. "jvmti-common/*.java", "utils/python/**/*.py", + ":art-run-test-bootclasspath", ":development_docs", ":asm-9.6-filegroup", ":ojluni-AbstractCollection", @@ -8905,7 +8900,9 @@ genrule_defaults { "testrunner/*.py", "knownfailures.json", "default_run.py", + "globals.py", "run-test", + "run_test_build.py", ], tools: [ "android-smali", diff --git a/test/Android.run-test.bp.py b/test/Android.run-test.bp.py index d7934661b3..e208672b11 100755 --- a/test/Android.run-test.bp.py +++ b/test/Android.run-test.bp.py @@ -111,16 +111,13 @@ def main(): f.write(textwrap.dedent(f""" genrule_defaults {{ name: "art-run-test-{mode}-data-defaults", - tool_files: [ - "run_test_build.py", - ":art-run-test-bootclasspath", - ], srcs: [ // Since genrules are sandboxed, all the sources they use must be listed in // the Android.bp file. Some tests have symlinks to files from other tests, and // those must also be listed to avoid a dangling symlink in the sandbox. "jvmti-common/*.java", "utils/python/**/*.py", + ":art-run-test-bootclasspath", ":development_docs", ":asm-9.6-filegroup", ":ojluni-AbstractCollection", @@ -135,7 +132,9 @@ def main(): "testrunner/*.py", "knownfailures.json", "default_run.py", + "globals.py", "run-test", + "run_test_build.py", ], tools: [ "android-smali", diff --git a/test/default_run.py b/test/default_run.py index c5e9d376ae..9d7179e68a 100755 --- a/test/default_run.py +++ b/test/default_run.py @@ -15,6 +15,7 @@ import sys, os, shutil, shlex, re, subprocess, glob from argparse import ArgumentParser, BooleanOptionalAction, Namespace +from globals import BOOTCLASSPATH from os import path from os.path import isfile, isdir, basename from subprocess import check_output, DEVNULL, PIPE, STDOUT @@ -144,29 +145,11 @@ def get_target_arch(is64: bool) -> str: assert len(arches) == 1, f"Can not find (unique) 32-bit arch in {arches}" return arches[0] -# Note: This must start with the CORE_IMG_JARS in Android.common_path.mk -# because that's what we use for compiling the boot.art image. -# It may contain additional modules from TEST_CORE_JARS. -bpath_modules = ("core-oj core-libart okhttp bouncycastle apache-xml core-icu4j" - " conscrypt") - # Helper function to construct paths for apex modules (for both -Xbootclasspath and # -Xbootclasspath-location). def get_apex_bootclasspath_impl(bpath_prefix: str): - bpath_separator = "" - bpath = "" - bpath_jar = "" - for bpath_module in bpath_modules.split(" "): - apex_module = "com.android.art" - if bpath_module == "conscrypt": - apex_module = "com.android.conscrypt" - if bpath_module == "core-icu4j": - apex_module = "com.android.i18n" - bpath_jar = f"/apex/{apex_module}/javalib/{bpath_module}.jar" - bpath += f"{bpath_separator}{bpath_prefix}{bpath_jar}" - bpath_separator = ":" - return bpath + return ":".join(bpath_prefix + bpath for bpath in BOOTCLASSPATH) # Gets a -Xbootclasspath paths with the apex modules. diff --git a/test/globals.py b/test/globals.py new file mode 100644 index 0000000000..fa51150776 --- /dev/null +++ b/test/globals.py @@ -0,0 +1,24 @@ +# +# Copyright (C) 2024 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. + +BOOTCLASSPATH = [ + "/apex/com.android.art/javalib/core-oj.jar", + "/apex/com.android.art/javalib/core-libart.jar", + "/apex/com.android.art/javalib/okhttp.jar", + "/apex/com.android.art/javalib/bouncycastle.jar", + "/apex/com.android.art/javalib/apache-xml.jar", + "/apex/com.android.i18n/javalib/core-icu4j.jar", + "/apex/com.android.conscrypt/javalib/conscrypt.jar", +]
\ No newline at end of file diff --git a/test/run-test b/test/run-test index 5af84c89ca..cc31077818 100755 --- a/test/run-test +++ b/test/run-test @@ -105,11 +105,12 @@ class RunTestContext: # Make unique temporary directory guarded by lock file. # The name is deterministic (appending suffix as needed). def make_tmp_dir(): - parent = Path(os.environ.get("TMPDIR", "/tmp/art/rtest")) + parent = Path(os.environ.get("TMPDIR", "/tmp")) / "art" / "test" parent.mkdir(parents=True, exist_ok=True) - args_hash = sha1((" ".join(sys.argv[1:])).encode()).digest().hex() + args = [a for a in sys.argv[1:] if not a.startswith("--create-runner")] + hash = sha1((" ".join(args)).encode()).hexdigest() for i in range(100): - tmp_dir = parent / (f"{args_hash[:8]}" + (f"-{i}" if i > 0 else "")) + tmp_dir = parent / (f"{hash[:8]}" + (f"-{i}" if i > 0 else "")) lock = tmp_dir.with_suffix(".lock") # NB: Next to the directory, not inside. lock_handle = open(lock, "w") try: @@ -177,7 +178,10 @@ if True: export("PYTHON3", f"{ANDROID_BUILD_TOP}/prebuilts/build-tools/path/linux-x86/python3") export("RUN", f"{PYTHON3} {progdir}/etc/run-test-jar") - export("DEX_LOCATION", f"/data/run-test/{test_dir}") + if env.ART_TEST_RUN_FROM_SOONG: + export("DEX_LOCATION", f"/data/local/tmp/art/test/{test_dir}") + else: + export("DEX_LOCATION", f"/data/run-test/{test_dir}") # OUT_DIR defaults to out, and may be relative to ANDROID_BUILD_TOP. # Convert it to an absolute path, since we cd into the tmp_dir to run the tests. @@ -689,7 +693,10 @@ if True: run_args += [ f'--runtime-option "-Djava.library.path=/data/nativetest{suffix64}/art/{target_arch_name}"' ] - run_args += ['--boot "/system/framework/art_boot_images/boot.art"'] + if env.ART_TEST_RUN_FROM_SOONG: + run_args += ['--boot "/data/local/tmp/art/apex/art_boot_images/boot.art"'] + else: + run_args += ['--boot "/system/framework/art_boot_images/boot.art"'] if relocate: run_args += ["--relocate"] else: @@ -773,15 +780,22 @@ if True: # Extract run-test data from the zip file. def unzip(): - shutil.rmtree(tmp_dir) if env.ART_TEST_RUN_FROM_SOONG: # We already have the unzipped copy of the data. assert target_mode src = Path(ANDROID_BUILD_TOP) / "out" / "zip" / "target" / TEST_NAME assert src.exists(), src + shutil.rmtree(tmp_dir) copytree(src, tmp_dir) os.chdir(tmp_dir) return + + # Clear the contents, but keep the directory just in case it is open in terminal. + for file in Path(tmp_dir).iterdir(): + if file.is_file() or file.is_symlink(): + file.unlink() + else: + shutil.rmtree(file) os.makedirs(f"{tmp_dir}/.unzipped") os.chdir(tmp_dir) m = re.match("[0-9]*([0-9][0-9])-.*", TEST_NAME) @@ -809,12 +823,12 @@ if True: if always_clean or (passed and not never_clean): os.chdir(oldwd) shutil.rmtree(tmp_dir) - os.remove(tmp_dir_lock) if target_mode: if ON_VM: run(f"{SSH_CMD} \"rm -rf {chroot_dex_location}\"") else: run(f"adb shell rm -rf {chroot_dex_location}") + os.remove(tmp_dir_lock) print(f"{TEST_NAME} files deleted from host" + (" and from target" if target_mode else "")) else: @@ -874,10 +888,9 @@ if True: if args.create_runner: # TODO: Generate better unique names. - name = [a for a in sys.argv[1:] if not a.startswith("--create-runner")] - hash = sha1((" ".join(name)).encode()).digest().hex()[:8] - dst = args.create_runner / f"{TEST_NAME}-{hash}" + dst = args.create_runner / TEST_NAME / f"{Path(tmp_dir).name}.sh" assert not dst.exists(), dst + dst.parent.mkdir(parents=True, exist_ok=True) copyfile(runner, dst) # Script debugging feature - just export the runner script into a directory, diff --git a/test/run_test_build.py b/test/run_test_build.py index 1d64897436..a93950f02f 100755 --- a/test/run_test_build.py +++ b/test/run_test_build.py @@ -42,6 +42,8 @@ from tempfile import TemporaryDirectory, NamedTemporaryFile from typing import Dict, List, Union, Set, Optional from multiprocessing import cpu_count +from globals import BOOTCLASSPATH + USE_RBE = 100 # Percentage of tests that can use RBE (between 0 and 100) lock_file = None # Keep alive as long as this process is alive. @@ -510,31 +512,83 @@ class BuildTestContext: else: zip(Path(self.test_name + ".jar"), Path("classes.dex")) +# Create bash script that compiles the boot image on device. +# This is currently only used for eng-prod testing (which is different +# to the local and LUCI code paths that use buildbot-sync.sh script). +def create_setup_script(is64: bool): + out = "/data/local/tmp/art/apex/art_boot_images" + isa = 'arm64' if is64 else 'arm' + jar = BOOTCLASSPATH + cmd = [ + f"/apex/com.android.art/bin/{'dex2oat64' if is64 else 'dex2oat32'}", + "--runtime-arg", f"-Xbootclasspath:{':'.join(jar)}", + "--runtime-arg", f"-Xbootclasspath-locations:{':'.join(jar)}", + ] + [f"--dex-file={j}" for j in jar] + [f"--dex-location={j}" for j in jar] + [ + f"--instruction-set={isa}", + "--base=0x70000000", + "--compiler-filter=speed-profile", + "--profile-file=/apex/com.android.art/etc/boot-image.prof", + "--avoid-storing-invocation", + "--generate-debug-info", + "--generate-build-id", + "--image-format=lz4hc", + "--strip", + "--android-root=out/empty", + f"--image={out}/{isa}/boot.art", + f"--oat-file={out}/{isa}/boot.oat", + ] + return [ + f"rm -rf {out}/{isa}", + f"mkdir -p {out}/{isa}", + " ".join(cmd), + ] + # Create bash scripts that can fully execute the run tests. # This can be used in CI to execute the tests without running `testrunner.py`. # This takes into account any custom behaviour defined in per-test `run.py`. # We generate distinct scripts for all of the pre-defined variants. -def create_ci_runner_scripts(mode, test_names): - with TemporaryDirectory() as tmpdir: - python = sys.executable - script = 'art/test/testrunner/testrunner.py' - envs = { - "ANDROID_BUILD_TOP": str(Path(getcwd()).absolute()), - "ART_TEST_RUN_FROM_SOONG": "true", - # TODO: Make the runner scripts target agnostic. - # The only dependency is setting of "-Djava.library.path". - "TARGET_ARCH": "arm64", - "TARGET_2ND_ARCH": "arm", - "TMPDIR": Path(getcwd()) / "tmp", +def create_ci_runner_scripts(out, mode, test_names): + out.mkdir(parents=True) + setup = out / "setup.sh" + setup_script = create_setup_script(False) + create_setup_script(True) + setup.write_text("\n".join(setup_script)) + + python = sys.executable + script = 'art/test/testrunner/testrunner.py' + envs = { + "ANDROID_BUILD_TOP": str(Path(getcwd()).absolute()), + "ART_TEST_RUN_FROM_SOONG": "true", + # TODO: Make the runner scripts target agnostic. + # The only dependency is setting of "-Djava.library.path". + "TARGET_ARCH": "arm64", + "TARGET_2ND_ARCH": "arm", + "TMPDIR": Path(getcwd()) / "tmp", + } + args = [ + f"--run-test-option=--create-runner={out}", + f"-j={cpu_count()}", + f"--{mode}", + ] + run([python, script] + args + test_names, env=envs, check=True) + tests = { + "setup": { + "adb push": [[str(setup.relative_to(out)), "/data/local/tmp/art/setup.sh"]], + "adb shell": [["sh", "/data/local/tmp/art/setup.sh"]], + }, + } + for runner in Path(out).glob("*/*.sh"): + test_name = runner.parent.name + test_hash = runner.stem + target_dir = f"/data/local/tmp/art/test/{test_hash}" + tests[f"{test_name}-{test_hash}"] = { + "dependencies": ["setup"], + "adb push": [ + [f"../{mode}/{test_name}/", f"{target_dir}/"], + [str(runner.relative_to(out)), f"{target_dir}/run.sh"] + ], + "adb shell": [["sh", f"{target_dir}/run.sh"]], } - args = [ - f"--run-test-option=--create-runner={tmpdir}", - f"-j={cpu_count()}", - f"--{mode}", - ] - run([python, script] + args + test_names, env=envs, check=True) - runners = {r.name: r.read_text().split("\n") for r in Path(tmpdir).glob("*")} - return [{"name": name, "runner": bash} for name, bash in runners.items()] + return tests # If we build just individual shard, we want to split the work among all the cores, # but if the build system builds all shards, we don't want to overload the machine. @@ -603,11 +657,9 @@ def main() -> None: if args.mode == "target": os.chdir(android_build_top) test_names = [ctx.test_name for ctx in tests] - data = create_ci_runner_scripts(args.mode, test_names) - dst = ziproot / "runner" / args.out.with_suffix(".json").name - dst.parent.mkdir(parents=True) - text = json.dumps(data, ensure_ascii=False, indent=2) - Path(dst).write_text(text) + dst = ziproot / "runner" / args.out.with_suffix(".tests.json").name + tests = create_ci_runner_scripts(dst.parent, args.mode, test_names) + dst.write_text(json.dumps(tests, indent=2, sort_keys=True)) # Create the final zip file which contains the content of the temporary directory. soong_zip = android_build_top / args.soong_zip |