diff options
author | Xin Li <delphij@google.com> | 2021-12-13 21:43:45 -0800 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2021-12-14 08:39:20 -0800 |
commit | 5a045685b3c74894a38ffb96c2deddc535a014e3 (patch) | |
tree | 9a0d9f388bb80df399f387faf30f520bc5f40270 | |
parent | e11e803ab2c854e3be4649ce9f9af80b779578d8 (diff) | |
parent | b5691ede96e3dd7f3a856f4f05dad267d7485ab2 (diff) | |
download | platform-compat-5a045685b3c74894a38ffb96c2deddc535a014e3.tar.gz |
Merge Android 12 QPR1android-t-preview-2android-t-preview-1android-t-beta-3android-s-v2-beta-3android-s-qpr3-beta-1android-t-preview-1android-s-v2-beta-3android-s-qpr3-beta-1
Bug: 210511427
Merged-In: I5ede845ba556f000171684dd843319895d097c5c
Change-Id: I259ecefbd461e48e7afec6f1048e9ba4fb021f76
-rw-r--r-- | java/android/compat/testing/Android.bp | 8 | ||||
-rw-r--r-- | java/android/compat/testing/Classpaths.java | 85 | ||||
-rw-r--r-- | java/android/compat/testing/SharedLibraryInfo.java | 69 | ||||
-rw-r--r-- | java/android/compat/testing/app/Android.bp | 39 | ||||
-rw-r--r-- | java/android/compat/testing/app/AndroidManifest.xml | 35 | ||||
-rw-r--r-- | java/android/compat/testing/app/SharedLibraryInfoDeviceTest.java | 95 |
6 files changed, 322 insertions, 9 deletions
diff --git a/java/android/compat/testing/Android.bp b/java/android/compat/testing/Android.bp index d99da38..5265d89 100644 --- a/java/android/compat/testing/Android.bp +++ b/java/android/compat/testing/Android.bp @@ -18,14 +18,20 @@ package { java_test_helper_library { name: "compat-classpaths-testing", - srcs: ["Classpaths.java"], + srcs: [ + "Classpaths.java", + "SharedLibraryInfo.java" + ], host_supported: true, device_supported: false, libs: [ "guava", "tradefed", + "cts-tradefed", + "compatibility-host-util", ], static_libs: [ "dexlib2-no-guava-no-cli", + "modules-utils-build-testing", ], } diff --git a/java/android/compat/testing/Classpaths.java b/java/android/compat/testing/Classpaths.java index 3b4efd0..2c61154 100644 --- a/java/android/compat/testing/Classpaths.java +++ b/java/android/compat/testing/Classpaths.java @@ -17,9 +17,19 @@ package android.compat.testing; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; +import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; +import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; +import com.android.ddmlib.testrunner.TestResult.TestStatus; +import com.android.tradefed.build.IBuildInfo; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.INativeDevice; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.result.CollectingTestListener; +import com.android.tradefed.result.TestDescription; +import com.android.tradefed.result.TestResult; +import com.android.tradefed.result.TestRunResult; import com.android.tradefed.util.CommandResult; import com.android.tradefed.util.CommandStatus; import com.android.tradefed.util.FileUtil; @@ -34,11 +44,13 @@ import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.MultiDexContainer; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Map; import java.util.Objects; /** - * Testing utilities for parsing *CLASSPATH environ variables on a test device. + * Testing utilities for parsing *CLASSPATH environ variables and shared libs on a test device. */ public final class Classpaths { @@ -51,6 +63,8 @@ public final class Classpaths { SYSTEMSERVERCLASSPATH, } + private static final String TEST_RUNNER = "androidx.test.runner.AndroidJUnitRunner"; + /** Returns on device filepaths to the jars that are part of a given classpath. */ public static ImmutableList<String> getJarsOnClasspath(INativeDevice device, ClasspathType classpath) throws DeviceNotAvailableException { @@ -63,6 +77,21 @@ public final class Classpaths { return ImmutableList.copyOf(value.split(":")); } + /** Returns {@link SharedLibraryInfo} about the shared libs available on the test device. */ + public static ImmutableList<SharedLibraryInfo> getSharedLibraryInfos(ITestDevice device, + IBuildInfo buildInfo) throws DeviceNotAvailableException, FileNotFoundException { + runDeviceTests(device, buildInfo, SharedLibraryInfo.HELPER_APP_APK, + SharedLibraryInfo.HELPER_APP_PACKAGE, SharedLibraryInfo.HELPER_APP_CLASS); + String remoteFile = "/sdcard/shared-libs.txt"; + String content; + try { + content = device.pullFileContents(remoteFile); + } finally { + device.deleteFile(remoteFile); + } + return SharedLibraryInfo.getSharedLibraryInfos(content); + } + /** Returns classes defined a given jar file on the test device. */ public static ImmutableSet<ClassDef> getClassDefsFromJar(INativeDevice device, String remoteJarPath) throws DeviceNotAvailableException, IOException { @@ -72,16 +101,56 @@ public final class Classpaths { if (jar == null) { throw new IllegalStateException("could not pull remote file " + remoteJarPath); } - MultiDexContainer<? extends DexBackedDexFile> container = - DexFileFactory.loadDexContainer(jar, Opcodes.getDefault()); - ImmutableSet.Builder<ClassDef> set = ImmutableSet.builder(); - for (String dexName : container.getDexEntryNames()) { - set.addAll(Objects.requireNonNull(container.getEntry(dexName)).getClasses()); - } - return set.build(); + return getClassDefsFromJar(jar); } finally { FileUtil.deleteFile(jar); } } + /** Returns classes defined a given jar file on the test device. */ + public static ImmutableSet<ClassDef> getClassDefsFromJar(File jar) throws IOException { + MultiDexContainer<? extends DexBackedDexFile> container = + DexFileFactory.loadDexContainer(jar, Opcodes.getDefault()); + ImmutableSet.Builder<ClassDef> set = ImmutableSet.builder(); + for (String dexName : container.getDexEntryNames()) { + set.addAll(Objects.requireNonNull(container.getEntry(dexName)).getClasses()); + } + return set.build(); + } + + private static void runDeviceTests(ITestDevice device, IBuildInfo buildInfo, String apkName, + String packageName, String className) throws DeviceNotAvailableException, + FileNotFoundException { + try { + final CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(buildInfo); + final String installError = device.installPackage(buildHelper.getTestFile(apkName), + false); + assertWithMessage("Failed to install %s due to: %s", apkName, installError). + that(installError).isNull(); + // Trigger helper app to collect and write info about shared libraries on the device. + final RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName, + TEST_RUNNER, device.getIDevice()); + testRunner.setClassName(className); + final CollectingTestListener listener = new CollectingTestListener(); + assertThat(device.runInstrumentationTests(testRunner, listener)).isTrue(); + final TestRunResult result = listener.getCurrentRunResults(); + assertWithMessage("Failed to successfully run device tests for " + result.getName() + + ": " + result.getRunFailureMessage()) + .that(result.isRunFailure()).isFalse(); + assertWithMessage("No tests were run!").that(result.getNumTests()).isGreaterThan(0); + StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n"); + for (Map.Entry<TestDescription, TestResult> resultEntry : + result.getTestResults().entrySet()) { + if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) { + errorBuilder.append(resultEntry.getKey().toString()); + errorBuilder.append(":\n"); + errorBuilder.append(resultEntry.getValue().getStackTrace()); + } + } + assertWithMessage(errorBuilder.toString()).that(result.hasFailedTests()).isFalse(); + } finally { + device.uninstallPackage(packageName); + } + } + } diff --git a/java/android/compat/testing/SharedLibraryInfo.java b/java/android/compat/testing/SharedLibraryInfo.java new file mode 100644 index 0000000..b2139a0 --- /dev/null +++ b/java/android/compat/testing/SharedLibraryInfo.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 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. + */ + +package android.compat.testing; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import com.google.common.collect.ImmutableList; + +/** + * For extracting and storing shared library information in CTS. + */ +public final class SharedLibraryInfo { + static final String HELPER_APP_APK = "SharedLibraryInfoTestApp.apk"; + static final String HELPER_APP_PACKAGE = "android.compat.testing.app"; + static final String HELPER_APP_CLASS = HELPER_APP_PACKAGE + ".SharedLibraryInfoDeviceTest"; + + public final String name; + public final String type; + public final String version; + public final ImmutableList<String> paths; + + private SharedLibraryInfo(String name, String type, String version, + ImmutableList<String> paths) { + this.name = name; + this.type = type; + this.version = version; + this.paths = paths; + } + + private static SharedLibraryInfo readFromLine(String line) { + String[] words = line.split(" "); + assertWithMessage( + "expected each line to be in the format: <name> <type> <version> <path>...") + .that(words.length) + .isAtLeast(4); + String libraryName = words[0]; + String libraryType = words[1]; + String libraryVersion = words[2]; + ImmutableList.Builder<String> paths = ImmutableList.builder(); + for (int i = 3; i < words.length; i++) { + String path = words[i]; + paths.add(path); + } + return new SharedLibraryInfo(libraryName, libraryType, libraryVersion, paths.build()); + } + + static ImmutableList<SharedLibraryInfo> getSharedLibraryInfos(String sharedLibsTxtContent) { + ImmutableList.Builder<SharedLibraryInfo> libraries = ImmutableList.builder(); + for (String line : sharedLibsTxtContent.split("\n")) { + libraries.add(readFromLine(line)); + } + return libraries.build(); + } +} diff --git a/java/android/compat/testing/app/Android.bp b/java/android/compat/testing/app/Android.bp new file mode 100644 index 0000000..5b0e66f --- /dev/null +++ b/java/android/compat/testing/app/Android.bp @@ -0,0 +1,39 @@ +// Copyright (C) 2021 Google LLC. +// +// 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. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "SharedLibraryInfoTestApp", + srcs: ["SharedLibraryInfoDeviceTest.java"], + defaults: ["cts_defaults"], + min_sdk_version: "26", + target_sdk_version: "31", + static_libs: [ + "androidx.test.rules", + "androidx.test.core", + "modules-utils-build", + "guava", + ], + sdk_version: "test_current", + test_suites: [ + "ats", + "cts", + "gts", + "general-tests", + "tvts", + ], +} diff --git a/java/android/compat/testing/app/AndroidManifest.xml b/java/android/compat/testing/app/AndroidManifest.xml new file mode 100644 index 0000000..309fd9a --- /dev/null +++ b/java/android/compat/testing/app/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.compat.testing.app" + android:targetSandboxVersion="2"> + + <uses-sdk android:minSdkVersion="26" android:targetSdkVersion="31"/> + + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.ACCESS_SHARED_LIBRARIES" /> + + <application android:requestLegacyExternalStorage="true"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.compat.testing.app" /> + +</manifest> diff --git a/java/android/compat/testing/app/SharedLibraryInfoDeviceTest.java b/java/android/compat/testing/app/SharedLibraryInfoDeviceTest.java new file mode 100644 index 0000000..054f51f --- /dev/null +++ b/java/android/compat/testing/app/SharedLibraryInfoDeviceTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 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. + */ + +package android.compat.testing.app; + +import static org.junit.Assume.assumeTrue; + +import android.app.Instrumentation; +import android.content.Context; +import android.content.pm.SharedLibraryInfo; +import android.util.Log; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.modules.utils.build.SdkLevel; + +import com.google.common.collect.ImmutableList; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Locale; + +/** + * Device-side helper app for obtaining shared libraries. + * + * <p>It is not technically a test as it simply collects information, but it simplifies the usage + * and communication with host-side tests. + */ +@RunWith(AndroidJUnit4.class) +public class SharedLibraryInfoDeviceTest { + + private static final String TAG = "SharedLibraryInfoDeviceTest"; + + private final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + private final Context context = instrumentation.getTargetContext(); + + @Before + public void before() { + assumeTrue(SdkLevel.isAtLeastS()); + instrumentation.getUiAutomation().adoptShellPermissionIdentity(); + } + + @After + public void after() { + instrumentation.getUiAutomation().dropShellPermissionIdentity(); + } + + /** + * Collects details about all shared libraries on the device and writes them to disk. + */ + @Test + public void collectSharedLibraryPaths() throws Exception { + List<SharedLibraryInfo> sharedLibraries = + context.getPackageManager().getSharedLibraries(0); + + ImmutableList.Builder<String> content = ImmutableList.builder(); + for (SharedLibraryInfo sharedLibrary : sharedLibraries) { + if (!sharedLibrary.isNative()) { + content.add(String.format(Locale.US, "%s %d %d %s", + sharedLibrary.getName(), + sharedLibrary.getType(), + sharedLibrary.getLongVersion(), + String.join(" ", sharedLibrary.getAllCodePaths()))); + } + } + + Path detailsFilepath = new File("/sdcard/shared-libs.txt").toPath(); + ImmutableList<String> lines = content.build(); + Log.i(TAG, String.format("Writing details about %d shared libraries to %s", + lines.size(), detailsFilepath)); + Files.write(detailsFilepath, lines); + } + +} |