blob: f4ef9aec930dd1242838540e1ab348a5add4d17b [file] [log] [blame]
/*
* Copyright (C) 2020 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.junit.Assert.*;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IAbiReceiver;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.log.LogUtil.CLog;
import org.apache.commons.compress.compressors.CompressorStreamFactory;
import org.junit.Before;
import org.junit.Test;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
public abstract class VirtHostTestCase extends DeviceTestCase
implements IBuildReceiver, IAbiReceiver {
private static final String DEVICE_TMP_DIR = "/data/local/tmp/";
private static final String KERNEL_DEVICE_PATH = DEVICE_TMP_DIR + "/virt-host-test-kernel";
private static final String RAMDISK_DEVICE_PATH = DEVICE_TMP_DIR + "/virt-host-test-initramfs";
private static final String HOST_BINARY_DEVICE_PATH = DEVICE_TMP_DIR + "/virt-host-test-host";
private static final int EXIT_SUCCESS = 0;
private static final int EXIT_FAILURE = 1;
private static final String CMDLINE_TEST_NAME_PARAM = "virt_test_name";
private static final int CID_RESERVED = 2;
private IBuildInfo mBuildInfo = null;
private IAbi mAbi = null;
/**
* Waits for device to be online, marks the most recent boottime of the device
*/
@Before
public void setUp() throws Exception {
getDevice().waitForDeviceAvailable();
}
protected boolean enableRoot() throws Exception {
return getDevice().enableAdbRoot();
}
protected boolean disableRoot() throws Exception {
return getDevice().disableAdbRoot();
}
protected String runCommand(String cmd) throws Exception {
return getDevice().executeShellCommand(cmd);
}
protected int runCommandGetExitCode(String cmd) throws Exception {
String output = runCommand("(" + cmd + ") > /dev/null 2>&1; echo $?").trim();
try {
return Integer.parseInt(output);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(String.format(
"Could not get the exit status (%s) for '%s'.", output, cmd));
}
}
private void extractResource(String resFilePath, File file) throws Exception {
try (InputStream in = this.getClass().getResourceAsStream(resFilePath);
OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
if (in == null) {
throw new IllegalArgumentException("Resource not found: " + resFilePath);
}
byte[] buf = new byte[65536];
int chunkSize;
while ((chunkSize = in.read(buf)) != -1) {
out.write(buf, 0, chunkSize);
}
}
}
private void pushResource(String resFilePath, String deviceFilePath, boolean decompress)
throws Exception {
File resFile = File.createTempFile("VirtHostTestResource", "");
try {
extractResource(resFilePath, resFile);
getDevice().pushFile(resFile, deviceFilePath);
} finally {
resFile.delete();
}
}
private void pushResource(String resFilePath, String deviceFilePath) throws Exception {
pushResource(resFilePath, deviceFilePath, /* decompress */ false);
}
private boolean deleteResource(String deviceFilePath) throws Exception {
return runCommandGetExitCode(String.format("rm %s", deviceFilePath)) == EXIT_SUCCESS;
}
private boolean makeExecutable(String deviceFilePath) throws Exception {
return runCommandGetExitCode(String.format("chmod +x %s", deviceFilePath)) == EXIT_SUCCESS;
}
protected boolean isVirtEnabled() throws Exception {
return (runCommandGetExitCode("ls /dev/kvm") == EXIT_SUCCESS) &&
(runCommandGetExitCode("which crosvm") == EXIT_SUCCESS);
}
private String getKernelResourcePath() {
return String.format("/kernel_%s", mAbi.getName());
}
private String getRamdiskResourcePath() {
return String.format("/initramfs_%s.cpio", mAbi.getName());
}
private String getHostBinaryResourcePath() {
return String.format("/host_binary");
}
protected void prepareGuestVM() throws Exception {
if (!isVirtEnabled()) {
throw new IllegalStateException("Virtualization is not enabled on the target device");
}
if (!enableRoot()) {
throw new IllegalStateException("Cannot enable root on the target device");
}
pushResource(getKernelResourcePath(), KERNEL_DEVICE_PATH);
pushResource(getRamdiskResourcePath(), RAMDISK_DEVICE_PATH);
pushResource(getHostBinaryResourcePath(), HOST_BINARY_DEVICE_PATH);
if (!makeExecutable(HOST_BINARY_DEVICE_PATH)) {
throw new IllegalStateException("Could not make host binary executable");
}
}
protected void finishGuestVM() throws Exception {
deleteResource(KERNEL_DEVICE_PATH);
deleteResource(RAMDISK_DEVICE_PATH);
deleteResource(HOST_BINARY_DEVICE_PATH);
disableRoot();
}
protected void runGuestVM(String testName, Integer cid) throws Exception {
ArrayList<String> cmd = new ArrayList<>();
String params = String.format("%s=%s", CMDLINE_TEST_NAME_PARAM, testName);
cmd.add("crosvm");
cmd.add("run");
cmd.add("--disable-sandbox");
if (cid != null) {
if (cid > CID_RESERVED) {
cmd.add("--cid");
cmd.add(cid.toString());
} else {
throw new IllegalArgumentException("Invalid CID");
}
}
cmd.add("--params");
cmd.add("\"" + params.replace("\"", "\\\"") + "\"");
cmd.add("--initrd");
cmd.add(RAMDISK_DEVICE_PATH);
cmd.add(KERNEL_DEVICE_PATH);
runCommand(String.join(" ", cmd));
}
protected boolean runHostBinary(String testName) throws Exception {
ArrayList<String> cmd = new ArrayList<>();
cmd.add(HOST_BINARY_DEVICE_PATH);
cmd.add(testName);
return runCommandGetExitCode(String.join(" ", cmd)) == EXIT_SUCCESS;
}
@Override
public void setBuild(IBuildInfo buildInfo) {
// Get the build, this is used to access the APK.
mBuildInfo = buildInfo;
}
@Override
public void setAbi(IAbi abi) {
mAbi = abi;
}
}