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