1#!/usr/bin/env python3.4 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 base_test 25from acts.controllers import android_device 26 27# Mock log path for a test run. 28MOCK_LOG_PATH = "/tmp/logs/MockTest/xx-xx-xx_xx-xx-xx/" 29# The expected result of the cat adb operation. 30MOCK_ADB_LOGCAT_CAT_RESULT = [ 31 "02-29 14:02:21.456 4454 Something\n", 32 "02-29 14:02:21.789 4454 Something again\n" 33] 34# A mockd piece of adb logcat output. 35MOCK_ADB_LOGCAT = ("02-29 14:02:19.123 4454 Nothing\n" 36 "%s" 37 "02-29 14:02:22.123 4454 Something again and again\n" 38 ) % ''.join(MOCK_ADB_LOGCAT_CAT_RESULT) 39# Mock start and end time of the adb cat. 40MOCK_ADB_LOGCAT_BEGIN_TIME = "02-29 14:02:20.123" 41MOCK_ADB_LOGCAT_END_TIME = "02-29 14:02:22.000" 42 43 44def get_mock_ads(num): 45 """Generates a list of mock AndroidDevice objects. 46 47 The serial number of each device will be integer 0 through num - 1. 48 49 Args: 50 num: An integer that is the number of mock AndroidDevice objects to 51 create. 52 """ 53 ads = [] 54 for i in range(num): 55 ad = mock.MagicMock(name="AndroidDevice", serial=i, h_port=None) 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(): 69 """Mock class that swaps out calls to adb with mock calls.""" 70 71 def __init__(self, serial, fail_br=False, fail_br_before_N=False): 72 self.serial = serial 73 self.fail_br = fail_br 74 self.fail_br_before_N = fail_br_before_N 75 76 def shell(self, params, ignore_status=False, timeout=60): 77 if params == "id -u": 78 return "root" 79 elif params == "bugreportz": 80 if self.fail_br: 81 return "OMG I died!\n" 82 return "OK:/path/bugreport.zip\n" 83 elif params == "bugreportz -v": 84 if self.fail_br_before_N: 85 return "/system/bin/sh: bugreportz: not found" 86 return "1.1" 87 88 def getprop(self, params): 89 if params == "ro.build.id": 90 return "AB42" 91 elif params == "ro.build.type": 92 return "userdebug" 93 elif params == "ro.build.product" or params == "ro.product.name": 94 return "FakeModel" 95 elif params == "sys.boot_completed": 96 return "1" 97 98 def devices(self): 99 return "\t".join([str(self.serial), "device"]) 100 101 def bugreport(self, params, timeout=android_device.BUG_REPORT_TIMEOUT): 102 expected = os.path.join( 103 logging.log_path, "AndroidDevice%s" % self.serial, 104 "test_something", "AndroidDevice%s_sometime" % self.serial) 105 assert expected in params, "Expected '%s', got '%s'." % (expected, 106 params) 107 108 def __getattr__(self, name): 109 """All calls to the none-existent functions in adb proxy would 110 simply return the adb command string. 111 """ 112 113 def adb_call(*args, **kwargs): 114 arg_str = ' '.join(str(elem) for elem in args) 115 return arg_str 116 117 return adb_call 118 119 120class MockFastbootProxy(): 121 """Mock class that swaps out calls to adb with mock calls.""" 122 123 def __init__(self, serial): 124 self.serial = serial 125 126 def devices(self): 127 return "xxxx\tdevice\nyyyy\tdevice" 128 129 def __getattr__(self, name): 130 def fastboot_call(*args): 131 arg_str = ' '.join(str(elem) for elem in args) 132 return arg_str 133 134 return fastboot_call 135 136 137class ActsAndroidDeviceTest(unittest.TestCase): 138 """This test class has unit tests for the implementation of everything 139 under acts.controllers.android_device. 140 """ 141 142 def setUp(self): 143 # Set log_path to logging since acts logger setup is not called. 144 if not hasattr(logging, "log_path"): 145 setattr(logging, "log_path", "/tmp/logs") 146 # Creates a temp dir to be used by tests in this test class. 147 self.tmp_dir = tempfile.mkdtemp() 148 149 def tearDown(self): 150 """Removes the temp dir. 151 """ 152 shutil.rmtree(self.tmp_dir) 153 154 # Tests for android_device module functions. 155 # These tests use mock AndroidDevice instances. 156 157 @mock.patch.object( 158 android_device, "get_all_instances", new=mock_get_all_instances) 159 @mock.patch.object( 160 android_device, "list_adb_devices", new=mock_list_adb_devices) 161 def test_create_with_pickup_all(self): 162 pick_all_token = android_device.ANDROID_DEVICE_PICK_ALL_TOKEN 163 actual_ads = android_device.create(pick_all_token) 164 for actual, expected in zip(actual_ads, get_mock_ads(5)): 165 self.assertEqual(actual.serial, expected.serial) 166 167 def test_create_with_empty_config(self): 168 expected_msg = android_device.ANDROID_DEVICE_EMPTY_CONFIG_MSG 169 with self.assertRaisesRegex(android_device.AndroidDeviceError, 170 expected_msg): 171 android_device.create([]) 172 173 def test_create_with_not_list_config(self): 174 expected_msg = android_device.ANDROID_DEVICE_NOT_LIST_CONFIG_MSG 175 with self.assertRaisesRegex(android_device.AndroidDeviceError, 176 expected_msg): 177 android_device.create("HAHA") 178 179 def test_get_device_success_with_serial(self): 180 ads = get_mock_ads(5) 181 expected_serial = 0 182 ad = android_device.get_device(ads, serial=expected_serial) 183 self.assertEqual(ad.serial, expected_serial) 184 185 def test_get_device_success_with_serial_and_extra_field(self): 186 ads = get_mock_ads(5) 187 expected_serial = 1 188 expected_h_port = 5555 189 ads[1].h_port = expected_h_port 190 ad = android_device.get_device( 191 ads, serial=expected_serial, h_port=expected_h_port) 192 self.assertEqual(ad.serial, expected_serial) 193 self.assertEqual(ad.h_port, expected_h_port) 194 195 def test_get_device_no_match(self): 196 ads = get_mock_ads(5) 197 expected_msg = ("Could not find a target device that matches condition" 198 ": {'serial': 5}.") 199 with self.assertRaisesRegex(android_device.AndroidDeviceError, 200 expected_msg): 201 ad = android_device.get_device(ads, serial=len(ads)) 202 203 def test_get_device_too_many_matches(self): 204 ads = get_mock_ads(5) 205 target_serial = ads[1].serial = ads[0].serial 206 expected_msg = "More than one device matched: \[0, 0\]" 207 with self.assertRaisesRegex(android_device.AndroidDeviceError, 208 expected_msg): 209 ad = android_device.get_device(ads, serial=target_serial) 210 211 def test_start_services_on_ads(self): 212 """Makes sure when an AndroidDevice fails to start some services, all 213 AndroidDevice objects get cleaned up. 214 """ 215 msg = "Some error happened." 216 ads = get_mock_ads(3) 217 ads[0].start_services = mock.MagicMock() 218 ads[0].clean_up = mock.MagicMock() 219 ads[1].start_services = mock.MagicMock() 220 ads[1].clean_up = mock.MagicMock() 221 ads[2].start_services = mock.MagicMock( 222 side_effect=android_device.AndroidDeviceError(msg)) 223 ads[2].clean_up = mock.MagicMock() 224 with self.assertRaisesRegex(android_device.AndroidDeviceError, msg): 225 android_device._start_services_on_ads(ads) 226 ads[0].clean_up.assert_called_once_with() 227 ads[1].clean_up.assert_called_once_with() 228 ads[2].clean_up.assert_called_once_with() 229 230 # Tests for android_device.AndroidDevice class. 231 # These tests mock out any interaction with the OS and real android device 232 # in AndroidDeivce. 233 234 @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1)) 235 @mock.patch( 236 'acts.controllers.fastboot.FastbootProxy', 237 return_value=MockFastbootProxy(1)) 238 def test_AndroidDevice_instantiation(self, MockFastboot, MockAdbProxy): 239 """Verifies the AndroidDevice object's basic attributes are correctly 240 set after instantiation. 241 """ 242 mock_serial = 1 243 ad = android_device.AndroidDevice(serial=mock_serial) 244 self.assertEqual(ad.serial, 1) 245 self.assertEqual(ad.model, "fakemodel") 246 self.assertIsNone(ad.adb_logcat_process) 247 self.assertIsNone(ad.adb_logcat_file_path) 248 expected_lp = os.path.join(logging.log_path, 249 "AndroidDevice%s" % mock_serial) 250 self.assertEqual(ad.log_path, expected_lp) 251 252 @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1)) 253 @mock.patch( 254 'acts.controllers.fastboot.FastbootProxy', 255 return_value=MockFastbootProxy(1)) 256 def test_AndroidDevice_build_info(self, MockFastboot, MockAdbProxy): 257 """Verifies the AndroidDevice object's basic attributes are correctly 258 set after instantiation. 259 """ 260 ad = android_device.AndroidDevice(serial=1) 261 build_info = ad.build_info 262 self.assertEqual(build_info["build_id"], "AB42") 263 self.assertEqual(build_info["build_type"], "userdebug") 264 265 @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1)) 266 @mock.patch( 267 'acts.controllers.fastboot.FastbootProxy', 268 return_value=MockFastbootProxy(1)) 269 @mock.patch('acts.utils.create_dir') 270 @mock.patch('acts.utils.exe_cmd') 271 def test_AndroidDevice_take_bug_report(self, exe_mock, create_dir_mock, 272 FastbootProxy, MockAdbProxy): 273 """Verifies AndroidDevice.take_bug_report calls the correct adb command 274 and writes the bugreport file to the correct path. 275 """ 276 mock_serial = 1 277 ad = android_device.AndroidDevice(serial=mock_serial) 278 ad.take_bug_report("test_something", "sometime") 279 expected_path = os.path.join( 280 logging.log_path, "AndroidDevice%s" % ad.serial, "test_something") 281 create_dir_mock.assert_called_with(expected_path) 282 283 @mock.patch( 284 'acts.controllers.adb.AdbProxy', 285 return_value=MockAdbProxy(1, fail_br=True)) 286 @mock.patch( 287 'acts.controllers.fastboot.FastbootProxy', 288 return_value=MockFastbootProxy(1)) 289 @mock.patch('acts.utils.create_dir') 290 @mock.patch('acts.utils.exe_cmd') 291 def test_AndroidDevice_take_bug_report_fail( 292 self, exe_mock, create_dir_mock, FastbootProxy, MockAdbProxy): 293 """Verifies AndroidDevice.take_bug_report writes out the correct message 294 when taking bugreport fails. 295 """ 296 mock_serial = 1 297 ad = android_device.AndroidDevice(serial=mock_serial) 298 expected_msg = "Failed to take bugreport on 1: OMG I died!" 299 with self.assertRaisesRegex(android_device.AndroidDeviceError, 300 expected_msg): 301 ad.take_bug_report("test_something", "sometime") 302 303 @mock.patch( 304 'acts.controllers.adb.AdbProxy', 305 return_value=MockAdbProxy(1, fail_br_before_N=True)) 306 @mock.patch( 307 'acts.controllers.fastboot.FastbootProxy', 308 return_value=MockFastbootProxy(1)) 309 @mock.patch('acts.utils.create_dir') 310 @mock.patch('acts.utils.exe_cmd') 311 def test_AndroidDevice_take_bug_report_fallback( 312 self, exe_mock, create_dir_mock, FastbootProxy, MockAdbProxy): 313 """Verifies AndroidDevice.take_bug_report falls back to traditional 314 bugreport on builds that do not have bugreportz. 315 """ 316 mock_serial = 1 317 ad = android_device.AndroidDevice(serial=mock_serial) 318 ad.take_bug_report("test_something", "sometime") 319 expected_path = os.path.join( 320 logging.log_path, "AndroidDevice%s" % ad.serial, "test_something") 321 create_dir_mock.assert_called_with(expected_path) 322 323 @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1)) 324 @mock.patch( 325 'acts.controllers.fastboot.FastbootProxy', 326 return_value=MockFastbootProxy(1)) 327 @mock.patch('acts.utils.create_dir') 328 @mock.patch('acts.utils.start_standing_subprocess', return_value="process") 329 @mock.patch('acts.utils.stop_standing_subprocess') 330 def test_AndroidDevice_take_logcat(self, stop_proc_mock, start_proc_mock, 331 creat_dir_mock, FastbootProxy, 332 MockAdbProxy): 333 """Verifies the steps of collecting adb logcat on an AndroidDevice 334 object, including various function calls and the expected behaviors of 335 the calls. 336 """ 337 mock_serial = 1 338 ad = android_device.AndroidDevice(serial=mock_serial) 339 expected_msg = ("Android device .* does not have an ongoing adb logcat" 340 " collection.") 341 # Expect error if stop is called before start. 342 with self.assertRaisesRegex(android_device.AndroidDeviceError, 343 expected_msg): 344 ad.stop_adb_logcat() 345 ad.start_adb_logcat() 346 # Verify start did the correct operations. 347 self.assertTrue(ad.adb_logcat_process) 348 expected_log_path = os.path.join(logging.log_path, 349 "AndroidDevice%s" % ad.serial, 350 "adblog,fakemodel,%s.txt" % ad.serial) 351 creat_dir_mock.assert_called_with(os.path.dirname(expected_log_path)) 352 adb_cmd = 'adb -s %s logcat -v threadtime -b all >> %s' 353 start_proc_mock.assert_called_with(adb_cmd % (ad.serial, 354 expected_log_path)) 355 self.assertEqual(ad.adb_logcat_file_path, expected_log_path) 356 expected_msg = ("Android device .* already has an adb logcat thread " 357 "going on. Cannot start another one.") 358 # Expect error if start is called back to back. 359 with self.assertRaisesRegex(android_device.AndroidDeviceError, 360 expected_msg): 361 ad.start_adb_logcat() 362 # Verify stop did the correct operations. 363 ad.stop_adb_logcat() 364 stop_proc_mock.assert_called_with("process") 365 self.assertIsNone(ad.adb_logcat_process) 366 self.assertEqual(ad.adb_logcat_file_path, expected_log_path) 367 368 @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1)) 369 @mock.patch( 370 'acts.controllers.fastboot.FastbootProxy', 371 return_value=MockFastbootProxy(1)) 372 @mock.patch('acts.utils.create_dir') 373 @mock.patch('acts.utils.start_standing_subprocess', return_value="process") 374 @mock.patch('acts.utils.stop_standing_subprocess') 375 def test_AndroidDevice_take_logcat_with_user_param( 376 self, stop_proc_mock, start_proc_mock, creat_dir_mock, 377 FastbootProxy, MockAdbProxy): 378 """Verifies the steps of collecting adb logcat on an AndroidDevice 379 object, including various function calls and the expected behaviors of 380 the calls. 381 """ 382 mock_serial = 1 383 ad = android_device.AndroidDevice(serial=mock_serial) 384 ad.adb_logcat_param = "-b radio" 385 expected_msg = ("Android device .* does not have an ongoing adb logcat" 386 " collection.") 387 # Expect error if stop is called before start. 388 with self.assertRaisesRegex(android_device.AndroidDeviceError, 389 expected_msg): 390 ad.stop_adb_logcat() 391 ad.start_adb_logcat() 392 # Verify start did the correct operations. 393 self.assertTrue(ad.adb_logcat_process) 394 expected_log_path = os.path.join(logging.log_path, 395 "AndroidDevice%s" % ad.serial, 396 "adblog,fakemodel,%s.txt" % ad.serial) 397 creat_dir_mock.assert_called_with(os.path.dirname(expected_log_path)) 398 adb_cmd = 'adb -s %s logcat -v threadtime -b radio >> %s' 399 start_proc_mock.assert_called_with(adb_cmd % (ad.serial, 400 expected_log_path)) 401 self.assertEqual(ad.adb_logcat_file_path, expected_log_path) 402 403 @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1)) 404 @mock.patch( 405 'acts.controllers.fastboot.FastbootProxy', 406 return_value=MockFastbootProxy(1)) 407 @mock.patch('acts.utils.start_standing_subprocess', return_value="process") 408 @mock.patch('acts.utils.stop_standing_subprocess') 409 @mock.patch( 410 'acts.logger.get_log_line_timestamp', 411 return_value=MOCK_ADB_LOGCAT_END_TIME) 412 def test_AndroidDevice_cat_adb_log(self, mock_timestamp_getter, 413 stop_proc_mock, start_proc_mock, 414 FastbootProxy, MockAdbProxy): 415 """Verifies that AndroidDevice.cat_adb_log loads the correct adb log 416 file, locates the correct adb log lines within the given time range, 417 and writes the lines to the correct output file. 418 """ 419 mock_serial = 1 420 ad = android_device.AndroidDevice(serial=mock_serial) 421 # Expect error if attempted to cat adb log before starting adb logcat. 422 expected_msg = ("Attempting to cat adb log when none has been " 423 "collected on Android device .*") 424 with self.assertRaisesRegex(android_device.AndroidDeviceError, 425 expected_msg): 426 ad.cat_adb_log("some_test", MOCK_ADB_LOGCAT_BEGIN_TIME) 427 ad.start_adb_logcat() 428 # Direct the log path of the ad to a temp dir to avoid racing. 429 ad.log_path = os.path.join(self.tmp_dir, ad.log_path) 430 mock_adb_log_path = os.path.join(ad.log_path, "adblog,%s,%s.txt" % 431 (ad.model, ad.serial)) 432 with open(mock_adb_log_path, 'w') as f: 433 f.write(MOCK_ADB_LOGCAT) 434 ad.cat_adb_log("some_test", MOCK_ADB_LOGCAT_BEGIN_TIME) 435 cat_file_path = os.path.join( 436 ad.log_path, "AdbLogExcerpts", 437 ("some_test,02-29 14:02:20.123,%s,%s.txt") % (ad.model, ad.serial)) 438 with open(cat_file_path, 'r') as f: 439 actual_cat = f.read() 440 self.assertEqual(actual_cat, ''.join(MOCK_ADB_LOGCAT_CAT_RESULT)) 441 # Stops adb logcat. 442 ad.stop_adb_logcat() 443 444 445if __name__ == "__main__": 446 unittest.main() 447