• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#! /usr/bin/env python3
2#
3# Copyright 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.
16
17# Regenerate some ART test related files.
18
19# This script handles only a subset of ART run-tests at the moment; additional
20# cases will be added later.
21
22import argparse
23import copy
24import collections
25import itertools
26import json
27import logging
28import os
29import re
30import sys
31import textwrap
32import xml.dom.minidom
33
34logging.basicConfig(format='%(levelname)s: %(message)s')
35
36ME = os.path.basename(sys.argv[0])
37
38# Common advisory placed at the top of all generated files.
39ADVISORY = f"Generated by `{ME}`. Do not edit manually."
40
41# Default indentation unit.
42INDENT = "  "
43
44# Indentation unit for XML files.
45XML_INDENT = "    "
46
47def reindent(str, indent = ""):
48  """Reindent literal string while removing common leading spaces."""
49  return textwrap.indent(textwrap.dedent(str), indent)
50
51def copyright_header_text(year):
52  """Return the copyright header text used in XML files."""
53  return reindent(f"""\
54    Copyright (C) {year} The Android Open Source Project
55
56        Licensed under the Apache License, Version 2.0 (the "License");
57        you may not use this file except in compliance with the License.
58        You may obtain a copy of the License at
59
60             http://www.apache.org/licenses/LICENSE-2.0
61
62        Unless required by applicable law or agreed to in writing, software
63        distributed under the License is distributed on an "AS IS" BASIS,
64        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
65        See the License for the specific language governing permissions and
66        limitations under the License.
67    """, " ")
68
69def split_list(l, n):
70  """Return a list of `n` sublists of (contiguous) elements of list `l`."""
71  assert n > 0
72  (d, m) = divmod(len(l), n)
73  # If the length of `l` is divisible by `n`, use that that divisor (`d`) as size of each sublist;
74  # otherwise, the next integer value (`d + 1`).
75  s = d if m == 0 else d + 1
76  result = [l[i:i + s] for i in range(0, len(l), s)]
77  assert len(result) == n
78  return result
79
80# The prefix used in the Soong module name of all ART run-tests.
81ART_RUN_TEST_MODULE_NAME_PREFIX = "art-run-test-"
82
83# Number of shards used to declare ART run-tests in the sharded ART MTS test plan.
84NUM_MTS_ART_RUN_TEST_SHARDS = 1
85
86# Name of the ART MTS test list containing "eng-only" test modules,
87# which require a device-under-test running a `userdebug` or `eng`
88# build.
89ENG_ONLY_TEST_LIST_NAME = "mts-art-tests-list-eng-only"
90
91# Name of Lint baseline filename used in certain ART run-tests,
92# e.g. for the `NewApi` check (see e.g. b/268261262).
93LINT_BASELINE_FILENAME = "lint-baseline.xml"
94
95# Curated list of tests that have a custom `run` script, but that are
96# known to work fine with the default test execution strategy (i.e.
97# when ignoring their `run` script), even if not exactly as they would
98# with the original ART run-test harness.
99runnable_test_exceptions = frozenset([
100  "059-finalizer-throw",
101  "080-oom-throw",
102  "133-static-invoke-super",
103  "159-app-image-fields",
104  "160-read-barrier-stress",
105  "163-app-image-methods",
106  "165-lock-owner-proxy",
107  "168-vmstack-annotated",
108  "176-app-image-string",
109  "304-method-tracing",
110  "628-vdex",
111  "643-checker-bogus-ic",
112  "676-proxy-jit-at-first-use",
113  "677-fsi2",
114  "678-quickening",
115  "818-clinit-nterp",
116  "821-madvise-willneed",
117  "1004-checker-volatile-ref-load",
118  "1338-gc-no-los",
119])
120
121# Known slow tests, for which the timeout value is raised.
122known_slow_tests = frozenset([
123  "080-oom-throw",
124  "099-vmdebug",
125  "109-suspend-check",
126  "175-alloc-big-bignums",
127])
128
129# Known failing ART run-tests.
130# TODO(rpl): Investigate and address the causes of failures.
131known_failing_tests = frozenset([
132  "004-SignalTest",
133  "004-UnsafeTest",
134  "051-thread",
135  # 055-enum-performance is time sensitive and disabled in test.py
136  "055-enum-performance",
137  "086-null-super",
138  "087-gc-after-link",
139  "136-daemon-jni-shutdown",
140  "139-register-natives",
141  "148-multithread-gc-annotations",
142  "149-suspend-all-stress",
143  "150-loadlibrary",
144  "154-gc-loop",
145  "169-threadgroup-jni",
146  "177-visibly-initialized-deadlock",
147  "179-nonvirtual-jni",
148  "203-multi-checkpoint",
149  "305-other-fault-handler",
150  # 449-checker-bce: Dependency on `libarttest`.
151  "449-checker-bce",
152  "454-get-vreg",
153  "461-get-reference-vreg",
154  "466-get-live-vreg",
155  "497-inlining-and-class-loader",
156  "530-regression-lse",
157  "555-UnsafeGetLong-regression",
158  # 596-monitor-inflation: Dependency on `libarttest`.
159  "596-monitor-inflation",
160  "602-deoptimizeable",
161  "604-hot-static-interface",
162  "616-cha-native",
163  "616-cha-regression-proxy-method",
164  # 623-checker-loop-regressions: Dependency on `libarttest`.
165  "623-checker-loop-regressions",
166  "626-set-resolved-string",
167  "642-fp-callees",
168  "647-jni-get-field-id",
169  "655-jit-clinit",
170  "656-loop-deopt",
171  "664-aget-verifier",
172  # 680-checker-deopt-dex-pc-0: Dependency on `libarttest`.
173  "680-checker-deopt-dex-pc-0",
174  "685-deoptimizeable",
175  "687-deopt",
176  "693-vdex-inmem-loader-evict",
177  "708-jit-cache-churn",
178  # 716-jli-jit-samples: Dependency on `libarttest`.
179  "716-jli-jit-samples",
180  "717-integer-value-of",
181  "720-thread-priority",
182  # 730-cha-deopt: Fails with:
183  #
184  #   Test command execution failed with status FAILED: CommandResult: exit code=1, out=, err=Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
185  #           at Main.main(Main.java:24)
186  #
187  "730-cha-deopt",
188  # 813-fp-args: Dependency on `libarttest`.
189  "813-fp-args",
190  # 821-many-args: Fails with:
191  #
192  #   Test command execution failed with status FAILED: CommandResult: exit code=1, out=, err=Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
193  #           at Main.main(Main.java:20)
194  #
195  "821-many-args",
196  # 823-cha-inlining: Dependency on `libarttest`.
197  "823-cha-inlining",
198  # 826-infinite-loop: The test expects an argument passed to `Main.main` (the test library,
199  # usually `arttestd` or `arttest)`, but the ART run-test TradeFed test runner
200  # (`com.android.tradefed.testtype.ArtRunTest`) does not implement this yet.
201  "826-infinite-loop",
202  # 832-cha-recursive: Dependency on `libarttest`.
203  "832-cha-recursive",
204  # 837-deopt: Dependency on `libarttest`.
205  "837-deopt",
206  # 844-exception: Dependency on `libarttest`.
207  "844-exception",
208  # 844-exception2: Dependency on `libarttest`.
209  "844-exception2",
210  # 966-default-conflict: Dependency on `libarttest`.
211  "966-default-conflict",
212  # These tests need native code.
213  "993-breakpoints-non-debuggable",
214  # 1002-notify-startup: Dependency on `libarttest` + custom `check` script.
215  "1002-notify-startup",
216  "1337-gc-coverage",
217  "1339-dead-reference-safe",
218  "1945-proxy-method-arguments",
219  "2011-stack-walk-concurrent-instrument",
220  "2033-shutdown-mechanics",
221  "2036-jni-filechannel",
222  "2037-thread-name-inherit",
223  # 2040-huge-native-alloc: Fails with:
224  #
225  #   Test command execution failed with status FAILED: CommandResult: exit code=1, out=, err=Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
226  #           at Main.main(Main.java:56)
227  #
228  "2040-huge-native-alloc",
229  "2235-JdkUnsafeTest",
230  "2243-single-step-default",
231  "2262-miranda-methods",
232  "2262-default-conflict-methods",
233  # 2275-pthread-name: Dependency on `libarttest`.
234  "2275-pthread-name",
235])
236
237# These ART run-tests are new and have not had enough post-submit runs
238# to meet pre-submit SLOs. Monitor their post-submit runs before
239# removing them from this set (in order to promote them to
240# presubmits).
241postsubmit_only_tests = frozenset([
242])
243
244known_failing_on_hwasan_tests = frozenset([
245  "CtsJdwpTestCases", # times out
246  # apexd fails to unmount com.android.runtime on ASan builds.
247  "art_standalone_dexopt_chroot_setup_tests",
248])
249
250# ART gtests that do not need root access to the device.
251art_gtest_user_module_names = [
252    "art_libnativebridge_cts_tests",
253    "art_standalone_artd_tests",
254    "art_standalone_cmdline_tests",
255    "art_standalone_compiler_tests",
256    "art_standalone_dex2oat_cts_tests",
257    "art_standalone_dex2oat_tests",
258    "art_standalone_dexdump_tests",
259    "art_standalone_dexlist_tests",
260    "art_standalone_libartbase_tests",
261    "art_standalone_libartpalette_tests",
262    "art_standalone_libartservice_tests",
263    "art_standalone_libarttools_tests",
264    "art_standalone_libdexfile_support_tests",
265    "art_standalone_libdexfile_tests",
266    "art_standalone_libprofile_tests",
267    "art_standalone_oatdump_tests",
268    "art_standalone_odrefresh_tests",
269    "art_standalone_runtime_tests",
270    "art_standalone_sigchain_tests",
271    "libnativebridge-lazy-tests",
272    "libnativebridge-tests",
273    "libnativeloader_test",
274]
275
276# ART gtests that need root access to the device.
277art_gtest_eng_only_module_names = [
278    "art_standalone_dexopt_chroot_setup_tests",
279    "art_standalone_dexoptanalyzer_tests",
280    "art_standalone_profman_tests",
281    "libnativeloader_e2e_tests",
282]
283
284# All supported ART gtests.
285art_gtest_module_names = sorted(art_gtest_user_module_names + art_gtest_eng_only_module_names)
286
287# These ART gtests are new and have not had enough post-submit runs
288# to meet pre-submit SLOs. Monitor their post-submit runs before
289# removing them from this set (in order to promote them to
290# presubmits).
291art_gtest_postsubmit_only_module_names = [
292]
293
294# ART gtests not supported in MTS.
295art_gtest_modules_excluded_from_mts = [
296    # TODO(b/347717488): Consider adding this test to ART MTS.
297    "libnativebridge-tests",
298]
299
300# ART gtests supported in MTS that do not need root access to the device.
301art_gtest_mts_user_module_names = [t for t in art_gtest_user_module_names
302                                   if t not in art_gtest_modules_excluded_from_mts]
303
304# ART gtests supported in presubmits.
305art_gtest_presubmit_module_names = [t for t in art_gtest_module_names
306                                    if t not in art_gtest_postsubmit_only_module_names]
307
308# ART gtests supported in Mainline presubmits.
309art_gtest_mainline_presubmit_module_names = copy.copy(art_gtest_presubmit_module_names)
310
311# ART gtests supported in postsubmits.
312unknown_art_gtest_postsubmit_only_module_names = [t for t in art_gtest_postsubmit_only_module_names
313                                                  if t not in art_gtest_module_names]
314if unknown_art_gtest_postsubmit_only_module_names:
315  logging.error(textwrap.dedent("""\
316  The following `art_gtest_postsubmit_only_module_names` elements are not part of
317  `art_gtest_module_names`: """) + str(unknown_art_gtest_postsubmit_only_module_names))
318  sys.exit(1)
319art_gtest_postsubmit_module_names = copy.copy(art_gtest_postsubmit_only_module_names)
320
321# Tests exhibiting a flaky behavior, currently exluded from MTS for
322# the stake of stability / confidence (b/209958457).
323flaky_tests_excluded_from_mts = {
324    "CtsLibcoreFileIOTestCases": [
325        ("android.cts.FileChannelInterProcessLockTest#" + m) for m in [
326         "test_lockJJZ_Exclusive_asyncChannel",
327         "test_lockJJZ_Exclusive_syncChannel",
328         "test_lock_differentChannelTypes",
329         "test_lockJJZ_Shared_asyncChannel",
330         "test_lockJJZ_Shared_syncChannel",
331        ]
332    ],
333    "CtsLibcoreTestCases": [
334        ("com.android.org.conscrypt.javax.net.ssl.SSLSocketVersionCompatibilityTest#" + m + c)
335        for (m, c) in itertools.product(
336            [
337                "test_SSLSocket_interrupt_read_withoutAutoClose",
338                "test_SSLSocket_setSoWriteTimeout",
339            ],
340            [
341                "[0: TLSv1.2 client, TLSv1.2 server]",
342                "[1: TLSv1.2 client, TLSv1.3 server]",
343                "[2: TLSv1.3 client, TLSv1.2 server]",
344                "[3: TLSv1.3 client, TLSv1.3 server]",
345            ]
346        )
347    ] + [
348        ("libcore.dalvik.system.DelegateLastClassLoaderTest#" + m) for m in [
349            "testLookupOrderNodelegate_getResource",
350            "testLookupOrder_getResource",
351        ]
352    ]
353}
354
355# Tests excluded from all test mapping test groups.
356#
357# Example of admissible values in this dictionary:
358#
359#   "art_standalone_cmdline_tests": ["CmdlineParserTest#TestCompilerOption"],
360#   "art_standalone_dexopt_chroot_setup_tests": ["DexoptChrootSetupTest#HelloWorld"],
361#
362failing_tests_excluded_from_test_mapping = {
363  # Empty.
364}
365
366# Tests failing because of linking issues, currently exluded from MTS
367# and Mainline Presubmits to minimize noise in continuous runs while
368# we investigate.
369#
370# Example of admissible values in this dictionary: same as for
371# `failing_tests_excluded_from_test_mapping` (see above).
372#
373# TODO(b/247108425): Address the linking issues and re-enable these
374# tests.
375failing_tests_excluded_from_mts_and_mainline_presubmits = {
376    "art_standalone_compiler_tests": ["JniCompilerTest*"],
377    "art_standalone_libartpalette_tests": ["PaletteClientJniTest*"],
378}
379
380failing_tests_excluded_from_mainline_presubmits = (
381  failing_tests_excluded_from_test_mapping |
382  failing_tests_excluded_from_mts_and_mainline_presubmits
383)
384
385# Is `run_test` a Checker test (i.e. a test containing Checker
386# assertions)?
387def is_checker_test(run_test):
388  return re.match("^[0-9]+-checker-", run_test)
389
390def gen_mts_test_list_file(tests, test_list_file, copyright_year, configuration_description,
391                           tests_description, comments = []):
392  """Generate an ART MTS test list file."""
393  root = xml.dom.minidom.Document()
394
395  advisory_header = root.createComment(f" {ADVISORY} ")
396  root.appendChild(advisory_header)
397  copyright_header = root.createComment(copyright_header_text(copyright_year))
398  root.appendChild(copyright_header)
399
400  configuration = root.createElement("configuration")
401  root.appendChild(configuration)
402  configuration.setAttribute("description", configuration_description)
403
404  def append_option(name, value):
405    option = root.createElement("option")
406    option.setAttribute("name", name)
407    option.setAttribute("value", value)
408    configuration.appendChild(option)
409
410  def append_comment(comment):
411    xml_comment = root.createComment(f" {comment} ")
412    configuration.appendChild(xml_comment)
413
414  # Test declarations.
415  # ------------------
416
417  test_declarations_comments = [tests_description + "."]
418  test_declarations_comments.extend(comments)
419  for c in test_declarations_comments:
420    append_comment(c)
421  for t in tests:
422    append_option("compatibility:include-filter", t)
423
424  # `MainlineTestModuleController` configurations.
425  # ----------------------------------------------
426
427  module_controller_configuration_comments = [
428      f"Enable MainlineTestModuleController for {tests_description}."]
429  module_controller_configuration_comments.extend(comments)
430  for c in module_controller_configuration_comments:
431    append_comment(c)
432  for t in tests:
433    append_option("compatibility:module-arg", f"{t}:enable:true")
434  for t in tests:
435    if t in ["CtsLibcoreTestCases", "CtsLibcoreOjTestCases"]:
436      append_comment("core-test-mode=mts tells ExpectationBasedFilter to exclude @NonMts Tests")
437      append_option("compatibility:module-arg", f"{t}:instrumentation-arg:core-test-mode:=mts")
438
439  xml_str = root.toprettyxml(indent = XML_INDENT, encoding = "utf-8")
440
441  with open(test_list_file, "wb") as f:
442    logging.debug(f"Writing `{test_list_file}`.")
443    f.write(xml_str)
444
445class Generator:
446  def __init__(self, top_dir):
447    """Generator of ART test files for an Android source tree anchored at `top_dir`."""
448    # Path to the Android top source tree.
449    self.top_dir = top_dir
450    # Path to the ART directory
451    self.art_dir = os.path.join(top_dir, "art")
452    # Path to the ART tests top-level directory.
453    self.art_test_dir = os.path.join(self.art_dir, "test")
454    # Path to the MTS configuration directory.
455    self.mts_config_dir = os.path.join(
456        top_dir, "test", "mts", "tools", "mts-tradefed", "res", "config")
457    # Path to the ART JVM TI CTS tests top-level directory.
458    self.jvmti_cts_test_dir = os.path.join(top_dir, "cts/hostsidetests/jvmti/run-tests")
459
460  # Return the list of ART run-tests (in short form, i.e. `001-HelloWorld`,
461  # not `art-run-test-001-HelloWorld`).
462  def enumerate_run_tests(self):
463    return sorted([run_test
464                   for run_test in os.listdir(self.art_test_dir)
465                   if re.match("^[0-9]{3,}-", run_test)])
466
467  # Return the list of ART JVM TI CTS tests.
468  def enumerate_jvmti_cts_tests(self):
469    return sorted([re.sub(r"test-(\d+)", r"CtsJvmtiRunTest\1HostTestCases", cts_jvmti_test_dir)
470                   for cts_jvmti_test_dir in os.listdir(self.jvmti_cts_test_dir)
471                   if re.match(r"^test-\d+$", cts_jvmti_test_dir)])
472
473  # Return the metadata of a test, if any.
474  def get_test_metadata(self, run_test):
475    run_test_path = os.path.join(self.art_test_dir, run_test)
476    metadata_file = os.path.join(run_test_path, "test-metadata.json")
477    metadata = {}
478    if os.path.exists(metadata_file):
479      with open(metadata_file, "r") as f:
480        try:
481          metadata = json.load(f)
482        except json.decoder.JSONDecodeError:
483          logging.error(f"Unable to parse test metadata file `{metadata_file}`")
484          raise
485    return metadata
486
487  # Can the build script of `run_test` be safely ignored?
488  def can_ignore_build_script(self, run_test):
489    # Check whether there are test metadata with build parameters
490    # enabling us to safely ignore the build script.
491    metadata = self.get_test_metadata(run_test)
492    build_param = metadata.get("build-param", {})
493    # Ignore build scripts that are just about preventing building for
494    # the JVM and/or using VarHandles (Soong builds JARs with
495    # VarHandle support by default (i.e. by using an API level greater
496    # or equal to 28), so we can ignore build scripts that just
497    # request support for this feature.)
498    experimental_var_handles = {"experimental": "var-handles"}
499    jvm_supported_false = {"jvm-supported": "false"}
500    if (build_param == experimental_var_handles or
501        build_param == jvm_supported_false or
502        build_param == experimental_var_handles | jvm_supported_false):
503      return True
504    return False
505
506  # Can `run_test` be built with Soong?
507  # TODO(b/147814778): Add build support for more tests.
508  def is_soong_buildable(self, run_test):
509    run_test_path = os.path.join(self.art_test_dir, run_test)
510
511    # Skip tests with non-default build rules, unless these build
512    # rules can be safely ignored.
513    if (os.path.isfile(os.path.join(run_test_path, "generate-sources")) or
514        os.path.isfile(os.path.join(run_test_path, "javac_post.sh"))):
515      return False
516    if os.path.isfile(os.path.join(run_test_path, "build.py")):
517      if not self.can_ignore_build_script(run_test):
518        return False
519    # Skip tests with sources outside the `src` directory.
520    for subdir in ["jasmin",
521                   "jasmin-multidex",
522                   "smali",
523                   "smali-ex",
524                   "smali-multidex",
525                   "src-aotex",
526                   "src-bcpex",
527                   "src-ex",
528                   "src-ex2",
529                   "src-multidex"]:
530      if os.path.isdir(os.path.join(run_test_path, subdir)):
531        return False
532    # Skip tests that have both an `src` directory and an `src-art` directory.
533    if os.path.isdir(os.path.join(run_test_path, "src")) and \
534       os.path.isdir(os.path.join(run_test_path, "src-art")):
535        return False
536    # Skip tests that have neither an `src` directory nor an `src-art` directory.
537    if not os.path.isdir(os.path.join(run_test_path, "src")) and \
538       not os.path.isdir(os.path.join(run_test_path, "src-art")):
539      return False
540    # Skip test with a copy of `sun.misc.Unsafe`.
541    if os.path.isfile(os.path.join(run_test_path, "src", "sun", "misc", "Unsafe.java")):
542      return False
543    # Skip tests with Hidden API specs.
544    if os.path.isfile(os.path.join(run_test_path, "hiddenapi-flags.csv")):
545      return False
546    # All other tests are considered buildable.
547    return True
548
549  # Can the run script of `run_test` be safely ignored?
550  def can_ignore_run_script(self, run_test):
551    # Unconditionally consider some identified tests that have a
552    # (not-yet-handled) custom `run` script as runnable.
553    #
554    # TODO(rpl): Get rid of this exception mechanism by supporting
555    # these tests' `run` scripts properly.
556    if run_test in runnable_test_exceptions:
557      return True
558    # Check whether there are test metadata with run parameters
559    # enabling us to safely ignore the run script.
560    metadata = self.get_test_metadata(run_test)
561    run_param = metadata.get("run-param", {})
562    if run_param.get("default-run", ""):
563      return True
564    return False
565
566  # Generate a Blueprint property group as a string, i.e. something looking like
567  # this:
568  #
569  #   ```
570  #       <group_name>: {
571  #         <key0>: "<value0>",
572  #         ...
573  #         <keyN>: "<valueN>",
574  #       }
575  #   ```
576  #
577  # where `(key0, value0), ..., (keyN, valueN)` are key-value pairs in `props`.
578  def gen_prop_group(self, group_name, props):
579    props_joined = """,
580            """.join([f"{k}: \"{v}\"" for (k, v) in props.items()])
581    return f"""
582          {group_name}: {{
583              {props_joined},
584          }},"""
585
586  def gen_libs_list_impl(self, library_type, libraries):
587    if len(libraries) == 0:
588      return ""
589    libraries_joined = """,
590              """.join(libraries)
591    return f"""
592          {library_type}: [
593              {libraries_joined},
594          ],"""
595
596  def gen_libs_list(self, libraries):
597    return self.gen_libs_list_impl("libs", libraries);
598
599  def gen_static_libs_list(self, libraries):
600    return self.gen_libs_list_impl("static_libs", libraries);
601
602  def gen_java_library_rule(self, name, src_dir, libraries, extra_props):
603    return f"""\
604
605
606      // Library with {src_dir}/ sources for the test.
607      java_library {{
608          name: "{name}",
609          defaults: ["art-run-test-defaults"],{self.gen_libs_list(libraries)}
610          srcs: ["{src_dir}/**/*.java"],{extra_props}
611      }}"""
612
613  # Can `run_test` be succesfully run with TradeFed?
614  # TODO(b/147812905): Add run-time support for more tests.
615  def is_tradefed_runnable(self, run_test):
616    run_test_path = os.path.join(self.art_test_dir, run_test)
617
618    # Skip tests with non-default run rules, unless these run rules
619    # can be safely ignored.
620    if os.path.isfile(os.path.join(run_test_path, "run.py")):
621      if not self.can_ignore_run_script(run_test):
622        return False
623    # Skip tests known to fail.
624    if run_test in known_failing_tests:
625      return False
626    # All other tests are considered runnable.
627    return True
628
629  def is_slow(self, run_test):
630    return run_test in known_slow_tests
631
632  def regen_bp_files(self, run_tests, buildable_tests):
633    for run_test in run_tests:
634      # Remove any previously generated file.
635      bp_file = os.path.join(self.art_test_dir, run_test, "Android.bp")
636      if os.path.exists(bp_file):
637        logging.debug(f"Removing `{bp_file}`.")
638        os.remove(bp_file)
639
640    for run_test in buildable_tests:
641      self.regen_bp_file(run_test)
642
643  def regen_bp_file(self, run_test):
644    """Regenerate Blueprint file for an ART run-test."""
645
646    run_test_path = os.path.join(self.art_test_dir, run_test)
647    bp_file = os.path.join(run_test_path, "Android.bp")
648
649    # Optional test metadata (JSON file).
650    metadata = self.get_test_metadata(run_test)
651    test_suites = metadata.get("test_suites", [])
652    is_cts_test = "cts" in test_suites
653    is_mcts_test = "mcts-art" in test_suites
654
655    # For now we make it mandatory for an ART CTS test to be an ART
656    # MCTS test and vice versa.
657    if is_cts_test != is_mcts_test:
658      (present, absent) = ("mts", "mcts-art") if is_cts_test else ("mcts-art", "mts")
659      logging.error(f"Inconsistent test suites state in metadata for ART run-test `{run_test}`: " +
660                    f"`test_suites` contains `{present}` but not `{absent}`")
661      sys.exit(1)
662
663    # Do not package non-runnable ART run-tests in ART MTS (see b/363075236).
664    if self.is_tradefed_runnable(run_test):
665      test_suites.append("mts-art")
666
667    run_test_module_name = ART_RUN_TEST_MODULE_NAME_PREFIX + run_test
668
669    # Set the test configuration template.
670    if self.is_tradefed_runnable(run_test):
671      if is_cts_test:
672        test_config_template = "art-run-test-target-cts-template"
673      elif self.is_slow(run_test):
674        test_config_template = "art-run-test-target-slow-template"
675      else:
676        test_config_template = "art-run-test-target-template"
677    else:
678      test_config_template = "art-run-test-target-no-test-suite-tag-template"
679
680    # Define the `test_suites` property, if test suites are present in
681    # the test's metadata.
682    test_suites_prop = ""
683    if test_suites:
684      test_suites_joined = """,
685              """.join([f"\"{s}\"" for s in test_suites])
686      test_suites_prop = f"""\
687
688          test_suites: [
689              {test_suites_joined},
690          ],"""
691
692    include_srcs_prop = ""
693    if is_checker_test(run_test):
694      include_srcs_prop = """\
695
696          // Include the Java source files in the test's artifacts, to make Checker assertions
697          // available to the TradeFed test runner.
698          include_srcs: true,"""
699
700    # Set the version of the SDK to compile the Java test module
701    # against, if needed.
702    sdk_version_prop = ""
703    if is_cts_test:
704      # Have CTS and MCTS test modules use the test API
705      # (`test_current`) so that they do not depend on the framework
706      # private platform API (`private`), which is the default.
707      sdk_version_prop = """
708          sdk_version: "test_current","""
709
710    # The default source directory is `src`, except if `src-art` exists.
711    if os.path.isdir(os.path.join(run_test_path, "src-art")):
712      source_dir = "src-art"
713    else:
714      source_dir = "src"
715
716    src_library_rules = []
717    test_libraries = []
718    extra_props = ""
719    # Honor the Lint baseline file, if present.
720    if os.path.isfile(os.path.join(run_test_path, LINT_BASELINE_FILENAME)):
721      extra_props += self.gen_prop_group("lint", {"baseline_filename": LINT_BASELINE_FILENAME})
722    if os.path.isdir(os.path.join(run_test_path, "src2")):
723      test_library = f"{run_test_module_name}-{source_dir}"
724      src_library_rules.append(
725          self.gen_java_library_rule(test_library, source_dir, test_libraries, extra_props))
726      test_libraries.append(f"\"{test_library}\"")
727      source_dir = "src2"
728
729    with open(bp_file, "w") as f:
730      logging.debug(f"Writing `{bp_file}`.")
731      f.write(textwrap.dedent(f"""\
732      // {ADVISORY}
733
734      // Build rules for ART run-test `{run_test}`.
735
736      package {{
737          // See: http://go/android-license-faq
738          // A large-scale-change added 'default_applicable_licenses' to import
739          // all of the 'license_kinds' from "art_license"
740          // to get the below license kinds:
741          //   SPDX-license-identifier-Apache-2.0
742          default_applicable_licenses: ["art_license"],
743      }}{''.join(src_library_rules)}
744
745      // Test's Dex code.
746      java_test {{
747          name: "{run_test_module_name}",
748          defaults: ["art-run-test-defaults"],
749          test_config_template: ":{test_config_template}",
750          srcs: ["{source_dir}/**/*.java"],{self.gen_static_libs_list(test_libraries)}
751          data: [
752              ":{run_test_module_name}-expected-stdout",
753              ":{run_test_module_name}-expected-stderr",
754          ],{test_suites_prop}{include_srcs_prop}{sdk_version_prop}
755      }}
756      """))
757
758      def add_expected_output_genrule(type_str):
759        type_str_long = "standard output" if type_str == "stdout" else "standard error"
760        in_file = os.path.join(run_test_path, f"expected-{type_str}.txt")
761        if os.path.islink(in_file):
762          # Genrules are sandboxed, so if we just added the symlink to the srcs list, it would
763          # be a dangling symlink in the sandbox. Instead, if we see a symlink, depend on the
764          # genrule from the test that the symlink is pointing to instead of the symlink itself.
765          link_target = os.readlink(in_file)
766          basename = os.path.basename(in_file)
767          match = re.fullmatch('\.\./([a-zA-Z0-9_-]+)/' + re.escape(basename), link_target)
768          if not match:
769            sys.exit(f"Error: expected symlink to be '../something/{basename}', got {link_target}")
770          f.write(textwrap.dedent(f"""\
771
772            // Test's expected {type_str_long}.
773            genrule {{
774                name: "{run_test_module_name}-expected-{type_str}",
775                out: ["{run_test_module_name}-expected-{type_str}.txt"],
776                srcs: [":{ART_RUN_TEST_MODULE_NAME_PREFIX}{match.group(1)}-expected-{type_str}"],
777                cmd: "cp -f $(in) $(out)",
778            }}
779          """))
780        else:
781          f.write(textwrap.dedent(f"""\
782
783            // Test's expected {type_str_long}.
784            genrule {{
785                name: "{run_test_module_name}-expected-{type_str}",
786                out: ["{run_test_module_name}-expected-{type_str}.txt"],
787                srcs: ["expected-{type_str}.txt"],
788                cmd: "cp -f $(in) $(out)",
789            }}
790          """))
791
792      add_expected_output_genrule("stdout")
793      add_expected_output_genrule("stderr")
794
795
796  def regen_test_mapping_file(self, art_run_tests):
797    """Regenerate ART's `TEST_MAPPING`."""
798
799    # See go/test-mapping#attributes and
800    # https://source.android.com/docs/core/tests/development/test-mapping
801    # for more information about Test Mapping test groups.
802
803    # ART run-tests used in `*presubmit` test groups, used both in pre- and post-submit runs.
804    presubmit_run_test_module_names = [ART_RUN_TEST_MODULE_NAME_PREFIX + t
805                                       for t in art_run_tests
806                                       if t not in postsubmit_only_tests]
807    # ART run-tests used in the `postsubmit` test group, used in post-submit runs only.
808    postsubmit_run_test_module_names = [ART_RUN_TEST_MODULE_NAME_PREFIX + t
809                                        for t in art_run_tests
810                                        if t in postsubmit_only_tests]
811
812    def gen_tests_dict(tests, excluded_test_cases = {}, excluded_test_modules = [], suffix = ""):
813      return [
814          ({"name": t + suffix,
815            "options": [
816                {"exclude-filter": e}
817                for e in excluded_test_cases[t]
818            ]}
819           if t in excluded_test_cases
820           else {"name": t + suffix})
821          for t in tests
822          if t not in excluded_test_modules
823      ]
824
825    # Mainline presubmits.
826    mainline_presubmit_apex_suffix = "[com.google.android.art.apex]"
827    mainline_other_presubmit_tests = []
828    mainline_presubmit_tests = (mainline_other_presubmit_tests + presubmit_run_test_module_names +
829                                art_gtest_mainline_presubmit_module_names)
830    mainline_presubmit_tests_dict = \
831      gen_tests_dict(mainline_presubmit_tests,
832                     failing_tests_excluded_from_mainline_presubmits,
833                     [],
834                     mainline_presubmit_apex_suffix)
835
836    # ART mainline presubmits tests without APEX suffix
837    art_mainline_presubmit_tests_dict = \
838        gen_tests_dict(mainline_presubmit_tests,
839                       failing_tests_excluded_from_mainline_presubmits,
840                       [],
841                       "")
842
843    # Android Virtualization Framework presubmits
844    avf_presubmit_tests = ["ComposHostTestCases"]
845    avf_presubmit_tests_dict = gen_tests_dict(avf_presubmit_tests,
846                                              failing_tests_excluded_from_test_mapping)
847
848    # Presubmits.
849    other_presubmit_tests = [
850        "ArtServiceTests",
851        "BootImageProfileTest",
852        "CtsJdwpTestCases",
853        "art-apex-update-rollback",
854        "art_standalone_dexpreopt_tests",
855    ]
856    presubmit_tests = (other_presubmit_tests + presubmit_run_test_module_names +
857                       art_gtest_presubmit_module_names)
858    presubmit_tests_dict = gen_tests_dict(presubmit_tests,
859                                          failing_tests_excluded_from_test_mapping)
860    hwasan_presubmit_tests_dict = gen_tests_dict(presubmit_tests,
861                                                 failing_tests_excluded_from_test_mapping,
862                                                 known_failing_on_hwasan_tests)
863
864    # Postsubmits.
865    postsubmit_tests = postsubmit_run_test_module_names + art_gtest_postsubmit_module_names
866    postsubmit_tests_dict = [{"name": t} for t in postsubmit_tests]
867    postsubmit_tests_dict = gen_tests_dict(postsubmit_tests,
868                                           failing_tests_excluded_from_test_mapping)
869
870    # Use an `OrderedDict` container to preserve the order in which items are inserted.
871    # Do not produce an entry for a test group if it is empty.
872    test_mapping_dict = collections.OrderedDict([
873        (test_group_name, test_group_dict)
874        for (test_group_name, test_group_dict)
875        in [
876            ("art-mainline-presubmit", art_mainline_presubmit_tests_dict),
877            ("mainline-presubmit", mainline_presubmit_tests_dict),
878            ("presubmit", presubmit_tests_dict),
879            ("hwasan-presubmit", hwasan_presubmit_tests_dict),
880            ("avf-presubmit", avf_presubmit_tests_dict),
881            ("postsubmit", postsubmit_tests_dict),
882        ]
883        if test_group_dict
884    ])
885    test_mapping_contents = json.dumps(test_mapping_dict, indent = INDENT)
886
887    test_mapping_file = os.path.join(self.art_dir, "TEST_MAPPING")
888    with open(test_mapping_file, "w") as f:
889      logging.debug(f"Writing `{test_mapping_file}`.")
890      f.write(f"// {ADVISORY}\n")
891      f.write(test_mapping_contents)
892      f.write("\n")
893
894  def create_mts_test_shard(self, tests_description, tests, shard_num, copyright_year,
895                            comments = []):
896    """Factory method instantiating an `MtsTestShard`."""
897    return self.MtsTestShard(self.mts_config_dir, tests_description, tests, shard_num,
898                             copyright_year, comments)
899
900  class MtsTestShard:
901    """Class encapsulating data and generation logic for an ART MTS test shard."""
902
903    def __init__(self, mts_config_dir, tests_description, tests, shard_num, copyright_year,
904                 comments):
905      self.mts_config_dir = mts_config_dir
906      self.tests_description = tests_description
907      self.tests = tests
908      self.shard_num = shard_num
909      self.copyright_year = copyright_year
910      self.comments = comments
911
912    def shard_id(self):
913      return f"{self.shard_num:02}"
914
915    def test_plan_name(self):
916      return "mts-art-shard-" + self.shard_id()
917
918    def test_list_name(self):
919      return "mts-art-tests-list-user-shard-" + self.shard_id()
920
921    def regen_test_plan_file(self):
922      """Regenerate ART MTS test plan file shard (`mts-art-shard-<shard_num>.xml`)."""
923      root = xml.dom.minidom.Document()
924
925      advisory_header = root.createComment(f" {ADVISORY} ")
926      root.appendChild(advisory_header)
927      copyright_header = root.createComment(copyright_header_text(self.copyright_year))
928      root.appendChild(copyright_header)
929
930      configuration = root.createElement("configuration")
931      root.appendChild(configuration)
932      configuration.setAttribute(
933          "description",
934          f"Run {self.test_plan_name()} from a preexisting MTS installation.")
935
936      # Included XML files.
937      included_xml_files = ["mts", self.test_list_name()]
938      # Special case for the test plan of shard 03 (ART gtests), where we also
939      # include ART MTS eng-only tests.
940      #
941      # TODO(rpl): Restucture the MTS generation logic to avoid special-casing
942      # at that level of the generator.
943      if self.shard_num == 3:
944        included_xml_files.append(ENG_ONLY_TEST_LIST_NAME)
945      for xml_file in included_xml_files:
946        include = root.createElement("include")
947        include.setAttribute("name", xml_file)
948        configuration.appendChild(include)
949
950      # Test plan name.
951      option = root.createElement("option")
952      option.setAttribute("name", "plan")
953      option.setAttribute("value", self.test_plan_name())
954      configuration.appendChild(option)
955
956      xml_str = root.toprettyxml(indent = XML_INDENT, encoding = "utf-8")
957
958      test_plan_file = os.path.join(self.mts_config_dir, self.test_plan_name() + ".xml")
959      with open(test_plan_file, "wb") as f:
960        logging.debug(f"Writing `{test_plan_file}`.")
961        f.write(xml_str)
962
963    def regen_test_list_file(self):
964      """Regenerate ART MTS test list file (`mts-art-tests-list-user-shard-<shard_num>.xml`)."""
965      configuration_description = \
966        f"List of ART MTS tests that do not need root access (shard {self.shard_id()})"
967      test_list_file = os.path.join(self.mts_config_dir, self.test_list_name() + ".xml")
968      gen_mts_test_list_file(self.tests, test_list_file, self.copyright_year,
969                             configuration_description, self.tests_description, self.comments)
970
971  def regen_mts_art_tests_list_user_file(self, num_mts_art_run_test_shards):
972    """Regenerate ART MTS test list file (`mts-art-tests-list-user.xml`)."""
973    root = xml.dom.minidom.Document()
974
975    advisory_header = root.createComment(f" {ADVISORY} ")
976    root.appendChild(advisory_header)
977    copyright_header = root.createComment(copyright_header_text(2020))
978    root.appendChild(copyright_header)
979
980    configuration = root.createElement("configuration")
981    root.appendChild(configuration)
982    configuration.setAttribute("description", "List of ART MTS tests that do not need root access.")
983
984    # Included XML files.
985    for s in range(num_mts_art_run_test_shards):
986      include = root.createElement("include")
987      include.setAttribute("name", f"mts-art-tests-list-user-shard-{s:02}")
988      configuration.appendChild(include)
989
990    def append_test_exclusion(test):
991      option = root.createElement("option")
992      option.setAttribute("name", "compatibility:exclude-filter")
993      option.setAttribute("value", test)
994      configuration.appendChild(option)
995
996    # Excluded flaky tests.
997    xml_comment = root.createComment(" Excluded flaky tests (b/209958457). ")
998    configuration.appendChild(xml_comment)
999    for module in flaky_tests_excluded_from_mts:
1000      for testcase in flaky_tests_excluded_from_mts[module]:
1001        append_test_exclusion(f"{module} {testcase}")
1002
1003    # Excluded failing tests.
1004    xml_comment = root.createComment(" Excluded failing tests (b/247108425). ")
1005    configuration.appendChild(xml_comment)
1006    for module in failing_tests_excluded_from_mts_and_mainline_presubmits:
1007      for testcase in failing_tests_excluded_from_mts_and_mainline_presubmits[module]:
1008        append_test_exclusion(f"{module} {testcase}")
1009
1010    xml_str = root.toprettyxml(indent = XML_INDENT, encoding = "utf-8")
1011
1012    mts_art_tests_list_user_file = os.path.join(self.mts_config_dir, "mts-art-tests-list-user.xml")
1013    with open(mts_art_tests_list_user_file, "wb") as f:
1014      logging.debug(f"Writing `{mts_art_tests_list_user_file}`.")
1015      f.write(xml_str)
1016
1017  def regen_art_mts_files(self, art_run_tests, art_jvmti_cts_tests):
1018    """Regenerate ART MTS definition files."""
1019
1020    # Remove any previously MTS ART test plan shard (`mts-art-shard-[0-9]+.xml`)
1021    # and any test list shard (`mts-art-tests-list-user-shard-[0-9]+.xml`).
1022    old_test_plan_shards = sorted([
1023        test_plan_shard
1024        for test_plan_shard in os.listdir(self.mts_config_dir)
1025        if re.match("^mts-art-(tests-list-user-)?shard-[0-9]+.xml$", test_plan_shard)])
1026    for shard in old_test_plan_shards:
1027      shard_path = os.path.join(self.mts_config_dir, shard)
1028      if os.path.exists(shard_path):
1029        logging.debug(f"Removing `{shard_path}`.")
1030        os.remove(shard_path)
1031
1032    mts_test_shards = []
1033
1034    # ART run-tests shard(s).
1035    art_run_test_module_names = [ART_RUN_TEST_MODULE_NAME_PREFIX + t for t in art_run_tests]
1036    art_run_test_shards = split_list(art_run_test_module_names, NUM_MTS_ART_RUN_TEST_SHARDS)
1037    for i in range(len(art_run_test_shards)):
1038      art_tests_shard_i_tests = art_run_test_shards[i]
1039      art_tests_shard_i = self.create_mts_test_shard(
1040          "ART run-tests", art_tests_shard_i_tests, i, 2020,
1041          ["TODO(rpl): Find a way to express this list in a more concise fashion."])
1042      mts_test_shards.append(art_tests_shard_i)
1043
1044    # CTS Libcore non-OJ tests (`CtsLibcoreTestCases`) shard.
1045    cts_libcore_tests_shard_num = len(mts_test_shards)
1046    cts_libcore_tests_shard = self.create_mts_test_shard(
1047        "CTS Libcore non-OJ tests", ["CtsLibcoreTestCases"], cts_libcore_tests_shard_num, 2020)
1048    mts_test_shards.append(cts_libcore_tests_shard)
1049
1050    # Other CTS tests shard.
1051    other_cts_tests_shard_num = len(mts_test_shards)
1052    other_cts_libcore_tests_shard_tests = [
1053        "CtsLibcoreApiEvolutionTestCases",
1054        "CtsLibcoreFileIOTestCases",
1055        "CtsLibcoreJsr166TestCases",
1056        "CtsLibcoreLegacy22TestCases",
1057        "CtsLibcoreOjTestCases",
1058        "CtsLibcoreWycheproofBCTestCases",
1059        "MtsLibcoreOkHttpTestCases",
1060        "MtsLibcoreBouncyCastleTestCases",
1061    ]
1062    other_cts_tests_shard_tests = art_jvmti_cts_tests + other_cts_libcore_tests_shard_tests
1063    other_cts_tests_shard = self.create_mts_test_shard(
1064        "Other CTS tests", other_cts_tests_shard_tests, other_cts_tests_shard_num, 2021)
1065    mts_test_shards.append(other_cts_tests_shard)
1066
1067    # ART gtests shard.
1068    art_gtests_shard_num = len(mts_test_shards)
1069    art_gtests_shard_tests = art_gtest_mts_user_module_names
1070    art_gtests_shard = self.create_mts_test_shard(
1071        "ART gtests", art_gtests_shard_tests, art_gtests_shard_num, 2022)
1072    mts_test_shards.append(art_gtests_shard)
1073
1074    for s in mts_test_shards:
1075      s.regen_test_plan_file()
1076      s.regen_test_list_file()
1077
1078    # Generate the MTS test list file of "eng-only" tests (tests that
1079    # need root access to the device-under-test and are not part of
1080    # "user" test plans).
1081    #
1082    # TODO(rpl): Refactor the MTS file generation logic to better
1083    # handle the special case of "eng-only" tests, which do not play
1084    # well with `MtsTestShard` at the moment).
1085    eng_only_test_list_file = os.path.join(self.mts_config_dir, ENG_ONLY_TEST_LIST_NAME + ".xml")
1086    gen_mts_test_list_file(
1087        art_gtest_eng_only_module_names, eng_only_test_list_file,
1088        copyright_year = 2020,
1089        configuration_description = "List of ART MTS tests that need root access.",
1090        tests_description = "ART gtests")
1091
1092    self.regen_mts_art_tests_list_user_file(len(mts_test_shards))
1093
1094  def regen_test_files(self, regen_art_mts):
1095    """Regenerate ART test files.
1096
1097    Args:
1098      regen_art_mts: If true, also regenerate the ART MTS definition.
1099    """
1100    run_tests = self.enumerate_run_tests()
1101
1102    # Create a list of the tests that can currently be built, and for
1103    # which a Blueprint file is to be generated.
1104    buildable_tests = list(filter(self.is_soong_buildable, run_tests))
1105
1106    # Create a list of the tests that can be built and run
1107    # (successfully). These tests are to be added to ART's
1108    # `TEST_MAPPING` file and also tagged as part of TradeFed's
1109    # `art-target-run-test` test suite via the `test-suite-tag` option
1110    # in their configuration file.
1111    expected_succeeding_tests = list(filter(self.is_tradefed_runnable,
1112                                            buildable_tests))
1113
1114    # Regenerate Blueprint files.
1115    # ---------------------------
1116
1117    self.regen_bp_files(run_tests, buildable_tests)
1118
1119    buildable_tests_percentage = int(len(buildable_tests) * 100 / len(run_tests))
1120
1121    print(f"Generated Blueprint files for {len(buildable_tests)} ART run-tests out of"
1122          f" {len(run_tests)} ({buildable_tests_percentage}%).")
1123
1124    # Regenerate `TEST_MAPPING` file.
1125    # -------------------------------
1126
1127    # Note: We only include ART run-tests expected to succeed for now.
1128    num_expected_succeeding_tests = len(expected_succeeding_tests)
1129
1130    presubmit_run_tests = set(expected_succeeding_tests).difference(postsubmit_only_tests)
1131    num_presubmit_run_tests = len(presubmit_run_tests)
1132    presubmit_run_tests_percentage = int(
1133        num_presubmit_run_tests * 100 / num_expected_succeeding_tests)
1134
1135    num_mainline_presubmit_run_tests = num_presubmit_run_tests
1136    mainline_presubmit_run_tests_percentage = presubmit_run_tests_percentage
1137
1138    postsubmit_run_tests = set(expected_succeeding_tests).intersection(postsubmit_only_tests)
1139    num_postsubmit_run_tests = len(postsubmit_run_tests)
1140    postsubmit_run_tests_percentage = int(
1141        num_postsubmit_run_tests * 100 / num_expected_succeeding_tests)
1142
1143    self.regen_test_mapping_file(expected_succeeding_tests)
1144
1145    expected_succeeding_tests_percentage = int(
1146        num_expected_succeeding_tests * 100 / len(run_tests))
1147
1148    num_gtests = len(art_gtest_module_names)
1149
1150    num_presubmit_gtests = len(art_gtest_presubmit_module_names)
1151    presubmit_gtests_percentage = int(num_presubmit_gtests * 100 / num_gtests)
1152
1153    num_mainline_presubmit_gtests = len(art_gtest_mainline_presubmit_module_names)
1154    mainline_presubmit_gtests_percentage = int(num_mainline_presubmit_gtests * 100 / num_gtests)
1155
1156    num_postsubmit_gtests = len(art_gtest_postsubmit_module_names)
1157    postsubmit_gtests_percentage = int(num_postsubmit_gtests * 100 / num_gtests)
1158
1159    print(f"Generated TEST_MAPPING entries for {num_expected_succeeding_tests} ART run-tests out"
1160          f" of {len(run_tests)} ({expected_succeeding_tests_percentage}%):")
1161    for (num_tests, test_kind, tests_percentage, test_group_name) in [
1162        (num_mainline_presubmit_run_tests, "ART run-tests", mainline_presubmit_run_tests_percentage,
1163         "art-mainline-presubmit"),
1164        (num_mainline_presubmit_run_tests, "ART run-tests", mainline_presubmit_run_tests_percentage,
1165         "mainline-presubmit"),
1166        (num_presubmit_run_tests, "ART run-tests", presubmit_run_tests_percentage, "presubmit"),
1167        (num_postsubmit_run_tests, "ART run-tests", postsubmit_run_tests_percentage, "postsubmit"),
1168        (num_mainline_presubmit_gtests, "ART gtests", mainline_presubmit_gtests_percentage,
1169         "mainline-presubmit"),
1170        (num_presubmit_gtests, "ART gtests", presubmit_gtests_percentage, "presubmit"),
1171        (num_postsubmit_gtests, "ART gtests", postsubmit_gtests_percentage, "postsubmit"),
1172    ]:
1173      print(
1174          f"  {num_tests:3d} {test_kind} ({tests_percentage}%) in `{test_group_name}` test group.")
1175    print("""  Note: Tests in `*presubmit` test groups are executed in pre- and
1176        post-submit test runs. Tests in the `postsubmit` test group
1177        are only executed in post-submit test runs.""")
1178
1179    # Regenerate ART MTS definition (optional).
1180    # -----------------------------------------
1181
1182    if regen_art_mts:
1183      self.regen_art_mts_files(expected_succeeding_tests, self.enumerate_jvmti_cts_tests())
1184      print(f"Generated ART MTS entries for {num_expected_succeeding_tests} ART run-tests out"
1185            f" of {len(run_tests)} ({expected_succeeding_tests_percentage}%).")
1186
1187def main():
1188  if "ANDROID_BUILD_TOP" not in os.environ:
1189    logging.error("ANDROID_BUILD_TOP environment variable is empty; did you forget to run `lunch`?")
1190    sys.exit(1)
1191
1192  parser = argparse.ArgumentParser(
1193      formatter_class=argparse.RawDescriptionHelpFormatter,
1194      description=textwrap.dedent("Regenerate some ART test related files."),
1195      epilog=textwrap.dedent("""\
1196        Regenerate ART run-tests Blueprint files, ART's `TEST_MAPPING` file, and
1197        optionally the ART MTS (Mainline Test Suite) definition.
1198        """))
1199  parser.add_argument("-m", "--regen-art-mts", help="regenerate the ART MTS definition as well",
1200                      action="store_true")
1201  parser.add_argument("-v", "--verbose", help="enable verbose output", action="store_true")
1202  args = parser.parse_args()
1203
1204  if args.verbose:
1205    logging.getLogger().setLevel(logging.DEBUG)
1206
1207  generator = Generator(os.path.join(os.environ["ANDROID_BUILD_TOP"]))
1208  generator.regen_test_files(args.regen_art_mts)
1209
1210
1211if __name__ == "__main__":
1212  main()
1213