blob: e4419cdfa7d60c69a737674ffc9c5ef1ca5d493f [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 com.android.tools.deployer;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.testutils.AssumeUtil;
import com.android.testutils.TestUtils;
import com.android.tools.deployer.devices.FakeDevice;
import com.android.tools.deployer.rules.ApiLevel;
import com.android.tools.deployer.rules.FakeDeviceConnection;
import com.android.utils.FileUtils;
import com.android.utils.ILogger;
import com.google.common.base.Charsets;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@RunWith(ApiLevel.class)
public class OptimisticInstallTest {
@Rule public TestName name = new TestName();
@Rule @ApiLevel.Init public FakeDeviceConnection connection;
private static final String BASE = "tools/base/deploy/deployer/src/test/resource/";
private static File installersPath;
private static File dexDbFile;
private FakeDevice device;
private DeployerRunner runner;
private DeploymentCacheDatabase cacheDb;
private SqlApkFileDatabase dexDb;
private UIService service;
private ILogger logger;
@BeforeClass
public static void prepare() throws Exception {
AssumeUtil.assumeNotWindows();
installersPath = DeployerTestUtils.prepareInstaller();
// Fill in the database file by calling dump() at least once.
// From then on, we will just keep copying this file and reusing it
// for every test.
dexDbFile = File.createTempFile("cached_db", ".bin");
dexDbFile.delete();
new SqlApkFileDatabase(dexDbFile, null).dump();
dexDbFile.deleteOnExit();
}
@Before
public void setUp() throws Exception {
device = connection.getDevice();
service = Mockito.mock(UIService.class);
logger = new TestLogger();
File dbFile = File.createTempFile("test_db", ".bin");
dbFile.deleteOnExit();
FileUtils.copyFile(dexDbFile, dbFile);
dexDb = new SqlApkFileDatabase(dbFile, null);
cacheDb = new DeploymentCacheDatabase(2);
runner = new DeployerRunner(cacheDb, dexDb, service);
}
@After
public void tearDown() throws Exception {
System.out.print(getLogcatContent(device));
Mockito.verifyNoMoreInteractions(service);
}
@Test
@ApiLevel.InRange(max = 29)
public void noOptimisticInstallBeforeApi30() throws Exception {
assertTrue(device.getApps().isEmpty());
Path file = TestUtils.resolveWorkspacePath(BASE + "sample.apk");
assertEquals(
0,
runDeployCommand(
"install",
"com.example.helloworld",
file.toString(),
"--force-full-install",
"--optimistic-install"));
assertEquals(1, device.getApps().size());
assertInstalled("com.example.helloworld", file);
assertMetrics(
runner.getMetrics(),
"DELTAINSTALL:DISABLED",
"INSTALL:OK",
"DDMLIB_UPLOAD",
"DDMLIB_INSTALL");
}
@Test
@ApiLevel.InRange(min = 30)
public void fallBackIfNotInstalled() throws Exception {
assertTrue(device.getApps().isEmpty());
Path file = TestUtils.resolveWorkspacePath(BASE + "sample.apk");
assertEquals(
0,
runDeployCommand(
"install",
"com.example.helloworld",
file.toString(),
"--optimistic-install"));
assertEquals(1, device.getApps().size());
assertInstalled("com.example.helloworld", file);
assertMetrics(
runner.getMetrics(),
"IWI_INSTALL_DUMP:DUMP_UNKNOWN_PACKAGE",
":Success",
":Success",
":Success",
"PARSE_PATHS:Success",
"OPTIMISTIC_INSTALL:Failed",
"DELTAINSTALL:DUMP_UNKNOWN_PACKAGE",
"INSTALL:OK",
"DDMLIB_UPLOAD",
"DDMLIB_INSTALL");
}
@Test
@ApiLevel.InRange(min = 30)
public void fallBackOnAddedSplit() throws Exception {
assertTrue(device.getApps().isEmpty());
Path base = TestUtils.resolveWorkspacePath(BASE + "apks/simple.apk");
Path split = TestUtils.resolveWorkspacePath(BASE + "apks/split.apk");
assertEquals(
0,
runDeployCommand(
"install",
"com.example.simpleapp",
base.toString(),
"--force-full-install"));
assertInstalled("com.example.simpleapp", base);
assertMetrics(
runner.getMetrics(),
"DELTAINSTALL:DISABLED",
"INSTALL:OK",
"DDMLIB_UPLOAD",
"DDMLIB_INSTALL");
assertEquals(
0,
runDeployCommand(
"install",
"com.example.simpleapp",
base.toString(),
split.toString(),
"--optimistic-install"));
assertInstalled("com.example.simpleapp", base, split);
assertMetrics(
runner.getMetrics(),
"IWI_INSTALL_DUMP:Success",
"IWI_INSTALL_DIFF:DIFFERENT_NUMBER_OF_APKS",
":Success",
":Success",
":Success",
"PARSE_PATHS:Success",
"OPTIMISTIC_INSTALL:Failed",
"DELTAINSTALL:CANNOT_GENERATE_DELTA",
"INSTALL:OK",
"DDMLIB_UPLOAD",
"DDMLIB_INSTALL");
}
private int runDeployCommand(String... args) {
args = Arrays.copyOf(args, args.length + 1);
args[args.length - 1] = "--installers-path=" + installersPath.getAbsolutePath();
return runner.run(args, logger);
}
public void assertInstalled(String packageName, Path... files) throws IOException {
assertArrayEquals(new String[] {packageName}, device.getApps().toArray());
List<String> paths = device.getAppPaths(packageName);
assertEquals(files.length, paths.size());
for (int i = 0; i < paths.size(); i++) {
byte[] expected = Files.readAllBytes(files[i]);
assertArrayEquals(expected, device.readFile(paths.get(i)));
}
}
private void assertMetrics(List<DeployMetric> metrics, String... expected) {
String[] actual =
metrics.stream()
.map(m -> m.getName() + (m.hasStatus() ? ":" + m.getStatus() : ""))
.toArray(String[]::new);
assertArrayEquals(expected, actual);
}
private static String getLogcatContent(FakeDevice device) {
try {
return new String(Files.readAllBytes(device.getLogcatFile().toPath()), Charsets.UTF_8);
} catch (IOException io) {
return "";
}
}
}