Add CompOS to odsign_e2e_tests
Output of CompOS should pass the same verification for the early boot
compilation. Some test cases are currently ignored due to the known
issues.
The implementation extracts the post-boot verification tests into a new
test class. Since I couldn't find another way to include a test class
(given BaseHostJUnit4Test and DeviceJUnit4ClassRunner), I can only make
it work as a base class (not SuiteClasses;
DeviceJUnit4ClassRunnerWithParameters might work but would be more
complicated).
The new test coverage would have prevent b/216618545.
However, the VM currently fails to boot on user build (b/216321149), so
it's excluded with an assumption.
Bug: 213573626
Test: adb root; atest odsign_e2e_tests
Test: flash -user build; atest odsign_e2e_tests
Change-Id: I54b33742a936329ad0d593b3f623b8a35c2ba627
diff --git a/test/odsign/test-src/com/android/tests/odsign/ActivationTest.java b/test/odsign/test-src/com/android/tests/odsign/ActivationTest.java
new file mode 100644
index 0000000..c6c8b38
--- /dev/null
+++ b/test/odsign/test-src/com/android/tests/odsign/ActivationTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2022 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 com.android.tests.odsign;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Test to check if odrefresh, odsign, fs-verity, and ART runtime work together properly.
+ */
+@Ignore("See derived classes, each produces the file for running the following verification")
+abstract class ActivationTest extends BaseHostJUnit4Test {
+ private static final String TEST_APP_PACKAGE_NAME = "com.android.tests.odsign";
+ private static final String TEST_APP_APK = "odsign_e2e_test_app.apk";
+
+ private OdsignTestUtils mTestUtils;
+
+ @Before
+ public void setUp() throws Exception {
+ mTestUtils = new OdsignTestUtils(getTestInformation());
+ }
+
+ @Test
+ public void verifyArtUpgradeSignsFiles() throws Exception {
+ installPackage(TEST_APP_APK);
+ DeviceTestRunOptions options = new DeviceTestRunOptions(TEST_APP_PACKAGE_NAME);
+ options.setTestClassName(TEST_APP_PACKAGE_NAME + ".ArtifactsSignedTest");
+ options.setTestMethodName("testArtArtifactsHaveFsverity");
+ runDeviceTests(options);
+ }
+
+ @Test
+ public void verifyArtUpgradeGeneratesRequiredArtifacts() throws Exception {
+ installPackage(TEST_APP_APK);
+ DeviceTestRunOptions options = new DeviceTestRunOptions(TEST_APP_PACKAGE_NAME);
+ options.setTestClassName(TEST_APP_PACKAGE_NAME + ".ArtifactsSignedTest");
+ options.setTestMethodName("testGeneratesRequiredArtArtifacts");
+ runDeviceTests(options);
+ }
+
+ @Test
+ public void verifyGeneratedArtifactsLoaded() throws Exception {
+ // Checking zygote and system_server need the device have adb root to walk process maps.
+ mTestUtils.enableAdbRootOrSkipTest();
+
+ // Check there is a compilation log, we expect compilation to have occurred.
+ assertTrue("Compilation log not found", mTestUtils.haveCompilationLog());
+
+ // Check both zygote and system_server processes to see that they have loaded the
+ // artifacts compiled and signed by odrefresh and odsign. We check both here rather than
+ // having a separate test because the device reboots between each @Test method and
+ // that is an expensive use of time.
+ verifyZygotesLoadedArtifacts();
+ verifySystemServerLoadedArtifacts();
+ }
+
+ @Test
+ public void verifyGeneratedArtifactsLoadedAfterReboot() throws Exception {
+ mTestUtils.enableAdbRootOrSkipTest();
+
+ mTestUtils.reboot();
+ verifyGeneratedArtifactsLoaded();
+ }
+
+ @Test
+ public void verifyGeneratedArtifactsLoadedAfterPartialCompilation() throws Exception {
+ mTestUtils.enableAdbRootOrSkipTest();
+
+ Set<String> mappedArtifacts = mTestUtils.getSystemServerLoadedArtifacts();
+ // Delete an arbitrary artifact.
+ getDevice().deleteFile(mappedArtifacts.iterator().next());
+ mTestUtils.removeCompilationLogToAvoidBackoff();
+ mTestUtils.reboot();
+ verifyGeneratedArtifactsLoaded();
+ }
+
+ private String[] getListFromEnvironmentVariable(String name) throws Exception {
+ String systemServerClasspath = getDevice().executeShellCommand("echo $" + name).trim();
+ if (!systemServerClasspath.isEmpty()) {
+ return systemServerClasspath.split(":");
+ }
+ return new String[0];
+ }
+
+ private String getSystemServerIsa(String mappedArtifact) {
+ // Artifact path for system server artifacts has the form:
+ // ART_APEX_DALVIK_CACHE_DIRNAME + "/<arch>/system@framework@some.jar@classes.odex"
+ String[] pathComponents = mappedArtifact.split("/");
+ return pathComponents[pathComponents.length - 2];
+ }
+
+ private void verifySystemServerLoadedArtifacts() throws Exception {
+ String[] classpathElements = getListFromEnvironmentVariable("SYSTEMSERVERCLASSPATH");
+ assertTrue("SYSTEMSERVERCLASSPATH is empty", classpathElements.length > 0);
+ String[] standaloneJars = getListFromEnvironmentVariable("STANDALONE_SYSTEMSERVER_JARS");
+ String[] allSystemServerJars = Stream
+ .concat(Arrays.stream(classpathElements), Arrays.stream(standaloneJars))
+ .toArray(String[]::new);
+
+ final Set<String> mappedArtifacts = mTestUtils.getSystemServerLoadedArtifacts();
+ assertTrue(
+ "No mapped artifacts under " + OdsignTestUtils.ART_APEX_DALVIK_CACHE_DIRNAME,
+ mappedArtifacts.size() > 0);
+ final String isa = getSystemServerIsa(mappedArtifacts.iterator().next());
+ final String isaCacheDirectory =
+ String.format("%s/%s", OdsignTestUtils.ART_APEX_DALVIK_CACHE_DIRNAME, isa);
+
+ // Check components in the system_server classpath have mapped artifacts.
+ for (String element : allSystemServerJars) {
+ String escapedPath = element.substring(1).replace('/', '@');
+ for (String extension : OdsignTestUtils.APP_ARTIFACT_EXTENSIONS) {
+ final String fullArtifactPath =
+ String.format("%s/%s@classes%s", isaCacheDirectory, escapedPath, extension);
+ assertTrue("Missing " + fullArtifactPath, mappedArtifacts.contains(fullArtifactPath));
+ }
+ }
+
+ for (String mappedArtifact : mappedArtifacts) {
+ // Check the mapped artifact has a .art, .odex or .vdex extension.
+ final boolean knownArtifactKind =
+ OdsignTestUtils.APP_ARTIFACT_EXTENSIONS.stream().anyMatch(
+ e -> mappedArtifact.endsWith(e));
+ assertTrue("Unknown artifact kind: " + mappedArtifact, knownArtifactKind);
+ }
+ }
+
+ private void verifyZygoteLoadedArtifacts(String zygoteName, Set<String> mappedArtifacts)
+ throws Exception {
+ final String bootImageStem = "boot";
+
+ assertTrue("Expect 3 bootclasspath artifacts", mappedArtifacts.size() == 3);
+
+ String allArtifacts = mappedArtifacts.stream().collect(Collectors.joining(","));
+ for (String extension : OdsignTestUtils.BCP_ARTIFACT_EXTENSIONS) {
+ final String artifact = bootImageStem + extension;
+ final boolean found = mappedArtifacts.stream().anyMatch(a -> a.endsWith(artifact));
+ assertTrue(zygoteName + " " + artifact + " not found: '" + allArtifacts + "'", found);
+ }
+ }
+
+ private void verifyZygotesLoadedArtifacts() throws Exception {
+ // There are potentially two zygote processes "zygote" and "zygote64". These are
+ // instances 32-bit and 64-bit unspecialized app_process processes.
+ // (frameworks/base/cmds/app_process).
+ int zygoteCount = 0;
+ for (String zygoteName : OdsignTestUtils.ZYGOTE_NAMES) {
+ final Optional<Set<String>> mappedArtifacts =
+ mTestUtils.getZygoteLoadedArtifacts(zygoteName);
+ if (!mappedArtifacts.isPresent()) {
+ continue;
+ }
+ verifyZygoteLoadedArtifacts(zygoteName, mappedArtifacts.get());
+ zygoteCount += 1;
+ }
+ assertTrue("No zygote processes found", zygoteCount > 0);
+ }
+}
diff --git a/test/odsign/test-src/com/android/tests/odsign/CompOsSigningHostTest.java b/test/odsign/test-src/com/android/tests/odsign/CompOsSigningHostTest.java
new file mode 100644
index 0000000..8119a9e
--- /dev/null
+++ b/test/odsign/test-src/com/android/tests/odsign/CompOsSigningHostTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2022 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 com.android.tests.odsign;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+import static org.junit.Assume.assumeFalse;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+import com.android.tradefed.util.CommandResult;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Test to check if CompOS works properly.
+ *
+ * @see ActivationTest for actual tests
+ * @see OnDeviceSigningHostTest for the same test with compilation happens in early boot
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CompOsSigningHostTest extends ActivationTest {
+
+ private static final String COMPOSD_CMD_BIN = "/apex/com.android.compos/bin/composd_cmd";
+ private static final String PENDING_ARTIFACTS_DIR =
+ "/data/misc/apexdata/com.android.art/compos-pending";
+
+ /** odrefresh is currently hard-coded to fail if it does not complete in 300 seconds. */
+ private static final int ODREFRESH_MAX_SECONDS = 300;
+
+ /** Waiting time before starting to check odrefresh progress. */
+ private static final int SECONDS_BEFORE_PROGRESS_CHECK = 30;
+
+ /** Job ID of the pending compilation with staged APEXes. */
+ private static final String JOB_ID = "5132251";
+
+ private static final String ORIGINAL_CHECKSUMS_KEY = "compos_test_orig_checksums";
+ private static final String PENDING_CHECKSUMS_KEY = "compos_test_pending_checksums";
+
+ @BeforeClassWithInfo
+ public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
+ ITestDevice device = testInfo.getDevice();
+
+ // TODO(216321149): enable when the bug is fixed.
+ assumeFalse("VM fails to boot on user build", device.getBuildFlavor().endsWith("-user"));
+
+ assumeCompOsPresent(device);
+
+ testInfo.properties().put(ORIGINAL_CHECKSUMS_KEY,
+ checksumDirectoryContentPartial(device,
+ "/data/misc/apexdata/com.android.art/dalvik-cache/"));
+
+ OdsignTestUtils testUtils = new OdsignTestUtils(testInfo);
+ testUtils.installTestApex();
+
+ // Once the test APK is installed, a CompilationJob is scheduled to run when certain
+ // criteria are met, e.g. the device is charging and idle. Since we don't want to wait in
+ // the test, here we start the job by ID immediately.
+ assertCommandSucceeds(device, "cmd jobscheduler run android " + JOB_ID);
+ // It takes time. Just don't spam.
+ TimeUnit.SECONDS.sleep(SECONDS_BEFORE_PROGRESS_CHECK);
+ // The job runs asynchronously. To wait until it completes.
+ waitForJobExit(device, ODREFRESH_MAX_SECONDS - SECONDS_BEFORE_PROGRESS_CHECK);
+
+ // Checks the output validity, then store the hashes of pending files.
+ assertThat(device.getChildren(PENDING_ARTIFACTS_DIR)).asList().containsAtLeast(
+ "cache-info.xml", "compos.info", "compos.info.signature");
+ testInfo.properties().put(PENDING_CHECKSUMS_KEY,
+ checksumDirectoryContentPartial(device,
+ "/data/misc/apexdata/com.android.art/compos-pending/"));
+
+ testUtils.reboot();
+ }
+
+ @AfterClassWithInfo
+ public static void afterClassWithDevice(TestInformation testInfo) throws Exception {
+ OdsignTestUtils testUtils = new OdsignTestUtils(testInfo);
+ testUtils.uninstallTestApex();
+ testUtils.reboot();
+ testUtils.restoreAdbRoot();
+ }
+
+ @Test
+ public void checkFileChecksums() throws Exception {
+ String actualChecksums = checksumDirectoryContentPartial(getDevice(),
+ "/data/misc/apexdata/com.android.art/dalvik-cache/");
+
+ String pendingChecksums = getTestInformation().properties().get(PENDING_CHECKSUMS_KEY);
+ assertThat(actualChecksums).isEqualTo(pendingChecksums);
+
+ // With test apex, the output should be different.
+ String originalChecksums = getTestInformation().properties().get(ORIGINAL_CHECKSUMS_KEY);
+ assertThat(actualChecksums).isNotEqualTo(originalChecksums);
+ }
+
+ @Ignore("Implement timestamp check when possible. b/215589015")
+ public void verifyFileTimestamps() {}
+
+ @Ignore("Override base class. Due to b/211458160 and b/210998761.")
+ public void verifyGeneratedArtifactsLoaded() {}
+
+ @Ignore("Override base class. Due to b/211458160 and b/210998761.")
+ public void verifyGeneratedArtifactsLoadedAfterPartialCompilation() {}
+
+ @Ignore("Override base class. Due to b/211458160 and b/210998761.")
+ public void verifyGeneratedArtifactsLoadedAfterReboot() {}
+
+ private static String checksumDirectoryContentPartial(ITestDevice device, String path)
+ throws Exception {
+ // Sort by filename (second column) to make comparison easier.
+ // Filter out compos.info* (which will be deleted at boot) and cache-info.xm
+ // compos.info.signature since it's only generated by CompOS.
+ // TODO(b/210473615): Remove irrelevant APEXes (i.e. those aren't contributing to the
+ // classpaths, thus not in the VM) from cache-info.xml.
+ return assertCommandSucceeds(device, "cd " + path + "; find -type f -exec sha256sum {} \\;"
+ + "| grep -v cache-info.xml | grep -v compos.info"
+ + "| sort -k2");
+ }
+
+ private static String assertCommandSucceeds(ITestDevice device, String command)
+ throws DeviceNotAvailableException {
+ CommandResult result = device.executeShellV2Command(command);
+ assertWithMessage(result.toString()).that(result.getExitCode()).isEqualTo(0);
+ return result.getStdout();
+ }
+
+ private static void waitForJobExit(ITestDevice device, int timeout)
+ throws Exception {
+ boolean started = false;
+ for (int i = 0; i < timeout; i++) {
+ CommandResult result = device.executeShellV2Command(
+ "cmd jobscheduler get-job-state android " + JOB_ID);
+ String state = result.getStdout().toString();
+ if (state.contains("ready") || state.contains("active")) {
+ started = true;
+ TimeUnit.SECONDS.sleep(1);
+ } else if (state.startsWith("unknown")) {
+ if (!started) {
+ // It's likely that the job hasn't been scheduled. So try again.
+ TimeUnit.SECONDS.sleep(1);
+ continue;
+ } else {
+ // Job has completed
+ return;
+ }
+ } else {
+ fail("Failing due to unexpected job state: " + result);
+ }
+ }
+ fail("Timed out waiting for the job to complete");
+ }
+
+ public static void assumeCompOsPresent(ITestDevice device) throws Exception {
+ // We have to have kernel support for a VM.
+ assumeTrue(device.doesFileExist("/dev/kvm"));
+
+ // And the CompOS APEX must be present.
+ assumeTrue(device.doesFileExist("/apex/com.android.compos/"));
+ }
+}
diff --git a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
index 67d45b3..c6fc37a 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
@@ -61,6 +61,7 @@
public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
OdsignTestUtils testUtils = new OdsignTestUtils(testInfo);
testUtils.installTestApex();
+ testUtils.reboot();
testUtils.enableAdbRootOrSkipTest();
HashSet<String> zygoteArtifacts = new HashSet<>();
@@ -79,6 +80,7 @@
public static void afterClassWithDevice(TestInformation testInfo) throws Exception {
OdsignTestUtils testUtils = new OdsignTestUtils(testInfo);
testUtils.uninstallTestApex();
+ testUtils.reboot();
testUtils.restoreAdbRoot();
}
diff --git a/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java b/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
index a18286e..edb9fa4 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
@@ -67,14 +67,12 @@
assumeTrue("Updating APEX is not supported", mInstallUtils.isApexUpdateSupported());
mInstallUtils.installApexes(APEX_FILENAME);
removeCompilationLogToAvoidBackoff();
- reboot();
}
public void uninstallTestApex() throws Exception {
ApexInfo apex = mInstallUtils.getApexInfo(mInstallUtils.getTestFile(APEX_FILENAME));
mTestInfo.getDevice().uninstallPackage(apex.name);
removeCompilationLogToAvoidBackoff();
- reboot();
}
public Set<String> getMappedArtifacts(String pid, String grepPattern) throws Exception {
diff --git a/test/odsign/test-src/com/android/tests/odsign/OnDeviceSigningHostTest.java b/test/odsign/test-src/com/android/tests/odsign/OnDeviceSigningHostTest.java
index 62d61a2..20a9ba7 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OnDeviceSigningHostTest.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OnDeviceSigningHostTest.java
@@ -16,186 +16,36 @@
package com.android.tests.odsign;
-import static org.junit.Assert.assertTrue;
-
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
-import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Stream;
-import java.util.stream.Collectors;
-
/**
- * Test to check if odrefresh, odsign, fs-verity, and ART runtime work together properly.
+ * Test to check if the early boot compilation works properly.
+ *
+ * @see ActivationTest for actual tests
+ * @see CompOsSigningHostTest for the same test with compilation happens in the CompOS VM
*/
@RunWith(DeviceJUnit4ClassRunner.class)
-public class OnDeviceSigningHostTest extends BaseHostJUnit4Test {
- private static final String TEST_APP_PACKAGE_NAME = "com.android.tests.odsign";
- private static final String TEST_APP_APK = "odsign_e2e_test_app.apk";
-
- private OdsignTestUtils mTestUtils;
-
+public class OnDeviceSigningHostTest extends ActivationTest {
@BeforeClassWithInfo
public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
OdsignTestUtils testUtils = new OdsignTestUtils(testInfo);
testUtils.installTestApex();
+ testUtils.reboot();
}
@AfterClassWithInfo
public static void afterClassWithDevice(TestInformation testInfo) throws Exception {
OdsignTestUtils testUtils = new OdsignTestUtils(testInfo);
testUtils.uninstallTestApex();
+ testUtils.reboot();
testUtils.restoreAdbRoot();
}
-
- @Before
- public void setUp() throws Exception {
- mTestUtils = new OdsignTestUtils(getTestInformation());
- }
-
- @Test
- public void verifyArtUpgradeSignsFiles() throws Exception {
- installPackage(TEST_APP_APK);
- DeviceTestRunOptions options = new DeviceTestRunOptions(TEST_APP_PACKAGE_NAME);
- options.setTestClassName(TEST_APP_PACKAGE_NAME + ".ArtifactsSignedTest");
- options.setTestMethodName("testArtArtifactsHaveFsverity");
- runDeviceTests(options);
- }
-
- @Test
- public void verifyArtUpgradeGeneratesRequiredArtifacts() throws Exception {
- installPackage(TEST_APP_APK);
- DeviceTestRunOptions options = new DeviceTestRunOptions(TEST_APP_PACKAGE_NAME);
- options.setTestClassName(TEST_APP_PACKAGE_NAME + ".ArtifactsSignedTest");
- options.setTestMethodName("testGeneratesRequiredArtArtifacts");
- runDeviceTests(options);
- }
-
- @Test
- public void verifyGeneratedArtifactsLoaded() throws Exception {
- // Checking zygote and system_server need the device have adb root to walk process maps.
- mTestUtils.enableAdbRootOrSkipTest();
-
- // Check there is a compilation log, we expect compilation to have occurred.
- assertTrue("Compilation log not found", mTestUtils.haveCompilationLog());
-
- // Check both zygote and system_server processes to see that they have loaded the
- // artifacts compiled and signed by odrefresh and odsign. We check both here rather than
- // having a separate test because the device reboots between each @Test method and
- // that is an expensive use of time.
- verifyZygotesLoadedArtifacts();
- verifySystemServerLoadedArtifacts();
- }
-
- @Test
- public void verifyGeneratedArtifactsLoadedAfterReboot() throws Exception {
- mTestUtils.enableAdbRootOrSkipTest();
-
- mTestUtils.reboot();
- verifyGeneratedArtifactsLoaded();
- }
-
- @Test
- public void verifyGeneratedArtifactsLoadedAfterPartialCompilation() throws Exception {
- mTestUtils.enableAdbRootOrSkipTest();
-
- Set<String> mappedArtifacts = mTestUtils.getSystemServerLoadedArtifacts();
- // Delete an arbitrary artifact.
- getDevice().deleteFile(mappedArtifacts.iterator().next());
- mTestUtils.removeCompilationLogToAvoidBackoff();
- mTestUtils.reboot();
- verifyGeneratedArtifactsLoaded();
- }
-
- private String[] getListFromEnvironmentVariable(String name) throws Exception {
- String systemServerClasspath = getDevice().executeShellCommand("echo $" + name).trim();
- if (!systemServerClasspath.isEmpty()) {
- return systemServerClasspath.split(":");
- }
- return new String[0];
- }
-
- private String getSystemServerIsa(String mappedArtifact) {
- // Artifact path for system server artifacts has the form:
- // ART_APEX_DALVIK_CACHE_DIRNAME + "/<arch>/system@framework@some.jar@classes.odex"
- String[] pathComponents = mappedArtifact.split("/");
- return pathComponents[pathComponents.length - 2];
- }
-
- private void verifySystemServerLoadedArtifacts() throws Exception {
- String[] classpathElements = getListFromEnvironmentVariable("SYSTEMSERVERCLASSPATH");
- assertTrue("SYSTEMSERVERCLASSPATH is empty", classpathElements.length > 0);
- String[] standaloneJars = getListFromEnvironmentVariable("STANDALONE_SYSTEMSERVER_JARS");
- String[] allSystemServerJars = Stream
- .concat(Arrays.stream(classpathElements), Arrays.stream(standaloneJars))
- .toArray(String[]::new);
-
- final Set<String> mappedArtifacts = mTestUtils.getSystemServerLoadedArtifacts();
- assertTrue(
- "No mapped artifacts under " + OdsignTestUtils.ART_APEX_DALVIK_CACHE_DIRNAME,
- mappedArtifacts.size() > 0);
- final String isa = getSystemServerIsa(mappedArtifacts.iterator().next());
- final String isaCacheDirectory =
- String.format("%s/%s", OdsignTestUtils.ART_APEX_DALVIK_CACHE_DIRNAME, isa);
-
- // Check components in the system_server classpath have mapped artifacts.
- for (String element : allSystemServerJars) {
- String escapedPath = element.substring(1).replace('/', '@');
- for (String extension : OdsignTestUtils.APP_ARTIFACT_EXTENSIONS) {
- final String fullArtifactPath =
- String.format("%s/%s@classes%s", isaCacheDirectory, escapedPath, extension);
- assertTrue("Missing " + fullArtifactPath, mappedArtifacts.contains(fullArtifactPath));
- }
- }
-
- for (String mappedArtifact : mappedArtifacts) {
- // Check the mapped artifact has a .art, .odex or .vdex extension.
- final boolean knownArtifactKind =
- OdsignTestUtils.APP_ARTIFACT_EXTENSIONS.stream().anyMatch(
- e -> mappedArtifact.endsWith(e));
- assertTrue("Unknown artifact kind: " + mappedArtifact, knownArtifactKind);
- }
- }
-
- private void verifyZygoteLoadedArtifacts(String zygoteName, Set<String> mappedArtifacts)
- throws Exception {
- final String bootImageStem = "boot";
-
- assertTrue("Expect 3 bootclasspath artifacts", mappedArtifacts.size() == 3);
-
- String allArtifacts = mappedArtifacts.stream().collect(Collectors.joining(","));
- for (String extension : OdsignTestUtils.BCP_ARTIFACT_EXTENSIONS) {
- final String artifact = bootImageStem + extension;
- final boolean found = mappedArtifacts.stream().anyMatch(a -> a.endsWith(artifact));
- assertTrue(zygoteName + " " + artifact + " not found: '" + allArtifacts + "'", found);
- }
- }
-
- private void verifyZygotesLoadedArtifacts() throws Exception {
- // There are potentially two zygote processes "zygote" and "zygote64". These are
- // instances 32-bit and 64-bit unspecialized app_process processes.
- // (frameworks/base/cmds/app_process).
- int zygoteCount = 0;
- for (String zygoteName : OdsignTestUtils.ZYGOTE_NAMES) {
- final Optional<Set<String>> mappedArtifacts =
- mTestUtils.getZygoteLoadedArtifacts(zygoteName);
- if (!mappedArtifacts.isPresent()) {
- continue;
- }
- verifyZygoteLoadedArtifacts(zygoteName, mappedArtifacts.get());
- zygoteCount += 1;
- }
- assertTrue("No zygote processes found", zygoteCount > 0);
- }
}