1 /* 2 * Copyright (C) 2010 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 package com.android.tradefed.device; 17 18 import static com.google.common.truth.Truth.assertThat; 19 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertNotNull; 23 import static org.junit.Assert.assertNull; 24 import static org.junit.Assert.assertTrue; 25 import static org.junit.Assert.fail; 26 import static org.junit.Assume.assumeFalse; 27 import static org.junit.Assume.assumeTrue; 28 import static org.mockito.Mockito.mock; 29 30 import com.android.ddmlib.IDevice; 31 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; 32 import com.android.tradefed.TestAppConstants; 33 import com.android.tradefed.log.LogUtil.CLog; 34 import com.android.tradefed.result.CollectingTestListener; 35 import com.android.tradefed.result.FileInputStreamSource; 36 import com.android.tradefed.result.InputStreamSource; 37 import com.android.tradefed.result.TestStatus; 38 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 39 import com.android.tradefed.testtype.IDeviceTest; 40 import com.android.tradefed.util.CommandResult; 41 import com.android.tradefed.util.CommandStatus; 42 import com.android.tradefed.util.FileUtil; 43 import com.android.tradefed.util.KeyguardControllerState; 44 import com.android.tradefed.util.ProcessInfo; 45 import com.android.tradefed.util.RunUtil; 46 import com.android.tradefed.util.StreamUtil; 47 48 import org.junit.Assume; 49 import org.junit.Before; 50 import org.junit.Ignore; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 54 import java.awt.image.BufferedImage; 55 import java.io.BufferedInputStream; 56 import java.io.File; 57 import java.io.FileInputStream; 58 import java.io.FileOutputStream; 59 import java.io.IOException; 60 import java.io.InputStream; 61 import java.net.URLConnection; 62 import java.util.Set; 63 import java.util.concurrent.TimeUnit; 64 65 import javax.imageio.ImageIO; 66 67 /** 68 * Functional tests for {@link TestDevice}. 69 * 70 * <p>Requires a physical device to be connected. 71 */ 72 @RunWith(DeviceJUnit4ClassRunner.class) 73 public class TestDeviceFuncTest implements IDeviceTest { 74 75 private TestDevice mTestDevice; 76 private IDeviceStateMonitor mMonitor; 77 /** Expect bugreports to be at least a meg. */ 78 private static final int MIN_BUGREPORT_BYTES = 1024 * 1024; 79 assumeNotVirtualDevice()80 private void assumeNotVirtualDevice() { 81 assumeFalse(getDevice() instanceof RemoteAndroidDevice); 82 return; 83 } 84 85 @Override setDevice(ITestDevice device)86 public void setDevice(ITestDevice device) { 87 mTestDevice = (TestDevice) device; 88 } 89 90 @Override getDevice()91 public ITestDevice getDevice() { 92 return mTestDevice; 93 } 94 95 @Before setUp()96 public void setUp() throws Exception { 97 mMonitor = mTestDevice.getDeviceStateMonitor(); 98 // Ensure at set-up that the device is available. 99 mTestDevice.waitForDeviceAvailable(); 100 } 101 102 /** Simple testcase to ensure that the grabbing a bugreport from a real TestDevice works. */ 103 @Test testBugreport()104 public void testBugreport() throws Exception { 105 InputStreamSource bugreport = mTestDevice.getBugreport(); 106 try { 107 String data = StreamUtil.getStringFromStream(bugreport.createInputStream()); 108 assertTrue( 109 String.format( 110 "Expected at least %d characters; only saw %d", 111 MIN_BUGREPORT_BYTES, data.length()), 112 data.length() >= MIN_BUGREPORT_BYTES); 113 } finally { 114 StreamUtil.cancel(bugreport); 115 } 116 } 117 118 /** Simple testcase to ensure that the grabbing a bugreportz from a real TestDevice works. */ 119 @Test testBugreportz()120 public void testBugreportz() throws Exception { 121 if (mTestDevice.getApiLevel() < 24) { 122 CLog.i("testBugreportz() not supported by this device, skipping."); 123 return; 124 } 125 FileInputStreamSource f = null; 126 try { 127 f = (FileInputStreamSource) mTestDevice.getBugreportz(); 128 assertNotNull(f); 129 FileInputStream contents = (FileInputStream) f.createInputStream(); 130 assertTrue( 131 String.format( 132 "Expected at least %d characters; only saw %d", 133 MIN_BUGREPORT_BYTES, contents.available()), 134 contents.available() >= MIN_BUGREPORT_BYTES); 135 } finally { 136 StreamUtil.cancel(f); 137 if (f != null) { 138 f.cleanFile(); 139 } 140 } 141 } 142 143 /** 144 * Simple normal case test for {@link TestDevice#executeShellCommand(String)}. 145 * 146 * <p>Do a 'shell ls' command, and verify /data and /system are listed in result. 147 */ 148 @Test testExecuteShellCommand()149 public void testExecuteShellCommand() throws DeviceNotAvailableException { 150 CLog.i("testExecuteShellCommand"); 151 assertSimpleShellCommand(); 152 } 153 154 /** 155 * Verify that a simple {@link TestDevice#executeShellCommand(String)} command is successful. 156 */ assertSimpleShellCommand()157 private void assertSimpleShellCommand() throws DeviceNotAvailableException { 158 final String output = mTestDevice.executeShellCommand("ls"); 159 assertTrue(output.contains("data")); 160 assertTrue(output.contains("system")); 161 } 162 163 /** Test install and uninstall of package */ 164 @Test testInstallUninstall()165 public void testInstallUninstall() throws IOException, DeviceNotAvailableException { 166 CLog.i("testInstallUninstall"); 167 // use the wifi util apk 168 File tmpFile = WifiHelper.extractWifiUtilApk(); 169 try { 170 assertWifiApkInstall(tmpFile); 171 } finally { 172 FileUtil.deleteFile(tmpFile); 173 } 174 } 175 176 /** Verifies that the given wifi util apk can be installed and uninstalled successfully */ assertWifiApkInstall(File tmpFile)177 void assertWifiApkInstall(File tmpFile) throws DeviceNotAvailableException { 178 try { 179 mTestDevice.uninstallPackage(WifiHelper.INSTRUMENTATION_PKG); 180 assertFalse( 181 mTestDevice 182 .getInstalledPackageNames() 183 .contains(WifiHelper.INSTRUMENTATION_PKG)); 184 assertNull(mTestDevice.installPackage(tmpFile, false)); 185 assertTrue( 186 mTestDevice 187 .getInstalledPackageNames() 188 .contains(WifiHelper.INSTRUMENTATION_PKG)); 189 assertFalse( 190 "apk file was not cleaned up after install", 191 mTestDevice.doesFileExist( 192 String.format("/data/local/tmp/%s", tmpFile.getName()))); 193 } finally { 194 FileUtil.deleteFile(tmpFile); 195 } 196 } 197 198 /** Test install and uninstall of package with spaces in file name */ 199 @Test testInstallUninstall_space()200 public void testInstallUninstall_space() throws IOException, DeviceNotAvailableException { 201 CLog.i("testInstallUninstall_space"); 202 203 File tmpFile = WifiHelper.extractWifiUtilApk(); 204 File tmpFileSpaces = null; 205 try { 206 tmpFileSpaces = FileUtil.createTempFile("wifi util (3)", ".apk"); 207 FileUtil.copyFile(tmpFile, tmpFileSpaces); 208 assertWifiApkInstall(tmpFileSpaces); 209 } finally { 210 FileUtil.deleteFile(tmpFile); 211 FileUtil.deleteFile(tmpFileSpaces); 212 } 213 } 214 215 /** Push and then pull a file from device, and verify contents are as expected. */ 216 @Test testPushPull_normal()217 public void testPushPull_normal() throws IOException, DeviceNotAvailableException { 218 CLog.i("testPushPull"); 219 File tmpFile = null; 220 File tmpDestFile = null; 221 String deviceFilePath = null; 222 223 try { 224 tmpFile = createTempTestFile(null); 225 String externalStorePath = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); 226 assertNotNull(externalStorePath); 227 deviceFilePath = String.format("%s/%s", externalStorePath, "tmp_testPushPull.txt"); 228 // ensure file does not already exist 229 mTestDevice.deleteFile(deviceFilePath); 230 assertFalse( 231 String.format("%s exists", deviceFilePath), 232 mTestDevice.doesFileExist(deviceFilePath)); 233 234 assertTrue(mTestDevice.pushFile(tmpFile, deviceFilePath)); 235 assertTrue(mTestDevice.doesFileExist(deviceFilePath)); 236 tmpDestFile = FileUtil.createTempFile("tmp", "txt"); 237 assertTrue(mTestDevice.pullFile(deviceFilePath, tmpDestFile)); 238 assertTrue(compareFiles(tmpFile, tmpDestFile)); 239 } finally { 240 FileUtil.deleteFile(tmpFile); 241 if (tmpDestFile != null) { 242 tmpDestFile.delete(); 243 } 244 if (deviceFilePath != null) { 245 mTestDevice.deleteFile(deviceFilePath); 246 } 247 } 248 } 249 250 /** 251 * Push and then pull a file from device, and verify contents are as expected. 252 * 253 * <p>This variant of the test uses "${EXTERNAL_STORAGE}" in the pathname. 254 */ 255 @Test testPushPull_extStorageVariable()256 public void testPushPull_extStorageVariable() throws IOException, DeviceNotAvailableException { 257 CLog.i("testPushPull"); 258 File tmpFile = null; 259 File tmpDestFile = null; 260 File tmpDestFile2 = null; 261 String deviceFilePath = null; 262 final String filename = "tmp_testPushPull.txt"; 263 264 try { 265 tmpFile = createTempTestFile(null); 266 String externalStorePath = "${EXTERNAL_STORAGE}"; 267 assertNotNull(externalStorePath); 268 deviceFilePath = String.format("%s/%s", externalStorePath, filename); 269 // ensure file does not already exist 270 mTestDevice.executeShellCommand(String.format("rm %s", deviceFilePath)); 271 assertFalse( 272 String.format("%s exists", deviceFilePath), 273 mTestDevice.doesFileExist(deviceFilePath)); 274 275 assertTrue(mTestDevice.pushFile(tmpFile, deviceFilePath)); 276 assertTrue(mTestDevice.doesFileExist(deviceFilePath)); 277 tmpDestFile = FileUtil.createTempFile("tmp", "txt"); 278 assertTrue(mTestDevice.pullFile(deviceFilePath, tmpDestFile)); 279 assertTrue(compareFiles(tmpFile, tmpDestFile)); 280 281 tmpDestFile2 = mTestDevice.pullFileFromExternal(filename); 282 assertNotNull(tmpDestFile2); 283 assertTrue(compareFiles(tmpFile, tmpDestFile2)); 284 } finally { 285 FileUtil.deleteFile(tmpFile); 286 FileUtil.deleteFile(tmpDestFile); 287 FileUtil.deleteFile(tmpDestFile2); 288 if (deviceFilePath != null) { 289 mTestDevice.executeShellCommand(String.format("rm %s", deviceFilePath)); 290 } 291 } 292 } 293 294 /** 295 * Test pulling a file from device that does not exist. 296 * 297 * <p>Expect {@link TestDevice#pullFile(String)} to return <code>false</code> 298 */ 299 @Test testPull_noexist()300 public void testPull_noexist() throws DeviceNotAvailableException { 301 CLog.i("testPull_noexist"); 302 303 // make sure the root path is valid 304 String externalStorePath = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); 305 assertNotNull(externalStorePath); 306 String deviceFilePath = String.format("%s/%s", externalStorePath, "thisfiledoesntexist"); 307 assertFalse( 308 String.format("%s exists", deviceFilePath), 309 mTestDevice.doesFileExist(deviceFilePath)); 310 assertNull(mTestDevice.pullFile(deviceFilePath)); 311 } 312 313 /** 314 * Test pulling a file from device into a local file that cannot be written to. 315 * 316 * <p>Expect {@link TestDevice#pullFile(String, File)} to return <code>false</code> 317 */ 318 @Test testPull_nopermissions()319 public void testPull_nopermissions() throws IOException, DeviceNotAvailableException { 320 Assume.assumeFalse( 321 "Skipping test since harness is running as root.", 322 "root".equals(System.getProperty("user.name"))); 323 // Make sure the root path is valid 324 String externalStorePath = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); 325 assertNotNull(externalStorePath); 326 String deviceFilePath = String.format("%s/%s", externalStorePath, "testPull_nopermissions"); 327 // first push a file so we have something to retrieve 328 assertTrue(mTestDevice.pushString("test data", deviceFilePath)); 329 assertTrue( 330 String.format("%s does not exist", deviceFilePath), 331 mTestDevice.doesFileExist(deviceFilePath)); 332 File tmpFile = null; 333 try { 334 tmpFile = FileUtil.createTempFile("testPull_nopermissions", ".txt"); 335 tmpFile.setReadOnly(); 336 assertFalse(mTestDevice.pullFile(deviceFilePath, tmpFile)); 337 } finally { 338 if (tmpFile != null) { 339 tmpFile.setWritable(true); 340 FileUtil.deleteFile(tmpFile); 341 } 342 } 343 } 344 345 /** 346 * Test pushing a file onto device that does not exist. 347 * 348 * <p>Expect {@link TestDevice#pushFile(File, String)} to return <code>false</code> 349 */ 350 @Test testPush_noexist()351 public void testPush_noexist() throws DeviceNotAvailableException { 352 CLog.i("testPush_noexist"); 353 354 // make sure the root path is valid 355 String externalStorePath = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); 356 assertNotNull(externalStorePath); 357 String deviceFilePath = String.format("%s/%s", externalStorePath, "remotepath"); 358 assertFalse(mTestDevice.pushFile(new File("idontexist"), deviceFilePath)); 359 } 360 createTempTestFile(File dir)361 private File createTempTestFile(File dir) throws IOException { 362 File tmpFile = null; 363 try { 364 final String fileContents = "this is the test file contents"; 365 tmpFile = FileUtil.createTempFile("tmp", ".txt", dir); 366 FileUtil.writeToFile(fileContents, tmpFile); 367 return tmpFile; 368 } catch (IOException e) { 369 if (tmpFile != null) { 370 tmpFile.delete(); 371 } 372 throw e; 373 } 374 } 375 376 /** Utility method to do byte-wise content comparison of two files. */ compareFiles(File file1, File file2)377 private boolean compareFiles(File file1, File file2) throws IOException { 378 BufferedInputStream stream1 = null; 379 BufferedInputStream stream2 = null; 380 381 try { 382 stream1 = new BufferedInputStream(new FileInputStream(file1)); 383 stream2 = new BufferedInputStream(new FileInputStream(file2)); 384 boolean eof = false; 385 while (!eof) { 386 int byte1 = stream1.read(); 387 int byte2 = stream2.read(); 388 if (byte1 != byte2) { 389 return false; 390 } 391 eof = byte1 == -1; 392 } 393 return true; 394 } finally { 395 StreamUtil.close(stream1); 396 StreamUtil.close(stream2); 397 } 398 } 399 400 /** 401 * Make sure that we can correctly index directories that have a symlink in the middle. This 402 * verifies a ddmlib bugfix which added/fixed this functionality. 403 */ 404 @Test testListSymlinkDir()405 public void testListSymlinkDir() throws Exception { 406 final String extStore = "/data/local"; 407 408 // Clean up after potential failed run 409 mTestDevice.deleteFile(String.format("%s/testdir", extStore)); 410 mTestDevice.deleteFile(String.format("%s/testdir2/foo.txt", extStore)); 411 mTestDevice.deleteFile(String.format("%s/testdir2", extStore)); 412 413 try { 414 assertEquals( 415 "", 416 mTestDevice.executeShellCommand(String.format("mkdir %s/testdir2", extStore))); 417 assertEquals( 418 "", 419 mTestDevice.executeShellCommand( 420 String.format("touch %s/testdir2/foo.txt", extStore))); 421 assertEquals( 422 "", 423 mTestDevice.executeShellCommand( 424 String.format("ln -s %s/testdir2 %s/testdir", extStore, extStore))); 425 426 assertNotNull(mTestDevice.getFileEntry(String.format("%s/testdir/foo.txt", extStore))); 427 } finally { 428 mTestDevice.deleteFile(String.format("%s/testdir", extStore)); 429 mTestDevice.deleteFile(String.format("%s/testdir2/foo.txt", extStore)); 430 mTestDevice.deleteFile(String.format("%s/testdir2", extStore)); 431 } 432 } 433 434 /** Test syncing a single file using {@link TestDevice#syncFiles(File, String)}. */ 435 @Ignore 436 @Test testSyncFiles_normal()437 public void testSyncFiles_normal() throws Exception { 438 doTestSyncFiles(mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE)); 439 } 440 441 /** 442 * Test syncing a single file using {@link TestDevice#syncFiles(File, String)}. 443 * 444 * <p>This variant of the test uses "${EXTERNAL_STORAGE}" in the pathname. 445 */ 446 @Ignore 447 @Test testSyncFiles_extStorageVariable()448 public void testSyncFiles_extStorageVariable() throws Exception { 449 doTestSyncFiles("${EXTERNAL_STORAGE}"); 450 } 451 452 /** Test syncing a single file using {@link TestDevice#syncFiles(File, String)}. */ doTestSyncFiles(String externalStorePath)453 private void doTestSyncFiles(String externalStorePath) throws Exception { 454 String expectedDeviceFilePath = null; 455 456 // create temp dir with one temp file 457 File tmpDir = FileUtil.createTempDir("tmp"); 458 try { 459 File tmpFile = createTempTestFile(tmpDir); 460 // set last modified to 10 minutes ago 461 tmpFile.setLastModified(System.currentTimeMillis() - 10 * 60 * 1000); 462 assertNotNull(externalStorePath); 463 expectedDeviceFilePath = 464 String.format( 465 "%s/%s/%s", externalStorePath, tmpDir.getName(), tmpFile.getName()); 466 467 assertTrue(mTestDevice.syncFiles(tmpDir, externalStorePath)); 468 assertTrue(mTestDevice.doesFileExist(expectedDeviceFilePath)); 469 470 // get 'ls -l' attributes of file which includes timestamp 471 String origTmpFileStamp = 472 mTestDevice.executeShellCommand( 473 String.format("ls -l %s", expectedDeviceFilePath)); 474 // now create another file and verify that is synced 475 File tmpFile2 = createTempTestFile(tmpDir); 476 tmpFile2.setLastModified(System.currentTimeMillis() - 10 * 60 * 1000); 477 assertTrue(mTestDevice.syncFiles(tmpDir, externalStorePath)); 478 String expectedDeviceFilePath2 = 479 String.format( 480 "%s/%s/%s", externalStorePath, tmpDir.getName(), tmpFile2.getName()); 481 assertTrue(mTestDevice.doesFileExist(expectedDeviceFilePath2)); 482 483 // verify 1st file timestamp did not change 484 String unchangedTmpFileStamp = 485 mTestDevice.executeShellCommand( 486 String.format("ls -l %s", expectedDeviceFilePath)); 487 assertEquals(origTmpFileStamp, unchangedTmpFileStamp); 488 489 // now modify 1st file and verify it does change remotely 490 String testString = "blah"; 491 FileOutputStream stream = new FileOutputStream(tmpFile); 492 stream.write(testString.getBytes()); 493 stream.close(); 494 495 assertTrue(mTestDevice.syncFiles(tmpDir, externalStorePath)); 496 String tmpFileContents = 497 mTestDevice.executeShellCommand( 498 String.format("cat %s", expectedDeviceFilePath)); 499 assertTrue(tmpFileContents.contains(testString)); 500 } finally { 501 if (expectedDeviceFilePath != null && externalStorePath != null) { 502 // note that expectedDeviceFilePath has externalStorePath prepended at definition 503 mTestDevice.deleteFile(expectedDeviceFilePath); 504 } 505 FileUtil.recursiveDelete(tmpDir); 506 } 507 } 508 509 /** Test pushing a directory */ 510 @Test testPushDir()511 public void testPushDir() throws IOException, DeviceNotAvailableException { 512 String expectedDeviceFilePath = null; 513 String externalStorePath = null; 514 File rootDir = FileUtil.createTempDir("tmp"); 515 // create temp dir with one temp file 516 try { 517 File tmpDir = FileUtil.createTempDir("tmp", rootDir); 518 File tmpFile = createTempTestFile(tmpDir); 519 externalStorePath = mTestDevice.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE); 520 assertNotNull(externalStorePath); 521 expectedDeviceFilePath = 522 String.format( 523 "%s/%s/%s", externalStorePath, tmpDir.getName(), tmpFile.getName()); 524 525 assertTrue(mTestDevice.pushDir(rootDir, externalStorePath)); 526 assertTrue(mTestDevice.doesFileExist(expectedDeviceFilePath)); 527 528 } finally { 529 if (expectedDeviceFilePath != null && externalStorePath != null) { 530 mTestDevice.deleteFile( 531 String.format("%s/%s", externalStorePath, expectedDeviceFilePath)); 532 } 533 FileUtil.recursiveDelete(rootDir); 534 } 535 } 536 537 /** 538 * Test {@link TestDevice#executeFastbootCommand(String...)} when device is in adb mode. 539 * 540 * <p>Expect fastboot recovery to be invoked, which will boot device back to fastboot mode and 541 * command will succeed. 542 */ 543 @Test testExecuteFastbootCommand_deviceInAdb()544 public void testExecuteFastbootCommand_deviceInAdb() throws DeviceNotAvailableException { 545 CLog.i("testExecuteFastbootCommand_deviceInAdb"); 546 if (!mTestDevice.isFastbootEnabled()) { 547 CLog.i("Fastboot not enabled skipping testExecuteFastbootCommand_deviceInAdb"); 548 return; 549 } 550 551 assumeNotVirtualDevice(); 552 553 long origTimeout = mTestDevice.getCommandTimeout(); 554 try { 555 assertEquals(TestDeviceState.ONLINE, mMonitor.getDeviceState()); 556 // reset operation timeout to small value to make test run quicker 557 mTestDevice.setCommandTimeout(5 * 1000); 558 assertEquals( 559 CommandStatus.SUCCESS, 560 mTestDevice.executeFastbootCommand("getvar", "product").getStatus()); 561 assertEquals(TestDeviceState.FASTBOOT, mMonitor.getDeviceState()); 562 } finally { 563 mTestDevice.setCommandTimeout(origTimeout); 564 mTestDevice.reboot(); 565 assertEquals(TestDeviceState.ONLINE, mMonitor.getDeviceState()); 566 } 567 } 568 569 /** 570 * Test {@link TestDevice#executeFastbootCommand(String...)} when an invalid command is passed. 571 * 572 * <p>Expect the result indicate failure, and recovery not to be invoked. 573 */ 574 @Test testExecuteFastbootCommand_badCommand()575 public void testExecuteFastbootCommand_badCommand() throws DeviceNotAvailableException { 576 CLog.i("testExecuteFastbootCommand_badCommand"); 577 if (!mTestDevice.isFastbootEnabled()) { 578 CLog.i("Fastboot not enabled skipping testExecuteFastbootCommand_badCommand"); 579 return; 580 } 581 582 assumeNotVirtualDevice(); 583 584 IDeviceRecovery origRecovery = mTestDevice.getRecovery(); 585 try { 586 mTestDevice.rebootIntoBootloader(); 587 assertEquals(TestDeviceState.FASTBOOT, mMonitor.getDeviceState()); 588 // substitute recovery mechanism to ensure recovery is not called when bad command 589 // is passed 590 IDeviceRecovery mockRecovery = mock(IDeviceRecovery.class); 591 mTestDevice.setRecovery(mockRecovery); 592 assertEquals( 593 CommandStatus.FAILED, 594 mTestDevice.executeFastbootCommand("badcommand").getStatus()); 595 } finally { 596 mTestDevice.setRecovery(origRecovery); 597 mTestDevice.reboot(); 598 assertEquals(TestDeviceState.ONLINE, mMonitor.getDeviceState()); 599 } 600 } 601 602 /** Verify device can be rebooted into bootloader and back to adb. */ 603 @Test testRebootIntoBootloader()604 public void testRebootIntoBootloader() throws DeviceNotAvailableException { 605 CLog.i("testRebootIntoBootloader"); 606 if (!mTestDevice.isFastbootEnabled()) { 607 CLog.i("Fastboot not enabled skipping testRebootInBootloader"); 608 return; 609 } 610 611 assumeNotVirtualDevice(); 612 613 try { 614 mTestDevice.rebootIntoBootloader(); 615 assertEquals(TestDeviceState.FASTBOOT, mMonitor.getDeviceState()); 616 } finally { 617 mTestDevice.reboot(); 618 assertEquals(TestDeviceState.ONLINE, mMonitor.getDeviceState()); 619 } 620 } 621 622 /** Verify device can be rebooted into adb. */ 623 @Test testReboot()624 public void testReboot() throws DeviceNotAvailableException { 625 CLog.i("testReboot"); 626 mTestDevice.reboot(); 627 assertEquals(TestDeviceState.ONLINE, mMonitor.getDeviceState()); 628 // check that device has root 629 assertTrue(mTestDevice.executeShellCommand("id").contains("root")); 630 } 631 632 /** Verify device can be rebooted into adb recovery. */ 633 @Test testRebootIntoRecovery()634 public void testRebootIntoRecovery() throws Exception { 635 CLog.i("testRebootIntoRecovery"); 636 if (!mTestDevice.isFastbootEnabled()) { 637 CLog.i("Fastboot not enabled skipping testRebootInRecovery"); 638 return; 639 } 640 641 assumeNotVirtualDevice(); 642 643 try { 644 mTestDevice.rebootIntoRecovery(); 645 assertEquals(TestDeviceState.RECOVERY, mMonitor.getDeviceState()); 646 } finally { 647 // Recovery is a special case to recover from, we need to call reboot on the idevice. 648 RunUtil.getDefault().sleep(15 * 1000); 649 getDevice().getIDevice().reboot(null); 650 } 651 } 652 waitForSystemServerProcess()653 private ProcessInfo waitForSystemServerProcess() throws DeviceNotAvailableException { 654 ProcessInfo systemServer = null; 655 for (int i = 0; i < 5; i++) { 656 systemServer = mTestDevice.getProcessByName("system_server"); 657 if (systemServer != null) { 658 return systemServer; 659 } 660 RunUtil.getDefault().sleep(1000); 661 } 662 CLog.i("The system_server process fails to come up"); 663 return null; 664 } 665 666 /** Test device soft-restart detection API. */ 667 @Test testDeviceSoftRestart()668 public void testDeviceSoftRestart() throws DeviceNotAvailableException { 669 CLog.i("testDeviceSoftRestartSince"); 670 671 // API 29 was the first to support soft reboot detection in this way 672 assumeTrue( 673 "Test only valid for devices at or above API level 29", 674 mTestDevice.getApiLevel() >= 29); 675 676 // Get system_server process info 677 ProcessInfo prev = mTestDevice.getProcessByName("system_server"); 678 long deviceTimeMs = mTestDevice.getDeviceDate(); 679 if (prev == null) { 680 CLog.i("System_server process does not exist. Abort testDeviceSoftRestart."); 681 return; 682 } 683 assertFalse(mTestDevice.deviceSoftRestartedSince(prev.getStartTime(), TimeUnit.SECONDS)); 684 assertFalse(mTestDevice.deviceSoftRestarted(prev)); 685 if (!mTestDevice.isAdbRoot()) { 686 mTestDevice.enableAdbRoot(); 687 } 688 mTestDevice.executeShellCommand(String.format("kill %s", prev.getPid())); 689 RunUtil.getDefault().sleep(1000); 690 assertTrue(mTestDevice.deviceSoftRestartedSince(deviceTimeMs, TimeUnit.MILLISECONDS)); 691 assertTrue(mTestDevice.deviceSoftRestarted(prev)); 692 prev = waitForSystemServerProcess(); 693 deviceTimeMs = mTestDevice.getDeviceDate(); 694 // Sleep for a second to ensure the reboot happens in at least the second after 695 // we took this timestamp 696 RunUtil.getDefault().sleep(1000); 697 mTestDevice.reboot(); 698 if (!mTestDevice.isAdbRoot()) { 699 mTestDevice.enableAdbRoot(); 700 } 701 assertFalse(mTestDevice.deviceSoftRestartedSince(deviceTimeMs, TimeUnit.MILLISECONDS)); 702 assertFalse(mTestDevice.deviceSoftRestarted(prev)); 703 // Restart system_server 10 seconds after reboot 704 RunUtil.getDefault().sleep(10000); 705 mTestDevice.executeShellCommand( 706 String.format("kill %s", mTestDevice.getProcessByName("system_server").getPid())); 707 RunUtil.getDefault().sleep(1000); 708 assertTrue(mTestDevice.deviceSoftRestartedSince(deviceTimeMs, TimeUnit.MILLISECONDS)); 709 assertTrue(mTestDevice.deviceSoftRestarted(prev)); 710 waitForSystemServerProcess(); 711 assertTrue(mTestDevice.deviceSoftRestartedSince(deviceTimeMs, TimeUnit.MILLISECONDS)); 712 assertTrue(mTestDevice.deviceSoftRestarted(prev)); 713 } 714 715 /** 716 * Verify that {@link TestDevice#clearErrorDialogs()} can successfully clear an error dialog 717 * from screen. 718 * 719 * <p>This is done by running a test app which will crash, then running another app that does UI 720 * based tests. 721 * 722 * <p>Assumes DevTools and TradeFedUiApp are currently installed. 723 */ 724 @Ignore 725 @Test testClearErrorDialogs_crash()726 public void testClearErrorDialogs_crash() throws DeviceNotAvailableException { 727 CLog.i("testClearErrorDialogs_crash"); 728 // Ensure device is in a known state, we doing extra care here otherwise it may be flaky 729 int retry = 5; 730 mTestDevice.disableKeyguard(); 731 while (!runUITests()) { 732 getDevice().reboot(); 733 mTestDevice.waitForDeviceAvailable(); 734 mTestDevice.disableKeyguard(); 735 RunUtil.getDefault().sleep(2000); 736 if (retry == 0) { 737 fail("Fail to setup the device in a known state"); 738 } 739 retry--; 740 } 741 // now cause a crash dialog to appear 742 getDevice().executeShellCommand("am start -n " + TestAppConstants.CRASH_ACTIVITY); 743 RunUtil.getDefault().sleep(2000); 744 // Ensure the error dialogue is here 745 assertFalse(runUITests()); 746 RunUtil.getDefault().sleep(2000); 747 748 assertTrue("Clear dialogs did not clear anything", getDevice().clearErrorDialogs()); 749 assertTrue(runUITests()); 750 } 751 752 /** 753 * Verify the steps taken to disable keyguard after reboot are successfully 754 * 755 * <p>This is done by rebooting then run a app that does UI based tests. 756 * 757 * <p>Assumes DevTools and TradeFedUiApp are currently installed. 758 */ 759 @Test testDisableKeyguard()760 public void testDisableKeyguard() throws DeviceNotAvailableException { 761 CLog.i("testDisableKeyguard"); 762 getDevice().reboot(); 763 mTestDevice.waitForDeviceAvailable(); 764 // Bump from 3000 to reduce risk of racing "wm dismiss-keyguard" 765 RunUtil.getDefault().sleep(5000); 766 KeyguardControllerState keyguard = mTestDevice.getKeyguardState(); 767 if (keyguard == null) { 768 // If the getKeyguardState is not supported. 769 assertTrue(runUITests()); 770 } else { 771 assertFalse(keyguard.isKeyguardShowing()); 772 } 773 } 774 775 /** Test that TradeFed can successfully recover from the adb host daemon process being killed */ 776 @Test testExecuteShellCommand_adbKilled()777 public void testExecuteShellCommand_adbKilled() { 778 // FIXME: adb typically does not recover, and this causes rest of tests to fail 779 // Log.i(LOG_TAG, "testExecuteShellCommand_adbKilled"); 780 // CommandResult result = RunUtil.getInstance().runTimedCmd(30*1000, "adb", "kill-server"); 781 // assertEquals(CommandStatus.SUCCESS, result.getStatus()); 782 // assertSimpleShellCommand(); 783 } 784 785 /** 786 * Basic test for {@link TestDevice#getScreenshot()}. 787 * 788 * <p>Grab a screenshot, save it to a file, and perform a cursory size check to ensure its 789 * valid. 790 */ 791 @Test testGetScreenshot()792 public void testGetScreenshot() throws DeviceNotAvailableException, IOException { 793 CLog.i("testGetScreenshot"); 794 InputStreamSource source = getDevice().getScreenshot(); 795 assertNotNull(source); 796 File tmpPngFile = FileUtil.createTempFile("screenshot", ".png"); 797 try { 798 FileUtil.writeToFile(source.createInputStream(), tmpPngFile); 799 CLog.i("Created file at %s", tmpPngFile.getAbsolutePath()); 800 // Decode the content, will return null if not an image. 801 BufferedImage image = ImageIO.read(tmpPngFile); 802 assertNotNull(image); 803 // All our device screenshot should be bigger than 200px 804 assertTrue(image.getWidth() > 200); 805 assertTrue(image.getHeight() > 200); 806 } finally { 807 FileUtil.deleteFile(tmpPngFile); 808 source.close(); 809 } 810 } 811 812 /** 813 * Basic test for {@link TestDevice#getLogcat(int)}. 814 * 815 * <p>Dumps a bunch of messages to logcat, calls getLogcat(), and verifies size of capture file 816 * is equal to provided data. 817 */ 818 @Test testGetLogcat_size()819 public void testGetLogcat_size() throws DeviceNotAvailableException, IOException { 820 CLog.i("testGetLogcat_size"); 821 for (int i = 0; i < 100; i++) { 822 getDevice().executeShellCommand(String.format("log testGetLogcat_size log dump %d", i)); 823 } 824 // sleep a small amount of time to ensure last log message makes it into capture 825 RunUtil.getDefault().sleep(500); 826 File tmpTxtFile = FileUtil.createTempFile("logcat", ".txt"); 827 try (InputStreamSource source = getDevice().getLogcatDump()) { 828 assertNotNull(source); 829 FileUtil.writeToFile(source.createInputStream(), tmpTxtFile); 830 CLog.i("Created file at %s", tmpTxtFile.getAbsolutePath()); 831 // Check we have at least our 100 lines. 832 assertTrue( 833 "Saved text file is smaller than expected", 100 * 1024 <= tmpTxtFile.length()); 834 // ensure last log message is present in log 835 String s = FileUtil.readStringFromFile(tmpTxtFile); 836 assertTrue( 837 "last log message is not in captured logcat", 838 s.contains("testGetLogcat_size log dump 99")); 839 } finally { 840 FileUtil.deleteFile(tmpTxtFile); 841 } 842 } 843 844 /** 845 * Basic test for testing LOGCAT_CMD backward compatibility. 846 * 847 * <p>Checks if future changes to LOGCAT_CMD does not break APIs. 848 */ 849 @Test testLogcatCmd()850 public void testLogcatCmd() throws DeviceNotAvailableException { 851 CLog.i("testLogcatCmd"); 852 // Adding -d flag to dump the log and exit, to make this a non-blocking call 853 CommandResult result = 854 mTestDevice.executeShellV2Command( 855 LogcatReceiver.getDefaultLogcatCmd(mTestDevice) + " -d"); 856 assertThat(result.getStatus()).isEqualTo(CommandStatus.SUCCESS); 857 } 858 859 /** Test that {@link TestDevice#getProperty(String)} works after a reboot. */ 860 @Test testGetProperty()861 public void testGetProperty() throws Exception { 862 assertNotNull(getDevice().getProperty(DeviceProperties.SDK_VERSION)); 863 getDevice().rebootUntilOnline(); 864 assertNotNull(getDevice().getProperty(DeviceProperties.SDK_VERSION)); 865 } 866 867 /** Test that {@link TestDevice#getProperty(String)} works for volatile properties. */ 868 @Test testGetProperty_volatile()869 public void testGetProperty_volatile() throws Exception { 870 getDevice().setProperty("prop.test", "0"); 871 assertEquals("0", getDevice().getProperty("prop.test")); 872 getDevice().setProperty("prop.test", "1"); 873 assertEquals("1", getDevice().getProperty("prop.test")); 874 } 875 876 /** Test that the recovery mechanism works in {@link TestDevice#getFileEntry(String)} */ 877 @Test testGetFileEntry_recovery()878 public void testGetFileEntry_recovery() throws Exception { 879 if (!mTestDevice.isFastbootEnabled()) { 880 CLog.i("Fastboot not enabled skipping testGetFileEntry_recovery"); 881 return; 882 } 883 884 // Recovery not implemented on Cuttlefish / Goldfish 885 assumeNotVirtualDevice(); 886 887 try { 888 getDevice().rebootIntoBootloader(); 889 // expect recovery to kick in, and reboot device back to adb so the call works 890 IFileEntry entry = getDevice().getFileEntry("/data"); 891 assertNotNull(entry); 892 } finally { 893 getDevice().reboot(); 894 } 895 } 896 897 /** Test that {@link TestDevice#pullFileContents} works correctly. */ 898 @Test testPullFileContents()899 public void testPullFileContents() throws Exception { 900 final String path = "/data/misc_ce/0/apexrollback/foo/test_file.txt"; 901 final String content = "The quick brown fox jumps over the lazy dog"; 902 try { 903 // need root to access files under /data/misc_ce 904 mTestDevice.enableAdbRoot(); 905 assertTrue(mTestDevice.pushString(content, path)); 906 // check the content is the same as what we just pushed 907 assertEquals(content, mTestDevice.pullFileContents(path)); 908 mTestDevice.reboot(); 909 // check the content remains the same after reboot 910 assertEquals(content, mTestDevice.pullFileContents(path)); 911 } finally { 912 mTestDevice.disableAdbRoot(); 913 } 914 } 915 916 /** Run the test app UI tests and return true if they all pass. */ runUITests()917 private boolean runUITests() throws DeviceNotAvailableException { 918 RemoteAndroidTestRunner uirunner = 919 new RemoteAndroidTestRunner( 920 TestAppConstants.UITESTAPP_PACKAGE, getDevice().getIDevice()); 921 CollectingTestListener uilistener = new CollectingTestListener(); 922 getDevice().runInstrumentationTests(uirunner, uilistener); 923 return TestAppConstants.UI_TOTAL_TESTS == uilistener.getNumTestsInState(TestStatus.PASSED); 924 } 925 926 /** Test for {@link NativeDevice#setSetting(int, String, String, String)} */ 927 @Test testPutSettings()928 public void testPutSettings() throws Exception { 929 String initValue = mTestDevice.getSetting(0, "system", "screen_brightness"); 930 CLog.i("initial value was: %s", initValue); 931 assertTrue(!initValue.equals("50")); 932 mTestDevice.setSetting(0, "system", "screen_brightness", "50"); 933 String secondValue = mTestDevice.getSetting(0, "system", "screen_brightness"); 934 assertEquals("50", secondValue); 935 // restore initial value 936 mTestDevice.setSetting(0, "system", "screen_brightness", initValue); 937 } 938 939 /** Test for {@link TestDevice#getScreenshot()}. */ 940 @Test testScreenshot()941 public void testScreenshot() throws Exception { 942 InputStreamSource screenshot = mTestDevice.getScreenshot(); 943 assertNotNull(screenshot); 944 try (InputStream stream = screenshot.createInputStream(); 945 BufferedInputStream bs = new BufferedInputStream(stream)) { 946 assertEquals("image/png", URLConnection.guessContentTypeFromStream(bs)); 947 } finally { 948 StreamUtil.close(screenshot); 949 } 950 } 951 952 /** Test for {@link TestDevice#getScreenshot(long)}. */ 953 @Test testScreenshot_withDisplay()954 public void testScreenshot_withDisplay() throws Exception { 955 Set<Long> displays = mTestDevice.listDisplayIds(); 956 assertThat(displays).isNotEmpty(); 957 InputStreamSource screenshot = mTestDevice.getScreenshot(displays.iterator().next()); 958 assertNotNull(screenshot); 959 try (InputStream stream = screenshot.createInputStream(); 960 BufferedInputStream bs = new BufferedInputStream(stream)) { 961 assertEquals("image/png", URLConnection.guessContentTypeFromStream(bs)); 962 } finally { 963 StreamUtil.close(screenshot); 964 } 965 } 966 } 967