1#!/usr/bin/env python3 2# 3# Copyright 2016 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import logging 18import mock 19import os 20import shutil 21import tempfile 22import unittest 23 24from acts import logger 25from acts.controllers import android_device 26from acts.controllers.android_lib import errors 27 28# Mock log path for a test run. 29MOCK_LOG_PATH = "/tmp/logs/MockTest/xx-xx-xx_xx-xx-xx/" 30 31# Mock start and end time of the adb cat. 32MOCK_ADB_EPOCH_BEGIN_TIME = 191000123 33MOCK_ADB_LOGCAT_BEGIN_TIME = logger.normalize_log_line_timestamp( 34 logger.epoch_to_log_line_timestamp(MOCK_ADB_EPOCH_BEGIN_TIME)) 35MOCK_ADB_LOGCAT_END_TIME = "1970-01-02 21:22:02.000" 36 37MOCK_SERIAL = 1 38MOCK_RELEASE_BUILD_ID = "ABC1.123456.007" 39MOCK_DEV_BUILD_ID = "ABC-MR1" 40MOCK_NYC_BUILD_ID = "N4F27P" 41 42 43def get_mock_ads(num): 44 """Generates a list of mock AndroidDevice objects. 45 46 The serial number of each device will be integer 0 through num - 1. 47 48 Args: 49 num: An integer that is the number of mock AndroidDevice objects to 50 create. 51 """ 52 ads = [] 53 for i in range(num): 54 ad = mock.MagicMock(name="AndroidDevice", serial=i, h_port=None) 55 ad.ensure_screen_on = mock.MagicMock(return_value=True) 56 ads.append(ad) 57 return ads 58 59 60def mock_get_all_instances(): 61 return get_mock_ads(5) 62 63 64def mock_list_adb_devices(): 65 return [ad.serial for ad in get_mock_ads(5)] 66 67 68class MockAdbProxy(object): 69 """Mock class that swaps out calls to adb with mock calls.""" 70 71 def __init__(self, 72 serial, 73 fail_br=False, 74 fail_br_before_N=False, 75 build_id=MOCK_RELEASE_BUILD_ID, 76 return_value=None): 77 self.serial = serial 78 self.fail_br = fail_br 79 self.fail_br_before_N = fail_br_before_N 80 self.return_value = return_value 81 self.return_multiple = False 82 self.build_id = build_id 83 84 def shell(self, params, ignore_status=False, timeout=60): 85 if params == "id -u": 86 return "root" 87 elif params == "bugreportz": 88 if self.fail_br: 89 return "OMG I died!\n" 90 return "OK:/path/bugreport.zip\n" 91 elif params == "bugreportz -v": 92 if self.fail_br_before_N: 93 return "/system/bin/sh: bugreportz: not found" 94 return "1.1" 95 else: 96 if self.return_multiple: 97 return self.return_value.pop(0) 98 else: 99 return self.return_value 100 101 def getprop(self, params): 102 if params == "ro.build.id": 103 return self.build_id 104 elif params == "ro.build.version.incremental": 105 return "123456789" 106 elif params == "ro.build.type": 107 return "userdebug" 108 elif params == "ro.build.product" or params == "ro.product.name": 109 return "FakeModel" 110 elif params == "sys.boot_completed": 111 return "1" 112 113 def devices(self): 114 return "\t".join([str(self.serial), "device"]) 115 116 def bugreport(self, params, timeout=android_device.BUG_REPORT_TIMEOUT): 117 expected = os.path.join( 118 logging.log_path, "AndroidDevice%s" % self.serial, 119 "AndroidDevice%s_%s.txt" % 120 (self.serial, 121 logger.normalize_log_line_timestamp(MOCK_ADB_LOGCAT_BEGIN_TIME))) 122 assert expected in params, "Expected '%s', got '%s'." % (expected, 123 params) 124 125 def __getattr__(self, name): 126 """All calls to the none-existent functions in adb proxy would 127 simply return the adb command string. 128 """ 129 130 def adb_call(*args, **kwargs): 131 arg_str = ' '.join(str(elem) for elem in args) 132 return arg_str 133 134 return adb_call 135 136 137class MockFastbootProxy(): 138 """Mock class that swaps out calls to adb with mock calls.""" 139 140 def __init__(self, serial): 141 self.serial = serial 142 143 def devices(self): 144 return "xxxx\tdevice\nyyyy\tdevice" 145 146 def __getattr__(self, name): 147 def fastboot_call(*args): 148 arg_str = ' '.join(str(elem) for elem in args) 149 return arg_str 150 151 return fastboot_call 152 153 154class ActsAndroidDeviceTest(unittest.TestCase): 155 """This test class has unit tests for the implementation of everything 156 under acts.controllers.android_device. 157 """ 158 159 def setUp(self): 160 # Set log_path to logging since acts logger setup is not called. 161 if not hasattr(logging, "log_path"): 162 setattr(logging, "log_path", "/tmp/logs") 163 # Creates a temp dir to be used by tests in this test class. 164 self.tmp_dir = tempfile.mkdtemp() 165 166 def tearDown(self): 167 """Removes the temp dir. 168 """ 169 shutil.rmtree(self.tmp_dir) 170 171 # Tests for android_device module functions. 172 # These tests use mock AndroidDevice instances. 173 174 @mock.patch.object( 175 android_device, "get_all_instances", new=mock_get_all_instances) 176 @mock.patch.object( 177 android_device, "list_adb_devices", new=mock_list_adb_devices) 178 def test_create_with_pickup_all(self): 179 pick_all_token = android_device.ANDROID_DEVICE_PICK_ALL_TOKEN 180 actual_ads = android_device.create(pick_all_token) 181 for actual, expected in zip(actual_ads, get_mock_ads(5)): 182 self.assertEqual(actual.serial, expected.serial) 183 184 def test_create_with_empty_config(self): 185 expected_msg = android_device.ANDROID_DEVICE_EMPTY_CONFIG_MSG 186 with self.assertRaisesRegex(errors.AndroidDeviceConfigError, 187 expected_msg): 188 android_device.create([]) 189 190 def test_create_with_not_list_config(self): 191 expected_msg = android_device.ANDROID_DEVICE_NOT_LIST_CONFIG_MSG 192 with self.assertRaisesRegex(errors.AndroidDeviceConfigError, 193 expected_msg): 194 android_device.create("HAHA") 195 196 def test_get_device_success_with_serial(self): 197 ads = get_mock_ads(5) 198 expected_serial = 0 199 ad = android_device.get_device(ads, serial=expected_serial) 200 self.assertEqual(ad.serial, expected_serial) 201 202 def test_get_device_success_with_serial_and_extra_field(self): 203 ads = get_mock_ads(5) 204 expected_serial = 1 205 expected_h_port = 5555 206 ads[1].h_port = expected_h_port 207 ad = android_device.get_device( 208 ads, serial=expected_serial, h_port=expected_h_port) 209 self.assertEqual(ad.serial, expected_serial) 210 self.assertEqual(ad.h_port, expected_h_port) 211 212 def test_get_device_no_match(self): 213 ads = get_mock_ads(5) 214 expected_msg = ("Could not find a target device that matches condition" 215 ": {'serial': 5}.") 216 with self.assertRaisesRegex(ValueError, expected_msg): 217 ad = android_device.get_device(ads, serial=len(ads)) 218 219 def test_get_device_too_many_matches(self): 220 ads = get_mock_ads(5) 221 target_serial = ads[1].serial = ads[0].serial 222 expected_msg = "More than one device matched: \[0, 0\]" 223 with self.assertRaisesRegex(ValueError, expected_msg): 224 ad = android_device.get_device(ads, serial=target_serial) 225 226 def test_start_services_on_ads(self): 227 """Makes sure when an AndroidDevice fails to start some services, all 228 AndroidDevice objects get cleaned up. 229 """ 230 msg = "Some error happened." 231 ads = get_mock_ads(3) 232 ads[0].start_services = mock.MagicMock() 233 ads[0].clean_up = mock.MagicMock() 234 ads[1].start_services = mock.MagicMock() 235 ads[1].clean_up = mock.MagicMock() 236 ads[2].start_services = mock.MagicMock( 237 side_effect=errors.AndroidDeviceError(msg)) 238 ads[2].clean_up = mock.MagicMock() 239 with self.assertRaisesRegex(errors.AndroidDeviceError, msg): 240 android_device._start_services_on_ads(ads) 241 ads[0].clean_up.assert_called_once_with() 242 ads[1].clean_up.assert_called_once_with() 243 ads[2].clean_up.assert_called_once_with() 244 245 # Tests for android_device.AndroidDevice class. 246 # These tests mock out any interaction with the OS and real android device 247 # in AndroidDeivce. 248 249 @mock.patch( 250 'acts.controllers.adb.AdbProxy', 251 return_value=MockAdbProxy(MOCK_SERIAL)) 252 @mock.patch( 253 'acts.controllers.fastboot.FastbootProxy', 254 return_value=MockFastbootProxy(MOCK_SERIAL)) 255 def test_AndroidDevice_instantiation(self, MockFastboot, MockAdbProxy): 256 """Verifies the AndroidDevice object's basic attributes are correctly 257 set after instantiation. 258 """ 259 ad = android_device.AndroidDevice(serial=MOCK_SERIAL) 260 self.assertEqual(ad.serial, 1) 261 self.assertEqual(ad.model, "fakemodel") 262 self.assertIsNone(ad.adb_logcat_process) 263 expected_lp = os.path.join(logging.log_path, 264 "AndroidDevice%s" % MOCK_SERIAL) 265 self.assertEqual(ad.log_path, expected_lp) 266 267 @mock.patch( 268 'acts.controllers.adb.AdbProxy', 269 return_value=MockAdbProxy(MOCK_SERIAL)) 270 @mock.patch( 271 'acts.controllers.fastboot.FastbootProxy', 272 return_value=MockFastbootProxy(MOCK_SERIAL)) 273 def test_AndroidDevice_build_info_release(self, MockFastboot, 274 MockAdbProxy): 275 """Verifies the AndroidDevice object's basic attributes are correctly 276 set after instantiation. 277 """ 278 ad = android_device.AndroidDevice(serial=1) 279 build_info = ad.build_info 280 self.assertEqual(build_info["build_id"], "ABC1.123456.007") 281 self.assertEqual(build_info["build_type"], "userdebug") 282 283 @mock.patch( 284 'acts.controllers.adb.AdbProxy', 285 return_value=MockAdbProxy(MOCK_SERIAL, build_id=MOCK_DEV_BUILD_ID)) 286 @mock.patch( 287 'acts.controllers.fastboot.FastbootProxy', 288 return_value=MockFastbootProxy(MOCK_SERIAL)) 289 def test_AndroidDevice_build_info_dev(self, MockFastboot, MockAdbProxy): 290 """Verifies the AndroidDevice object's basic attributes are correctly 291 set after instantiation. 292 """ 293 ad = android_device.AndroidDevice(serial=1) 294 build_info = ad.build_info 295 self.assertEqual(build_info["build_id"], "123456789") 296 self.assertEqual(build_info["build_type"], "userdebug") 297 298 @mock.patch( 299 'acts.controllers.adb.AdbProxy', 300 return_value=MockAdbProxy(MOCK_SERIAL, build_id=MOCK_NYC_BUILD_ID)) 301 @mock.patch( 302 'acts.controllers.fastboot.FastbootProxy', 303 return_value=MockFastbootProxy(MOCK_SERIAL)) 304 def test_AndroidDevice_build_info_nyc(self, MockFastboot, MockAdbProxy): 305 """Verifies the AndroidDevice object's build id is set correctly for 306 NYC releases. 307 """ 308 ad = android_device.AndroidDevice(serial=1) 309 build_info = ad.build_info 310 self.assertEqual(build_info["build_id"], MOCK_NYC_BUILD_ID) 311 312 @mock.patch( 313 'acts.controllers.adb.AdbProxy', 314 return_value=MockAdbProxy(MOCK_SERIAL)) 315 @mock.patch( 316 'acts.controllers.fastboot.FastbootProxy', 317 return_value=MockFastbootProxy(MOCK_SERIAL)) 318 @mock.patch('acts.utils.create_dir') 319 @mock.patch('acts.utils.exe_cmd') 320 @mock.patch('acts.controllers.android_device.AndroidDevice.device_log_path', 321 new_callable=mock.PropertyMock) 322 def test_AndroidDevice_take_bug_report(self, mock_log_path, exe_mock, 323 create_dir_mock, FastbootProxy, 324 MockAdbProxy): 325 """Verifies AndroidDevice.take_bug_report calls the correct adb command 326 and writes the bugreport file to the correct path. 327 """ 328 ad = android_device.AndroidDevice(serial=MOCK_SERIAL) 329 mock_log_path.return_value = os.path.join( 330 logging.log_path, "AndroidDevice%s" % ad.serial) 331 ad.take_bug_report("test_something", 234325.32) 332 create_dir_mock.assert_called_with(mock_log_path()) 333 334 @mock.patch( 335 'acts.controllers.adb.AdbProxy', 336 return_value=MockAdbProxy(MOCK_SERIAL, fail_br=True)) 337 @mock.patch( 338 'acts.controllers.fastboot.FastbootProxy', 339 return_value=MockFastbootProxy(MOCK_SERIAL)) 340 @mock.patch('acts.utils.create_dir') 341 @mock.patch('acts.utils.exe_cmd') 342 @mock.patch('acts.controllers.android_device.AndroidDevice.device_log_path', 343 new_callable=mock.PropertyMock) 344 def test_AndroidDevice_take_bug_report_fail( 345 self, mock_log_path, exe_mock, create_dir_mock, FastbootProxy, 346 MockAdbProxy): 347 """Verifies AndroidDevice.take_bug_report writes out the correct message 348 when taking bugreport fails. 349 """ 350 ad = android_device.AndroidDevice(serial=MOCK_SERIAL) 351 mock_log_path.return_value = os.path.join( 352 logging.log_path, "AndroidDevice%s" % ad.serial) 353 expected_msg = "Failed to take bugreport on 1: OMG I died!" 354 with self.assertRaisesRegex(errors.AndroidDeviceError, 355 expected_msg): 356 ad.take_bug_report("test_something", 4346343.23) 357 358 @mock.patch( 359 'acts.controllers.adb.AdbProxy', 360 return_value=MockAdbProxy(MOCK_SERIAL, fail_br_before_N=True)) 361 @mock.patch( 362 'acts.controllers.fastboot.FastbootProxy', 363 return_value=MockFastbootProxy(MOCK_SERIAL)) 364 @mock.patch('acts.utils.create_dir') 365 @mock.patch('acts.utils.exe_cmd') 366 @mock.patch('acts.controllers.android_device.AndroidDevice.device_log_path', 367 new_callable=mock.PropertyMock) 368 def test_AndroidDevice_take_bug_report_fallback( 369 self, mock_log_path, exe_mock, create_dir_mock, FastbootProxy, 370 MockAdbProxy): 371 """Verifies AndroidDevice.take_bug_report falls back to traditional 372 bugreport on builds that do not have bugreportz. 373 """ 374 ad = android_device.AndroidDevice(serial=MOCK_SERIAL) 375 mock_log_path.return_value = os.path.join( 376 logging.log_path, "AndroidDevice%s" % ad.serial) 377 ad.take_bug_report("test_something", MOCK_ADB_EPOCH_BEGIN_TIME) 378 create_dir_mock.assert_called_with(mock_log_path()) 379 380 @mock.patch( 381 'acts.controllers.adb.AdbProxy', 382 return_value=MockAdbProxy(MOCK_SERIAL)) 383 @mock.patch( 384 'acts.controllers.fastboot.FastbootProxy', 385 return_value=MockFastbootProxy(MOCK_SERIAL)) 386 @mock.patch('acts.libs.proc.process.Process') 387 def test_AndroidDevice_start_adb_logcat(self, proc_mock, FastbootProxy, 388 MockAdbProxy): 389 """Verifies the AndroidDevice method start_adb_logcat. Checks that the 390 underlying logcat process is started properly and correct warning msgs 391 are generated. 392 """ 393 with mock.patch( 394 ('acts.controllers.android_lib.logcat.' 395 'create_logcat_keepalive_process'), 396 return_value=proc_mock) as create_proc_mock: 397 ad = android_device.AndroidDevice(serial=MOCK_SERIAL) 398 ad.start_adb_logcat() 399 # Verify start did the correct operations. 400 self.assertTrue(ad.adb_logcat_process) 401 log_dir = "AndroidDevice%s" % ad.serial 402 create_proc_mock.assert_called_with( 403 ad.serial, log_dir, '-b all') 404 proc_mock.start.assert_called_with() 405 # Expect warning msg if start is called back to back. 406 expected_msg = "Android device .* already has a running adb logcat" 407 proc_mock.is_running.return_value = True 408 with self.assertLogs(level='WARNING') as log: 409 ad.start_adb_logcat() 410 self.assertRegex(log.output[0], expected_msg) 411 412 @mock.patch( 413 'acts.controllers.adb.AdbProxy', 414 return_value=MockAdbProxy(MOCK_SERIAL)) 415 @mock.patch( 416 'acts.controllers.fastboot.FastbootProxy', 417 return_value=MockFastbootProxy(MOCK_SERIAL)) 418 @mock.patch('acts.controllers.android_lib.logcat.' 419 'create_logcat_keepalive_process') 420 def test_AndroidDevice_start_adb_logcat_with_user_param( 421 self, create_proc_mock, FastbootProxy, MockAdbProxy): 422 """Verifies that start_adb_logcat generates the correct adb logcat 423 command if adb_logcat_param is specified. 424 """ 425 ad = android_device.AndroidDevice(serial=MOCK_SERIAL) 426 ad.adb_logcat_param = "-b radio" 427 ad.start_adb_logcat() 428 # Verify that create_logcat_keepalive_process is called with the 429 # correct command. 430 log_dir = "AndroidDevice%s" % ad.serial 431 create_proc_mock.assert_called_with( 432 ad.serial, log_dir, '-b radio') 433 434 @mock.patch( 435 'acts.controllers.adb.AdbProxy', 436 return_value=MockAdbProxy(MOCK_SERIAL)) 437 @mock.patch( 438 'acts.controllers.fastboot.FastbootProxy', 439 return_value=MockFastbootProxy(MOCK_SERIAL)) 440 @mock.patch('acts.libs.proc.process.Process') 441 def test_AndroidDevice_stop_adb_logcat(self, proc_mock, FastbootProxy, 442 MockAdbProxy): 443 """Verifies the AndroidDevice method stop_adb_logcat. Checks that the 444 underlying logcat process is stopped properly and correct warning msgs 445 are generated. 446 """ 447 ad = android_device.AndroidDevice(serial=MOCK_SERIAL) 448 ad.adb_logcat_process = proc_mock 449 # Expect warning msg if stop is called before start. 450 expected_msg = ( 451 "Android device .* does not have an ongoing adb logcat") 452 proc_mock.is_running.return_value = False 453 with self.assertLogs(level='WARNING') as log: 454 ad.stop_adb_logcat() 455 self.assertRegex(log.output[0], expected_msg) 456 457 # Verify the underlying process is stopped. 458 proc_mock.is_running.return_value = True 459 ad.stop_adb_logcat() 460 proc_mock.stop.assert_called_with() 461 462 @mock.patch( 463 'acts.controllers.adb.AdbProxy', 464 return_value=MockAdbProxy(MOCK_SERIAL)) 465 def test_get_apk_process_id_process_cannot_find(self, adb_proxy): 466 ad = android_device.AndroidDevice(serial=MOCK_SERIAL) 467 ad.adb.return_value = "does_not_contain_value" 468 self.assertEqual(None, ad.get_package_pid("some_package")) 469 470 @mock.patch( 471 'acts.controllers.adb.AdbProxy', 472 return_value=MockAdbProxy(MOCK_SERIAL)) 473 def test_get_apk_process_id_process_exists_second_try(self, adb_proxy): 474 ad = android_device.AndroidDevice(serial=MOCK_SERIAL) 475 ad.adb.return_multiple = True 476 ad.adb.return_value = ["", "system 1 2 3 4 S com.some_package"] 477 self.assertEqual(1, ad.get_package_pid("some_package")) 478 479 @mock.patch( 480 'acts.controllers.adb.AdbProxy', 481 return_value=MockAdbProxy(MOCK_SERIAL)) 482 def test_get_apk_process_id_bad_return(self, adb_proxy): 483 ad = android_device.AndroidDevice(serial=MOCK_SERIAL) 484 ad.adb.return_value = "bad_return_index_error" 485 self.assertEqual(None, ad.get_package_pid("some_package")) 486 487 @mock.patch( 488 'acts.controllers.adb.AdbProxy', 489 return_value=MockAdbProxy(MOCK_SERIAL)) 490 def test_get_apk_process_id_bad_return(self, adb_proxy): 491 ad = android_device.AndroidDevice(serial=MOCK_SERIAL) 492 ad.adb.return_value = "bad return value error" 493 self.assertEqual(None, ad.get_package_pid("some_package")) 494 495 496if __name__ == "__main__": 497 unittest.main() 498