1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tradefed.targetprep; 18 19 import static org.junit.Assert.assertArrayEquals; 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertThrows; 23 import static org.junit.Assert.assertTrue; 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.ArgumentMatchers.anyString; 26 import static org.mockito.ArgumentMatchers.eq; 27 import static org.mockito.Mockito.atLeastOnce; 28 import static org.mockito.Mockito.never; 29 import static org.mockito.Mockito.times; 30 import static org.mockito.Mockito.verify; 31 import static org.mockito.Mockito.when; 32 33 import com.android.tradefed.device.DeviceNotAvailableException; 34 import com.android.tradefed.device.ITestDevice; 35 import com.android.tradefed.util.CommandResult; 36 import com.android.tradefed.util.CommandStatus; 37 38 import com.google.common.collect.ImmutableMap; 39 import com.google.common.collect.ImmutableMultimap; 40 41 import org.junit.Before; 42 import org.junit.Rule; 43 import org.junit.Test; 44 import org.junit.rules.TemporaryFolder; 45 import org.junit.runner.RunWith; 46 import org.junit.runners.JUnit4; 47 import org.mockito.Mock; 48 import org.mockito.MockitoAnnotations; 49 50 import java.io.File; 51 import java.nio.file.Files; 52 import java.nio.file.Path; 53 import java.nio.file.Paths; 54 import java.util.Collections; 55 import java.util.HashSet; 56 import java.util.Map; 57 58 /** Unit test for {@link ModulePusher} */ 59 @RunWith(JUnit4.class) 60 public final class ModulePusherTest { 61 public static final String TESTHARNESS_ENABLE = "cmd testharness enable"; 62 private static final String APEX_PACKAGE_NAME = "com.android.FAKE.APEX.PACKAGE.NAME"; 63 private static final String APK_PACKAGE_NAME = "com.android.FAKE.APK.PACKAGE.NAME"; 64 private static final String SPLIT_APK_PACKAGE_NAME = "com.android.SPLIT.FAKE.APK.PACKAGE.NAME"; 65 private static final String APEX_PRELOAD_NAME = APEX_PACKAGE_NAME + ".apex"; 66 private static final String APK_PRELOAD_NAME = APK_PACKAGE_NAME + ".apk"; 67 private static final String SPLIT_APK_PRELOAD_NAME = SPLIT_APK_PACKAGE_NAME + ".apk"; 68 private static final String APEX_PATH_ON_DEVICE = "/system/apex/" + APEX_PRELOAD_NAME; 69 private static final String APK_PATH_ON_DEVICE = "/system/apps/" + APK_PRELOAD_NAME; 70 public static final String SPLIT_APK_PACKAGE_ON_DEVICE = 71 "/system/apps/com.android.SPLIT.FAKE.APK.PACKAGE.NAME"; 72 private static final String SPLIT_APK_PATH_ON_DEVICE = 73 SPLIT_APK_PACKAGE_ON_DEVICE + "/" + SPLIT_APK_PRELOAD_NAME; 74 private static final String HDPI_PATH_ON_DEVICE = 75 SPLIT_APK_PACKAGE_ON_DEVICE + "/com.android.SPLIT.FAKE.APK.PACKAGE.NAME-hdpi.apk"; 76 private static final String TEST_APEX_NAME = "fakeApex.apex"; 77 private static final String TEST_APK_NAME = "fakeApk.apk"; 78 private static final String TEST_SPLIT_APK_NAME = "FakeSplit/base-master.apk"; 79 private static final String TEST_HDPI_APK_NAME = "FakeSplit/base-hdpi.apk"; 80 81 @Rule public TemporaryFolder testDir = new TemporaryFolder(); 82 private static final String SERIAL = "serial"; 83 private static final int API = 30; 84 private ModulePusher mPusher; 85 @Mock ITestDevice mMockDevice; 86 private File mFakeApex; 87 private File mFakeApk; 88 private File mFakeSplitDir; 89 private File mFakeSplitApk; 90 private File mFakeHdpiApk; 91 92 @Before setUp()93 public void setUp() throws Exception { 94 MockitoAnnotations.initMocks(this); 95 96 mFakeApex = testDir.newFile(TEST_APEX_NAME); 97 mFakeApk = testDir.newFile(TEST_APK_NAME); 98 mFakeSplitDir = testDir.newFolder("FakeSplit"); 99 mFakeSplitApk = testDir.newFile(TEST_SPLIT_APK_NAME); 100 mFakeHdpiApk = testDir.newFile(TEST_HDPI_APK_NAME); 101 102 when(mMockDevice.executeAdbCommand("disable-verity")).thenReturn("disabled"); 103 when(mMockDevice.executeAdbCommand("remount")).thenReturn("remount succeeded"); 104 when(mMockDevice.getApiLevel()).thenReturn(API); 105 when(mMockDevice.getSerialNumber()).thenReturn(SERIAL); 106 when(mMockDevice.getDeviceDescriptor()).thenReturn(null); 107 CommandResult apexCr = getCommandResult(APEX_PRELOAD_NAME + "\n"); 108 when(mMockDevice.executeShellV2Command("ls /system/apex/")).thenReturn(apexCr); 109 CommandResult cr = getCommandResult("Good!"); 110 when(mMockDevice.executeShellV2Command("pm get-moduleinfo | grep 'com.google'")) 111 .thenReturn(cr); 112 when(mMockDevice.executeShellV2Command("cmd testharness enable")).thenReturn(cr); 113 CommandResult cr1 = 114 getCommandResult("package:/system/apex/com.android.FAKE.APEX.PACKAGE.NAME.apex\n"); 115 when(mMockDevice.executeShellV2Command("pm path " + APEX_PACKAGE_NAME)).thenReturn(cr1); 116 CommandResult cr2 = 117 getCommandResult("package:/system/apps/com.android.FAKE.APK.PACKAGE.NAME.apk\n"); 118 when(mMockDevice.executeShellV2Command("pm path " + APK_PACKAGE_NAME)).thenReturn(cr2); 119 CommandResult cr3 = 120 getCommandResult( 121 String.format( 122 "package:%s\npackage:%s\n", 123 SPLIT_APK_PATH_ON_DEVICE, HDPI_PATH_ON_DEVICE)); 124 when(mMockDevice.executeShellV2Command("pm path " + SPLIT_APK_PACKAGE_NAME)) 125 .thenReturn(cr3); 126 CommandResult cr4 = 127 getCommandResult( 128 "com.android.SPLIT.FAKE.APK.PACKAGE.NAME.apk\n" 129 + "com.android.SPLIT.FAKE.APK.PACKAGE.NAME-hdpi.apk\n"); 130 when(mMockDevice.executeShellV2Command( 131 "ls /system/apps/com.android.SPLIT.FAKE.APK.PACKAGE.NAME")) 132 .thenReturn(cr4); 133 134 mPusher = 135 new ModulePusher(mMockDevice, 0, 0) { 136 @Override 137 protected ModulePusher.ModuleInfo retrieveModuleInfo(File packageFile) { 138 if (mFakeApex.equals(packageFile)) { 139 return ModuleInfo.create(APEX_PACKAGE_NAME, "2", false); 140 } else if (mFakeApk.equals(packageFile)) { 141 return ModuleInfo.create(APK_PACKAGE_NAME, "2", true); 142 } else { 143 return ModuleInfo.create(SPLIT_APK_PACKAGE_NAME, "2", true); 144 } 145 } 146 147 @Override 148 protected void waitForDeviceToBeResponsive(long waitTime) {} 149 }; 150 } 151 152 /** Test parsePackageVersionCodes get the right version codes. */ 153 @Test testParsePackageVersionCodes()154 public void testParsePackageVersionCodes() { 155 String outputs = 156 "package:com.google.android.media versionCode:301800200\n" 157 + "package:com.google.android.mediaprovider versionCode:301501700\n" 158 + "package: com.google.wrong.pattern version:\n" 159 + "package:com.google.empty versionCode:\n" 160 + "package:com.google.android.media.swcodec versionCode:301700000"; 161 162 Map<String, String> versionCodes = mPusher.parsePackageVersionCodes(outputs); 163 164 assertEquals(3, versionCodes.size()); 165 assertEquals("301800200", versionCodes.get("com.google.android.media")); 166 assertEquals("301501700", versionCodes.get("com.google.android.mediaprovider")); 167 assertEquals("301700000", versionCodes.get("com.google.android.media.swcodec")); 168 } 169 170 /** Test getting paths on device. */ 171 @Test testGetPathsOnDevice()172 public void testGetPathsOnDevice() throws Exception { 173 String[] files = mPusher.getPathsOnDevice(mMockDevice, SPLIT_APK_PACKAGE_NAME); 174 175 assertArrayEquals(new String[] {SPLIT_APK_PATH_ON_DEVICE, HDPI_PATH_ON_DEVICE}, files); 176 } 177 178 @Test testGetApexPathUnderSystem()179 public void testGetApexPathUnderSystem() throws Exception { 180 CommandResult apexCr = 181 getCommandResult( 182 "com.android.apex.cts.shim.apex\n" 183 + "com.android.wifi.capex\n" 184 + "com.android.appsearch.apex\n" 185 + "com.google.android.adbd_trimmed_compressed.apex\n" 186 + "com.google.android.art_compressed.apex\n" 187 + "com.google.android.media.swcodec_compressed.apex\n" 188 + "com.google.android.media_compressed.apex\n" 189 + "com.google.android.mediaprovider_compressed.apex\n" 190 + "com.google.mainline.primary.libs.apex"); 191 when(mMockDevice.executeShellV2Command("ls /system/apex/")).thenReturn(apexCr); 192 193 assertEquals( 194 Paths.get("/system/apex/com.android.appsearch.apex"), 195 mPusher.getApexPathUnderSystem(mMockDevice, "com.android.appsearch")); 196 assertEquals( 197 Paths.get("/system/apex/com.google.android.media_compressed.apex"), 198 mPusher.getApexPathUnderSystem(mMockDevice, "com.google.android.media")); 199 assertEquals( 200 Paths.get("/system/apex/com.google.android.adbd_trimmed_compressed.apex"), 201 mPusher.getApexPathUnderSystem(mMockDevice, "com.google.android.adbd")); 202 } 203 204 /** Test getting preload paths for split apks. */ 205 @Test testGetPreLoadPathsOnSplitApk()206 public void testGetPreLoadPathsOnSplitApk() throws Exception { 207 File[] files = new File[] {mFakeSplitApk, mFakeHdpiApk}; 208 Path[] actual = 209 new Path[] { 210 Paths.get(SPLIT_APK_PACKAGE_ON_DEVICE), 211 Paths.get(SPLIT_APK_PATH_ON_DEVICE), 212 Paths.get(HDPI_PATH_ON_DEVICE) 213 }; 214 215 Path[] results = mPusher.getPreloadPaths(mMockDevice, files, SPLIT_APK_PACKAGE_NAME, 30); 216 217 assertArrayEquals(results, actual); 218 } 219 220 /** Test getting preload paths for apex */ 221 @Test testGetPreLoadPathsOnApex()222 public void testGetPreLoadPathsOnApex() throws Exception { 223 File[] files = new File[] {mFakeApex}; 224 Path[] actual = new Path[] {Paths.get(APEX_PATH_ON_DEVICE)}; 225 226 Path[] results = mPusher.getPreloadPaths(mMockDevice, files, APEX_PACKAGE_NAME, 30); 227 228 assertArrayEquals(results, actual); 229 } 230 231 /** Test getting default path if `pm path` fails on Q */ 232 @Test testGetPreLoadPathsOnQReturnDefault()233 public void testGetPreLoadPathsOnQReturnDefault() throws Exception { 234 File[] files = new File[] {mFakeApex}; 235 Path[] actual = new Path[] {Paths.get(APEX_PATH_ON_DEVICE)}; 236 when(mMockDevice.executeShellV2Command("pm path " + APEX_PACKAGE_NAME)) 237 .thenReturn(getCommandResult("")); 238 239 Path[] results = mPusher.getPreloadPaths(mMockDevice, files, APEX_PACKAGE_NAME, 29); 240 241 assertArrayEquals(results, actual); 242 } 243 244 /** Test get preload paths throws an exception if `pm path` fails on S */ 245 @Test testGetPreLoadPathsOnSThrowsException()246 public void testGetPreLoadPathsOnSThrowsException() throws Exception { 247 File[] files = new File[] {mFakeApex}; 248 when(mMockDevice.executeShellV2Command("pm path " + APEX_PACKAGE_NAME)) 249 .thenReturn(getCommandResult("")); 250 251 assertThrows( 252 ModulePusher.ModulePushError.class, 253 () -> mPusher.getPreloadPaths(mMockDevice, files, APEX_PACKAGE_NAME, 31)); 254 } 255 256 /** Test getting preload paths for non-split apk. */ 257 @Test testGetPreLoadPathsOnApk()258 public void testGetPreLoadPathsOnApk() throws Exception { 259 File[] files = new File[] {mFakeApk}; 260 Path[] actual = new Path[] {Paths.get(APK_PATH_ON_DEVICE)}; 261 262 Path[] results = mPusher.getPreloadPaths(mMockDevice, files, APK_PACKAGE_NAME, 30); 263 264 assertArrayEquals(results, actual); 265 } 266 267 /** Test getting preload paths for /data/apex/decompressed */ 268 @Test testGetPreLoadPathsOverridesApexDecompressedPath()269 public void testGetPreLoadPathsOverridesApexDecompressedPath() throws Exception { 270 File[] files = new File[] {mFakeApex}; 271 when(mMockDevice.executeShellV2Command("pm path " + APEX_PACKAGE_NAME)) 272 .thenReturn( 273 getCommandResult( 274 "package:/data/apex/decompressed/" 275 + APEX_PACKAGE_NAME 276 + "@310000000.decompressed.apex\n")); 277 Path[] actual = new Path[] {Paths.get(APEX_PATH_ON_DEVICE)}; 278 279 Path[] results = mPusher.getPreloadPaths(mMockDevice, files, APEX_PACKAGE_NAME, 31); 280 281 assertArrayEquals(results, actual); 282 } 283 284 /** Test install modules when there are non-split files to push. */ 285 @Test testInstallModulesSuccess()286 public void testInstallModulesSuccess() throws Exception { 287 Path dir = mFakeApex.toPath().getParent(); 288 File renamedApex = dir.resolve(APEX_PRELOAD_NAME).toFile(); 289 File renamedApk = dir.resolve(APK_PRELOAD_NAME).toFile(); 290 when(mMockDevice.pushFile(renamedApex, APEX_PATH_ON_DEVICE)).thenReturn(true); 291 when(mMockDevice.pushFile(renamedApk, APK_PATH_ON_DEVICE)).thenReturn(true); 292 setVersionCodesOnDevice(ImmutableMap.of(APEX_PACKAGE_NAME, "2", APK_PACKAGE_NAME, "2")); 293 activateVersion(2); 294 295 mPusher.installModules( 296 ImmutableMultimap.of(APEX_PACKAGE_NAME, mFakeApex, APK_PACKAGE_NAME, mFakeApk), 297 /*factoryReset=*/ true, 298 /*disablePackageCache=*/ false); 299 300 verify(mMockDevice, atLeastOnce()).getActiveApexes(); 301 verify(mMockDevice, times(2)).reboot(); 302 verify(mMockDevice, times(1)).executeShellV2Command("cmd testharness enable"); 303 verify(mMockDevice, times(1)).pushFile(renamedApex, APEX_PATH_ON_DEVICE); 304 verify(mMockDevice, times(1)).pushFile(renamedApk, APK_PATH_ON_DEVICE); 305 assertFalse(Files.exists(mFakeApex.toPath())); 306 assertFalse(Files.exists(mFakeApk.toPath())); 307 assertTrue(Files.isRegularFile(renamedApex.toPath())); 308 assertTrue(Files.isRegularFile(renamedApk.toPath())); 309 } 310 311 /** Test install modules when there are non-split files to push with reboot. */ 312 @Test testInstallModulesSuccessViaReboot()313 public void testInstallModulesSuccessViaReboot() throws Exception { 314 Path dir = mFakeApex.toPath().getParent(); 315 File renamedApex = dir.resolve(APEX_PRELOAD_NAME).toFile(); 316 File renamedApk = dir.resolve(APK_PRELOAD_NAME).toFile(); 317 when(mMockDevice.pushFile(renamedApex, APEX_PATH_ON_DEVICE)).thenReturn(true); 318 when(mMockDevice.pushFile(renamedApk, APK_PATH_ON_DEVICE)).thenReturn(true); 319 setVersionCodesOnDevice(ImmutableMap.of(APEX_PACKAGE_NAME, "2", APK_PACKAGE_NAME, "2")); 320 activateVersion(2); 321 322 mPusher.installModules( 323 ImmutableMultimap.of(APEX_PACKAGE_NAME, mFakeApex, APK_PACKAGE_NAME, mFakeApk), 324 /*factoryReset=*/ false, 325 /*disablePackageCache=*/ false); 326 327 verify(mMockDevice, atLeastOnce()).getActiveApexes(); 328 verify(mMockDevice, never()).executeShellV2Command("cmd testharness enable"); 329 verify(mMockDevice, times(3)).reboot(); 330 verify(mMockDevice, times(1)).pushFile(renamedApex, APEX_PATH_ON_DEVICE); 331 verify(mMockDevice, times(1)).pushFile(renamedApk, APK_PATH_ON_DEVICE); 332 assertFalse(Files.exists(mFakeApex.toPath())); 333 assertFalse(Files.exists(mFakeApk.toPath())); 334 assertTrue(Files.isRegularFile(renamedApex.toPath())); 335 assertTrue(Files.isRegularFile(renamedApk.toPath())); 336 } 337 338 /** Test install modules when there are apks to push. */ 339 @Test testInstallModulesSuccessWithApks()340 public void testInstallModulesSuccessWithApks() throws Exception { 341 Path dir = mFakeApex.toPath().getParent(); 342 File renamedApex = dir.resolve(APEX_PRELOAD_NAME).toFile(); 343 File renamedSplitApk = dir.resolve(SPLIT_APK_PACKAGE_NAME).toFile(); 344 when(mMockDevice.pushFile(renamedApex, APEX_PATH_ON_DEVICE)).thenReturn(true); 345 when(mMockDevice.pushDir(renamedSplitApk, SPLIT_APK_PACKAGE_ON_DEVICE)).thenReturn(true); 346 setVersionCodesOnDevice( 347 ImmutableMap.of(APEX_PACKAGE_NAME, "2", SPLIT_APK_PACKAGE_NAME, "2")); 348 activateVersion(2); 349 350 mPusher.installModules( 351 ImmutableMultimap.of( 352 APEX_PACKAGE_NAME, 353 mFakeApex, 354 SPLIT_APK_PACKAGE_NAME, 355 mFakeSplitApk, 356 SPLIT_APK_PACKAGE_NAME, 357 mFakeHdpiApk), 358 /*factoryReset=*/ true, 359 /*disablePackageCache=*/ false); 360 361 verify(mMockDevice, atLeastOnce()).getActiveApexes(); 362 verify(mMockDevice, times(1)).executeShellV2Command("cmd testharness enable"); 363 verify(mMockDevice, times(2)).reboot(); 364 verify(mMockDevice, times(1)).pushFile(renamedApex, APEX_PATH_ON_DEVICE); 365 verify(mMockDevice, times(1)).pushDir(renamedSplitApk, SPLIT_APK_PACKAGE_ON_DEVICE); 366 assertFalse(Files.exists(mFakeApex.toPath())); 367 assertFalse(Files.exists(mFakeSplitDir.toPath())); 368 assertTrue(Files.isRegularFile(renamedApex.toPath())); 369 assertTrue(Files.isDirectory(renamedSplitApk.toPath())); 370 assertTrue(Files.isRegularFile(renamedSplitApk.toPath().resolve("base-master.apk"))); 371 assertTrue(Files.isRegularFile(renamedSplitApk.toPath().resolve("base-hdpi.apk"))); 372 } 373 374 /** Test install modules when disable package cache. */ 375 @Test testInstallModulesSuccessDisablePackageCache()376 public void testInstallModulesSuccessDisablePackageCache() throws Exception { 377 Path dir = mFakeApex.toPath().getParent(); 378 File renamedApex = dir.resolve(APEX_PRELOAD_NAME).toFile(); 379 File renamedApk = dir.resolve(APK_PRELOAD_NAME).toFile(); 380 when(mMockDevice.pushFile(renamedApex, APEX_PATH_ON_DEVICE)).thenReturn(true); 381 when(mMockDevice.pushFile(renamedApk, APK_PATH_ON_DEVICE)).thenReturn(true); 382 setVersionCodesOnDevice(ImmutableMap.of(APEX_PACKAGE_NAME, "2", APK_PACKAGE_NAME, "2")); 383 activateVersion(2); 384 385 mPusher.installModules( 386 ImmutableMultimap.of(APEX_PACKAGE_NAME, mFakeApex, APK_PACKAGE_NAME, mFakeApk), 387 /*factoryReset=*/ false, 388 /*disablePackageCache=*/ true); 389 390 verify(mMockDevice, atLeastOnce()).getActiveApexes(); 391 verify(mMockDevice, never()).executeShellV2Command("cmd testharness enable"); 392 verify(mMockDevice, times(1)).executeShellV2Command("rm -Rf /data/system/package_cache/"); 393 verify(mMockDevice, times(3)).reboot(); 394 verify(mMockDevice, times(1)).pushFile(renamedApex, APEX_PATH_ON_DEVICE); 395 verify(mMockDevice, times(1)).pushFile(renamedApk, APK_PATH_ON_DEVICE); 396 assertFalse(Files.exists(mFakeApex.toPath())); 397 assertFalse(Files.exists(mFakeApk.toPath())); 398 assertTrue(Files.isRegularFile(renamedApex.toPath())); 399 assertTrue(Files.isRegularFile(renamedApk.toPath())); 400 } 401 402 /** Throws exception when missing one version code. */ 403 @Test testInstallModulesThrowsExceptionWhenMissingVersionCode()404 public void testInstallModulesThrowsExceptionWhenMissingVersionCode() throws Exception { 405 Path dir = mFakeApex.toPath().getParent(); 406 File renamedApex = dir.resolve(APEX_PRELOAD_NAME).toFile(); 407 File renamedApk = dir.resolve(APK_PRELOAD_NAME).toFile(); 408 when(mMockDevice.pushFile(renamedApex, APEX_PATH_ON_DEVICE)).thenReturn(true); 409 when(mMockDevice.pushFile(renamedApk, APK_PATH_ON_DEVICE)).thenReturn(true); 410 setVersionCodesOnDevice(ImmutableMap.of(APEX_PACKAGE_NAME, "2")); 411 activateVersion(2); 412 413 assertThrows( 414 ModulePusher.ModulePushError.class, 415 () -> 416 mPusher.installModules( 417 ImmutableMultimap.of( 418 APEX_PACKAGE_NAME, mFakeApex, APK_PACKAGE_NAME, mFakeApk), 419 /*factoryReset=*/ false, 420 /*disablePackageCache=*/ false)); 421 verify(mMockDevice, atLeastOnce()).getActiveApexes(); 422 verify(mMockDevice, never()).executeShellV2Command("cmd testharness enable"); 423 verify(mMockDevice, times(2)).reboot(); 424 verify(mMockDevice, never()).pushFile(any(File.class), anyString()); 425 } 426 427 /** Throws ModulePushError if the only file push fails. */ 428 @Test testInstallModulesFailureIfPushFails()429 public void testInstallModulesFailureIfPushFails() throws Exception { 430 Path dir = mFakeApex.toPath().getParent(); 431 File renamedApex = dir.resolve(APEX_PRELOAD_NAME).toFile(); 432 when(mMockDevice.pushFile(any(), eq(APEX_PATH_ON_DEVICE))).thenReturn(false); 433 setVersionCodesOnDevice(ImmutableMap.of(APEX_PACKAGE_NAME, "2")); 434 435 assertThrows( 436 ModulePusher.ModulePushError.class, 437 () -> 438 mPusher.installModules( 439 ImmutableMultimap.of(APEX_PACKAGE_NAME, mFakeApex), 440 /*factoryReset=*/ true, 441 /*disablePackageCache=*/ false)); 442 verify(mMockDevice, never()).executeShellV2Command(TESTHARNESS_ENABLE); 443 verify(mMockDevice, times(1)).pushFile(renamedApex, APEX_PATH_ON_DEVICE); 444 } 445 446 /** Throws ModulePushError if any file push fails and there are more than one test files. */ 447 @Test testInstallModulesFailureIfAnyPushFails()448 public void testInstallModulesFailureIfAnyPushFails() throws Exception { 449 Path dir = mFakeApex.toPath().getParent(); 450 File renamedApex = dir.resolve(APEX_PRELOAD_NAME).toFile(); 451 File renamedApk = dir.resolve(APK_PRELOAD_NAME).toFile(); 452 when(mMockDevice.pushFile(any(), eq(APEX_PATH_ON_DEVICE))).thenReturn(true); 453 when(mMockDevice.pushFile(any(), eq(APK_PATH_ON_DEVICE))).thenReturn(false); 454 setVersionCodesOnDevice(ImmutableMap.of(APEX_PACKAGE_NAME, "2", APK_PACKAGE_NAME, "2")); 455 456 assertThrows( 457 ModulePusher.ModulePushError.class, 458 () -> 459 mPusher.installModules( 460 ImmutableMultimap.of( 461 APEX_PACKAGE_NAME, mFakeApex, APK_PACKAGE_NAME, mFakeApk), 462 /*factoryReset=*/ true, 463 /*disablePackageCache=*/ false)); 464 465 verify(mMockDevice, never()).executeShellV2Command(TESTHARNESS_ENABLE); 466 verify(mMockDevice, times(1)).pushFile(renamedApex, APEX_PATH_ON_DEVICE); 467 verify(mMockDevice, times(1)).pushFile(renamedApk, APK_PATH_ON_DEVICE); 468 assertFalse(Files.exists(mFakeApex.toPath())); 469 assertFalse(Files.exists(mFakeApk.toPath())); 470 assertTrue(Files.isRegularFile(renamedApex.toPath())); 471 assertTrue(Files.isRegularFile(renamedApk.toPath())); 472 } 473 474 /** Throws ModulePushError if activated version code is different. */ 475 @Test testInstallModulesFailureIfActivationVersionCodeDifferent()476 public void testInstallModulesFailureIfActivationVersionCodeDifferent() throws Exception { 477 when(mMockDevice.pushFile(any(), eq(APEX_PATH_ON_DEVICE))).thenReturn(true); 478 setVersionCodesOnDevice(ImmutableMap.of(APEX_PACKAGE_NAME, "2")); 479 activateVersion(1); 480 481 assertThrows( 482 ModulePusher.ModulePushError.class, 483 () -> 484 mPusher.installModules( 485 ImmutableMultimap.of(APEX_PACKAGE_NAME, mFakeApex), 486 /*factoryReset=*/ true, 487 /*disablePackageCache=*/ false)); 488 verify(mMockDevice, times(1)).pushFile(any(), any()); 489 } 490 491 /** Throws ModulePushError if failed to activate. */ 492 @Test testInstallModulesFailureIfActivationFailed()493 public void testInstallModulesFailureIfActivationFailed() throws Exception { 494 when(mMockDevice.pushFile(any(), eq(APEX_PATH_ON_DEVICE))).thenReturn(true); 495 setVersionCodesOnDevice(ImmutableMap.of(APEX_PACKAGE_NAME, "2")); 496 when(mMockDevice.getActiveApexes()).thenReturn(new HashSet<>()); 497 498 assertThrows( 499 ModulePusher.ModulePushError.class, 500 () -> 501 mPusher.installModules( 502 ImmutableMultimap.of(APEX_PACKAGE_NAME, mFakeApex), 503 /*factoryReset=*/ true, 504 /*disablePackageCache=*/ false)); 505 verify(mMockDevice, times(1)).pushFile(any(), any()); 506 } 507 508 /** Throws ModulePushError if version code not updated. */ 509 @Test testInstallModulesFailureIfVersionCodeDifferent()510 public void testInstallModulesFailureIfVersionCodeDifferent() throws Exception { 511 when(mMockDevice.pushFile(any(), eq(APEX_PATH_ON_DEVICE))).thenReturn(true); 512 setVersionCodesOnDevice(ImmutableMap.of(APEX_PACKAGE_NAME, "1")); 513 activateVersion(2); 514 515 assertThrows( 516 ModulePusher.ModulePushError.class, 517 () -> 518 mPusher.installModules( 519 ImmutableMultimap.of(APEX_PACKAGE_NAME, mFakeApex), 520 /*factoryReset=*/ true, 521 /*disablePackageCache=*/ false)); 522 verify(mMockDevice, times(1)).pushFile(any(), any()); 523 } 524 setVersionCodesOnDevice(Map<String, String> updatedPackageCodes)525 private void setVersionCodesOnDevice(Map<String, String> updatedPackageCodes) throws Exception { 526 StringBuilder apkSb1 = new StringBuilder(); 527 StringBuilder apkSb2 = new StringBuilder(); 528 StringBuilder apexSb1 = new StringBuilder(); 529 StringBuilder apexSb2 = new StringBuilder(); 530 for (String pack : updatedPackageCodes.keySet()) { 531 String old = String.format("package:%s versionCode:%s\n", pack, "1"); 532 String updated = 533 String.format( 534 "package:%s versionCode:%s\n", pack, updatedPackageCodes.get(pack)); 535 if (pack.contains("APEX")) { 536 apexSb1.append(old); 537 apexSb2.append(updated); 538 } else { 539 apkSb1.append(old); 540 apkSb2.append(updated); 541 } 542 } 543 when(mMockDevice.executeShellV2Command( 544 "cmd package list packages --apex-only --show-versioncode| grep" 545 + " 'com.google'")) 546 .thenReturn(getCommandResult(apexSb1.toString())) 547 .thenReturn(getCommandResult(apexSb2.toString())); 548 when(mMockDevice.executeShellV2Command( 549 "cmd package list packages --show-versioncode| grep 'com.google'")) 550 .thenReturn(getCommandResult(apkSb1.toString())) 551 .thenReturn(getCommandResult(apkSb2.toString())); 552 } 553 activateVersion(long versionCode)554 private void activateVersion(long versionCode) throws DeviceNotAvailableException { 555 ITestDevice.ApexInfo fakeApexData = 556 new ITestDevice.ApexInfo(APEX_PACKAGE_NAME, versionCode, APEX_PATH_ON_DEVICE); 557 when(mMockDevice.getActiveApexes()) 558 .thenReturn(new HashSet<>(Collections.singletonList(fakeApexData))); 559 } 560 getCommandResult(String output)561 private static CommandResult getCommandResult(String output) { 562 CommandResult result = new CommandResult(); 563 result.setStatus(CommandStatus.SUCCESS); 564 result.setStdout(output); 565 result.setExitCode(0); 566 return result; 567 } 568 } 569