You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
988 lines
36 KiB
988 lines
36 KiB
#! /usr/bin/env python3
|
|
#
|
|
# Copyright 2020 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.
|
|
|
|
# Regenerate some ART test related files.
|
|
|
|
# This script handles only a subset of ART run-tests at the moment; additional
|
|
# cases will be added later.
|
|
|
|
import argparse
|
|
import collections
|
|
import json
|
|
import logging
|
|
import os
|
|
import re
|
|
import sys
|
|
import textwrap
|
|
import xml.dom.minidom
|
|
|
|
logging.basicConfig(format='%(levelname)s: %(message)s')
|
|
|
|
ME = os.path.basename(sys.argv[0])
|
|
|
|
# Common advisory placed at the top of all generated files.
|
|
ADVISORY = f"Generated by `{ME}`. Do not edit manually."
|
|
|
|
# Default indentation unit.
|
|
INDENT = " "
|
|
|
|
# Indentation unit for XML files.
|
|
XML_INDENT = " "
|
|
|
|
def reindent(str, indent = ""):
|
|
"""Reindent literal string while removing common leading spaces."""
|
|
return textwrap.indent(textwrap.dedent(str), indent)
|
|
|
|
def copyright_header_text(year):
|
|
"""Return the copyright header text used in XML files."""
|
|
return reindent(f"""\
|
|
Copyright (C) {year} 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.
|
|
""", " ")
|
|
|
|
def split_list(l, n):
|
|
"""Return a list of `n` sublists of (contiguous) elements of list `l`."""
|
|
assert n > 0
|
|
(d, m) = divmod(len(l), n)
|
|
# If the length of `l` is divisible by `n`, use that that divisor (`d`) as size of each sublist;
|
|
# otherwise, the next integer value (`d + 1`).
|
|
s = d if m == 0 else d + 1
|
|
result = [l[i:i + s] for i in range(0, len(l), s)]
|
|
assert len(result) == n
|
|
return result
|
|
|
|
# The prefix used in the Soong module name of all ART run-tests.
|
|
ART_RUN_TEST_MODULE_NAME_PREFIX = "art-run-test-"
|
|
|
|
# Number of shards used to declare ART run-tests in the sharded ART MTS test plan.
|
|
NUM_MTS_ART_RUN_TEST_SHARDS = 1
|
|
|
|
# Known failing ART run-tests.
|
|
# TODO(rpl): Investigate and address the causes of failures.
|
|
known_failing_tests = [
|
|
"004-SignalTest",
|
|
"004-UnsafeTest",
|
|
"030-bad-finalizer",
|
|
"034-call-null",
|
|
"038-inner-null",
|
|
"044-proxy",
|
|
"051-thread",
|
|
"054-uncaught",
|
|
"086-null-super",
|
|
"087-gc-after-link",
|
|
"096-array-copy-concurrent-gc",
|
|
"1004-checker-volatile-ref-load",
|
|
"115-native-bridge",
|
|
"116-nodex2oat",
|
|
"1336-short-finalizer-timeout",
|
|
"1337-gc-coverage",
|
|
"1339-dead-reference-safe",
|
|
"134-nodex2oat-nofallback",
|
|
"136-daemon-jni-shutdown",
|
|
"139-register-natives",
|
|
"148-multithread-gc-annotations",
|
|
"149-suspend-all-stress",
|
|
"150-loadlibrary",
|
|
"154-gc-loop",
|
|
"158-app-image-class-table",
|
|
"169-threadgroup-jni",
|
|
"172-app-image-twice",
|
|
"177-visibly-initialized-deadlock",
|
|
"178-app-image-native-method",
|
|
"179-nonvirtual-jni",
|
|
"450-checker-types",
|
|
"1900-track-alloc",
|
|
"1901-get-bytecodes",
|
|
"1902-suspend",
|
|
"1903-suspend-self",
|
|
"1904-double-suspend",
|
|
"1905-suspend-native",
|
|
"1906-suspend-list-me-first",
|
|
"1907-suspend-list-self-twice",
|
|
"1908-suspend-native-resume-self",
|
|
"1909-per-agent-tls",
|
|
"1910-transform-with-default",
|
|
"1911-get-local-var-table",
|
|
"1912-get-set-local-primitive",
|
|
"1913-get-set-local-objects",
|
|
"1914-get-local-instance",
|
|
"1915-get-set-local-current-thread",
|
|
"1916-get-set-current-frame",
|
|
"1917-get-stack-frame",
|
|
"1919-vminit-thread-start-timing",
|
|
"1920-suspend-native-monitor",
|
|
"1921-suspend-native-recursive-monitor",
|
|
"1922-owned-monitors-info",
|
|
"1923-frame-pop",
|
|
"1924-frame-pop-toggle",
|
|
"1925-self-frame-pop",
|
|
"1926-missed-frame-pop",
|
|
"1927-exception-event",
|
|
"1928-exception-event-exception",
|
|
"1930-monitor-info",
|
|
"1931-monitor-events",
|
|
"1932-monitor-events-misc",
|
|
"1933-monitor-current-contended",
|
|
"1934-jvmti-signal-thread",
|
|
"1935-get-set-current-frame-jit",
|
|
"1936-thread-end-events",
|
|
"1937-transform-soft-fail",
|
|
"1938-transform-abstract-single-impl",
|
|
"1939-proxy-frames",
|
|
"1941-dispose-stress",
|
|
"1942-suspend-raw-monitor-exit",
|
|
"1943-suspend-raw-monitor-wait",
|
|
"1945-proxy-method-arguments",
|
|
"1947-breakpoint-redefine-deopt",
|
|
"1949-short-dex-file",
|
|
"1951-monitor-enter-no-suspend",
|
|
"1953-pop-frame",
|
|
"1954-pop-frame-jit",
|
|
"1955-pop-frame-jit-called",
|
|
"1956-pop-frame-jit-calling",
|
|
"1957-error-ext",
|
|
"1958-transform-try-jit",
|
|
"1959-redefine-object-instrument",
|
|
"1960-obsolete-jit-multithread-native",
|
|
"1961-obsolete-jit-multithread",
|
|
"1962-multi-thread-events",
|
|
"1963-add-to-dex-classloader-in-memory",
|
|
"1967-get-set-local-bad-slot",
|
|
"1968-force-early-return",
|
|
"1969-force-early-return-void",
|
|
"1970-force-early-return-long",
|
|
"1971-multi-force-early-return",
|
|
"1972-jni-id-swap-indices",
|
|
"1973-jni-id-swap-pointer",
|
|
"1974-resize-array",
|
|
"1975-hello-structural-transformation",
|
|
"1976-hello-structural-static-methods",
|
|
"1977-hello-structural-obsolescence",
|
|
"1978-regular-obsolete-then-structural-obsolescence",
|
|
"1979-threaded-structural-transformation",
|
|
"1980-obsolete-object-cleared",
|
|
"1982-no-virtuals-structural-redefinition",
|
|
"1984-structural-redefine-field-trace",
|
|
"1985-structural-redefine-stack-scope",
|
|
"1986-structural-redefine-multi-thread-stack-scope",
|
|
"1987-structural-redefine-recursive-stack-scope",
|
|
"1988-multi-structural-redefine",
|
|
"1989-transform-bad-monitor",
|
|
"1990-structural-bad-verify",
|
|
"1991-hello-structural-retransform",
|
|
"1992-retransform-no-such-field",
|
|
"1993-fallback-non-structural",
|
|
"1994-final-virtual-structural",
|
|
"1995-final-virtual-structural-multithread",
|
|
"1996-final-override-virtual-structural",
|
|
"1997-structural-shadow-method",
|
|
"1998-structural-shadow-field",
|
|
"1999-virtual-structural",
|
|
"2003-double-virtual-structural",
|
|
"2004-double-virtual-structural-abstract",
|
|
"2005-pause-all-redefine-multithreaded",
|
|
"2008-redefine-then-old-reflect-field",
|
|
"2011-stack-walk-concurrent-instrument",
|
|
"203-multi-checkpoint",
|
|
"2031-zygote-compiled-frame-deopt",
|
|
"2033-shutdown-mechanics",
|
|
"2036-jni-filechannel",
|
|
"2037-thread-name-inherit",
|
|
"305-other-fault-handler",
|
|
"449-checker-bce",
|
|
"454-get-vreg",
|
|
"461-get-reference-vreg",
|
|
"466-get-live-vreg",
|
|
"497-inlining-and-class-loader",
|
|
"530-regression-lse",
|
|
"555-UnsafeGetLong-regression",
|
|
"566-polymorphic-inlining",
|
|
"595-profile-saving",
|
|
"597-deopt-busy-loop",
|
|
"597-deopt-invoke-stub",
|
|
"597-deopt-new-string",
|
|
"602-deoptimizeable",
|
|
"604-hot-static-interface",
|
|
"616-cha-abstract",
|
|
"616-cha-interface",
|
|
"616-cha-miranda",
|
|
"616-cha-native",
|
|
"616-cha-regression-proxy-method",
|
|
"616-cha",
|
|
"623-checker-loop-regressions",
|
|
"626-set-resolved-string",
|
|
"629-vdex-speed",
|
|
"638-checker-inline-cache-intrinsic",
|
|
"642-fp-callees",
|
|
"647-jni-get-field-id",
|
|
"652-deopt-intrinsic",
|
|
"655-jit-clinit",
|
|
"656-loop-deopt",
|
|
"660-clinit",
|
|
"661-oat-writer-layout",
|
|
"664-aget-verifier",
|
|
"667-jit-jni-stub",
|
|
"674-hotness-compiled",
|
|
"679-locks",
|
|
"680-checker-deopt-dex-pc-0",
|
|
"685-deoptimizeable",
|
|
"687-deopt",
|
|
"689-zygote-jit-deopt",
|
|
"693-vdex-inmem-loader-evict",
|
|
"707-checker-invalid-profile",
|
|
"708-jit-cache-churn",
|
|
"717-integer-value-of",
|
|
"720-thread-priority",
|
|
# 728-imt-conflict-zygote: Custom `run` script + dependency on `libarttest`.
|
|
"728-imt-conflict-zygote",
|
|
# 730-cha-deopt: Fails with:
|
|
#
|
|
# Test command execution failed with status FAILED: CommandResult: exit code=1, out=, err=Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
|
|
# at Main.main(Main.java:24)
|
|
#
|
|
"730-cha-deopt",
|
|
# 813-fp-args: Dependency on `libarttest`.
|
|
"813-fp-args",
|
|
# 821-many-args: Fails with:
|
|
#
|
|
# Test command execution failed with status FAILED: CommandResult: exit code=1, out=, err=Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
|
|
# at Main.main(Main.java:20)
|
|
#
|
|
"821-many-args",
|
|
"900-hello-plugin",
|
|
"901-hello-ti-agent",
|
|
"902-hello-transformation",
|
|
"903-hello-tagging",
|
|
"904-object-allocation",
|
|
"905-object-free",
|
|
"906-iterate-heap",
|
|
"907-get-loaded-classes",
|
|
"908-gc-start-finish",
|
|
"910-methods",
|
|
"911-get-stack-trace",
|
|
"913-heaps",
|
|
"914-hello-obsolescence",
|
|
"915-obsolete-2",
|
|
"916-obsolete-jit",
|
|
"917-fields-transformation",
|
|
"918-fields",
|
|
"919-obsolete-fields",
|
|
"920-objects",
|
|
"921-hello-failure",
|
|
"922-properties",
|
|
"923-monitors",
|
|
"924-threads",
|
|
"925-threadgroups",
|
|
"926-multi-obsolescence",
|
|
"927-timers",
|
|
"928-jni-table",
|
|
"930-hello-retransform",
|
|
"931-agent-thread",
|
|
"932-transform-saves",
|
|
"933-misc-events",
|
|
"937-hello-retransform-package",
|
|
"939-hello-transformation-bcp",
|
|
"940-recursive-obsolete",
|
|
"941-recursive-obsolete-jit",
|
|
"942-private-recursive",
|
|
"943-private-recursive-jit",
|
|
"944-transform-classloaders",
|
|
"945-obsolete-native",
|
|
"946-obsolete-throw",
|
|
"947-reflect-method",
|
|
"949-in-memory-transform",
|
|
"950-redefine-intrinsic",
|
|
"951-threaded-obsolete",
|
|
"982-ok-no-retransform",
|
|
"983-source-transform-verify",
|
|
"984-obsolete-invoke",
|
|
"985-re-obsolete",
|
|
"986-native-method-bind",
|
|
"987-agent-bind",
|
|
"988-method-trace",
|
|
"989-method-trace-throw",
|
|
"990-field-trace",
|
|
"991-field-trace-2",
|
|
"992-source-data",
|
|
"993-breakpoints",
|
|
"994-breakpoint-line",
|
|
"995-breakpoints-throw",
|
|
"996-breakpoint-obsolete",
|
|
"997-single-step",
|
|
]
|
|
|
|
# More known failing tests, related to Checker.
|
|
# TODO(rpl): Investigate and address the causes of failures.
|
|
known_failing_tests.extend([
|
|
# Fails (on flame-userdebug) with:
|
|
#
|
|
# java.lang.RuntimeException: Error running Checker
|
|
# error: Statement could not be matched starting from line 564575
|
|
# RemTest.java:289: lsr x{{\d+}}, x{{\d+}}, #32
|
|
# ISA_FEATURES = {'a53': True, 'crc': True, 'lse': False, 'fp16': False, 'dotprod': False, 'sve': False}
|
|
#
|
|
"411-checker-hdiv-hrem-const",
|
|
# Fails (on aosp_cf_x86_phone-userdebug) with:
|
|
#
|
|
# Error while running Checker: java.lang.RuntimeException: Error running Checker command:
|
|
# error: Statement could not be matched starting from line 317325
|
|
# Main.java:296: InvokeStaticOrDirect
|
|
# ISA_FEATURES = {'ssse3': True, 'sse4': False}
|
|
# NewInstance = l6
|
|
#
|
|
"476-checker-ctor-fence-redun-elim",
|
|
# Fails (on aosp_cf_x86_phone-userdebug) with:
|
|
#
|
|
# Error while running Checker: java.lang.RuntimeException: Error running Checker command:
|
|
# error: Statement could not be matched starting from line 264874
|
|
# Main.java:77: InvokeStaticOrDirect [{{([ij]\d+,)?}}<<ClinitCheck>>]
|
|
# ClinitCheck = l4
|
|
# ISA_FEATURES = {'ssse3': True, 'sse4': False}
|
|
# LoadClass = l3
|
|
#
|
|
"478-checker-clinit-check-pruning",
|
|
# Fails (on aosp_cf_x86_phone-userdebug) with:
|
|
#
|
|
# Error while running Checker: java.lang.RuntimeException: Error running Checker command:
|
|
# error: Statement could not be matched starting from line 124181
|
|
# Main.java:178: <<Arg:z\d+>> StaticFieldGet liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgUse:\d+>>)} uses:[<<ArgUse>>]
|
|
# ISA_FEATURES = {'ssse3': True, 'sse4': False}
|
|
#
|
|
"482-checker-loop-back-edge-use",
|
|
# Fails (on aosp_cf_x86_phone-userdebug) with:
|
|
#
|
|
# Error while running Checker: java.lang.RuntimeException: Error running Checker command:
|
|
# error: Statement could not be matched starting from line 7333
|
|
# Main.java:66: <<t1:i\d+>> Add [<<Arg>>,<<Const1>>] {{.*->e(bp|si|di)}}
|
|
# Arg = i0
|
|
# Const1 = i3
|
|
# ISA_FEATURES = {'ssse3': True, 'sse4': False}
|
|
#
|
|
"526-checker-caller-callee-regs",
|
|
# Fails (on aosp_cf_x86_phone-userdebug) with:
|
|
#
|
|
# Error while running Checker: java.lang.RuntimeException: Error running Checker command:
|
|
# error: NOT statement matched line 379347
|
|
# Main.java:538: NewInstance
|
|
# ISA_FEATURES = {'ssse3': True, 'sse4': False}
|
|
#
|
|
"530-checker-lse",
|
|
# Fails (on cf_x86_phone-userdebug_coverage) with:
|
|
#
|
|
# error: NOT statement matched line 117078
|
|
# Main.java:108: NewInstance
|
|
# ISA_FEATURES = {'ssse3': True, 'sse4': False}
|
|
#
|
|
"530-checker-lse2",
|
|
# Fails (on aosp_cf_x86_phone-userdebug) with:
|
|
#
|
|
# Error while running Checker: java.lang.RuntimeException: Error running Checker command:
|
|
# error: NOT statement matched line 238857
|
|
# Main.java:650: Shl
|
|
# ISA_FEATURES = {'ssse3': True, 'sse4': False}
|
|
#
|
|
"551-checker-shifter-operand",
|
|
# Fails (on aosp_cf_x86_phone-userdebug) with:
|
|
#
|
|
# Error while running Checker: java.lang.RuntimeException: Error running Checker command:
|
|
# error: Statement could not be matched starting from line 158575
|
|
# Main.java:97: X86ComputeBaseMethodAddress
|
|
# ISA_FEATURES = {'ssse3': True, 'sse4': False}
|
|
#
|
|
"552-checker-sharpening",
|
|
# Fails (on flame-userdebug) with:
|
|
#
|
|
# java.lang.RuntimeException: Error running Checker
|
|
# error: Statement could not be matched starting from line 11964
|
|
# Main.java:59: <<ConstM42:i\d+>> IntConstant -42
|
|
# ISA_FEATURES = {'a53': True, 'crc': True, 'lse': False, 'fp16': False, 'dotprod': False, 'sve': False}
|
|
#
|
|
#
|
|
"562-checker-no-intermediate",
|
|
# Fails (on cf_x86_phone-userdebug_coverage) with:
|
|
#
|
|
# error: Statement could not be matched starting from line 5260
|
|
# Main.java:24: InstanceFieldSet
|
|
# ISA_FEATURES = {'ssse3': True, 'sse4': False}
|
|
#
|
|
"583-checker-zero",
|
|
# Fails (on aosp_cf_x86_phone-userdebug) with:
|
|
#
|
|
# Error while running Checker: java.lang.RuntimeException: Error running Checker command:
|
|
# error: Statement could not be matched starting from line 149082
|
|
# Main.java:312: <<LoadClass:l\d+>> LoadClass class_name:Main
|
|
# ISA_FEATURES = {'ssse3': True, 'sse4': False}
|
|
# Int42 = i8
|
|
# Int43 = i10
|
|
#
|
|
"639-checker-code-sinking",
|
|
# Fails (on aosp_cf_x86_phone-userdebug) with:
|
|
#
|
|
# error: Statement could not be matched starting from line 12527
|
|
# Main.java:24: InvokeVirtual method_name:java.lang.StringBuilder.toString intrinsic:StringBuilderToString
|
|
# ISA_FEATURES = {'ssse3': True, 'sse4': False}
|
|
#
|
|
"729-checker-polymorphic-intrinsic",
|
|
])
|
|
|
|
known_failing_tests = frozenset(known_failing_tests)
|
|
|
|
# Percentage of ART run-tests (among the ones expected to succeed) to include in
|
|
# the `presubmit` test group in `TEST_MAPPING` file -- the rest will be included
|
|
# in `postsubmit` test group.
|
|
# This value has to be a number between 0 and 100.
|
|
presubmit_tests_percentage = 100
|
|
|
|
# Percentage of ART run-tests (among the ones expected to succeed) to include in
|
|
# the `mainline-presubmit` test group in `TEST_MAPPING` file.
|
|
# This value has to be a number between 0 and 100.
|
|
mainline_presubmit_tests_percentage = 100
|
|
|
|
# ART gtests that do not need root access to the device.
|
|
art_gtest_user_module_names = [
|
|
"art_standalone_cmdline_tests",
|
|
"art_standalone_compiler_tests",
|
|
"art_standalone_dex2oat_tests",
|
|
"art_standalone_dexdump_tests",
|
|
"art_standalone_dexlist_tests",
|
|
"art_standalone_libartbase_tests",
|
|
"art_standalone_libartpalette_tests",
|
|
"art_standalone_libdexfile_support_tests",
|
|
"art_standalone_libdexfile_tests",
|
|
"art_standalone_libprofile_tests",
|
|
"art_standalone_oatdump_tests",
|
|
"art_standalone_odrefresh_tests",
|
|
"art_standalone_runtime_compiler_tests",
|
|
"art_standalone_runtime_tests",
|
|
"art_standalone_sigchain_tests",
|
|
]
|
|
|
|
# ART gtests that need root access to the device.
|
|
art_gtest_eng_only_module_names = [
|
|
"art_standalone_dexoptanalyzer_tests",
|
|
"art_standalone_profman_tests",
|
|
]
|
|
|
|
# All supported ART gtests.
|
|
art_gtest_module_names = sorted(art_gtest_user_module_names + art_gtest_eng_only_module_names)
|
|
|
|
|
|
# Is `run_test` a Checker test (i.e. a test containing Checker
|
|
# assertions)?
|
|
def is_checker_test(run_test):
|
|
return re.match("^[0-9]+-checker-", run_test)
|
|
|
|
# Is `run_test` expected to succeed?
|
|
def is_expected_succeeding(run_test):
|
|
return run_test not in known_failing_tests
|
|
|
|
|
|
class Generator:
|
|
def __init__(self, top_dir):
|
|
"""Generator of ART test files for an Android source tree anchored at `top_dir`."""
|
|
# Path to the Android top source tree.
|
|
self.top_dir = top_dir
|
|
# Path to the ART directory
|
|
self.art_dir = os.path.join(top_dir, "art")
|
|
# Path to the ART tests directory.
|
|
self.art_test_dir = os.path.join(self.art_dir, "test")
|
|
# Path to the MTS configuration directory.
|
|
self.mts_config_dir = os.path.join(
|
|
top_dir, "test", "mts", "tools", "mts-tradefed", "res", "config")
|
|
|
|
def enumerate_run_tests(self):
|
|
return sorted([run_test
|
|
for run_test in os.listdir(self.art_test_dir)
|
|
if re.match("^[0-9]{3,}-", run_test)])
|
|
|
|
# Is building `run_test` supported?
|
|
# TODO(b/147814778): Add build support for more tests.
|
|
def is_buildable(self, run_test):
|
|
run_test_path = os.path.join(self.art_test_dir, run_test)
|
|
|
|
# Ignore tests with non-default build rules.
|
|
if os.path.isfile(os.path.join(run_test_path, "build")):
|
|
return False
|
|
# Ignore tests with no `src` directory.
|
|
if not os.path.isdir(os.path.join(run_test_path, "src")):
|
|
return False
|
|
# Ignore tests with sources outside the `src` directory.
|
|
for subdir in ["jasmin",
|
|
"jasmin-multidex",
|
|
"smali",
|
|
"smali-ex",
|
|
"smali-multidex",
|
|
"src-art",
|
|
"src-dex2oat-unresolved",
|
|
"src-ex",
|
|
"src-ex2",
|
|
"src-multidex",
|
|
"src2"]:
|
|
if os.path.isdir(os.path.join(run_test_path, subdir)):
|
|
return False
|
|
# Ignore test with a copy of `sun.misc.Unsafe`.
|
|
if os.path.isfile(os.path.join(run_test_path, "src", "sun", "misc", "Unsafe.java")):
|
|
return False
|
|
# Ignore tests with Hidden API specs.
|
|
if os.path.isfile(os.path.join(run_test_path, "hiddenapi-flags.csv")):
|
|
return False
|
|
# All other tests are considered buildable.
|
|
return True
|
|
|
|
def regen_bp_files(self, run_tests, buildable_tests):
|
|
for run_test in run_tests:
|
|
# Remove any previously generated file.
|
|
bp_file = os.path.join(self.art_test_dir, run_test, "Android.bp")
|
|
if os.path.exists(bp_file):
|
|
logging.debug(f"Removing `{bp_file}`.")
|
|
os.remove(bp_file)
|
|
|
|
for run_test in buildable_tests:
|
|
self.regen_bp_file(run_test)
|
|
|
|
def regen_bp_file(self, run_test):
|
|
"""Regenerate Blueprint file for an ART run-test."""
|
|
|
|
bp_file = os.path.join(self.art_test_dir, run_test, "Android.bp")
|
|
|
|
run_test_module_name = ART_RUN_TEST_MODULE_NAME_PREFIX + run_test
|
|
|
|
if is_expected_succeeding(run_test):
|
|
test_config_template = "art-run-test-target-template"
|
|
else:
|
|
test_config_template = "art-run-test-target-no-test-suite-tag-template"
|
|
|
|
if is_checker_test(run_test):
|
|
include_src = """\
|
|
|
|
// Include the Java source files in the test's artifacts, to make Checker assertions
|
|
// available to the TradeFed test runner.
|
|
include_srcs: true,"""
|
|
else:
|
|
include_src = ""
|
|
with open(bp_file, "w") as f:
|
|
logging.debug(f"Writing `{bp_file}`.")
|
|
f.write(textwrap.dedent(f"""\
|
|
// {ADVISORY}
|
|
|
|
// Build rules for ART run-test `{run_test}`.
|
|
|
|
package {{
|
|
// See: http://go/android-license-faq
|
|
// A large-scale-change added 'default_applicable_licenses' to import
|
|
// all of the 'license_kinds' from "art_license"
|
|
// to get the below license kinds:
|
|
// SPDX-license-identifier-Apache-2.0
|
|
default_applicable_licenses: ["art_license"],
|
|
}}
|
|
|
|
// Test's Dex code.
|
|
java_test {{
|
|
name: "{run_test_module_name}",
|
|
defaults: ["art-run-test-defaults"],
|
|
test_config_template: ":{test_config_template}",
|
|
srcs: ["src/**/*.java"],
|
|
data: [
|
|
":{run_test_module_name}-expected-stdout",
|
|
":{run_test_module_name}-expected-stderr",
|
|
],{include_src}
|
|
}}
|
|
|
|
// Test's expected standard output.
|
|
genrule {{
|
|
name: "{run_test_module_name}-expected-stdout",
|
|
out: ["{run_test_module_name}-expected-stdout.txt"],
|
|
srcs: ["expected-stdout.txt"],
|
|
cmd: "cp -f $(in) $(out)",
|
|
}}
|
|
|
|
// Test's expected standard error.
|
|
genrule {{
|
|
name: "{run_test_module_name}-expected-stderr",
|
|
out: ["{run_test_module_name}-expected-stderr.txt"],
|
|
srcs: ["expected-stderr.txt"],
|
|
cmd: "cp -f $(in) $(out)",
|
|
}}
|
|
"""))
|
|
|
|
def regen_test_mapping_file(self, art_run_tests, num_presubmit_run_tests,
|
|
num_mainline_presubmit_run_tests):
|
|
"""Regenerate ART's `TEST_MAPPING`."""
|
|
|
|
run_test_module_names = [ART_RUN_TEST_MODULE_NAME_PREFIX + t for t in art_run_tests]
|
|
|
|
# Mainline presubmits.
|
|
mainline_presubmit_run_tests = run_test_module_names[0:num_mainline_presubmit_run_tests]
|
|
mainline_presubmit_tests = mainline_presubmit_run_tests + art_gtest_module_names
|
|
mainline_presubmit_tests_with_apex = [t + "[com.google.android.art.apex]"
|
|
for t
|
|
in mainline_presubmit_tests]
|
|
mainline_presubmit_tests_dict = [{"name": t} for t in mainline_presubmit_tests_with_apex]
|
|
|
|
# Presubmits.
|
|
other_presubmit_tests = [
|
|
"CtsJdwpTestCases",
|
|
"BootImageProfileTest",
|
|
]
|
|
presubmit_run_tests = run_test_module_names[0:num_presubmit_run_tests]
|
|
presubmit_tests = other_presubmit_tests + presubmit_run_tests + art_gtest_module_names
|
|
presubmit_tests_dict = [{"name": t} for t in presubmit_tests]
|
|
|
|
# Postsubmits.
|
|
postsubmit_run_tests = run_test_module_names[num_presubmit_run_tests:]
|
|
postsubmit_tests_dict = [{"name": t} for t in postsubmit_run_tests]
|
|
|
|
# Use an `OrderedDict` container to preserve the order in which items are inserted.
|
|
# Do not produce an entry for a test group if it is empty.
|
|
test_mapping_dict = collections.OrderedDict([
|
|
(test_group_name, test_group_dict)
|
|
for (test_group_name, test_group_dict)
|
|
in [
|
|
("mainline-presubmit", mainline_presubmit_tests_dict),
|
|
("presubmit", presubmit_tests_dict),
|
|
("postsubmit", postsubmit_tests_dict),
|
|
]
|
|
if test_group_dict
|
|
])
|
|
test_mapping_contents = json.dumps(test_mapping_dict, indent = INDENT)
|
|
|
|
test_mapping_file = os.path.join(self.art_dir, "TEST_MAPPING")
|
|
with open(test_mapping_file, "w") as f:
|
|
logging.debug(f"Writing `{test_mapping_file}`.")
|
|
f.write(f"// {ADVISORY}\n")
|
|
f.write(test_mapping_contents)
|
|
f.write("\n")
|
|
|
|
def create_mts_test_shard(self, description, tests, shard_num, copyright_year, comments = []):
|
|
"""Factory method instantiating an `MtsTestShard`."""
|
|
return self.MtsTestShard(self.mts_config_dir,
|
|
description, tests, shard_num, copyright_year, comments)
|
|
|
|
class MtsTestShard:
|
|
"""Class encapsulating data and generation logic for an ART MTS test shard."""
|
|
|
|
def __init__(self, mts_config_dir, description, tests, shard_num, copyright_year, comments):
|
|
self.mts_config_dir = mts_config_dir
|
|
self.description = description
|
|
self.tests = tests
|
|
self.shard_num = shard_num
|
|
self.copyright_year = copyright_year
|
|
self.comments = comments
|
|
|
|
def shard_id(self):
|
|
return f"{self.shard_num:02}"
|
|
|
|
def test_plan_name(self):
|
|
return "mts-art-shard-" + self.shard_id()
|
|
|
|
def test_list_name(self):
|
|
return "mts-art-tests-list-user-shard-" + self.shard_id()
|
|
|
|
def regen_test_plan_file(self):
|
|
"""Regenerate ART MTS test plan file shard (`mts-art-shard-<shard_num>.xml`)."""
|
|
root = xml.dom.minidom.Document()
|
|
|
|
advisory_header = root.createComment(f" {ADVISORY} ")
|
|
root.appendChild(advisory_header)
|
|
copyright_header = root.createComment(copyright_header_text(self.copyright_year))
|
|
root.appendChild(copyright_header)
|
|
|
|
configuration = root.createElement("configuration")
|
|
root.appendChild(configuration)
|
|
configuration.setAttribute(
|
|
"description",
|
|
f"Run mts-art-shard-{self.shard_id()} from a preexisting MTS installation.")
|
|
|
|
# Included XML files.
|
|
included_xml_files = ["mts", self.test_list_name()]
|
|
for xml_file in included_xml_files:
|
|
include = root.createElement("include")
|
|
include.setAttribute("name", xml_file)
|
|
configuration.appendChild(include)
|
|
|
|
# Test plan name.
|
|
option = root.createElement("option")
|
|
option.setAttribute("name", "plan")
|
|
option.setAttribute("value", self.test_plan_name())
|
|
configuration.appendChild(option)
|
|
|
|
xml_str = root.toprettyxml(indent = XML_INDENT, encoding = "utf-8")
|
|
|
|
test_plan_file = os.path.join(self.mts_config_dir, self.test_plan_name() + ".xml")
|
|
with open(test_plan_file, "wb") as f:
|
|
logging.debug(f"Writing `{test_plan_file}`.")
|
|
f.write(xml_str)
|
|
|
|
def regen_test_list_file(self):
|
|
"""Regenerate ART MTS test list file (`mts-art-tests-list-user-shard-<shard_num>.xml`)."""
|
|
root = xml.dom.minidom.Document()
|
|
|
|
advisory_header = root.createComment(f" {ADVISORY} ")
|
|
root.appendChild(advisory_header)
|
|
copyright_header = root.createComment(copyright_header_text(self.copyright_year))
|
|
root.appendChild(copyright_header)
|
|
|
|
configuration = root.createElement("configuration")
|
|
root.appendChild(configuration)
|
|
configuration.setAttribute(
|
|
"description",
|
|
f"List of ART MTS tests that do not need root access (shard {self.shard_id()})"
|
|
)
|
|
|
|
# Test declarations.
|
|
# ------------------
|
|
|
|
def append_test_declaration(test):
|
|
option = root.createElement("option")
|
|
option.setAttribute("name", "compatibility:include-filter")
|
|
option.setAttribute("value", test)
|
|
configuration.appendChild(option)
|
|
|
|
test_declarations_comments = [self.description + "."]
|
|
test_declarations_comments.extend(self.comments)
|
|
for c in test_declarations_comments:
|
|
xml_comment = root.createComment(f" {c} ")
|
|
configuration.appendChild(xml_comment)
|
|
for t in self.tests:
|
|
append_test_declaration(t)
|
|
|
|
# `MainlineTestModuleController` configurations.
|
|
# ----------------------------------------------
|
|
|
|
def append_module_controller_configuration(test):
|
|
option = root.createElement("option")
|
|
option.setAttribute("name", "compatibility:module-arg")
|
|
option.setAttribute("value", f"{test}:enable:true")
|
|
configuration.appendChild(option)
|
|
|
|
module_controller_configuration_comments = [
|
|
f"Enable MainlineTestModuleController for {self.description}."]
|
|
module_controller_configuration_comments.extend(self.comments)
|
|
for c in module_controller_configuration_comments:
|
|
xml_comment = root.createComment(f" {c} ")
|
|
configuration.appendChild(xml_comment)
|
|
for t in self.tests:
|
|
append_module_controller_configuration(t)
|
|
|
|
xml_str = root.toprettyxml(indent = XML_INDENT, encoding = "utf-8")
|
|
|
|
test_list_file = os.path.join(self.mts_config_dir, self.test_list_name() + ".xml")
|
|
with open(test_list_file, "wb") as f:
|
|
logging.debug(f"Writing `{test_list_file}`.")
|
|
f.write(xml_str)
|
|
|
|
def regen_mts_art_tests_list_user_file(self, num_mts_art_run_test_shards):
|
|
"""Regenerate ART MTS test list file (`mts-art-tests-list-user.xml`)."""
|
|
root = xml.dom.minidom.Document()
|
|
|
|
advisory_header = root.createComment(f" {ADVISORY} ")
|
|
root.appendChild(advisory_header)
|
|
copyright_header = root.createComment(copyright_header_text(2020))
|
|
root.appendChild(copyright_header)
|
|
|
|
configuration = root.createElement("configuration")
|
|
root.appendChild(configuration)
|
|
configuration.setAttribute("description", "List of ART MTS tests that do not need root access.")
|
|
|
|
# Included XML files.
|
|
for s in range(num_mts_art_run_test_shards):
|
|
include = root.createElement("include")
|
|
include.setAttribute("name", f"mts-art-tests-list-user-shard-{s:02}")
|
|
configuration.appendChild(include)
|
|
|
|
xml_str = root.toprettyxml(indent = XML_INDENT, encoding = "utf-8")
|
|
|
|
mts_art_tests_list_user_file = os.path.join(self.mts_config_dir, "mts-art-tests-list-user.xml")
|
|
with open(mts_art_tests_list_user_file, "wb") as f:
|
|
logging.debug(f"Writing `{mts_art_tests_list_user_file}`.")
|
|
f.write(xml_str)
|
|
|
|
def regen_art_mts_files(self, art_run_tests):
|
|
"""Regenerate ART MTS definition files."""
|
|
|
|
# Remove any previously MTS ART test plan shard (`mts-art-shard-[0-9]+.xml`)
|
|
# and any test list shard (`mts-art-tests-list-user-shard-[0-9]+.xml`).
|
|
old_test_plan_shards = sorted([
|
|
test_plan_shard
|
|
for test_plan_shard in os.listdir(self.mts_config_dir)
|
|
if re.match("^mts-art-(tests-list-user-)?shard-[0-9]+.xml$", test_plan_shard)])
|
|
for shard in old_test_plan_shards:
|
|
shard_path = os.path.join(self.mts_config_dir, shard)
|
|
if os.path.exists(shard_path):
|
|
logging.debug(f"Removing `{shard_path}`.")
|
|
os.remove(shard_path)
|
|
|
|
mts_test_shards = []
|
|
|
|
# ART test (gtest & run-test) shard(s).
|
|
# TODO: Also handle the case of gtests requiring root access to the device
|
|
# (`art_gtest_eng_only_module_names`).
|
|
art_run_test_module_names = [ART_RUN_TEST_MODULE_NAME_PREFIX + t for t in art_run_tests]
|
|
art_run_test_shards = split_list(art_run_test_module_names, NUM_MTS_ART_RUN_TEST_SHARDS)
|
|
for i in range(len(art_run_test_shards)):
|
|
art_tests_shard_i_tests = art_run_test_shards[i]
|
|
# Append ART gtests to the last ART run-test shard for now.
|
|
# If needed, consider moving them to their own shard to increase
|
|
# the parallelization of code coverage runs.
|
|
if i + 1 == len(art_run_test_shards):
|
|
art_tests_shard_i_tests.extend(art_gtest_user_module_names)
|
|
art_tests_shard_i = self.create_mts_test_shard(
|
|
"ART run-tests", art_tests_shard_i_tests, i, 2020,
|
|
["TODO(rpl): Find a way to express this list in a more concise fashion."])
|
|
mts_test_shards.append(art_tests_shard_i)
|
|
|
|
# CTS Libcore non-OJ tests (`CtsLibcoreTestCases`) shard.
|
|
cts_libcore_tests_shard_num = len(mts_test_shards)
|
|
cts_libcore_tests_shard = self.create_mts_test_shard(
|
|
"CTS Libcore non-OJ tests", ["CtsLibcoreTestCases"], cts_libcore_tests_shard_num, 2020)
|
|
mts_test_shards.append(cts_libcore_tests_shard)
|
|
|
|
# Other CTS Libcore tests shard.
|
|
other_cts_libcore_tests_shard_num = len(mts_test_shards)
|
|
other_cts_libcore_tests_shard_tests = [
|
|
"CtsLibcoreApiEvolutionTestCases",
|
|
"CtsLibcoreFileIOTestCases",
|
|
"CtsLibcoreJsr166TestCases",
|
|
"CtsLibcoreLegacy22TestCases",
|
|
"CtsLibcoreOjTestCases",
|
|
"CtsLibcoreWycheproofBCTestCases",
|
|
"MtsLibcoreOkHttpTestCases",
|
|
]
|
|
other_cts_libcore_tests_shard = self.create_mts_test_shard(
|
|
"CTS Libcore OJ tests", other_cts_libcore_tests_shard_tests,
|
|
other_cts_libcore_tests_shard_num, 2021)
|
|
mts_test_shards.append(other_cts_libcore_tests_shard)
|
|
|
|
for s in mts_test_shards:
|
|
s.regen_test_plan_file()
|
|
s.regen_test_list_file()
|
|
|
|
self.regen_mts_art_tests_list_user_file(len(mts_test_shards))
|
|
|
|
def regen_test_files(self, regen_art_mts):
|
|
"""Regenerate ART test files.
|
|
|
|
Args:
|
|
regen_art_mts: If true, also regenerate the ART MTS definition.
|
|
"""
|
|
run_tests = self.enumerate_run_tests()
|
|
|
|
# Create a list of the tests that can currently be built, and for
|
|
# which a Blueprint file is to be generated.
|
|
buildable_tests = list(filter(self.is_buildable, run_tests))
|
|
|
|
# Create a list of the tests that can be built and are expected to
|
|
# succeed. These tests are to be added to ART's `TEST_MAPPING`
|
|
# file and also tagged as part of TradeFed's `art-target-run-test`
|
|
# test suite via the `test-suite-tag` option in their
|
|
# configuration file.
|
|
expected_succeeding_tests = list(filter(is_expected_succeeding, buildable_tests))
|
|
|
|
# Regenerate Blueprint files.
|
|
# ---------------------------
|
|
|
|
self.regen_bp_files(run_tests, buildable_tests)
|
|
|
|
buildable_tests_percentage = int(len(buildable_tests) * 100 / len(run_tests))
|
|
|
|
print(f"Generated Blueprint files for {len(buildable_tests)} ART run-tests out of"
|
|
f" {len(run_tests)} ({buildable_tests_percentage}%).")
|
|
|
|
# Regenerate `TEST_MAPPING` file.
|
|
# -------------------------------
|
|
|
|
# Note: We only include ART run-tests expected to succeed for now.
|
|
|
|
# Note: We only include a (growing) fraction of the supported ART
|
|
# run-tests (see `presubmit_tests_percentage`) into the
|
|
# `presubmit` test group (the other ART run-tests are added to the
|
|
# `postsubmit` test group), as we initially had issues with
|
|
# Android presubmits when the whole set of supported ART run-tests
|
|
# was included in one go (b/169310621). This progressive rollout
|
|
# allows us to better monitor future potential presubmit failures.
|
|
#
|
|
# Likewise for tests in the `mainline-presubmit` group.
|
|
num_presubmit_run_tests = int(len(expected_succeeding_tests) * presubmit_tests_percentage / 100)
|
|
num_mainline_presubmit_run_tests = int(
|
|
len(expected_succeeding_tests) * mainline_presubmit_tests_percentage / 100)
|
|
self.regen_test_mapping_file(
|
|
expected_succeeding_tests, num_presubmit_run_tests, num_mainline_presubmit_run_tests)
|
|
|
|
expected_succeeding_tests_percentage = int(
|
|
len(expected_succeeding_tests) * 100 / len(run_tests))
|
|
|
|
num_postsubmit_tests = len(expected_succeeding_tests) - num_presubmit_run_tests
|
|
postsubmit_tests_percentage = 100 - presubmit_tests_percentage
|
|
|
|
print(f"Generated TEST_MAPPING entries for {len(expected_succeeding_tests)} ART run-tests out"
|
|
f" of {len(run_tests)} ({expected_succeeding_tests_percentage}%):")
|
|
for (num_tests, test_kind, tests_percentage, test_group_name) in [
|
|
(num_mainline_presubmit_run_tests, "ART run-tests", mainline_presubmit_tests_percentage,
|
|
"mainline-presubmit"),
|
|
(len(art_gtest_module_names), "ART gtests", 100, "mainline-presubmit"),
|
|
(num_presubmit_run_tests, "ART run-tests", presubmit_tests_percentage, "presubmit"),
|
|
(len(art_gtest_module_names), "ART gtests", 100, "presubmit"),
|
|
(num_postsubmit_tests, "ART run-tests", postsubmit_tests_percentage, "postsubmit"),
|
|
]:
|
|
print(
|
|
f" {num_tests:3d} {test_kind} ({tests_percentage}%) in `{test_group_name}` test group.")
|
|
|
|
# Regenerate ART MTS definition (optional).
|
|
# -----------------------------------------
|
|
|
|
if regen_art_mts:
|
|
self.regen_art_mts_files(expected_succeeding_tests)
|
|
print(f"Generated ART MTS entries for {len(expected_succeeding_tests)} ART run-tests out"
|
|
f" of {len(run_tests)} ({expected_succeeding_tests_percentage}%).")
|
|
|
|
def main():
|
|
if "ANDROID_BUILD_TOP" not in os.environ:
|
|
logging.error("ANDROID_BUILD_TOP environment variable is empty; did you forget to run `lunch`?")
|
|
sys.exit(1)
|
|
|
|
parser = argparse.ArgumentParser(
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
description=textwrap.dedent("Regenerate some ART test related files."),
|
|
epilog=textwrap.dedent("""\
|
|
Regenerate ART run-tests Blueprint files, ART's `TEST_MAPPING` file, and
|
|
optionally the ART MTS (Mainline Test Suite) definition.
|
|
"""))
|
|
parser.add_argument("-m", "--regen-art-mts", help="regenerate the ART MTS definition as well",
|
|
action="store_true")
|
|
parser.add_argument("-v", "--verbose", help="enable verbose output", action="store_true")
|
|
args = parser.parse_args()
|
|
|
|
if args.verbose:
|
|
logging.getLogger().setLevel(logging.DEBUG)
|
|
|
|
generator = Generator(os.path.join(os.environ["ANDROID_BUILD_TOP"]))
|
|
generator.regen_test_files(args.regen_art_mts)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|