Create VirtualizationTestCaseBase
The base case comes with reusable test methods, extracted from
MicrodroidTestCase.
Bug: 191056545
Bug: 182478337
Test: atest MicrodroidTestCase
Change-Id: I7506c757978cc6d7146e9ade35dd734f5157d235
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 8edd65d..968c991 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -8,7 +8,9 @@
test_suites: ["device-tests"],
libs: [
"tradefed",
- "compatibility-tradefed",
+ ],
+ static_libs: [
+ "VirtualizationTestHelper",
],
data: [":MicrodroidTestApp.signed"],
}
diff --git a/tests/hostside/helper/Android.bp b/tests/hostside/helper/Android.bp
new file mode 100644
index 0000000..05742a0
--- /dev/null
+++ b/tests/hostside/helper/Android.bp
@@ -0,0 +1,11 @@
+java_test_helper_library {
+ name: "VirtualizationTestHelper",
+ host_supported: true,
+ device_supported: false,
+ srcs: ["java/**/*.java"],
+ test_suites: ["device-tests"],
+ libs: [
+ "tradefed",
+ "compatibility-tradefed",
+ ],
+}
diff --git a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
new file mode 100644
index 0000000..dccdca8
--- /dev/null
+++ b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
@@ -0,0 +1,254 @@
+/*
+ * 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.virt.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeThat;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+import com.android.tradefed.util.RunUtil;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class VirtualizationTestCaseBase extends BaseHostJUnit4Test {
+ private static final String TEST_ROOT = "/data/local/tmp/virt/";
+ private static final String VIRT_APEX = "/apex/com.android.virt/";
+ private static final int TEST_VM_ADB_PORT = 8000;
+ private static final String MICRODROID_SERIAL = "localhost:" + TEST_VM_ADB_PORT;
+
+ // This is really slow on GCE (2m 40s) but fast on localhost or actual Android phones (< 10s)
+ // Set the maximum timeout value big enough.
+ private static final long MICRODROID_BOOT_TIMEOUT_MINUTES = 5;
+
+ public void prepareVirtualizationTestSetup() throws Exception {
+ // kill stale crosvm processes
+ tryRunOnAndroid("killall", "crosvm");
+
+ // Prepare the test root
+ tryRunOnAndroid("rm", "-rf", TEST_ROOT);
+ tryRunOnAndroid("mkdir", "-p", TEST_ROOT);
+
+ // disconnect from microdroid
+ tryRunOnHost("adb", "disconnect", MICRODROID_SERIAL);
+ }
+
+ public void cleanUpVirtualizationTestSetup() throws Exception {
+ // disconnect from microdroid
+ tryRunOnHost("adb", "disconnect", MICRODROID_SERIAL);
+
+ // kill stale VMs and directories
+ tryRunOnAndroid("killall", "crosvm");
+ tryRunOnAndroid("rm", "-rf", "/data/misc/virtualizationservice/*");
+ tryRunOnAndroid("stop", "virtualizationservice");
+ }
+
+ public void testIfDeviceIsCapable() throws Exception {
+ // Checks the preconditions to run microdroid. If the condition is not satisfied
+ // don't run the test (instead of failing)
+ skipIfFail("ls /dev/kvm");
+ skipIfFail("ls /dev/vhost-vsock");
+ skipIfFail("ls /apex/com.android.virt/bin/crosvm");
+ }
+
+ // Run an arbitrary command in the host side and returns the result
+ private String runOnHost(String... cmd) {
+ return runOnHostWithTimeout(10000, cmd);
+ }
+
+ // Same as runOnHost, but failure is not an error
+ private String tryRunOnHost(String... cmd) {
+ final long timeout = 10000;
+ CommandResult result = RunUtil.getDefault().runTimedCmd(timeout, cmd);
+ return result.getStdout().trim();
+ }
+
+ // Same as runOnHost, but with custom timeout
+ private String runOnHostWithTimeout(long timeoutMillis, String... cmd) {
+ assertTrue(timeoutMillis >= 0);
+ CommandResult result = RunUtil.getDefault().runTimedCmd(timeoutMillis, cmd);
+ assertThat(result.getStatus(), is(CommandStatus.SUCCESS));
+ return result.getStdout().trim();
+ }
+
+ // Run a shell command on Android. the default timeout is 2 min by tradefed
+ private String runOnAndroid(String... cmd) throws Exception {
+ CommandResult result = getDevice().executeShellV2Command(join(cmd));
+ if (result.getStatus() != CommandStatus.SUCCESS) {
+ fail(join(cmd) + " has failed: " + result);
+ }
+ return result.getStdout().trim();
+ }
+
+ // Same as runOnAndroid, but failure is not an error
+ private String tryRunOnAndroid(String... cmd) throws Exception {
+ CommandResult result = getDevice().executeShellV2Command(join(cmd));
+ return result.getStdout().trim();
+ }
+
+ private String runOnAndroidWithTimeout(long timeoutMillis, String... cmd) throws Exception {
+ CommandResult result =
+ getDevice()
+ .executeShellV2Command(
+ join(cmd),
+ timeoutMillis,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ if (result.getStatus() != CommandStatus.SUCCESS) {
+ fail(join(cmd) + " has failed: " + result);
+ }
+ return result.getStdout().trim();
+ }
+
+ // Run a shell command on Microdroid
+ public String runOnMicrodroid(String... cmd) {
+ final long timeout = 30000; // 30 sec. Microdroid is extremely slow on GCE-on-CF.
+ CommandResult result =
+ RunUtil.getDefault()
+ .runTimedCmd(timeout, "adb", "-s", MICRODROID_SERIAL, "shell", join(cmd));
+ if (result.getStatus() != CommandStatus.SUCCESS) {
+ fail(join(cmd) + " has failed: " + result);
+ }
+ return result.getStdout().trim();
+ }
+
+ private String join(String... strs) {
+ return String.join(" ", Arrays.asList(strs));
+ }
+
+ private File findTestFile(String name) throws Exception {
+ return (new CompatibilityBuildHelper(getBuild())).getTestFile(name);
+ }
+
+ public String startMicrodroid(String apkName, String packageName, String configPath)
+ throws Exception {
+ // Install APK
+ File apkFile = findTestFile(apkName);
+ getDevice().installPackage(apkFile, /* reinstall */ true);
+
+ // Get the path to the installed apk. Note that
+ // getDevice().getAppPackageInfo(...).getCodePath() doesn't work due to the incorrect
+ // parsing of the "=" character. (b/190975227). So we use the `pm path` command directly.
+ String apkPath = runOnAndroid("pm", "path", packageName);
+ assertTrue(apkPath.startsWith("package:"));
+ apkPath = apkPath.substring("package:".length());
+
+ // Push the idsig file to the device
+ File idsigOnHost = findTestFile(apkName + ".idsig");
+ final String apkIdsigPath = TEST_ROOT + apkName + ".idsig";
+ getDevice().pushFile(idsigOnHost, apkIdsigPath);
+
+ final String logPath = TEST_ROOT + "log.txt";
+
+ // Run the VM
+ runOnAndroid("start", "virtualizationservice");
+ String ret =
+ runOnAndroid(
+ VIRT_APEX + "bin/vm",
+ "run-app",
+ "--daemonize",
+ "--log " + logPath,
+ apkPath,
+ apkIdsigPath,
+ configPath);
+
+ // Redirect log.txt to logd using logwrapper
+ ExecutorService executor = Executors.newFixedThreadPool(1);
+ executor.execute(
+ () -> {
+ try {
+ // Keep redirecting sufficiently long enough
+ runOnAndroidWithTimeout(
+ MICRODROID_BOOT_TIMEOUT_MINUTES * 60 * 1000,
+ "logwrapper",
+ "tail",
+ "-f",
+ "-n +0",
+ logPath);
+ } catch (Exception e) {
+ // Consume
+ }
+ });
+
+ // Retrieve the CID from the vm tool output
+ Pattern pattern = Pattern.compile("with CID (\\d+)");
+ Matcher matcher = pattern.matcher(ret);
+ assertTrue(matcher.find());
+ return matcher.group(1);
+ }
+
+ public void shutdownMicrodroid(String cid) throws Exception {
+ // Shutdown microdroid
+ runOnAndroid(VIRT_APEX + "bin/vm", "stop", cid);
+ }
+
+ // Establish an adb connection to microdroid by letting Android forward the connection to
+ // microdroid. Wait until the connection is established and microdroid is booted.
+ public void adbConnectToMicrodroid(String cid, long timeoutMinutes) throws Exception {
+ long start = System.currentTimeMillis();
+ long timeoutMillis = timeoutMinutes * 60 * 1000;
+ long elapsed = 0;
+
+ final String serial = getDevice().getSerialNumber();
+ final String from = "tcp:" + TEST_VM_ADB_PORT;
+ final String to = "vsock:" + cid + ":5555";
+ runOnHost("adb", "-s", serial, "forward", from, to);
+
+ boolean disconnected = true;
+ while (disconnected) {
+ elapsed = System.currentTimeMillis() - start;
+ timeoutMillis -= elapsed;
+ start = System.currentTimeMillis();
+ String ret = runOnHostWithTimeout(timeoutMillis, "adb", "connect", MICRODROID_SERIAL);
+ disconnected = ret.equals("failed to connect to " + MICRODROID_SERIAL);
+ if (disconnected) {
+ // adb demands us to disconnect if the prior connection was a failure.
+ runOnHost("adb", "disconnect", MICRODROID_SERIAL);
+ }
+ }
+
+ elapsed = System.currentTimeMillis() - start;
+ timeoutMillis -= elapsed;
+ runOnHostWithTimeout(timeoutMillis, "adb", "-s", MICRODROID_SERIAL, "wait-for-device");
+
+ boolean dataAvailable = false;
+ while (!dataAvailable && timeoutMillis >= 0) {
+ elapsed = System.currentTimeMillis() - start;
+ timeoutMillis -= elapsed;
+ start = System.currentTimeMillis();
+ final String checkCmd = "if [ -d /data/local/tmp ]; then echo 1; fi";
+ dataAvailable = runOnMicrodroid(checkCmd).equals("1");
+ }
+
+ // Check if it actually booted by reading a sysprop.
+ assertThat(runOnMicrodroid("getprop", "ro.hardware"), is("microdroid"));
+ }
+
+ private void skipIfFail(String command) throws Exception {
+ CommandResult result = getDevice().executeShellV2Command(command);
+ assumeThat(result.getStatus(), is(CommandStatus.SUCCESS));
+ }
+}
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index a3288a1..08492b3 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -19,39 +19,17 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeThat;
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
-import com.android.tradefed.util.RunUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.File;
-import java.util.Arrays;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
@RunWith(DeviceJUnit4ClassRunner.class)
-public class MicrodroidTestCase extends BaseHostJUnit4Test {
- private static final String TEST_ROOT = "/data/local/tmp/virt/";
- private static final String VIRT_APEX = "/apex/com.android.virt/";
- private static final int TEST_VM_ADB_PORT = 8000;
- private static final String MICRODROID_SERIAL = "localhost:" + TEST_VM_ADB_PORT;
-
- // This is really slow on GCE (2m 40s) but fast on localhost or actual Android phones (< 10s)
- // Set the maximum timeout value big enough.
- private static final long MICRODROID_BOOT_TIMEOUT_MINUTES = 5;
+public class MicrodroidTestCase extends VirtualizationTestCaseBase {
+ private static final long MICRODROID_ADB_CONNECT_TIMEOUT_MINUTES = 5;
@Test
public void testMicrodroidBoots() throws Exception {
@@ -59,10 +37,7 @@
final String packageName = "com.android.microdroid.test";
final String configPath = "assets/vm_config.json"; // path inside the APK
final String cid = startMicrodroid(apkName, packageName, configPath);
- adbConnectToMicrodroid(cid, MICRODROID_BOOT_TIMEOUT_MINUTES);
-
- // Check if it actually booted by reading a sysprop.
- assertThat(runOnMicrodroid("getprop", "ro.hardware"), is("microdroid"));
+ adbConnectToMicrodroid(cid, MICRODROID_ADB_CONNECT_TIMEOUT_MINUTES);
// Test writing to /data partition
runOnMicrodroid("echo MicrodroidTest > /data/local/tmp/test.txt");
@@ -99,212 +74,21 @@
// Check that keystore was found by the payload
assertThat(runOnMicrodroid("getprop", "debug.microdroid.test.keystore"), is("PASS"));
- // Shutdown microdroid
- runOnAndroid(VIRT_APEX + "bin/vm", "stop", cid);
- }
-
- // Run an arbitrary command in the host side and returns the result
- private String runOnHost(String... cmd) {
- return runOnHostWithTimeout(10000, cmd);
- }
-
- // Same as runOnHost, but failure is not an error
- private String tryRunOnHost(String... cmd) {
- final long timeout = 10000;
- CommandResult result = RunUtil.getDefault().runTimedCmd(timeout, cmd);
- return result.getStdout().trim();
- }
-
- // Same as runOnHost, but with custom timeout
- private String runOnHostWithTimeout(long timeoutMillis, String... cmd) {
- assertTrue(timeoutMillis >= 0);
- CommandResult result = RunUtil.getDefault().runTimedCmd(timeoutMillis, cmd);
- assertThat(result.getStatus(), is(CommandStatus.SUCCESS));
- return result.getStdout().trim();
- }
-
- // Run a shell command on Android. the default timeout is 2 min by tradefed
- private String runOnAndroid(String... cmd) throws Exception {
- CommandResult result = getDevice().executeShellV2Command(join(cmd));
- if (result.getStatus() != CommandStatus.SUCCESS) {
- fail(join(cmd) + " has failed: " + result);
- }
- return result.getStdout().trim();
- }
-
- // Same as runOnAndroid, but failure is not an error
- private String tryRunOnAndroid(String... cmd) throws Exception {
- CommandResult result = getDevice().executeShellV2Command(join(cmd));
- return result.getStdout().trim();
- }
-
- private String runOnAndroidWithTimeout(long timeoutMillis, String... cmd) throws Exception {
- CommandResult result =
- getDevice()
- .executeShellV2Command(
- join(cmd),
- timeoutMillis,
- java.util.concurrent.TimeUnit.MILLISECONDS);
- if (result.getStatus() != CommandStatus.SUCCESS) {
- fail(join(cmd) + " has failed: " + result);
- }
- return result.getStdout().trim();
- }
-
- // Run a shell command on Microdroid
- private String runOnMicrodroid(String... cmd) {
- final long timeout = 30000; // 30 sec. Microdroid is extremely slow on GCE-on-CF.
- CommandResult result =
- RunUtil.getDefault()
- .runTimedCmd(timeout, "adb", "-s", MICRODROID_SERIAL, "shell", join(cmd));
- if (result.getStatus() != CommandStatus.SUCCESS) {
- fail(join(cmd) + " has failed: " + result);
- }
- return result.getStdout().trim();
- }
-
- private String join(String... strs) {
- return String.join(" ", Arrays.asList(strs));
- }
-
- private File findTestFile(String name) throws Exception {
- return (new CompatibilityBuildHelper(getBuild())).getTestFile(name);
- }
-
- private String startMicrodroid(String apkName, String packageName, String configPath)
- throws Exception {
- // Install APK
- File apkFile = findTestFile(apkName);
- getDevice().installPackage(apkFile, /* reinstall */ true);
-
- // Get the path to the installed apk. Note that
- // getDevice().getAppPackageInfo(...).getCodePath() doesn't work due to the incorrect
- // parsing of the "=" character. (b/190975227). So we use the `pm path` command directly.
- String apkPath = runOnAndroid("pm", "path", packageName);
- assertTrue(apkPath.startsWith("package:"));
- apkPath = apkPath.substring("package:".length());
-
- // Push the idsig file to the device
- File idsigOnHost = findTestFile(apkName + ".idsig");
- final String apkIdsigPath = TEST_ROOT + apkName + ".idsig";
- getDevice().pushFile(idsigOnHost, apkIdsigPath);
-
- final String logPath = TEST_ROOT + "log.txt";
-
- // Run the VM
- runOnAndroid("start", "virtualizationservice");
- String ret =
- runOnAndroid(
- VIRT_APEX + "bin/vm",
- "run-app",
- "--daemonize",
- "--log " + logPath,
- apkPath,
- apkIdsigPath,
- configPath);
-
- // Redirect log.txt to logd using logwrapper
- ExecutorService executor = Executors.newFixedThreadPool(1);
- executor.execute(
- () -> {
- try {
- // Keep redirecting sufficiently long enough
- runOnAndroidWithTimeout(
- MICRODROID_BOOT_TIMEOUT_MINUTES * 60 * 1000,
- "logwrapper",
- "tail",
- "-f",
- "-n +0",
- logPath);
- } catch (Exception e) {
- // Consume
- }
- });
-
- // Retrieve the CID from the vm tool output
- Pattern pattern = Pattern.compile("with CID (\\d+)");
- Matcher matcher = pattern.matcher(ret);
- assertTrue(matcher.find());
- return matcher.group(1);
- }
-
- // Establish an adb connection to microdroid by letting Android forward the connection to
- // microdroid. Wait until the connection is established and microdroid is booted.
- private void adbConnectToMicrodroid(String cid, long timeoutMinutes) throws Exception {
- long start = System.currentTimeMillis();
- long timeoutMillis = timeoutMinutes * 60 * 1000;
- long elapsed = 0;
-
- final String serial = getDevice().getSerialNumber();
- final String from = "tcp:" + TEST_VM_ADB_PORT;
- final String to = "vsock:" + cid + ":5555";
- runOnHost("adb", "-s", serial, "forward", from, to);
-
- boolean disconnected = true;
- while (disconnected) {
- elapsed = System.currentTimeMillis() - start;
- timeoutMillis -= elapsed;
- start = System.currentTimeMillis();
- String ret = runOnHostWithTimeout(timeoutMillis, "adb", "connect", MICRODROID_SERIAL);
- disconnected = ret.equals("failed to connect to " + MICRODROID_SERIAL);
- if (disconnected) {
- // adb demands us to disconnect if the prior connection was a failure.
- runOnHost("adb", "disconnect", MICRODROID_SERIAL);
- }
- }
-
- elapsed = System.currentTimeMillis() - start;
- timeoutMillis -= elapsed;
- runOnHostWithTimeout(timeoutMillis, "adb", "-s", MICRODROID_SERIAL, "wait-for-device");
-
- boolean dataAvailable = false;
- while (!dataAvailable && timeoutMillis >= 0) {
- elapsed = System.currentTimeMillis() - start;
- timeoutMillis -= elapsed;
- start = System.currentTimeMillis();
- final String checkCmd = "if [ -d /data/local/tmp ]; then echo 1; fi";
- dataAvailable = runOnMicrodroid(checkCmd).equals("1");
- }
- }
-
- private void skipIfFail(String command) throws Exception {
- CommandResult result = getDevice().executeShellV2Command(command);
- assumeThat(result.getStatus(), is(CommandStatus.SUCCESS));
- }
-
- @Before
- public void testIfDeviceIsCapable() throws Exception {
- // Checks the preconditions to run microdroid. If the condition is not satisfied
- // don't run the test (instead of failing)
- skipIfFail("ls /dev/kvm");
- skipIfFail("ls /dev/vhost-vsock");
- skipIfFail("ls /apex/com.android.virt/bin/crosvm");
+ shutdownMicrodroid(cid);
}
@Before
public void setUp() throws Exception {
- // kill stale crosvm processes
- tryRunOnAndroid("killall", "crosvm");
+ testIfDeviceIsCapable();
- // Prepare the test root
- tryRunOnAndroid("rm", "-rf", TEST_ROOT);
- tryRunOnAndroid("mkdir", "-p", TEST_ROOT);
-
- // disconnect from microdroid
- tryRunOnHost("adb", "disconnect", MICRODROID_SERIAL);
+ prepareVirtualizationTestSetup();
// clear the log
- tryRunOnAndroid("logcat", "-c");
+ getDevice().executeShellV2Command("logcat -c");
}
@After
public void shutdown() throws Exception {
- // disconnect from microdroid
- tryRunOnHost("adb", "disconnect", MICRODROID_SERIAL);
-
- // kill stale VMs and directories
- tryRunOnAndroid("killall", "crosvm");
- tryRunOnAndroid("rm", "-rf", "/data/misc/virtualizationservice/*");
- tryRunOnAndroid("stop", "virtualizationservice");
+ cleanUpVirtualizationTestSetup();
}
}