Integration test for CompOS
The only test case initially is to verify that odrefresh can succeed
with dex2oat running in the VM. The current goal is to keep the test
running.
In the longer term, when we're ready to increase the end-to-end test
coversage, we might want to figure out a better place to host the test,
e.g. possibly odsign_e2e_tests.
Bug: 192690283
Test: atest ComposHostTestCases
Change-Id: Ic1395437d8bc13c72b21e6460242ccb445be2b87
diff --git a/compos/apk/Android.bp b/compos/apk/Android.bp
index 95788bb..3a68b8e 100644
--- a/compos/apk/Android.bp
+++ b/compos/apk/Android.bp
@@ -23,7 +23,7 @@
"--ks-pass=pass:testkey --key-pass=pass:testkey " +
"--in $(in) " +
"--out $(genDir)/CompOSPayloadApp.apk",
- // $(genDir)/MicrodroidTestApp.apk.idsig is generated implicitly
+ // $(genDir)/CompOSPayloadApp.apk.idsig is generated implicitly
}
android_app_import {
diff --git a/compos/tests/Android.bp b/compos/tests/Android.bp
new file mode 100644
index 0000000..7e00d7b
--- /dev/null
+++ b/compos/tests/Android.bp
@@ -0,0 +1,20 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+ name: "ComposHostTestCases",
+ srcs: ["java/**/*.java"],
+ libs: [
+ "tradefed",
+ "compatibility-tradefed",
+ "compatibility-host-util",
+ ],
+ static_libs: [
+ "VirtualizationTestHelper",
+ ],
+ test_suites: ["general-tests"],
+ data: [
+ ":CompOSPayloadApp.signing",
+ ],
+}
diff --git a/compos/tests/AndroidTest.xml b/compos/tests/AndroidTest.xml
new file mode 100644
index 0000000..61b6d47
--- /dev/null
+++ b/compos/tests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<configuration description="Tests for CompOS">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="force-root" value="true" />
+ </target_preparer>
+
+ <!-- virtualizationservice doesn't have access to shell_data_file. Instead of giving it
+ a test-only permission, run it without selinux -->
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer"/>
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="ComposHostTestCases.jar" />
+ </test>
+</configuration>
diff --git a/compos/tests/java/android/compos/test/ComposTestCase.java b/compos/tests/java/android/compos/test/ComposTestCase.java
new file mode 100644
index 0000000..503e0f9
--- /dev/null
+++ b/compos/tests/java/android/compos/test/ComposTestCase.java
@@ -0,0 +1,116 @@
+/*
+ * 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.compos.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.platform.test.annotations.RootPermissionTest;
+import android.virt.test.CommandRunner;
+import android.virt.test.VirtualizationTestCaseBase;
+
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.util.CommandResult;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RootPermissionTest
+@RunWith(DeviceJUnit4ClassRunner.class)
+public final class ComposTestCase extends VirtualizationTestCaseBase {
+
+ /** Path to odrefresh on Microdroid */
+ private static final String ODREFRESH_BIN = "/apex/com.android.art/bin/odrefresh";
+
+ /** Timeout of odrefresh to finish */
+ private static final int ODREFRESH_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
+
+ /** Wait time for compsvc to be ready on boot */
+ private static final int COMPSVC_READY_LATENCY_MS = 10 * 1000; // 10 seconds
+
+ // ExitCode expanded from art/odrefresh/include/odrefresh/odrefresh.h.
+ private static final int OKAY = 0;
+ private static final int COMPILATION_SUCCESS = 80;
+
+ private String mCid;
+
+ @Before
+ public void setUp() throws DeviceNotAvailableException {
+ testIfDeviceIsCapable(getDevice());
+
+ prepareVirtualizationTestSetup(getDevice());
+
+ final String apkName = "CompOSPayloadApp.apk";
+ final String packageName = "com.android.compos.payload";
+ final String configPath = "assets/vm_config.json"; // path inside the APK
+ mCid =
+ startMicrodroid(
+ getDevice(),
+ getBuild(),
+ apkName,
+ packageName,
+ configPath,
+ /* debug */ true);
+ adbConnectToMicrodroid(getDevice(), mCid);
+ }
+
+ @After
+ public void tearDown() throws DeviceNotAvailableException {
+ if (mCid != null) {
+ shutdownMicrodroid(getDevice(), mCid);
+ mCid = null;
+ }
+
+ cleanUpVirtualizationTestSetup(getDevice());
+ }
+
+ @Test
+ public void testOdrefresh() throws DeviceNotAvailableException, InterruptedException {
+ waitForServiceRunning();
+
+ CommandRunner android = new CommandRunner(getDevice());
+
+ // Expect the compilation to finish successfully.
+ CommandResult result =
+ android.runForResultWithTimeout(
+ ODREFRESH_TIMEOUT_MS,
+ ODREFRESH_BIN,
+ "--use-compilation-os=" + mCid,
+ "--force-compile");
+ assertThat(result.getExitCode(), is(COMPILATION_SUCCESS));
+
+ // Expect the output to be valid.
+ result = android.runForResultWithTimeout(ODREFRESH_TIMEOUT_MS, ODREFRESH_BIN, "--check");
+ assertThat(result.getExitCode(), is(OKAY));
+ }
+
+ private void waitForServiceRunning() {
+ try {
+ PollingCheck.waitFor(COMPSVC_READY_LATENCY_MS, () -> isServiceRunning());
+ } catch (Exception e) {
+ throw new RuntimeException("Service unavailable", e);
+ }
+ }
+
+ private boolean isServiceRunning() {
+ return tryRunOnMicrodroid("pidof compsvc") != null;
+ }
+}
diff --git a/tests/hostside/helper/java/android/virt/test/CommandRunner.java b/tests/hostside/helper/java/android/virt/test/CommandRunner.java
index 696c89a..f1bdb8e 100644
--- a/tests/hostside/helper/java/android/virt/test/CommandRunner.java
+++ b/tests/hostside/helper/java/android/virt/test/CommandRunner.java
@@ -75,6 +75,12 @@
return result.getStdout().trim();
}
+ public CommandResult runForResultWithTimeout(long timeoutMillis, String... cmd)
+ throws DeviceNotAvailableException {
+ return mDevice.executeShellV2Command(
+ join(cmd), timeoutMillis, java.util.concurrent.TimeUnit.MILLISECONDS);
+ }
+
public CommandResult runForResult(String... cmd) throws DeviceNotAvailableException {
return mDevice.executeShellV2Command(join(cmd));
}