aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2021-12-13 21:43:45 -0800
committerXin Li <delphij@google.com>2021-12-14 08:39:20 -0800
commit5a045685b3c74894a38ffb96c2deddc535a014e3 (patch)
tree9a0d9f388bb80df399f387faf30f520bc5f40270
parente11e803ab2c854e3be4649ce9f9af80b779578d8 (diff)
parentb5691ede96e3dd7f3a856f4f05dad267d7485ab2 (diff)
downloadplatform-compat-android-s-qpr3-beta-1.tar.gz
Bug: 210511427 Merged-In: I5ede845ba556f000171684dd843319895d097c5c Change-Id: I259ecefbd461e48e7afec6f1048e9ba4fb021f76
-rw-r--r--java/android/compat/testing/Android.bp8
-rw-r--r--java/android/compat/testing/Classpaths.java85
-rw-r--r--java/android/compat/testing/SharedLibraryInfo.java69
-rw-r--r--java/android/compat/testing/app/Android.bp39
-rw-r--r--java/android/compat/testing/app/AndroidManifest.xml35
-rw-r--r--java/android/compat/testing/app/SharedLibraryInfoDeviceTest.java95
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);
+ }
+
+}