blob: 931aa2ccad25d47ccee929f7409fa3ca7c3efbc7 [file] [log] [blame]
#! /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 ART run-tests Blueprint files.
# This script handles only a subset of ART run-tests at the moment; additional
# cases will be added later.
import collections
import json
import logging
import os
import re
import sys
import textwrap
me = os.path.basename(sys.argv[0])
# Relative path to ART's tests directory from ART's source directory.
TESTS_DIR = "test"
# Indentation unit.
INDENT = " "
def reindent(str, indent_level = 0):
"""Reindent literal string while removing common leading spaces."""
return textwrap.indent(textwrap.dedent(str), INDENT * indent_level)
# Known failing ART run-tests.
# TODO(rpl): Investigate and address the causes of failures.
known_failing_tests = frozenset([
"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",
"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",
"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`.
"813-fp-args", # Dependency on `libarttest`.
"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",
])
# 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 = 25
# 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?
#
# Also temporarily consider Checker tests as known failing tests, as they
# currently break some test runs (see b/169852871).
# TODO(b/162408889): Complete Checker integration and re-include Checker
# tests in test mapping.
def is_expected_succeeding(run_test):
return run_test not in known_failing_tests and not is_checker_test(run_test)
class Generator:
def __init__(self, art_dir):
self.art_dir = art_dir
self.art_test_dir = os.path.join(art_dir, TESTS_DIR)
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):
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-" + 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:
f.write(reindent(f"""\
// Generated by `{me}`. Do not edit manually.
// Build rules for ART run-test `{run_test}`.
// 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):
"""Regenerate ART's `TEST_MAPPING`."""
run_test_module_names = list(map(lambda t: "art-run-test-" + t, art_run_tests))
# Mainline presubmits.
# TODO(rpl): Progressively add more tests to this test group.
mainline_presubmit_tests = [
"art-run-test-001-HelloWorld[com.google.android.art.apex]",
]
mainline_presubmit_tests_dict = [{"name": t} for t in mainline_presubmit_tests]
# Presubmits.
other_presubmit_tests = [
"CtsJdwpTestCases",
"BootImageProfileTest",
"ArtServiceTests",
]
art_gtests = [
"ArtGtestsTarget",
]
presubmit_run_tests = run_test_module_names[0:num_presubmit_run_tests]
presubmit_tests = other_presubmit_tests + art_gtests + presubmit_run_tests
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.
test_mapping_dict = collections.OrderedDict([
("mainline-presubmit", mainline_presubmit_tests_dict),
("presubmit", presubmit_tests_dict),
("postsubmit", postsubmit_tests_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:
f.write(f"// Generated by `{me}`. Do not edit manually.\n")
f.write(test_mapping_contents)
f.write("\n")
def regen_test_files(self):
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.
num_presubmit_run_tests = int(len(expected_succeeding_tests) * presubmit_tests_percentage / 100)
self.regen_test_mapping_file(expected_succeeding_tests, num_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}%):")
print(f" {num_presubmit_run_tests} tests ({presubmit_tests_percentage}%) in `presubmit` test"
f" group;")
print(f" {num_postsubmit_tests} tests ({postsubmit_tests_percentage}%) in `postsubmit` test"
f" group.")
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)
generator = Generator(os.path.join(os.environ["ANDROID_BUILD_TOP"], "art"))
generator.regen_test_files()
if __name__ == '__main__':
main()