blob: 3ce18d28f69a6be5e253b4485736248abea680c8 [file] [log] [blame]
David Srbecky854725b2021-04-27 19:30:41 +01001#!/usr/bin/env python3
2#
3# [VPYTHON:BEGIN]
4# python_version: "3.8"
5# [VPYTHON:END]
David Srbecky10132a02021-04-23 22:28:15 +01006#
7# Copyright (C) 2021 The Android Open Source Project
8#
9# Licensed under the Apache License, Version 2.0 (the "License");
10# you may not use this file except in compliance with the License.
11# You may obtain a copy of the License at
12#
13# http://www.apache.org/licenses/LICENSE-2.0
14#
15# Unless required by applicable law or agreed to in writing, software
16# distributed under the License is distributed on an "AS IS" BASIS,
17# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18# See the License for the specific language governing permissions and
19# limitations under the License.
20
David Srbeckya0ef40d2021-04-25 21:15:01 +010021import sys, os, argparse, subprocess, shlex, re, concurrent.futures, multiprocessing
David Srbecky10132a02021-04-23 22:28:15 +010022
23def parse_args():
24 parser = argparse.ArgumentParser(description="Run libcore tests using the vogar testing tool.")
25 parser.add_argument('--mode', choices=['device', 'host', 'jvm'], required=True,
26 help='Specify where tests should be run.')
27 parser.add_argument('--variant', choices=['X32', 'X64'],
28 help='Which dalvikvm variant to execute with.')
David Srbeckya0ef40d2021-04-25 21:15:01 +010029 parser.add_argument('-j', '--jobs', type=int,
30 help='Number of tests to run simultaneously.')
David Srbecky10132a02021-04-23 22:28:15 +010031 parser.add_argument('--timeout', type=int,
32 help='How long to run the test before aborting (seconds).')
33 parser.add_argument('--debug', action='store_true',
34 help='Use debug version of ART (device|host only).')
35 parser.add_argument('--dry-run', action='store_true',
36 help='Print vogar command-line, but do not run.')
37 parser.add_argument('--no-getrandom', action='store_false', dest='getrandom',
38 help='Ignore failures from getrandom() (for kernel < 3.17).')
39 parser.add_argument('--no-jit', action='store_false', dest='jit',
40 help='Disable JIT (device|host only).')
41 parser.add_argument('--gcstress', action='store_true',
42 help='Enable GC stress configuration (device|host only).')
43 parser.add_argument('tests', nargs="*",
44 help='Name(s) of the test(s) to run')
45 return parser.parse_args()
46
47ART_TEST_ANDROID_ROOT = os.environ.get("ART_TEST_ANDROID_ROOT", "/system")
48ART_TEST_CHROOT = os.environ.get("ART_TEST_CHROOT")
49ANDROID_PRODUCT_OUT = os.environ.get("ANDROID_PRODUCT_OUT")
50
51LIBCORE_TEST_NAMES = [
David Srbeckya0ef40d2021-04-25 21:15:01 +010052 # Naive critical path optimization: Run the longest tests first.
53 "org.apache.harmony.tests.java.util", # 90min under gcstress
54 "libcore.java.lang", # 90min under gcstress
55 "jsr166", # 60min under gcstress
56 "libcore.java.util", # 60min under gcstress
57 "libcore.java.math", # 50min under gcstress
58 "org.apache.harmony.crypto", # 30min under gcstress
59 "org.apache.harmony.tests.java.io", # 30min under gcstress
60 "org.apache.harmony.tests.java.text", # 30min under gcstress
61 # Split highmemorytest to individual classes since it is too big.
62 "libcore.highmemorytest.java.text.DateFormatTest",
63 "libcore.highmemorytest.java.text.DecimalFormatTest",
64 "libcore.highmemorytest.java.text.SimpleDateFormatTest",
65 "libcore.highmemorytest.java.time.format.DateTimeFormatterTest",
66 "libcore.highmemorytest.java.util.CalendarTest",
67 "libcore.highmemorytest.java.util.CurrencyTest",
Victor Changae7335e2022-02-09 10:59:42 +000068 "libcore.highmemorytest.libcore.icu.SimpleDateFormatDataTest",
David Srbeckya0ef40d2021-04-25 21:15:01 +010069 # All other tests in alphabetical order.
David Srbecky10132a02021-04-23 22:28:15 +010070 "libcore.android.system",
71 "libcore.build",
72 "libcore.dalvik.system",
73 "libcore.java.awt",
David Srbecky10132a02021-04-23 22:28:15 +010074 "libcore.java.text",
David Srbecky10132a02021-04-23 22:28:15 +010075 "libcore.javax.crypto",
76 "libcore.javax.net",
77 "libcore.javax.security",
78 "libcore.javax.sql",
79 "libcore.javax.xml",
80 "libcore.libcore.icu",
81 "libcore.libcore.internal",
82 "libcore.libcore.io",
83 "libcore.libcore.net",
84 "libcore.libcore.reflect",
85 "libcore.libcore.util",
86 "libcore.sun.invoke",
David Srbecky10132a02021-04-23 22:28:15 +010087 "libcore.sun.misc",
David Srbeckya0ef40d2021-04-25 21:15:01 +010088 "libcore.sun.net",
David Srbecky10132a02021-04-23 22:28:15 +010089 "libcore.sun.security",
90 "libcore.sun.util",
91 "libcore.xml",
92 "org.apache.harmony.annotation",
David Srbecky10132a02021-04-23 22:28:15 +010093 "org.apache.harmony.luni",
94 "org.apache.harmony.nio",
95 "org.apache.harmony.regex",
96 "org.apache.harmony.testframework",
David Srbecky10132a02021-04-23 22:28:15 +010097 "org.apache.harmony.tests.java.lang",
98 "org.apache.harmony.tests.java.math",
David Srbecky10132a02021-04-23 22:28:15 +010099 "org.apache.harmony.tests.javax.security",
100 "tests.java.lang.String",
David Srbecky10132a02021-04-23 22:28:15 +0100101]
102# "org.apache.harmony.security", # We don't have rights to revert changes in case of failures.
103
104# Note: This must start with the CORE_IMG_JARS in Android.common_path.mk
105# because that's what we use for compiling the boot.art image.
106# It may contain additional modules from TEST_CORE_JARS.
107BOOT_CLASSPATH = [
108 "/apex/com.android.art/javalib/core-oj.jar",
109 "/apex/com.android.art/javalib/core-libart.jar",
110 "/apex/com.android.art/javalib/okhttp.jar",
111 "/apex/com.android.art/javalib/bouncycastle.jar",
112 "/apex/com.android.art/javalib/apache-xml.jar",
113 "/apex/com.android.i18n/javalib/core-icu4j.jar",
114 "/apex/com.android.conscrypt/javalib/conscrypt.jar",
115]
116
117CLASSPATH = ["core-tests", "jsr166-tests", "mockito-target"]
118
119def get_jar_filename(classpath):
120 base_path = (ANDROID_PRODUCT_OUT + "/../..") if ANDROID_PRODUCT_OUT else "out/target"
121 base_path = os.path.normpath(base_path) # Normalize ".." components for readability.
122 return f"{base_path}/common/obj/JAVA_LIBRARIES/{classpath}_intermediates/classes.jar"
123
124def get_timeout_secs():
125 default_timeout_secs = 600
David Srbecky2ce26fd2021-05-19 18:26:54 +0100126 if args.gcstress:
David Srbecky10132a02021-04-23 22:28:15 +0100127 default_timeout_secs = 1200
128 if args.debug:
129 default_timeout_secs = 1800
130 return args.timeout or default_timeout_secs
131
132def get_expected_failures():
133 failures = ["art/tools/libcore_failures.txt"]
134 if args.mode != "jvm":
135 if args.gcstress:
136 failures.append("art/tools/libcore_gcstress_failures.txt")
137 if args.gcstress and args.debug:
138 failures.append("art/tools/libcore_gcstress_debug_failures.txt")
David Srbecky680d7682021-05-04 16:56:37 +0100139 if args.debug and not args.gcstress and args.getrandom:
David Srbecky10132a02021-04-23 22:28:15 +0100140 failures.append("art/tools/libcore_debug_failures.txt")
141 if not args.getrandom:
142 failures.append("art/tools/libcore_fugu_failures.txt")
143 return failures
144
145def get_test_names():
146 if args.tests:
147 return args.tests
148 test_names = list(LIBCORE_TEST_NAMES)
149 # See b/78228743 and b/178351808.
150 if args.gcstress or args.debug or args.mode == "jvm":
David Srbeckya0ef40d2021-04-25 21:15:01 +0100151 test_names = list(t for t in test_names if not t.startswith("libcore.highmemorytest"))
David Srbecky10132a02021-04-23 22:28:15 +0100152 return test_names
153
David Srbeckya0ef40d2021-04-25 21:15:01 +0100154def get_vogar_command(test_name):
David Srbecky10132a02021-04-23 22:28:15 +0100155 cmd = ["vogar"]
156 if args.mode == "device":
157 cmd.append("--mode=device --vm-arg -Ximage:/apex/com.android.art/javalib/boot.art")
158 cmd.append("--vm-arg -Xbootclasspath:" + ":".join(BOOT_CLASSPATH))
159 if args.mode == "host":
160 # We explicitly give a wrong path for the image, to ensure vogar
161 # will create a boot image with the default compiler. Note that
162 # giving an existing image on host does not work because of
163 # classpath/resources differences when compiling the boot image.
164 cmd.append("--mode=host --vm-arg -Ximage:/non/existent/vogar.art")
165 if args.mode == "jvm":
166 cmd.append("--mode=jvm")
167 if args.variant:
168 cmd.append("--variant=" + args.variant)
169 if args.gcstress:
170 cmd.append("--vm-arg -Xgc:gcstress")
Sorin Basca24bdf642022-02-07 15:47:01 +0000171 cmd.append('--vm-arg -Djsr166.delay.factor="1.50"')
David Srbecky10132a02021-04-23 22:28:15 +0100172 if args.debug:
173 cmd.append("--vm-arg -XXlib:libartd.so --vm-arg -XX:SlowDebug=true")
174
175 if args.mode == "device":
176 if ART_TEST_CHROOT:
David Srbeckya0ef40d2021-04-25 21:15:01 +0100177 cmd.append(f"--chroot {ART_TEST_CHROOT} --device-dir=/tmp/vogar/test-{test_name}")
David Srbecky10132a02021-04-23 22:28:15 +0100178 else:
David Srbeckya0ef40d2021-04-25 21:15:01 +0100179 cmd.append("--device-dir=/data/local/tmp/vogar/test-{test_name}")
David Srbecky10132a02021-04-23 22:28:15 +0100180 cmd.append(f"--vm-command={ART_TEST_ANDROID_ROOT}/bin/art")
David Srbeckya0ef40d2021-04-25 21:15:01 +0100181 else:
182 cmd.append(f"--device-dir=/tmp/vogar/test-{test_name}")
David Srbecky10132a02021-04-23 22:28:15 +0100183
184 if args.mode != "jvm":
185 cmd.append("--timeout {}".format(get_timeout_secs()))
David Srbecky10132a02021-04-23 22:28:15 +0100186 cmd.append("--toolchain d8 --language CUR")
187 if args.jit:
188 cmd.append("--vm-arg -Xcompiler-option --vm-arg --compiler-filter=quicken")
189 cmd.append("--vm-arg -Xusejit:{}".format(str(args.jit).lower()))
190
David Srbecky10132a02021-04-23 22:28:15 +0100191 # Suppress color codes if not attached to a terminal
192 if not sys.stdout.isatty():
193 cmd.append("--no-color")
194
195 cmd.extend("--expectations " + f for f in get_expected_failures())
196 cmd.extend("--classpath " + get_jar_filename(cp) for cp in CLASSPATH)
David Srbeckya0ef40d2021-04-25 21:15:01 +0100197 cmd.append(test_name)
David Srbecky10132a02021-04-23 22:28:15 +0100198 return cmd
199
David Srbeckya0ef40d2021-04-25 21:15:01 +0100200def get_target_cpu_count():
201 adb_command = 'adb shell cat /sys/devices/system/cpu/present'
202 with subprocess.Popen(adb_command.split(),
203 stderr=subprocess.STDOUT,
204 stdout=subprocess.PIPE,
205 universal_newlines=True) as proc:
206 assert(proc.wait() == 0) # Check the exit code.
207 match = re.match(r'\d*-(\d*)', proc.stdout.read())
208 assert(match)
209 return int(match.group(1)) + 1 # Add one to convert from "last-index" to "count"
210
David Srbecky10132a02021-04-23 22:28:15 +0100211def main():
212 global args
213 args = parse_args()
214
215 if not os.path.exists('build/envsetup.sh'):
216 raise AssertionError("Script needs to be run at the root of the android tree")
217 for jar in map(get_jar_filename, CLASSPATH):
218 if not os.path.exists(jar):
219 raise AssertionError(f"Missing {jar}. Run buildbot-build.sh first.")
220
David Srbeckya0ef40d2021-04-25 21:15:01 +0100221 if not args.jobs:
David Srbeckyf75b8e92021-06-07 11:11:38 +0000222 if args.mode == "device":
223 args.jobs = get_target_cpu_count()
224 else:
225 args.jobs = multiprocessing.cpu_count()
226 if args.gcstress:
227 # TODO: Investigate and fix the underlying issues.
228 args.jobs = args.jobs // 2
David Srbeckya0ef40d2021-04-25 21:15:01 +0100229
230 def run_test(test_name):
231 cmd = " ".join(get_vogar_command(test_name))
232 if args.dry_run:
233 return test_name, cmd, "Dry-run: skipping execution", 0
234 with subprocess.Popen(shlex.split(cmd),
235 stderr=subprocess.STDOUT,
236 stdout=subprocess.PIPE,
237 universal_newlines=True) as proc:
238 return test_name, cmd, proc.communicate()[0], proc.wait()
239
David Srbeckye059ef12021-05-04 18:54:34 +0100240 failed_regex = re.compile(r"^.* FAIL \((?:EXEC_FAILED|ERROR)\)$", re.MULTILINE)
David Srbeckya0ef40d2021-04-25 21:15:01 +0100241 failed_tests, max_exit_code = [], 0
242 with concurrent.futures.ThreadPoolExecutor(max_workers=args.jobs) as pool:
243 futures = [pool.submit(run_test, test_name) for test_name in get_test_names()]
David Srbecky0849c1c2021-04-30 12:22:50 +0100244 print(f"Running {len(futures)} tasks on {args.jobs} core(s)...\n")
David Srbeckya0ef40d2021-04-25 21:15:01 +0100245 for i, future in enumerate(concurrent.futures.as_completed(futures)):
246 test_name, cmd, stdout, exit_code = future.result()
David Srbeckya0ef40d2021-04-25 21:15:01 +0100247 if exit_code != 0 or args.dry_run:
248 print(cmd)
David Srbecky0849c1c2021-04-30 12:22:50 +0100249 print(stdout.strip())
David Srbeckya0ef40d2021-04-25 21:15:01 +0100250 else:
251 print(stdout.strip().split("\n")[-1]) # Vogar final summary line.
David Srbecky0849c1c2021-04-30 12:22:50 +0100252 failed_match = failed_regex.findall(stdout)
253 failed_tests.extend(failed_match)
David Srbeckya0ef40d2021-04-25 21:15:01 +0100254 max_exit_code = max(max_exit_code, exit_code)
David Srbecky0849c1c2021-04-30 12:22:50 +0100255 result = "PASSED" if exit_code == 0 else f"FAILED ({len(failed_match)} test(s) failed)"
256 print(f"[{i+1}/{len(futures)}] Test set {test_name} {result}\n")
257 print(f"Overall, {len(failed_tests)} test(s) failed:")
258 print("\n".join(failed_tests))
David Srbeckya0ef40d2021-04-25 21:15:01 +0100259 sys.exit(max_exit_code)
David Srbecky10132a02021-04-23 22:28:15 +0100260
261if __name__ == '__main__':
262 main()