| /* |
| * Copyright (C) 2019 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.dsu; |
| |
| import com.android.tradefed.build.BuildRetrievalError; |
| import com.android.tradefed.build.IBuildInfo; |
| import com.android.tradefed.build.IDeviceBuildInfo; |
| import com.android.tradefed.config.Option; |
| import com.android.tradefed.config.Option.Importance; |
| import com.android.tradefed.device.DeviceNotAvailableException; |
| import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; |
| import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; |
| import com.android.tradefed.util.CommandResult; |
| import com.android.tradefed.util.ZipUtil2; |
| |
| import org.apache.commons.compress.archivers.zip.ZipFile; |
| |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.Process; |
| import java.lang.Runtime; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * Test Dynamic System Updates by booting in and out of a supplied system image |
| */ |
| @RunWith(DeviceJUnit4ClassRunner.class) |
| public class DSUEndtoEndTest extends BaseHostJUnit4Test { |
| private static final long kDefaultUserdataSize = 4L * 1024 * 1024 * 1024; |
| private static final String LPUNPACK_PATH = "bin/lpunpack"; |
| private static final String SIMG2IMG_PATH = "bin/simg2img"; |
| |
| // Example: atest -v DSUEndtoEndTest -- --test-arg \ |
| // com.android.tradefed.testtype.HostTest:set-option:system_image_path:/full/path/to/system.img |
| @Option(name="system_image_path", |
| shortName='s', |
| description="full path to the system image to use. If not specified, attempt " + |
| "to download the image from the test infrastructure", |
| importance=Importance.ALWAYS) |
| private String mSystemImagePath; |
| |
| @Option(name="userdata_size", |
| shortName='u', |
| description="size in bytes of the new userdata partition", |
| importance=Importance.ALWAYS) |
| private long mUserdataSize = kDefaultUserdataSize; |
| |
| private File mUnsparseSystemImage; |
| |
| @After |
| public void teardown() throws Exception { |
| if (mUnsparseSystemImage != null) { |
| mUnsparseSystemImage.delete(); |
| } |
| } |
| |
| @Test |
| public void testDSU() throws Exception { |
| String simg2imgPath = "simg2img"; |
| if (mSystemImagePath == null) { |
| IBuildInfo buildInfo = getBuild(); |
| File imgs = ((IDeviceBuildInfo) buildInfo).getDeviceImageFile(); |
| Assert.assertNotEquals("Failed to fetch system image. See system_image_path parameter", null, imgs); |
| File otaTools = buildInfo.getFile("otatools.zip"); |
| File tempdir = ZipUtil2.extractZipToTemp(otaTools, "otatools"); |
| File system = ZipUtil2.extractFileFromZip(new ZipFile(imgs), "system.img"); |
| if (system == null) { |
| File superImg = ZipUtil2.extractFileFromZip(new ZipFile(imgs), "super.img"); |
| String lpunpackPath = new File(tempdir, LPUNPACK_PATH).getAbsolutePath(); |
| String outputDir = superImg.getParentFile().getAbsolutePath(); |
| String[] cmd = {lpunpackPath, "-p", "system_a", superImg.getAbsolutePath(), outputDir}; |
| Process p = Runtime.getRuntime().exec(cmd); |
| p.waitFor(); |
| if (p.exitValue() == 0) { |
| mSystemImagePath = new File(outputDir, "system_a.img").getAbsolutePath(); |
| } else { |
| ByteArrayOutputStream stderr = new ByteArrayOutputStream(); |
| int len; |
| byte[] buf = new byte[1024]; |
| while ((len = p.getErrorStream().read(buf)) != -1) { |
| stderr.write(buf, 0, len); |
| } |
| Assert.assertEquals("non-zero exit value (" + stderr.toString("UTF-8") + ")", 0, p.exitValue()); |
| } |
| } else { |
| mSystemImagePath = system.getAbsolutePath(); |
| } |
| simg2imgPath = new File(tempdir, SIMG2IMG_PATH).getAbsolutePath(); |
| } |
| File gsi = new File(mSystemImagePath); |
| Assert.assertTrue("not a valid file", gsi.isFile()); |
| String[] cmd = {simg2imgPath, mSystemImagePath, mSystemImagePath + ".raw"}; |
| Process p = Runtime.getRuntime().exec(cmd); |
| p.waitFor(); |
| if (p.exitValue() == 0) { |
| mUnsparseSystemImage = new File(mSystemImagePath + ".raw"); |
| gsi = mUnsparseSystemImage; |
| } |
| |
| boolean wasRoot = getDevice().isAdbRoot(); |
| if (!wasRoot) |
| Assert.assertTrue("Test requires root", getDevice().enableAdbRoot()); |
| |
| expectGsiStatus("normal"); |
| |
| // Sleep after installing to allow time for gsi_tool to reboot. This prevents a race between |
| // the device rebooting and waitForDeviceAvailable() returning. |
| getDevice().executeShellV2Command("gsi_tool install --userdata-size " + mUserdataSize + |
| " --gsi-size " + gsi.length() + " && sleep 10000000", gsi, null, 10, TimeUnit.MINUTES, 1); |
| getDevice().waitForDeviceAvailable(); |
| getDevice().enableAdbRoot(); |
| |
| expectGsiStatus("running"); |
| |
| getDevice().rebootUntilOnline(); |
| |
| expectGsiStatus("installed"); |
| |
| CommandResult result = getDevice().executeShellV2Command("gsi_tool enable"); |
| Assert.assertEquals("gsi_tool enable failed", 0, result.getExitCode().longValue()); |
| |
| getDevice().reboot(); |
| |
| expectGsiStatus("running"); |
| |
| getDevice().reboot(); |
| |
| expectGsiStatus("running"); |
| |
| getDevice().executeShellV2Command("gsi_tool wipe"); |
| |
| getDevice().rebootUntilOnline(); |
| |
| expectGsiStatus("normal"); |
| |
| if (wasRoot) { |
| getDevice().enableAdbRoot(); |
| } |
| } |
| |
| private void expectGsiStatus(String expected) throws Exception { |
| CommandResult result = getDevice().executeShellV2Command("gsi_tool status"); |
| String status = result.getStdout().split("\n", 2)[0].trim(); |
| Assert.assertEquals("Device not in expected DSU state", expected, status); |
| } |
| } |
| |