Example #1
0
 def test_check_app_installed_fail_not_instrumented(self):
   sc = self._make_client(
       mock_android_device.MockAdbProxy(
           installed_packages=[MOCK_PACKAGE_NAME]))
   expected_msg = ('.* %s is installed, but it is not instrumented.' %
                   MOCK_PACKAGE_NAME)
   with self.assertRaisesRegex(snippet_client.AppStartPreCheckError,
                               expected_msg):
     sc._check_app_installed()
Example #2
0
 def test_check_app_installed_fail_target_not_installed(self):
   sc = self._make_client(
       mock_android_device.MockAdbProxy(instrumented_packages=[(
           MOCK_PACKAGE_NAME, snippet_client._INSTRUMENTATION_RUNNER_PACKAGE,
           MOCK_MISSING_PACKAGE_NAME)]))
   expected_msg = ('.* Instrumentation target %s is not installed.' %
                   MOCK_MISSING_PACKAGE_NAME)
   with self.assertRaisesRegex(snippet_client.AppStartPreCheckError,
                               expected_msg):
     sc._check_app_installed()
Example #3
0
 def test_app_not_installed(self, mock_get_port,
                            mock_start_standing_subprocess,
                            mock_create_connection):
     self.setup_mock_socket_file(mock_create_connection)
     self._setup_mock_instrumentation_cmd(mock_start_standing_subprocess,
                                          resp_lines=[b'\n'])
     client = self._make_client(
         adb_proxy=mock_android_device.MockAdbProxy())
     with self.assertRaisesRegex(jsonrpc_client_base.AppStartError,
                                 '.* SL4A is not installed on .*'):
         client.start_app_and_connect()
Example #4
0
 def _make_client(self, adb_proxy=None):
     adb_proxy = adb_proxy or mock_android_device.MockAdbProxy(
         installed_packages=['com.googlecode.android_scripting'])
     ad = mock.Mock()
     ad.adb = adb_proxy
     ad.build_info = {
         'build_version_codename':
         ad.adb.getprop('ro.build.version.codename'),
         'build_version_sdk': ad.adb.getprop('ro.build.version.sdk'),
     }
     return sl4a_client.Sl4aClient(ad=ad)
Example #5
0
 def _make_client(self, adb_proxy=None):
   adb_proxy = adb_proxy or mock_android_device.MockAdbProxy(
       instrumented_packages=[(MOCK_PACKAGE_NAME,
                               snippet_client._INSTRUMENTATION_RUNNER_PACKAGE,
                               MOCK_PACKAGE_NAME)])
   ad = mock.Mock()
   ad.adb = adb_proxy
   ad.adb.current_user_id = MOCK_USER_ID
   ad.build_info = {
       'build_version_codename': ad.adb.getprop('ro.build.version.codename'),
       'build_version_sdk': ad.adb.getprop('ro.build.version.sdk'),
   }
   return snippet_client.SnippetClient(package=MOCK_PACKAGE_NAME, ad=ad)
Example #6
0
class LogcatTest(unittest.TestCase):
    """Tests for Logcat service and its integration with AndroidDevice."""

    def setUp(self):
        # Set log_path to logging since mobly logger setup is not called.
        if not hasattr(logging, 'log_path'):
            setattr(logging, 'log_path', '/tmp/logs')
        # Creates a temp dir to be used by tests in this test class.
        self.tmp_dir = tempfile.mkdtemp()

    def tearDown(self):
        """Removes the temp dir.
        """
        shutil.rmtree(self.tmp_dir)

    def AssertFileContains(self, content, file_path):
        with open(file_path, 'r') as f:
            output = f.read()
        self.assertIn(content, output)

    def AssertFileDoesNotContain(self, content, file_path):
        with open(file_path, 'r') as f:
            output = f.read()
        self.assertNotIn(content, output)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch('mobly.logger.get_log_file_timestamp')
    def test_start_and_stop(self, get_timestamp_mock, stop_proc_mock,
                            start_proc_mock, create_dir_mock, FastbootProxy,
                            MockAdbProxy):
        """Verifies the steps of collecting adb logcat on an AndroidDevice
        object, including various function calls and the expected behaviors of
        the calls.
        """
        mock_serial = '1'
        get_timestamp_mock.return_value = '123'
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service.start()
        # Verify start did the correct operations.
        self.assertTrue(logcat_service._adb_logcat_process)
        expected_log_path = os.path.join(
            logging.log_path, 'AndroidDevice%s' % ad.serial,
            'logcat,%s,fakemodel,123.txt' % ad.serial)
        create_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
        adb_cmd = '"adb" -s %s logcat -v threadtime  >> %s'
        start_proc_mock.assert_called_with(
            adb_cmd % (ad.serial, '"%s"' % expected_log_path), shell=True)
        self.assertEqual(logcat_service.adb_logcat_file_path,
                         expected_log_path)
        expected_msg = (
            'Logcat thread is already running, cannot start another'
            ' one.')
        # Expect error if start is called back to back.
        with self.assertRaisesRegex(logcat.Error, expected_msg):
            logcat_service.start()
        # Verify stop did the correct operations.
        logcat_service.stop()
        stop_proc_mock.assert_called_with('process')
        self.assertIsNone(logcat_service._adb_logcat_process)
        self.assertEqual(logcat_service.adb_logcat_file_path,
                         expected_log_path)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_update_config(self, stop_proc_mock, start_proc_mock,
                           create_dir_mock, FastbootProxy, MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service.start()
        logcat_service.stop()
        new_log_params = '-a -b -c'
        new_file_path = 'some/path/log.txt'
        new_config = logcat.Config(logcat_params=new_log_params,
                                   output_file_path=new_file_path)
        logcat_service.update_config(new_config)
        logcat_service.start()
        self.assertTrue(logcat_service._adb_logcat_process)
        create_dir_mock.assert_has_calls([mock.call('some/path')])
        expected_adb_cmd = ('"adb" -s 1 logcat -v threadtime -a -b -c >> '
                            '"some/path/log.txt"')
        start_proc_mock.assert_called_with(expected_adb_cmd, shell=True)
        self.assertEqual(logcat_service.adb_logcat_file_path,
                         'some/path/log.txt')

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_update_config_while_running(self, stop_proc_mock, start_proc_mock,
                                         create_dir_mock, FastbootProxy,
                                         MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service.start()
        new_config = logcat.Config(logcat_params='-blah',
                                   output_file_path='some/path/file.txt')
        with self.assertRaisesRegex(
                logcat.Error,
                'Logcat thread is already running, cannot start another one'):
            logcat_service.update_config(new_config)
        self.assertTrue(logcat_service.is_alive)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch(
        'mobly.controllers.android_device_lib.services.logcat.Logcat.clear_adb_log',
        return_value=mock_android_device.MockAdbProxy('1'))
    def test_pause_and_resume(self, clear_adb_mock, stop_proc_mock,
                              start_proc_mock, create_dir_mock, FastbootProxy,
                              MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad, logcat.Config(clear_log=True))
        logcat_service.start()
        clear_adb_mock.assert_called_once_with()
        self.assertTrue(logcat_service.is_alive)
        logcat_service.pause()
        self.assertFalse(logcat_service.is_alive)
        stop_proc_mock.assert_called_with('process')
        self.assertIsNone(logcat_service._adb_logcat_process)
        clear_adb_mock.reset_mock()
        logcat_service.resume()
        self.assertTrue(logcat_service.is_alive)
        clear_adb_mock.assert_not_called()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch(
        'mobly.controllers.android_device_lib.services.logcat.Logcat.clear_adb_log',
        return_value=mock_android_device.MockAdbProxy('1'))
    def test_logcat_service_create_excerpt(self, clear_adb_mock,
                                           stop_proc_mock, start_proc_mock,
                                           FastbootProxy, MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service.start()
        FILE_CONTENT = 'Some log.\n'
        with open(logcat_service.adb_logcat_file_path, 'w') as f:
            f.write(FILE_CONTENT)
        test_output_dir = os.path.join(self.tmp_dir, 'test_foo')
        mock_record = mock.MagicMock()
        mock_record.begin_time = 123
        test_run_info = runtime_test_info.RuntimeTestInfo(
            'test_foo', test_output_dir, mock_record)
        actual_path1 = logcat_service.create_output_excerpts(test_run_info)[0]
        expected_path1 = os.path.join(test_output_dir, 'test_foo-123',
                                      'logcat,1,fakemodel,test_foo-123.txt')
        self.assertEqual(expected_path1, actual_path1)
        self.assertTrue(os.path.exists(expected_path1))
        self.AssertFileContains(FILE_CONTENT, expected_path1)
        self.assertFalse(os.path.exists(logcat_service.adb_logcat_file_path))
        # Generate some new logs and do another excerpt.
        FILE_CONTENT = 'Some more logs!!!\n'
        with open(logcat_service.adb_logcat_file_path, 'w') as f:
            f.write(FILE_CONTENT)
        test_output_dir = os.path.join(self.tmp_dir, 'test_bar')
        mock_record = mock.MagicMock()
        mock_record.begin_time = 456
        test_run_info = runtime_test_info.RuntimeTestInfo(
            'test_bar', test_output_dir, mock_record)
        actual_path2 = logcat_service.create_output_excerpts(test_run_info)[0]
        expected_path2 = os.path.join(test_output_dir, 'test_bar-456',
                                      'logcat,1,fakemodel,test_bar-456.txt')
        self.assertEqual(expected_path2, actual_path2)
        self.assertTrue(os.path.exists(expected_path2))
        self.AssertFileContains(FILE_CONTENT, expected_path2)
        self.AssertFileDoesNotContain(FILE_CONTENT, expected_path1)
        self.assertFalse(os.path.exists(logcat_service.adb_logcat_file_path))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch(
        'mobly.controllers.android_device_lib.services.logcat.Logcat.clear_adb_log',
        return_value=mock_android_device.MockAdbProxy('1'))
    def test_logcat_service_create_output_excerpts(self, clear_adb_mock,
                                                   stop_proc_mock,
                                                   start_proc_mock,
                                                   FastbootProxy,
                                                   MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service.start()
        FILE_CONTENT = 'Some log.\n'
        with open(logcat_service.adb_logcat_file_path, 'w') as f:
            f.write(FILE_CONTENT)
        test_output_dir = os.path.join(self.tmp_dir, 'test_foo')
        mock_record = mock.MagicMock()
        mock_record.begin_time = 123
        test_run_info = runtime_test_info.RuntimeTestInfo(
            'test_foo', test_output_dir, mock_record)
        actual_path1 = logcat_service.create_output_excerpts(test_run_info)[0]
        expected_path1 = os.path.join(test_output_dir, 'test_foo-123',
                                      'logcat,1,fakemodel,test_foo-123.txt')
        self.assertEqual(actual_path1, expected_path1)
        self.assertTrue(os.path.exists(expected_path1))
        self.AssertFileContains(FILE_CONTENT, expected_path1)
        self.assertFalse(os.path.exists(logcat_service.adb_logcat_file_path))
        # Generate some new logs and do another excerpt.
        FILE_CONTENT = 'Some more logs!!!\n'
        with open(logcat_service.adb_logcat_file_path, 'w') as f:
            f.write(FILE_CONTENT)
        test_output_dir = os.path.join(self.tmp_dir, 'test_bar')
        mock_record = mock.MagicMock()
        mock_record.begin_time = 456
        test_run_info = runtime_test_info.RuntimeTestInfo(
            'test_bar', test_output_dir, mock_record)
        actual_path2 = logcat_service.create_output_excerpts(test_run_info)[0]
        expected_path2 = os.path.join(test_output_dir, 'test_bar-456',
                                      'logcat,1,fakemodel,test_bar-456.txt')
        self.assertEqual(actual_path2, expected_path2)
        self.assertTrue(os.path.exists(expected_path2))
        self.AssertFileContains(FILE_CONTENT, expected_path2)
        self.AssertFileDoesNotContain(FILE_CONTENT, expected_path1)
        self.assertFalse(os.path.exists(logcat_service.adb_logcat_file_path))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch('mobly.logger.get_log_file_timestamp')
    def test_take_logcat_with_extra_params(self, get_timestamp_mock,
                                           stop_proc_mock, start_proc_mock,
                                           create_dir_mock, FastbootProxy,
                                           MockAdbProxy):
        """Verifies the steps of collecting adb logcat on an AndroidDevice
        object, including various function calls and the expected behaviors of
        the calls.
        """
        mock_serial = '1'
        get_timestamp_mock.return_value = '123'
        ad = android_device.AndroidDevice(serial=mock_serial)
        configs = logcat.Config()
        configs.logcat_params = '-b radio'
        logcat_service = logcat.Logcat(ad, configs)
        logcat_service.start()
        # Verify start did the correct operations.
        self.assertTrue(logcat_service._adb_logcat_process)
        expected_log_path = os.path.join(
            logging.log_path, 'AndroidDevice%s' % ad.serial,
            'logcat,%s,fakemodel,123.txt' % ad.serial)
        create_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
        adb_cmd = '"adb" -s %s logcat -v threadtime -b radio >> %s'
        start_proc_mock.assert_called_with(
            adb_cmd % (ad.serial, '"%s"' % expected_log_path), shell=True)
        self.assertEqual(logcat_service.adb_logcat_file_path,
                         expected_log_path)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test_instantiation(self, MockFastboot, MockAdbProxy):
        """Verifies the AndroidDevice object's basic attributes are correctly
        set after instantiation.
        """
        mock_serial = 1
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        self.assertIsNone(logcat_service._adb_logcat_process)
        self.assertIsNone(logcat_service.adb_logcat_file_path)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch('mobly.logger.get_log_line_timestamp',
                return_value=MOCK_ADB_LOGCAT_END_TIME)
    def test_cat_adb_log(self, mock_timestamp_getter, stop_proc_mock,
                         start_proc_mock, FastbootProxy, MockAdbProxy):
        """Verifies that AndroidDevice.cat_adb_log loads the correct adb log
        file, locates the correct adb log lines within the given time range,
        and writes the lines to the correct output file.
        """
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service._enable_logpersist()
        # Direct the log path of the ad to a temp dir to avoid racing.
        logcat_service._ad._log_path = self.tmp_dir
        # Expect error if attempted to cat adb log before starting adb logcat.
        expected_msg = ('.* Attempting to cat adb log when none'
                        ' has been collected.')
        with self.assertRaisesRegex(logcat.Error, expected_msg):
            logcat_service.cat_adb_log('some_test', MOCK_ADB_LOGCAT_BEGIN_TIME)
        logcat_service.start()
        utils.create_dir(ad.log_path)
        mock_adb_log_path = logcat_service.adb_logcat_file_path
        with io.open(mock_adb_log_path, 'w', encoding='utf-8') as f:
            f.write(MOCK_ADB_LOGCAT)
        cat_file_path = logcat_service.cat_adb_log('some_test',
                                                   MOCK_ADB_LOGCAT_BEGIN_TIME)
        with io.open(cat_file_path, 'r', encoding='utf-8') as f:
            actual_cat = f.read()
        self.assertEqual(actual_cat, ''.join(MOCK_ADB_LOGCAT_CAT_RESULT))
        # Stops adb logcat.
        logcat_service.stop()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch('mobly.logger.get_log_line_timestamp',
                return_value=MOCK_ADB_LOGCAT_END_TIME)
    def test_cat_adb_log_with_unicode(self, mock_timestamp_getter,
                                      stop_proc_mock, start_proc_mock,
                                      FastbootProxy, MockAdbProxy):
        """Verifies that AndroidDevice.cat_adb_log loads the correct adb log
        file, locates the correct adb log lines within the given time range,
        and writes the lines to the correct output file.
        """
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service._enable_logpersist()
        # Direct the log path of the ad to a temp dir to avoid racing.
        logcat_service._ad._log_path = self.tmp_dir
        # Expect error if attempted to cat adb log before starting adb logcat.
        expected_msg = ('.* Attempting to cat adb log when none'
                        ' has been collected.')
        with self.assertRaisesRegex(logcat.Error, expected_msg):
            logcat_service.cat_adb_log('some_test', MOCK_ADB_LOGCAT_BEGIN_TIME)
        logcat_service.start()
        utils.create_dir(ad.log_path)
        mock_adb_log_path = logcat_service.adb_logcat_file_path
        with io.open(mock_adb_log_path, 'w', encoding='utf-8') as f:
            f.write(MOCK_ADB_UNICODE_LOGCAT)
        cat_file_path = logcat_service.cat_adb_log('some_test',
                                                   MOCK_ADB_LOGCAT_BEGIN_TIME)
        with io.open(cat_file_path, 'r', encoding='utf-8') as f:
            actual_cat = f.read()
        self.assertEqual(actual_cat,
                         ''.join(MOCK_ADB_UNICODE_LOGCAT_CAT_RESULT))
        # Stops adb logcat.
        logcat_service.stop()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock.MagicMock())
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test__enable_logpersist_with_logpersist(self, MockFastboot,
                                                MockAdbProxy):
        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprops.return_value = {
            'ro.build.id': 'AB42',
            'ro.build.type': 'userdebug',
            'ro.debuggable': '1',
        }
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': True,
            'logpersist.stop': True,
        }[command]
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service._enable_logpersist()
        mock_adb_proxy.shell.assert_has_calls([
            mock.call('logpersist.stop --clear'),
            mock.call('logpersist.start'),
        ])

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock.MagicMock())
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test__enable_logpersist_with_user_build_device(self, MockFastboot,
                                                       MockAdbProxy):
        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprops.return_value = {
            'ro.build.id': 'AB42',
            'ro.build.type': 'user',
            'ro.debuggable': '0',
        }
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': True,
            'logpersist.stop': True,
        }[command]
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service._enable_logpersist()
        mock_adb_proxy.shell.assert_not_called()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock.MagicMock())
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test__enable_logpersist_with_missing_all_logpersist(
            self, MockFastboot, MockAdbProxy):
        def adb_shell_helper(command):
            if command == 'logpersist.start':
                raise MOCK_LOGPERSIST_START_MISSING_ADB_ERROR
            elif command == 'logpersist.stop --clear':
                raise MOCK_LOGPERSIST_STOP_MISSING_ADB_ERROR
            else:
                return b''

        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprops.return_value = {
            'ro.build.id': 'AB42',
            'ro.build.type': 'userdebug',
            'ro.debuggable': '1',
        }
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': False,
            'logpersist.stop': False,
        }[command]
        mock_adb_proxy.shell.side_effect = adb_shell_helper
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service._enable_logpersist()
        mock_adb_proxy.shell.assert_not_called()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock.MagicMock())
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test__enable_logpersist_with_missing_logpersist_stop(
            self, MockFastboot, MockAdbProxy):
        def adb_shell_helper(command):
            if command == 'logpersist.stop --clear':
                raise MOCK_LOGPERSIST_STOP_MISSING_ADB_ERROR
            else:
                return b''

        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprops.return_value = {
            'ro.build.id': 'AB42',
            'ro.build.type': 'userdebug',
            'ro.debuggable': '1',
        }
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': True,
            'logpersist.stop': False,
        }[command]
        mock_adb_proxy.shell.side_effect = adb_shell_helper
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service._enable_logpersist()
        mock_adb_proxy.shell.assert_has_calls([
            mock.call('logpersist.stop --clear'),
        ])

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock.MagicMock())
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test__enable_logpersist_with_missing_logpersist_start(
            self, MockFastboot, MockAdbProxy):
        def adb_shell_helper(command):
            if command == 'logpersist.start':
                raise MOCK_LOGPERSIST_START_MISSING_ADB_ERROR
            else:
                return b''

        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprops.return_value = {
            'ro.build.id': 'AB42',
            'ro.build.type': 'userdebug',
            'ro.debuggable': '1',
        }
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': False,
            'logpersist.stop': True,
        }[command]
        mock_adb_proxy.shell.side_effect = adb_shell_helper
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service._enable_logpersist()
        mock_adb_proxy.shell.assert_not_called()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy')
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test_clear_adb_log(self, MockFastboot, MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        ad.adb.logcat = mock.MagicMock()
        ad.adb.logcat.side_effect = adb.AdbError(
            cmd='cmd',
            stdout=b'',
            stderr=b'failed to clear "main" log',
            ret_code=1)
        logcat_service = logcat.Logcat(ad)
        logcat_service.clear_adb_log()
Example #7
0
 def test_check_app_installed_fail_app_not_installed(self):
   sc = self._make_client(mock_android_device.MockAdbProxy())
   expected_msg = '.* %s is not installed.' % MOCK_PACKAGE_NAME
   with self.assertRaisesRegex(snippet_client.AppStartPreCheckError,
                               expected_msg):
     sc._check_app_installed()
Example #8
0
class TestRunnerTest(unittest.TestCase):
    """This test class has unit tests for the implementation of everything
    under mobly.test_runner.
    """
    def setUp(self):
        self.tmp_dir = tempfile.mkdtemp()
        self.base_mock_test_config = config_parser.TestRunConfig()
        self.base_mock_test_config.test_bed_name = 'SampleTestBed'
        self.base_mock_test_config.controller_configs = {}
        self.base_mock_test_config.user_params = {
            'icecream': 42,
            'extra_param': 'haha'
        }
        self.base_mock_test_config.log_path = self.tmp_dir
        self.log_dir = self.base_mock_test_config.log_path
        self.test_bed_name = self.base_mock_test_config.test_bed_name

    def tearDown(self):
        shutil.rmtree(self.tmp_dir)

    def test_register_controller_no_config(self):
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        with self.assertRaisesRegex(signals.ControllerError,
                                    'No corresponding config found for'):
            tr._register_controller(self.base_mock_test_config,
                                    mock_controller)

    def test_register_controller_no_config_no_register(self):
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        self.assertIsNone(
            tr._register_controller(self.base_mock_test_config,
                                    mock_controller,
                                    required=False))

    def test_register_controller_dup_register(self):
        """Verifies correctness of registration, internal tally of controllers
        objects, and the right error happen when a controller module is
        registered twice.
        """
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        mock_test_config.controller_configs = {
            mock_ctrlr_config_name: ['magic1', 'magic2']
        }
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        tr._register_controller(mock_test_config, mock_controller)
        registered_name = 'mock_controller'
        self.assertTrue(registered_name in tr._controller_registry)
        mock_ctrlrs = tr._controller_registry[registered_name]
        self.assertEqual(mock_ctrlrs[0].magic, 'magic1')
        self.assertEqual(mock_ctrlrs[1].magic, 'magic2')
        self.assertTrue(tr._controller_destructors[registered_name])
        expected_msg = 'Controller module .* has already been registered.'
        with self.assertRaisesRegex(signals.ControllerError, expected_msg):
            tr._register_controller(mock_test_config, mock_controller)

    def test_register_controller_no_get_info(self):
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        get_info = getattr(mock_controller, 'get_info')
        delattr(mock_controller, 'get_info')
        try:
            mock_test_config.controller_configs = {
                mock_ctrlr_config_name: ['magic1', 'magic2']
            }
            tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
            tr._register_controller(mock_test_config, mock_controller)
            self.assertEqual(tr.results.controller_info, {})
        finally:
            setattr(mock_controller, 'get_info', get_info)

    def test_register_controller_return_value(self):
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        mock_test_config.controller_configs = {
            mock_ctrlr_config_name: ['magic1', 'magic2']
        }
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        magic_devices = tr._register_controller(mock_test_config,
                                                mock_controller)
        self.assertEqual(magic_devices[0].magic, 'magic1')
        self.assertEqual(magic_devices[1].magic, 'magic2')

    def test_register_controller_change_return_value(self):
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        mock_test_config.controller_configs = {
            mock_ctrlr_config_name: ['magic1', 'magic2']
        }
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        magic_devices = tr._register_controller(mock_test_config,
                                                mock_controller)
        magic1 = magic_devices.pop(0)
        self.assertIs(magic1, tr._controller_registry['mock_controller'][0])
        self.assertEqual(len(tr._controller_registry['mock_controller']), 2)

    def test_register_controller_less_than_min_number(self):
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        mock_test_config.controller_configs = {
            mock_ctrlr_config_name: ['magic1', 'magic2']
        }
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        expected_msg = 'Expected to get at least 3 controller objects, got 2.'
        with self.assertRaisesRegex(signals.ControllerError, expected_msg):
            tr._register_controller(mock_test_config,
                                    mock_controller,
                                    min_number=3)

    def test_run_twice(self):
        """Verifies that:
        1. Repeated run works properly.
        2. The original configuration is not altered if a test controller
           module modifies configuration.
        """
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        my_config = [{
            'serial': 'xxxx',
            'magic': 'Magic1'
        }, {
            'serial': 'xxxx',
            'magic': 'Magic2'
        }]
        mock_test_config.controller_configs[mock_ctrlr_config_name] = my_config
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        tr.add_test_class(mock_test_config, integration_test.IntegrationTest)
        tr.run()
        self.assertFalse(tr._controller_registry)
        self.assertFalse(tr._controller_destructors)
        self.assertTrue(
            mock_test_config.controller_configs[mock_ctrlr_config_name][0])
        tr.run()
        self.assertFalse(tr._controller_registry)
        self.assertFalse(tr._controller_destructors)
        results = tr.results.summary_dict()
        self.assertEqual(results['Requested'], 2)
        self.assertEqual(results['Executed'], 2)
        self.assertEqual(results['Passed'], 2)
        expected_info = {
            'MagicDevice': [{
                'MyMagic': {
                    'magic': 'Magic1'
                }
            }, {
                'MyMagic': {
                    'magic': 'Magic2'
                }
            }]
        }
        self.assertEqual(tr.results.controller_info, expected_info)

    def test_summary_file_entries(self):
        """Verifies the output summary's file format.

        This focuses on the format of the file instead of the content of
        entries, which is covered in base_test_test.
        """
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        my_config = [{
            'serial': 'xxxx',
            'magic': 'Magic1'
        }, {
            'serial': 'xxxx',
            'magic': 'Magic2'
        }]
        mock_test_config.controller_configs[mock_ctrlr_config_name] = my_config
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        tr.add_test_class(mock_test_config, integration_test.IntegrationTest)
        tr.run()
        summary_path = os.path.join(mock_test_config.log_path,
                                    mock_test_config.test_bed_name, 'latest',
                                    records.OUTPUT_FILE_SUMMARY)
        with open(summary_path, 'r') as f:
            summary_entries = list(yaml.load_all(f))
        self.assertEqual(len(summary_entries), 4)
        # Verify the first entry is the list of test names.
        self.assertEqual(summary_entries[0]['Type'],
                         records.TestSummaryEntryType.TEST_NAME_LIST.value)
        self.assertEqual(summary_entries[1]['Type'],
                         records.TestSummaryEntryType.RECORD.value)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch('mobly.controllers.android_device.list_adb_devices',
                return_value=['1'])
    @mock.patch('mobly.controllers.android_device.get_all_instances',
                return_value=mock_android_device.get_mock_ads(1))
    def test_run_two_test_classes(self, mock_get_all, mock_list_adb,
                                  mock_fastboot, mock_adb):
        """Verifies that running more than one test class in one test run works
        properly.

        This requires using a built-in controller module. Using AndroidDevice
        module since it has all the mocks needed already.
        """
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        my_config = [{
            'serial': 'xxxx',
            'magic': 'Magic1'
        }, {
            'serial': 'xxxx',
            'magic': 'Magic2'
        }]
        mock_test_config.controller_configs[mock_ctrlr_config_name] = my_config
        mock_test_config.controller_configs['AndroidDevice'] = [{
            'serial': '1'
        }]
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        tr.add_test_class(mock_test_config, integration2_test.Integration2Test)
        tr.add_test_class(mock_test_config, integration_test.IntegrationTest)
        tr.run()
        self.assertFalse(tr._controller_registry)
        self.assertFalse(tr._controller_destructors)
        results = tr.results.summary_dict()
        self.assertEqual(results['Requested'], 2)
        self.assertEqual(results['Executed'], 2)
        self.assertEqual(results['Passed'], 2)

    def test_run_two_test_classes_different_configs(self):
        """Verifies that running more than one test class in one test run with
        different configs works properly.
        """
        config1 = self.base_mock_test_config.copy()
        config1.controller_configs[
            mock_controller.MOBLY_CONTROLLER_CONFIG_NAME] = [{
                'serial': 'xxxx'
            }]
        config2 = config1.copy()
        config2.user_params['icecream'] = 10
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        tr.add_test_class(config1, integration_test.IntegrationTest)
        tr.add_test_class(config2, integration_test.IntegrationTest)
        tr.run()
        results = tr.results.summary_dict()
        self.assertEqual(results['Requested'], 2)
        self.assertEqual(results['Executed'], 2)
        self.assertEqual(results['Passed'], 1)
        self.assertEqual(results['Failed'], 1)
        self.assertEqual(tr.results.failed[0].details, '10 != 42')

    def test_run_with_abort_all(self):
        mock_test_config = self.base_mock_test_config.copy()
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        tr.add_test_class(mock_test_config, integration3_test.Integration3Test)
        with self.assertRaises(signals.TestAbortAll):
            tr.run()
        results = tr.results.summary_dict()
        self.assertEqual(results['Requested'], 1)
        self.assertEqual(results['Executed'], 0)
        self.assertEqual(results['Passed'], 0)
        self.assertEqual(results['Failed'], 0)

    def test_add_test_class_mismatched_log_path(self):
        tr = test_runner.TestRunner('/different/log/dir', self.test_bed_name)
        with self.assertRaisesRegex(
                test_runner.Error,
                'TestRunner\'s log folder is "/different/log/dir", but a test '
                r'config with a different log folder \("%s"\) was added.' %
                self.log_dir):
            tr.add_test_class(self.base_mock_test_config,
                              integration_test.IntegrationTest)

    def test_add_test_class_mismatched_test_bed_name(self):
        tr = test_runner.TestRunner(self.log_dir, 'different_test_bed')
        with self.assertRaisesRegex(
                test_runner.Error,
                'TestRunner\'s test bed is "different_test_bed", but a test '
                r'config with a different test bed \("%s"\) was added.' %
                self.test_bed_name):
            tr.add_test_class(self.base_mock_test_config,
                              integration_test.IntegrationTest)

    def test_teardown_logger_before_setup_logger(self):
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        with self.assertRaisesRegex(
                test_runner.Error,
                'TestRunner\._teardown_logger\(\) called before '
                'TestRunner\.setup_logger\(\)!'):
            tr._teardown_logger()

    def test_run_no_tests(self):
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        with self.assertRaisesRegex(test_runner.Error, 'No tests to execute.'):
            tr.run()

    def test_verify_controller_module(self):
        test_runner.verify_controller_module(mock_controller)

    def test_verify_controller_module_null_attr(self):
        try:
            tmp = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
            mock_controller.MOBLY_CONTROLLER_CONFIG_NAME = None
            msg = 'Controller interface .* in .* cannot be null.'
            with self.assertRaisesRegex(signals.ControllerError, msg):
                test_runner.verify_controller_module(mock_controller)
        finally:
            mock_controller.MOBLY_CONTROLLER_CONFIG_NAME = tmp

    def test_verify_controller_module_missing_attr(self):
        try:
            tmp = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
            delattr(mock_controller, 'MOBLY_CONTROLLER_CONFIG_NAME')
            msg = 'Module .* missing required controller module attribute'
            with self.assertRaisesRegex(signals.ControllerError, msg):
                test_runner.verify_controller_module(mock_controller)
        finally:
            setattr(mock_controller, 'MOBLY_CONTROLLER_CONFIG_NAME', tmp)

    @mock.patch('mobly.test_runner._find_test_class',
                return_value=type('SampleTest', (), {}))
    @mock.patch('mobly.test_runner.config_parser.load_test_config_file',
                return_value=[config_parser.TestRunConfig()])
    @mock.patch('mobly.test_runner.TestRunner', return_value=mock.MagicMock())
    def test_main_parse_args(self, mock_test_runner, mock_config,
                             mock_find_test):
        test_runner.main(['-c', 'some/path/foo.yaml', '-b', 'hello'])
        mock_config.assert_called_with('some/path/foo.yaml', None)
class AndroidDeviceTest(unittest.TestCase):
    """This test class has unit tests for the implementation of everything
    under mobly.controllers.android_device.
    """
    def setUp(self):
        # Set log_path to logging since mobly logger setup is not called.
        if not hasattr(logging, 'log_path'):
            setattr(logging, 'log_path', '/tmp/logs')
        # Creates a temp dir to be used by tests in this test class.
        self.tmp_dir = tempfile.mkdtemp()

    def tearDown(self):
        """Removes the temp dir.
        """
        shutil.rmtree(self.tmp_dir)

    # Tests for android_device module functions.
    # These tests use mock AndroidDevice instances.

    @mock.patch.object(android_device,
                       'get_all_instances',
                       new=mock_android_device.get_all_instances)
    @mock.patch.object(android_device,
                       'list_adb_devices',
                       new=mock_android_device.list_adb_devices)
    @mock.patch.object(android_device,
                       'list_adb_devices_by_usb_id',
                       new=mock_android_device.list_adb_devices)
    def test_create_with_pickup_all(self):
        pick_all_token = android_device.ANDROID_DEVICE_PICK_ALL_TOKEN
        actual_ads = android_device.create(pick_all_token)
        for actual, expected in zip(actual_ads,
                                    mock_android_device.get_mock_ads(5)):
            self.assertEqual(actual.serial, expected.serial)

    @mock.patch.object(android_device,
                       'get_instances',
                       new=mock_android_device.get_instances)
    @mock.patch.object(android_device,
                       'list_adb_devices',
                       new=mock_android_device.list_adb_devices)
    @mock.patch.object(android_device,
                       'list_adb_devices_by_usb_id',
                       new=mock_android_device.list_adb_devices)
    def test_create_with_string_list(self):
        string_list = [u'1', '2']
        actual_ads = android_device.create(string_list)
        for actual_ad, expected_serial in zip(actual_ads, ['1', '2']):
            self.assertEqual(actual_ad.serial, expected_serial)

    @mock.patch.object(android_device,
                       'get_instances_with_configs',
                       new=mock_android_device.get_instances_with_configs)
    @mock.patch.object(android_device,
                       'list_adb_devices',
                       new=mock_android_device.list_adb_devices)
    @mock.patch.object(android_device,
                       'list_adb_devices_by_usb_id',
                       new=mock_android_device.list_adb_devices)
    def test_create_with_dict_list(self):
        string_list = [{'serial': '1'}, {'serial': '2'}]
        actual_ads = android_device.create(string_list)
        for actual_ad, expected_serial in zip(actual_ads, ['1', '2']):
            self.assertEqual(actual_ad.serial, expected_serial)

    @mock.patch.object(android_device,
                       'get_instances_with_configs',
                       new=mock_android_device.get_instances_with_configs)
    @mock.patch.object(android_device,
                       'list_adb_devices',
                       new=mock_android_device.list_adb_devices)
    @mock.patch.object(android_device,
                       'list_adb_devices_by_usb_id',
                       return_value=['usb:1'])
    def test_create_with_usb_id(self, mock_list_adb_devices_by_usb_id):
        string_list = [{'serial': '1'}, {'serial': '2'}, {'serial': 'usb:1'}]
        actual_ads = android_device.create(string_list)
        for actual_ad, expected_serial in zip(actual_ads, ['1', '2', 'usb:1']):
            self.assertEqual(actual_ad.serial, expected_serial)

    def test_create_with_empty_config(self):
        expected_msg = android_device.ANDROID_DEVICE_EMPTY_CONFIG_MSG
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            android_device.create([])

    def test_create_with_not_list_config(self):
        expected_msg = android_device.ANDROID_DEVICE_NOT_LIST_CONFIG_MSG
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            android_device.create('HAHA')

    def test_create_with_no_valid_config(self):
        expected_msg = 'No valid config found in: .*'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            android_device.create([1])

    def test_get_devices_success_with_extra_field(self):
        ads = mock_android_device.get_mock_ads(5)
        expected_label = 'selected'
        expected_count = 2
        for ad in ads[:expected_count]:
            ad.label = expected_label
        selected_ads = android_device.get_devices(ads, label=expected_label)
        self.assertEqual(expected_count, len(selected_ads))
        for ad in selected_ads:
            self.assertEqual(ad.label, expected_label)

    def test_get_devices_no_match(self):
        ads = mock_android_device.get_mock_ads(5)
        expected_msg = ('Could not find a target device that matches condition'
                        ": {'label': 'selected'}.")
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            selected_ads = android_device.get_devices(ads, label='selected')

    def test_get_device_success_with_serial(self):
        ads = mock_android_device.get_mock_ads(5)
        expected_serial = '0'
        ad = android_device.get_device(ads, serial=expected_serial)
        self.assertEqual(ad.serial, expected_serial)

    def test_get_device_success_with_serial_and_extra_field(self):
        ads = mock_android_device.get_mock_ads(5)
        expected_serial = '1'
        expected_h_port = 5555
        ads[1].h_port = expected_h_port
        ad = android_device.get_device(ads,
                                       serial=expected_serial,
                                       h_port=expected_h_port)
        self.assertEqual(ad.serial, expected_serial)
        self.assertEqual(ad.h_port, expected_h_port)

    def test_get_device_no_match(self):
        ads = mock_android_device.get_mock_ads(5)
        expected_msg = ('Could not find a target device that matches condition'
                        ": {'serial': 5}.")
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad = android_device.get_device(ads, serial=len(ads))

    def test_get_device_too_many_matches(self):
        ads = mock_android_device.get_mock_ads(5)
        target_serial = ads[1].serial = ads[0].serial
        expected_msg = r"More than one device matched: \['0', '0'\]"
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            android_device.get_device(ads, serial=target_serial)

    def test_start_services_on_ads(self):
        """Makes sure when an AndroidDevice fails to start some services, all
        AndroidDevice objects get cleaned up.
        """
        msg = 'Some error happened.'
        ads = mock_android_device.get_mock_ads(3)
        ads[0].services.register = mock.MagicMock()
        ads[0].services.stop_all = mock.MagicMock()
        ads[1].services.register = mock.MagicMock()
        ads[1].services.stop_all = mock.MagicMock()
        ads[2].services.register = mock.MagicMock(
            side_effect=android_device.Error(msg))
        ads[2].services.stop_all = mock.MagicMock()
        with self.assertRaisesRegex(android_device.Error, msg):
            android_device._start_services_on_ads(ads)
        ads[0].services.stop_all.assert_called_once_with()
        ads[1].services.stop_all.assert_called_once_with()
        ads[2].services.stop_all.assert_called_once_with()

    def test_start_services_on_ads_skip_logcat(self):
        ads = mock_android_device.get_mock_ads(3)
        ads[0].services.logcat.start = mock.MagicMock()
        ads[1].services.logcat.start = mock.MagicMock()
        ads[2].services.logcat.start = mock.MagicMock(
            side_effect=Exception('Should not have called this.'))
        ads[2].skip_logcat = True
        android_device._start_services_on_ads(ads)

    def test_take_bug_reports(self):
        ads = mock_android_device.get_mock_ads(3)
        android_device.take_bug_reports(ads, 'test_something', 'sometime')
        ads[0].take_bug_report.assert_called_once_with(
            test_name='test_something',
            begin_time='sometime',
            destination=None)
        ads[1].take_bug_report.assert_called_once_with(
            test_name='test_something',
            begin_time='sometime',
            destination=None)
        ads[2].take_bug_report.assert_called_once_with(
            test_name='test_something',
            begin_time='sometime',
            destination=None)

    # Tests for android_device.AndroidDevice class.
    # These tests mock out any interaction with the OS and real android device
    # in AndroidDeivce.

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_instantiation(self, MockFastboot, MockAdbProxy):
        """Verifies the AndroidDevice object's basic attributes are correctly
        set after instantiation.
        """
        mock_serial = 1
        ad = android_device.AndroidDevice(serial=mock_serial)
        self.assertEqual(ad.serial, '1')
        self.assertEqual(ad.model, 'fakemodel')
        expected_lp = os.path.join(logging.log_path,
                                   'AndroidDevice%s' % mock_serial)
        self.assertEqual(ad.log_path, expected_lp)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_build_info(self, MockFastboot, MockAdbProxy):
        """Verifies the AndroidDevice object's basic attributes are correctly
        set after instantiation.
        """
        ad = android_device.AndroidDevice(serial='1')
        build_info = ad.build_info
        self.assertEqual(build_info['build_id'], 'AB42')
        self.assertEqual(build_info['build_type'], 'userdebug')
        self.assertEqual(build_info['build_version_codename'], 'Z')
        self.assertEqual(build_info['build_version_sdk'], '28')
        self.assertEqual(build_info['build_product'], 'FakeModel')
        self.assertEqual(build_info['product_name'], 'FakeModel')
        self.assertEqual(build_info['debuggable'], '1')
        self.assertEqual(len(build_info),
                         len(android_device.CACHED_SYSTEM_PROPS))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(
                    '1',
                    mock_properties={
                        'ro.build.id': 'AB42',
                        'ro.build.type': 'userdebug',
                    }))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_build_info_with_minimal_properties(
            self, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        build_info = ad.build_info
        self.assertEqual(build_info['build_id'], 'AB42')
        self.assertEqual(build_info['build_type'], 'userdebug')
        self.assertEqual(build_info['build_version_codename'], '')
        self.assertEqual(build_info['build_version_sdk'], '')
        self.assertEqual(build_info['build_product'], '')
        self.assertEqual(build_info['product_name'], '')
        self.assertEqual(build_info['debuggable'], '')

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_build_info_cached(self, MockFastboot, MockAdbProxy):
        """Verifies the AndroidDevice object's basic attributes are correctly
        set after instantiation.
        """
        ad = android_device.AndroidDevice(serial='1')
        _ = ad.build_info
        _ = ad.build_info
        _ = ad.build_info
        self.assertEqual(ad.adb.getprops_call_count, 1)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(
                    '1',
                    mock_properties={
                        'ro.build.id': 'AB42',
                        'ro.build.type': 'userdebug',
                        'ro.debuggable': '1',
                    }))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_is_rootable_when_userdebug_device(
            self, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        self.assertTrue(ad.is_rootable)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(
                    '1',
                    mock_properties={
                        'ro.build.id': 'AB42',
                        'ro.build.type': 'user',
                        'ro.debuggable': '0',
                    }))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_is_rootable_when_user_device(
            self, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        self.assertFalse(ad.is_rootable)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_device_info(self, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial=1)
        device_info = ad.device_info
        self.assertEqual(device_info['serial'], '1')
        self.assertEqual(device_info['model'], 'fakemodel')
        self.assertEqual(device_info['build_info']['build_id'], 'AB42')
        self.assertEqual(device_info['build_info']['build_type'], 'userdebug')
        ad.add_device_info('sim_type', 'Fi')
        ad.add_device_info('build_id', 'CD42')
        device_info = ad.device_info
        self.assertEqual(device_info['user_added_info']['sim_type'], 'Fi')
        self.assertEqual(device_info['user_added_info']['build_id'], 'CD42')

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_serial_is_valid(self, MockFastboot, MockAdbProxy):
        """Verifies that the serial is a primitive string type and serializable.
        """
        ad = android_device.AndroidDevice(serial=1)
        # In py2, checks that ad.serial is not the backported py3 str type,
        # which is not dumpable by yaml in py2.
        # In py3, new_str is equivalent to str, so this check is not
        # appropirate in py3.
        if sys.version_info < (3, 0):
            self.assertFalse(isinstance(ad.serial, new_str))
        self.assertTrue(isinstance(ad.serial, str))
        yaml.safe_dump(ad.serial)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch('mobly.utils.create_dir')
    def test_AndroidDevice_take_bug_report(self, create_dir_mock,
                                           FastbootProxy, MockAdbProxy):
        """Verifies AndroidDevice.take_bug_report calls the correct adb command
        and writes the bugreport file to the correct path.
        """
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        output_path = ad.take_bug_report(test_name='test_something',
                                         begin_time='sometime')
        expected_path = os.path.join(logging.log_path,
                                     'AndroidDevice%s' % ad.serial,
                                     'BugReports')
        create_dir_mock.assert_called_with(expected_path)
        self.assertEqual(
            output_path,
            os.path.join(expected_path, 'test_something,sometime,1.zip'))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1',
                                                              fail_br=True))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    def test_AndroidDevice_take_bug_report_fail(self, create_dir_mock,
                                                FastbootProxy, MockAdbProxy):
        """Verifies AndroidDevice.take_bug_report writes out the correct message
        when taking bugreport fails.
        """
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        expected_msg = '.* Failed to take bugreport.'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.take_bug_report(test_name='test_something',
                               begin_time='sometime')

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.get_current_epoch_time')
    @mock.patch('mobly.logger.epoch_to_log_line_timestamp')
    def test_AndroidDevice_take_bug_report_without_args(
            self, epoch_to_log_line_timestamp_mock,
            get_current_epoch_time_mock, create_dir_mock, FastbootProxy,
            MockAdbProxy):
        get_current_epoch_time_mock.return_value = 1557446629606
        epoch_to_log_line_timestamp_mock.return_value = '05-09 17:03:49.606'
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        output_path = ad.take_bug_report()
        expected_path = os.path.join(logging.log_path,
                                     'AndroidDevice%s' % ad.serial,
                                     'BugReports')
        create_dir_mock.assert_called_with(expected_path)
        epoch_to_log_line_timestamp_mock.assert_called_once_with(1557446629606)
        self.assertEqual(
            output_path,
            os.path.join(expected_path, 'bugreport,05-09_17-03-49.606,1.zip'))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.get_current_epoch_time')
    @mock.patch('mobly.logger.epoch_to_log_line_timestamp')
    def test_AndroidDevice_take_bug_report_with_only_test_name(
            self, epoch_to_log_line_timestamp_mock,
            get_current_epoch_time_mock, create_dir_mock, FastbootProxy,
            MockAdbProxy):
        get_current_epoch_time_mock.return_value = 1557446629606
        epoch_to_log_line_timestamp_mock.return_value = '05-09 17:03:49.606'
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        output_path = ad.take_bug_report(test_name='test_something')
        expected_path = os.path.join(logging.log_path,
                                     'AndroidDevice%s' % ad.serial,
                                     'BugReports')
        create_dir_mock.assert_called_with(expected_path)
        epoch_to_log_line_timestamp_mock.assert_called_once_with(1557446629606)
        self.assertEqual(
            output_path,
            os.path.join(expected_path,
                         'test_something,05-09_17-03-49.606,1.zip'))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch('mobly.utils.create_dir')
    def test_AndroidDevice_take_bug_report_with_only_begin_time(
            self, create_dir_mock, FastbootProxy, MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        output_path = ad.take_bug_report(begin_time='sometime')
        expected_path = os.path.join(logging.log_path,
                                     'AndroidDevice%s' % ad.serial,
                                     'BugReports')
        create_dir_mock.assert_called_with(expected_path)
        self.assertEqual(
            output_path, os.path.join(expected_path,
                                      'bugreport,sometime,1.zip'))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch('mobly.utils.create_dir')
    def test_AndroidDevice_take_bug_report_with_positional_args(
            self, create_dir_mock, FastbootProxy, MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        output_path = ad.take_bug_report('test_something', 'sometime')
        expected_path = os.path.join(logging.log_path,
                                     'AndroidDevice%s' % ad.serial,
                                     'BugReports')
        create_dir_mock.assert_called_with(expected_path)
        self.assertEqual(
            output_path,
            os.path.join(expected_path, 'test_something,sometime,1.zip'))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    def test_AndroidDevice_take_bug_report_with_destination(
            self, create_dir_mock, FastbootProxy, MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        dest = tempfile.gettempdir()
        output_path = ad.take_bug_report(test_name="test_something",
                                         begin_time="sometime",
                                         destination=dest)
        expected_path = os.path.join(dest)
        create_dir_mock.assert_called_with(expected_path)
        self.assertEqual(
            output_path,
            os.path.join(expected_path, 'test_something,sometime,1.zip'))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(
                    '1', fail_br_before_N=True))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    def test_AndroidDevice_take_bug_report_fallback(self, create_dir_mock,
                                                    FastbootProxy,
                                                    MockAdbProxy):
        """Verifies AndroidDevice.take_bug_report falls back to traditional
        bugreport on builds that do not have bugreportz.
        """
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        output_path = ad.take_bug_report(test_name='test_something',
                                         begin_time='sometime')
        expected_path = os.path.join(logging.log_path,
                                     'AndroidDevice%s' % ad.serial,
                                     'BugReports')
        create_dir_mock.assert_called_with(expected_path)
        self.assertEqual(
            output_path,
            os.path.join(expected_path, 'test_something,sometime,1.txt'))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_change_log_path(self, stop_proc_mock,
                                           start_proc_mock, FastbootProxy,
                                           MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        old_path = ad.log_path
        new_log_path = tempfile.mkdtemp()
        ad.log_path = new_log_path
        self.assertTrue(os.path.exists(new_log_path))
        self.assertFalse(os.path.exists(old_path))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_change_log_path_no_log_exists(
            self, stop_proc_mock, start_proc_mock, FastbootProxy,
            MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        old_path = ad.log_path
        new_log_path = tempfile.mkdtemp()
        ad.log_path = new_log_path
        self.assertTrue(os.path.exists(new_log_path))
        self.assertFalse(os.path.exists(old_path))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('127.0.0.1:5557')
                )
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('127.0.0.1:5557'))
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_with_reserved_character_in_serial_log_path(
            self, stop_proc_mock, start_proc_mock, FastbootProxy,
            MockAdbProxy):
        ad = android_device.AndroidDevice(serial='127.0.0.1:5557')
        base_log_path = os.path.basename(ad.log_path)
        self.assertEqual(base_log_path, 'AndroidDevice127.0.0.1-5557')

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_change_log_path_with_service(
            self, stop_proc_mock, start_proc_mock, creat_dir_mock,
            FastbootProxy, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.services.register('logcat', logcat.Logcat)
        new_log_path = tempfile.mkdtemp()
        expected_msg = '.* Cannot change `log_path` when there is service running.'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.log_path = new_log_path

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_change_log_path_with_existing_file(
            self, stop_proc_mock, start_proc_mock, creat_dir_mock,
            FastbootProxy, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        new_log_path = tempfile.mkdtemp()
        new_file_path = os.path.join(new_log_path, 'file.txt')
        with io.open(new_file_path, 'w', encoding='utf-8') as f:
            f.write(u'hahah.')
        expected_msg = '.* Logs already exist .*'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.log_path = new_log_path

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_update_serial(self, stop_proc_mock, start_proc_mock,
                                         creat_dir_mock, FastbootProxy,
                                         MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.update_serial('2')
        self.assertEqual(ad.serial, '2')
        self.assertEqual(ad.debug_tag, ad.serial)
        self.assertEqual(ad.adb.serial, ad.serial)
        self.assertEqual(ad.fastboot.serial, ad.serial)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_update_serial_with_service_running(
            self, stop_proc_mock, start_proc_mock, creat_dir_mock,
            FastbootProxy, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.services.register('logcat', logcat.Logcat)
        expected_msg = '.* Cannot change device serial number when there is service running.'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.update_serial('2')

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet(self, MockGetPort, MockSnippetClient,
                                        MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        self.assertTrue(hasattr(ad, 'snippet'))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_getattr(self, MockGetPort, MockSnippetClient,
                                   MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        value = {'value': 42}
        actual_value = getattr(ad, 'some_attr', value)
        self.assertEqual(actual_value, value)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient',
        return_value=MockSnippetClient)
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_dup_package(self, MockGetPort,
                                                    MockSnippetClient,
                                                    MockFastboot,
                                                    MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        expected_msg = ('Snippet package "%s" has already been loaded under '
                        'name "snippet".') % MOCK_SNIPPET_PACKAGE_NAME
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.load_snippet('snippet2', MOCK_SNIPPET_PACKAGE_NAME)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient',
        return_value=MockSnippetClient)
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_dup_snippet_name(
            self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        expected_msg = '.* Attribute "snippet" already exists, please use a different name.'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME + 'haha')

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_dup_attribute_name(
            self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        expected_msg = ('Attribute "%s" already exists, please use a different'
                        ' name') % 'adb'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.load_snippet('adb', MOCK_SNIPPET_PACKAGE_NAME)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_start_app_fails(
            self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
        """Verifies that the correct exception is raised if start app failed.

        It's possible that the `stop_app` call as part of the start app failure
        teardown also fails. So we want the exception from the start app
        failure.
        """
        expected_e = Exception('start failed.')
        MockSnippetClient.start_app_and_connect = mock.Mock(
            side_effect=expected_e)
        MockSnippetClient.stop_app = mock.Mock(
            side_effect=Exception('stop failed.'))
        ad = android_device.AndroidDevice(serial='1')
        try:
            ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        except Exception as e:
            assertIs(e, expected_e)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_unload_snippet(self, MockGetPort, MockSnippetClient,
                                          MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        ad.unload_snippet('snippet')
        self.assertFalse(hasattr(ad, 'snippet'))
        with self.assertRaisesRegex(
                android_device.SnippetError,
                '<AndroidDevice|1> No snippet registered with name "snippet"'):
            ad.unload_snippet('snippet')
        # Loading the same snippet again should succeed
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        self.assertTrue(hasattr(ad, 'snippet'))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_snippet_cleanup(self, MockGetPort,
                                           MockSnippetClient, MockFastboot,
                                           MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.services.start_all()
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        ad.unload_snippet('snippet')
        self.assertFalse(hasattr(ad, 'snippet'))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_debug_tag(self, MockFastboot, MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        self.assertEqual(ad.debug_tag, '1')
        try:
            raise android_device.DeviceError(ad, 'Something')
        except android_device.DeviceError as e:
            self.assertEqual('<AndroidDevice|1> Something', str(e))
        # Verify that debug tag's setter updates the debug prefix correctly.
        ad.debug_tag = 'Mememe'
        try:
            raise android_device.DeviceError(ad, 'Something')
        except android_device.DeviceError as e:
            self.assertEqual('<AndroidDevice|Mememe> Something', str(e))
        # Verify that repr is changed correctly.
        try:
            raise Exception(ad, 'Something')
        except Exception as e:
            self.assertEqual("(<AndroidDevice|Mememe>, 'Something')", str(e))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_handle_usb_disconnect(self, stop_proc_mock,
                                                 start_proc_mock,
                                                 FastbootProxy, MockAdbProxy):
        class MockService(base_service.BaseService):
            def __init__(self, device, configs=None):
                self._alive = False
                self.pause_called = False
                self.resume_called = False

            @property
            def is_alive(self):
                return self._alive

            def start(self, configs=None):
                self._alive = True

            def stop(self):
                self._alive = False

            def pause(self):
                self._alive = False
                self.pause_called = True

            def resume(self):
                self._alive = True
                self.resume_called = True

        ad = android_device.AndroidDevice(serial='1')
        ad.services.start_all()
        ad.services.register('mock_service', MockService)
        with ad.handle_usb_disconnect():
            self.assertFalse(ad.services.is_any_alive)
            self.assertTrue(ad.services.mock_service.pause_called)
            self.assertFalse(ad.services.mock_service.resume_called)
        self.assertTrue(ad.services.is_any_alive)
        self.assertTrue(ad.services.mock_service.resume_called)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_handle_reboot(self, stop_proc_mock, start_proc_mock,
                                         FastbootProxy, MockAdbProxy):
        class MockService(base_service.BaseService):
            def __init__(self, device, configs=None):
                self._alive = False
                self.pause_called = False
                self.resume_called = False

            @property
            def is_alive(self):
                return self._alive

            def start(self, configs=None):
                self._alive = True

            def stop(self):
                self._alive = False

            def pause(self):
                self._alive = False
                self.pause_called = True

            def resume(self):
                self._alive = True
                self.resume_called = True

        ad = android_device.AndroidDevice(serial='1')
        ad.services.start_all()
        ad.services.register('mock_service', MockService)
        with ad.handle_reboot():
            self.assertFalse(ad.services.is_any_alive)
            self.assertFalse(ad.services.mock_service.pause_called)
            self.assertFalse(ad.services.mock_service.resume_called)
        self.assertTrue(ad.services.is_any_alive)
        self.assertFalse(ad.services.mock_service.resume_called)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_handle_reboot_changes_build_info(
            self, stop_proc_mock, start_proc_mock, FastbootProxy,
            MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        with ad.handle_reboot():
            ad.adb.mock_properties['ro.build.type'] = 'user'
            ad.adb.mock_properties['ro.debuggable'] = '0'
        self.assertEqual(ad.build_info['build_type'], 'user')
        self.assertEqual(ad.build_info['debuggable'], '0')
        self.assertFalse(ad.is_rootable)
        self.assertEqual(ad.adb.getprops_call_count, 2)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_handle_reboot_changes_build_info_with_caching(
            self, stop_proc_mock, start_proc_mock, FastbootProxy,
            MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')  # Call getprops 1.
        rootable_states = [ad.is_rootable]
        with ad.handle_reboot():
            rootable_states.append(ad.is_rootable)  # Call getprops 2.
            ad.adb.mock_properties['ro.debuggable'] = '0'
            rootable_states.append(ad.is_rootable)  # Call getprops 3.
        # Call getprops 4, on context manager end.
        rootable_states.append(ad.is_rootable)  # Cached call.
        rootable_states.append(ad.is_rootable)  # Cached call.
        self.assertEqual(ad.adb.getprops_call_count, 4)
        self.assertEqual(rootable_states, [True, True, False, False, False])

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device.AndroidDevice.is_boot_completed',
        side_effect=[
            False, False,
            adb.AdbTimeoutError(['adb', 'shell', 'getprop sys.boot_completed'],
                                timeout=5,
                                serial=1), True
        ])
    @mock.patch('time.sleep', return_value=None)
    @mock.patch('time.time', side_effect=[0, 5, 10, 15, 20, 25, 30])
    def test_AndroidDevice_wait_for_completion_completed(
            self, MockTime, MockSleep, MockIsBootCompleted, FastbootProxy,
            MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        raised = False
        try:
            ad.wait_for_boot_completion()
        except (adb.AdbError, adb.AdbTimeoutError):
            raised = True
        self.assertFalse(
            raised,
            'adb.AdbError or adb.AdbTimeoutError exception raised but not handled.'
        )

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device.AndroidDevice.is_boot_completed',
        side_effect=[
            False, False,
            adb.AdbTimeoutError(['adb', 'shell', 'getprop sys.boot_completed'],
                                timeout=5,
                                serial=1), False, False, False, False
        ])
    @mock.patch('time.sleep', return_value=None)
    @mock.patch('time.time', side_effect=[0, 5, 10, 15, 20, 25, 30])
    def test_AndroidDevice_wait_for_completion_never_boot(
            self, MockTime, MockSleep, MockIsBootCompleted, FastbootProxy,
            MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        raised = False
        try:
            with self.assertRaises(android_device.DeviceError):
                ad.wait_for_boot_completion(timeout=20)
        except (adb.AdbError, adb.AdbTimeoutError):
            raised = True
        self.assertFalse(
            raised,
            'adb.AdbError or adb.AdbTimeoutError exception raised but not handled.'
        )
Example #10
0
class TestRunnerTest(unittest.TestCase):
    """This test class has unit tests for the implementation of everything
    under mobly.test_runner.
    """
    def setUp(self):
        self.tmp_dir = tempfile.mkdtemp()
        self.base_mock_test_config = config_parser.TestRunConfig()
        self.base_mock_test_config.test_bed_name = 'SampleTestBed'
        self.base_mock_test_config.controller_configs = {}
        self.base_mock_test_config.user_params = {
            'icecream': 42,
            'extra_param': 'haha'
        }
        self.base_mock_test_config.log_path = self.tmp_dir
        self.log_dir = self.base_mock_test_config.log_path
        self.test_bed_name = self.base_mock_test_config.test_bed_name

    def tearDown(self):
        shutil.rmtree(self.tmp_dir)

    def _assertControllerInfoEqual(self, info, expected_info_dict):
        self.assertEqual(expected_info_dict['Controller Name'],
                         info.controller_name)
        self.assertEqual(expected_info_dict['Test Class'], info.test_class)
        self.assertEqual(expected_info_dict['Controller Info'],
                         info.controller_info)

    def test_run_twice(self):
        """Verifies that:
        1. Repeated run works properly.
        2. The original configuration is not altered if a test controller
           module modifies configuration.
        """
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        my_config = [{
            'serial': 'xxxx',
            'magic': 'Magic1'
        }, {
            'serial': 'xxxx',
            'magic': 'Magic2'
        }]
        mock_test_config.controller_configs[mock_ctrlr_config_name] = my_config
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        tr.add_test_class(mock_test_config, integration_test.IntegrationTest)
        tr.run()
        self.assertTrue(
            mock_test_config.controller_configs[mock_ctrlr_config_name][0])
        tr.run()
        results = tr.results.summary_dict()
        self.assertEqual(results['Requested'], 2)
        self.assertEqual(results['Executed'], 2)
        self.assertEqual(results['Passed'], 2)
        expected_info_dict = {
            'Controller Info': [{
                'MyMagic': {
                    'magic': 'Magic1'
                }
            }, {
                'MyMagic': {
                    'magic': 'Magic2'
                }
            }],
            'Controller Name':
            'MagicDevice',
            'Test Class':
            'IntegrationTest',
        }
        self._assertControllerInfoEqual(tr.results.controller_info[0],
                                        expected_info_dict)
        self._assertControllerInfoEqual(tr.results.controller_info[1],
                                        expected_info_dict)
        self.assertNotEqual(tr.results.controller_info[0],
                            tr.results.controller_info[1])

    def test_summary_file_entries(self):
        """Verifies the output summary's file format.

        This focuses on the format of the file instead of the content of
        entries, which is covered in base_test_test.
        """
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        my_config = [{
            'serial': 'xxxx',
            'magic': 'Magic1'
        }, {
            'serial': 'xxxx',
            'magic': 'Magic2'
        }]
        mock_test_config.controller_configs[mock_ctrlr_config_name] = my_config
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        tr.add_test_class(mock_test_config, integration_test.IntegrationTest)
        tr.run()
        summary_path = os.path.join(logging.log_path,
                                    records.OUTPUT_FILE_SUMMARY)
        with io.open(summary_path, 'r', encoding='utf-8') as f:
            summary_entries = list(yaml.safe_load_all(f))
        self.assertEqual(len(summary_entries), 4)
        # Verify the first entry is the list of test names.
        self.assertEqual(summary_entries[0]['Type'],
                         records.TestSummaryEntryType.TEST_NAME_LIST.value)
        self.assertEqual(summary_entries[1]['Type'],
                         records.TestSummaryEntryType.RECORD.value)
        self.assertEqual(summary_entries[2]['Type'],
                         records.TestSummaryEntryType.CONTROLLER_INFO.value)
        self.assertEqual(summary_entries[3]['Type'],
                         records.TestSummaryEntryType.SUMMARY.value)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch('mobly.controllers.android_device.list_adb_devices',
                return_value=['1'])
    @mock.patch('mobly.controllers.android_device.get_all_instances',
                return_value=mock_android_device.get_mock_ads(1))
    def test_run_two_test_classes(self, mock_get_all, mock_list_adb,
                                  mock_fastboot, mock_adb):
        """Verifies that running more than one test class in one test run works
        properly.

        This requires using a built-in controller module. Using AndroidDevice
        module since it has all the mocks needed already.
        """
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        my_config = [{
            'serial': 'xxxx',
            'magic': 'Magic1'
        }, {
            'serial': 'xxxx',
            'magic': 'Magic2'
        }]
        mock_test_config.controller_configs[mock_ctrlr_config_name] = my_config
        mock_test_config.controller_configs['AndroidDevice'] = [{
            'serial': '1'
        }]
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        tr.add_test_class(mock_test_config, integration2_test.Integration2Test)
        tr.add_test_class(mock_test_config, integration_test.IntegrationTest)
        tr.run()
        results = tr.results.summary_dict()
        self.assertEqual(results['Requested'], 2)
        self.assertEqual(results['Executed'], 2)
        self.assertEqual(results['Passed'], 2)
        # Tag of the test class defaults to the class name.
        record1 = tr.results.executed[0]
        record2 = tr.results.executed[1]
        self.assertEqual(record1.test_class, 'Integration2Test')
        self.assertEqual(record2.test_class, 'IntegrationTest')

    def test_run_two_test_classes_different_configs_and_aliases(self):
        """Verifies that running more than one test class in one test run with
        different configs works properly.
        """
        config1 = self.base_mock_test_config.copy()
        config1.controller_configs[
            mock_controller.MOBLY_CONTROLLER_CONFIG_NAME] = [{
                'serial': 'xxxx'
            }]
        config2 = config1.copy()
        config2.user_params['icecream'] = 10
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        tr.add_test_class(config1,
                          integration_test.IntegrationTest,
                          name_suffix='FirstConfig')
        tr.add_test_class(config2,
                          integration_test.IntegrationTest,
                          name_suffix='SecondConfig')
        tr.run()
        results = tr.results.summary_dict()
        self.assertEqual(results['Requested'], 2)
        self.assertEqual(results['Executed'], 2)
        self.assertEqual(results['Passed'], 1)
        self.assertEqual(results['Failed'], 1)
        self.assertEqual(tr.results.failed[0].details, '10 != 42')
        record1 = tr.results.executed[0]
        record2 = tr.results.executed[1]
        self.assertEqual(record1.test_class, 'IntegrationTest_FirstConfig')
        self.assertEqual(record2.test_class, 'IntegrationTest_SecondConfig')

    def test_run_with_abort_all(self):
        mock_test_config = self.base_mock_test_config.copy()
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        tr.add_test_class(mock_test_config, integration3_test.Integration3Test)
        with self.assertRaises(signals.TestAbortAll):
            tr.run()
        results = tr.results.summary_dict()
        self.assertEqual(results['Requested'], 1)
        self.assertEqual(results['Executed'], 0)
        self.assertEqual(results['Passed'], 0)
        self.assertEqual(results['Failed'], 0)

    def test_add_test_class_mismatched_log_path(self):
        tr = test_runner.TestRunner('/different/log/dir', self.test_bed_name)
        with self.assertRaisesRegex(
                test_runner.Error,
                'TestRunner\'s log folder is "/different/log/dir", but a test '
                r'config with a different log folder \("%s"\) was added.' %
                re.escape(self.log_dir)):
            tr.add_test_class(self.base_mock_test_config,
                              integration_test.IntegrationTest)

    def test_add_test_class_mismatched_test_bed_name(self):
        tr = test_runner.TestRunner(self.log_dir, 'different_test_bed')
        with self.assertRaisesRegex(
                test_runner.Error,
                'TestRunner\'s test bed is "different_test_bed", but a test '
                r'config with a different test bed \("%s"\) was added.' %
                self.test_bed_name):
            tr.add_test_class(self.base_mock_test_config,
                              integration_test.IntegrationTest)

    def test_teardown_logger_before_setup_logger(self):
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        with self.assertRaisesRegex(
                test_runner.Error,
                r'TestRunner\._teardown_logger\(\) called before '
                r'TestRunner\.setup_logger\(\)!'):
            tr._teardown_logger()

    def test_run_no_tests(self):
        tr = test_runner.TestRunner(self.log_dir, self.test_bed_name)
        with self.assertRaisesRegex(test_runner.Error, 'No tests to execute.'):
            tr.run()

    @mock.patch('mobly.test_runner._find_test_class',
                return_value=type('SampleTest', (), {}))
    @mock.patch('mobly.test_runner.config_parser.load_test_config_file',
                return_value=[config_parser.TestRunConfig()])
    @mock.patch('mobly.test_runner.TestRunner', return_value=mock.MagicMock())
    def test_main_parse_args(self, mock_test_runner, mock_config,
                             mock_find_test):
        test_runner.main(['-c', 'some/path/foo.yaml', '-b', 'hello'])
        mock_config.assert_called_with('some/path/foo.yaml', None)
Example #11
0
class TestRunnerTest(unittest.TestCase):
    """This test class has unit tests for the implementation of everything
    under mobly.test_runner.
    """
    def setUp(self):
        self.tmp_dir = tempfile.mkdtemp()
        self.base_mock_test_config = config_parser.TestRunConfig()
        self.base_mock_test_config.test_bed_name = 'SampleTestBed'
        self.base_mock_test_config.controller_configs = {}
        self.base_mock_test_config.user_params = {
            'icecream': 42,
            'extra_param': 'haha'
        }
        self.base_mock_test_config.log_path = self.tmp_dir
        self.mock_run_list = [('SampleTest', None)]

    def tearDown(self):
        shutil.rmtree(self.tmp_dir)

    def test_register_controller_no_config(self):
        tr = test_runner.TestRunner(self.base_mock_test_config,
                                    self.mock_run_list)
        with self.assertRaisesRegexp(signals.ControllerError,
                                     'No corresponding config found for'):
            tr.register_controller(mock_controller)

    def test_register_controller_no_config_no_register(self):
        tr = test_runner.TestRunner(self.base_mock_test_config,
                                    self.mock_run_list)
        self.assertIsNone(
            tr.register_controller(mock_controller, required=False))

    def test_register_controller_dup_register(self):
        """Verifies correctness of registration, internal tally of controllers
        objects, and the right error happen when a controller module is
        registered twice.
        """
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        mock_test_config.controller_configs = {
            mock_ctrlr_config_name: ['magic1', 'magic2']
        }
        tr = test_runner.TestRunner(mock_test_config, self.mock_run_list)
        tr.register_controller(mock_controller)
        registered_name = 'mock_controller'
        self.assertTrue(registered_name in tr.controller_registry)
        mock_ctrlrs = tr.controller_registry[registered_name]
        self.assertEqual(mock_ctrlrs[0].magic, 'magic1')
        self.assertEqual(mock_ctrlrs[1].magic, 'magic2')
        self.assertTrue(tr.controller_destructors[registered_name])
        expected_msg = 'Controller module .* has already been registered.'
        with self.assertRaisesRegexp(signals.ControllerError, expected_msg):
            tr.register_controller(mock_controller)

    def test_register_controller_no_get_info(self):
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        mock_ref_name = 'haha'
        get_info = getattr(mock_controller, 'get_info')
        delattr(mock_controller, 'get_info')
        try:
            mock_test_config.controller_configs = {
                mock_ctrlr_config_name: ['magic1', 'magic2']
            }
            tr = test_runner.TestRunner(mock_test_config, self.mock_run_list)
            tr.register_controller(mock_controller)
            self.assertEqual(tr.results.controller_info, {})
        finally:
            setattr(mock_controller, 'get_info', get_info)

    def test_register_controller_return_value(self):
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        mock_test_config.controller_configs = {
            mock_ctrlr_config_name: ['magic1', 'magic2']
        }
        tr = test_runner.TestRunner(mock_test_config, self.mock_run_list)
        magic_devices = tr.register_controller(mock_controller)
        self.assertEqual(magic_devices[0].magic, 'magic1')
        self.assertEqual(magic_devices[1].magic, 'magic2')

    def test_register_controller_less_than_min_number(self):
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        mock_test_config.controller_configs = {
            mock_ctrlr_config_name: ['magic1', 'magic2']
        }
        tr = test_runner.TestRunner(mock_test_config, self.mock_run_list)
        expected_msg = 'Expected to get at least 3 controller objects, got 2.'
        with self.assertRaisesRegexp(signals.ControllerError, expected_msg):
            tr.register_controller(mock_controller, min_number=3)

    def test_run_twice(self):
        """Verifies that:
        1. Repeated run works properly.
        2. The original configuration is not altered if a test controller
           module modifies configuration.
        """
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        my_config = [{
            'serial': 'xxxx',
            'magic': 'Magic1'
        }, {
            'serial': 'xxxx',
            'magic': 'Magic2'
        }]
        mock_test_config.controller_configs[mock_ctrlr_config_name] = my_config
        tr = test_runner.TestRunner(mock_test_config,
                                    [('IntegrationTest', None)])
        tr.run([integration_test.IntegrationTest])
        self.assertFalse(tr.controller_registry)
        self.assertFalse(tr.controller_destructors)
        self.assertTrue(
            mock_test_config.controller_configs[mock_ctrlr_config_name][0])
        tr.run([integration_test.IntegrationTest])
        tr.stop()
        self.assertFalse(tr.controller_registry)
        self.assertFalse(tr.controller_destructors)
        results = tr.results.summary_dict()
        self.assertEqual(results['Requested'], 2)
        self.assertEqual(results['Executed'], 2)
        self.assertEqual(results['Passed'], 2)
        expected_info = {
            'MagicDevice': [{
                'MyMagic': {
                    'magic': 'Magic1'
                }
            }, {
                'MyMagic': {
                    'magic': 'Magic2'
                }
            }]
        }
        self.assertEqual(tr.results.controller_info, expected_info)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch('mobly.controllers.android_device.list_adb_devices',
                return_value=['1'])
    @mock.patch('mobly.controllers.android_device.get_all_instances',
                return_value=mock_android_device.get_mock_ads(1))
    def test_run_two_test_classes(
        self,
        mock_get_all,
        mock_list_adb,
        mock_fastboot,
        mock_adb,
    ):
        """Verifies that runing more than one test class in one test run works
        proerly.

        This requires using a built-in controller module. Using AndroidDevice
        module since it has all the mocks needed already.
        """
        mock_test_config = self.base_mock_test_config.copy()
        mock_ctrlr_config_name = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
        my_config = [{
            'serial': 'xxxx',
            'magic': 'Magic1'
        }, {
            'serial': 'xxxx',
            'magic': 'Magic2'
        }]
        mock_test_config.controller_configs[mock_ctrlr_config_name] = my_config
        mock_test_config.controller_configs['AndroidDevice'] = [{
            'serial': '1'
        }]
        tr = test_runner.TestRunner(mock_test_config,
                                    [('Integration2Test', None),
                                     ('IntegrationTest', None)])
        tr.run([
            integration_test.IntegrationTest,
            integration2_test.Integration2Test
        ])
        tr.stop()
        self.assertFalse(tr.controller_registry)
        self.assertFalse(tr.controller_destructors)
        results = tr.results.summary_dict()
        self.assertEqual(results['Requested'], 2)
        self.assertEqual(results['Executed'], 2)
        self.assertEqual(results['Passed'], 2)

    def test_verify_controller_module(self):
        test_runner.TestRunner.verify_controller_module(mock_controller)

    def test_verify_controller_module_null_attr(self):
        try:
            tmp = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
            mock_controller.MOBLY_CONTROLLER_CONFIG_NAME = None
            msg = 'Controller interface .* in .* cannot be null.'
            with self.assertRaisesRegexp(signals.ControllerError, msg):
                test_runner.TestRunner.verify_controller_module(
                    mock_controller)
        finally:
            mock_controller.MOBLY_CONTROLLER_CONFIG_NAME = tmp

    def test_verify_controller_module_missing_attr(self):
        try:
            tmp = mock_controller.MOBLY_CONTROLLER_CONFIG_NAME
            delattr(mock_controller, 'MOBLY_CONTROLLER_CONFIG_NAME')
            msg = 'Module .* missing required controller module attribute'
            with self.assertRaisesRegexp(signals.ControllerError, msg):
                test_runner.TestRunner.verify_controller_module(
                    mock_controller)
        finally:
            setattr(mock_controller, 'MOBLY_CONTROLLER_CONFIG_NAME', tmp)
Example #12
0
class AndroidDeviceTest(unittest.TestCase):
    """This test class has unit tests for the implementation of everything
    under mobly.controllers.android_device.
    """

    def setUp(self):
        # Set log_path to logging since mobly logger setup is not called.
        if not hasattr(logging, 'log_path'):
            setattr(logging, 'log_path', '/tmp/logs')
        # Creates a temp dir to be used by tests in this test class.
        self.tmp_dir = tempfile.mkdtemp()

    def tearDown(self):
        """Removes the temp dir.
        """
        shutil.rmtree(self.tmp_dir)

    # Tests for android_device module functions.
    # These tests use mock AndroidDevice instances.

    @mock.patch.object(
        android_device,
        'get_all_instances',
        new=mock_android_device.get_all_instances)
    @mock.patch.object(
        android_device,
        'list_adb_devices',
        new=mock_android_device.list_adb_devices)
    @mock.patch.object(
        android_device,
        'list_adb_devices_by_usb_id',
        new=mock_android_device.list_adb_devices)
    def test_create_with_pickup_all(self):
        pick_all_token = android_device.ANDROID_DEVICE_PICK_ALL_TOKEN
        actual_ads = android_device.create(pick_all_token)
        for actual, expected in zip(actual_ads,
                                    mock_android_device.get_mock_ads(5)):
            self.assertEqual(actual.serial, expected.serial)

    @mock.patch.object(
        android_device, 'get_instances', new=mock_android_device.get_instances)
    @mock.patch.object(
        android_device,
        'list_adb_devices',
        new=mock_android_device.list_adb_devices)
    @mock.patch.object(
        android_device,
        'list_adb_devices_by_usb_id',
        new=mock_android_device.list_adb_devices)
    def test_create_with_string_list(self):
        string_list = [u'1', '2']
        actual_ads = android_device.create(string_list)
        for actual_ad, expected_serial in zip(actual_ads, ['1', '2']):
            self.assertEqual(actual_ad.serial, expected_serial)

    @mock.patch.object(
        android_device,
        'get_instances_with_configs',
        new=mock_android_device.get_instances_with_configs)
    @mock.patch.object(
        android_device,
        'list_adb_devices',
        new=mock_android_device.list_adb_devices)
    @mock.patch.object(
        android_device,
        'list_adb_devices_by_usb_id',
        new=mock_android_device.list_adb_devices)
    def test_create_with_dict_list(self):
        string_list = [{'serial': '1'}, {'serial': '2'}]
        actual_ads = android_device.create(string_list)
        for actual_ad, expected_serial in zip(actual_ads, ['1', '2']):
            self.assertEqual(actual_ad.serial, expected_serial)

    @mock.patch.object(
        android_device,
        'get_instances_with_configs',
        new=mock_android_device.get_instances_with_configs)
    @mock.patch.object(
        android_device,
        'list_adb_devices',
        new=mock_android_device.list_adb_devices)
    @mock.patch.object(
        android_device, 'list_adb_devices_by_usb_id', return_value=['usb:1'])
    def test_create_with_usb_id(self, mock_list_adb_devices_by_usb_id):
        string_list = [{'serial': '1'}, {'serial': '2'}, {'serial': 'usb:1'}]
        actual_ads = android_device.create(string_list)
        for actual_ad, expected_serial in zip(actual_ads, ['1', '2', 'usb:1']):
            self.assertEqual(actual_ad.serial, expected_serial)

    def test_create_with_empty_config(self):
        expected_msg = android_device.ANDROID_DEVICE_EMPTY_CONFIG_MSG
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            android_device.create([])

    def test_create_with_not_list_config(self):
        expected_msg = android_device.ANDROID_DEVICE_NOT_LIST_CONFIG_MSG
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            android_device.create('HAHA')

    def test_create_with_no_valid_config(self):
        expected_msg = 'No valid config found in: .*'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            android_device.create([1])

    def test_get_devices_success_with_extra_field(self):
        ads = mock_android_device.get_mock_ads(5)
        expected_label = 'selected'
        expected_count = 2
        for ad in ads[:expected_count]:
            ad.label = expected_label
        selected_ads = android_device.get_devices(ads, label=expected_label)
        self.assertEqual(expected_count, len(selected_ads))
        for ad in selected_ads:
            self.assertEqual(ad.label, expected_label)

    def test_get_devices_no_match(self):
        ads = mock_android_device.get_mock_ads(5)
        expected_msg = ('Could not find a target device that matches condition'
                        ": {'label': 'selected'}.")
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            selected_ads = android_device.get_devices(ads, label='selected')

    def test_get_device_success_with_serial(self):
        ads = mock_android_device.get_mock_ads(5)
        expected_serial = '0'
        ad = android_device.get_device(ads, serial=expected_serial)
        self.assertEqual(ad.serial, expected_serial)

    def test_get_device_success_with_serial_and_extra_field(self):
        ads = mock_android_device.get_mock_ads(5)
        expected_serial = '1'
        expected_h_port = 5555
        ads[1].h_port = expected_h_port
        ad = android_device.get_device(
            ads, serial=expected_serial, h_port=expected_h_port)
        self.assertEqual(ad.serial, expected_serial)
        self.assertEqual(ad.h_port, expected_h_port)

    def test_get_device_no_match(self):
        ads = mock_android_device.get_mock_ads(5)
        expected_msg = ('Could not find a target device that matches condition'
                        ": {'serial': 5}.")
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad = android_device.get_device(ads, serial=len(ads))

    def test_get_device_too_many_matches(self):
        ads = mock_android_device.get_mock_ads(5)
        target_serial = ads[1].serial = ads[0].serial
        expected_msg = "More than one device matched: \['0', '0'\]"
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            android_device.get_device(ads, serial=target_serial)

    def test_start_services_on_ads(self):
        """Makes sure when an AndroidDevice fails to start some services, all
        AndroidDevice objects get cleaned up.
        """
        msg = 'Some error happened.'
        ads = mock_android_device.get_mock_ads(3)
        ads[0].start_services = mock.MagicMock()
        ads[0].stop_services = mock.MagicMock()
        ads[1].start_services = mock.MagicMock()
        ads[1].stop_services = mock.MagicMock()
        ads[2].start_services = mock.MagicMock(
            side_effect=android_device.Error(msg))
        ads[2].stop_services = mock.MagicMock()
        with self.assertRaisesRegex(android_device.Error, msg):
            android_device._start_services_on_ads(ads)
        ads[0].stop_services.assert_called_once_with()
        ads[1].stop_services.assert_called_once_with()
        ads[2].stop_services.assert_called_once_with()

    def test_start_services_on_ads_skip_logcat(self):
        ads = mock_android_device.get_mock_ads(3)
        ads[0].start_services = mock.MagicMock()
        ads[1].start_services = mock.MagicMock()
        ads[2].start_services = mock.MagicMock(
            side_effect=Exception('Should not have called this.'))
        ads[2].skip_logcat = True
        android_device._start_services_on_ads(ads)

    # Tests for android_device.AndroidDevice class.
    # These tests mock out any interaction with the OS and real android device
    # in AndroidDeivce.

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_instantiation(self, MockFastboot, MockAdbProxy):
        """Verifies the AndroidDevice object's basic attributes are correctly
        set after instantiation.
        """
        mock_serial = 1
        ad = android_device.AndroidDevice(serial=mock_serial)
        self.assertEqual(ad.serial, '1')
        self.assertEqual(ad.model, 'fakemodel')
        self.assertIsNone(ad._adb_logcat_process)
        self.assertIsNone(ad.adb_logcat_file_path)
        expected_lp = os.path.join(logging.log_path,
                                   'AndroidDevice%s' % mock_serial)
        self.assertEqual(ad.log_path, expected_lp)

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_build_info(self, MockFastboot, MockAdbProxy):
        """Verifies the AndroidDevice object's basic attributes are correctly
        set after instantiation.
        """
        ad = android_device.AndroidDevice(serial='1')
        build_info = ad.build_info
        self.assertEqual(build_info['build_id'], 'AB42')
        self.assertEqual(build_info['build_type'], 'userdebug')

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_device_info(self, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial=1)
        device_info = ad.device_info
        self.assertEqual(device_info['serial'], '1')
        self.assertEqual(device_info['model'], 'fakemodel')
        self.assertEqual(device_info['build_info']['build_id'], 'AB42')
        self.assertEqual(device_info['build_info']['build_type'], 'userdebug')
        ad.add_device_info('sim_type', 'Fi')
        ad.add_device_info('build_id', 'CD42')
        device_info = ad.device_info
        self.assertEqual(device_info['user_added_info']['sim_type'], 'Fi')
        self.assertEqual(device_info['user_added_info']['build_id'], 'CD42')

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_serial_is_valid(self, MockFastboot, MockAdbProxy):
        """Verifies that the serial is a primitive string type and serializable.
        """
        ad = android_device.AndroidDevice(serial=1)
        # In py2, checks that ad.serial is not the backported py3 str type,
        # which is not dumpable by yaml in py2.
        # In py3, new_str is equivalent to str, so this check is not
        # appropirate in py3.
        if sys.version_info < (3, 0):
            self.assertFalse(isinstance(ad.serial, new_str))
        self.assertTrue(isinstance(ad.serial, str))
        yaml.safe_dump(ad.serial)

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch('mobly.utils.create_dir')
    def test_AndroidDevice_take_bug_report(self, create_dir_mock,
                                           FastbootProxy, MockAdbProxy):
        """Verifies AndroidDevice.take_bug_report calls the correct adb command
        and writes the bugreport file to the correct path.
        """
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        ad.take_bug_report('test_something', 'sometime')
        expected_path = os.path.join(
            logging.log_path, 'AndroidDevice%s' % ad.serial, 'BugReports')
        create_dir_mock.assert_called_with(expected_path)

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1', fail_br=True))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    def test_AndroidDevice_take_bug_report_fail(self, create_dir_mock,
                                                FastbootProxy, MockAdbProxy):
        """Verifies AndroidDevice.take_bug_report writes out the correct message
        when taking bugreport fails.
        """
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        expected_msg = '.* Failed to take bugreport.'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.take_bug_report('test_something', 'sometime')

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    def test_AndroidDevice_take_bug_report_with_destination(
            self, create_dir_mock, FastbootProxy, MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        dest = tempfile.gettempdir()
        ad.take_bug_report("test_something", "sometime", destination=dest)
        expected_path = os.path.join(dest)
        create_dir_mock.assert_called_with(expected_path)

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy(
            '1', fail_br_before_N=True))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    def test_AndroidDevice_take_bug_report_fallback(
            self, create_dir_mock, FastbootProxy, MockAdbProxy):
        """Verifies AndroidDevice.take_bug_report falls back to traditional
        bugreport on builds that do not have bugreportz.
        """
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        ad.take_bug_report('test_something', 'sometime')
        expected_path = os.path.join(
            logging.log_path, 'AndroidDevice%s' % ad.serial, 'BugReports')
        create_dir_mock.assert_called_with(expected_path)

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch(
        'mobly.utils.start_standing_subprocess', return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_take_logcat(self, stop_proc_mock, start_proc_mock,
                                       creat_dir_mock, FastbootProxy,
                                       MockAdbProxy):
        """Verifies the steps of collecting adb logcat on an AndroidDevice
        object, including various function calls and the expected behaviors of
        the calls.
        """
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        expected_msg = '.* No ongoing adb logcat collection found.'
        # Expect error if stop is called before start.
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.stop_adb_logcat()
        ad.start_adb_logcat()
        # Verify start did the correct operations.
        self.assertTrue(ad._adb_logcat_process)
        expected_log_path = os.path.join(logging.log_path,
                                         'AndroidDevice%s' % ad.serial,
                                         'adblog,fakemodel,%s.txt' % ad.serial)
        creat_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
        adb_cmd = '"adb" -s %s logcat -v threadtime  >> %s'
        start_proc_mock.assert_called_with(
            adb_cmd % (ad.serial, '"%s"' % expected_log_path), shell=True)
        self.assertEqual(ad.adb_logcat_file_path, expected_log_path)
        expected_msg = (
            'Logcat thread is already running, cannot start another'
            ' one.')
        # Expect error if start is called back to back.
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.start_adb_logcat()
        # Verify stop did the correct operations.
        ad.stop_adb_logcat()
        stop_proc_mock.assert_called_with('process')
        self.assertIsNone(ad._adb_logcat_process)
        self.assertEqual(ad.adb_logcat_file_path, expected_log_path)

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch(
        'mobly.utils.start_standing_subprocess', return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_take_logcat_with_user_param(
            self, stop_proc_mock, start_proc_mock, creat_dir_mock,
            FastbootProxy, MockAdbProxy):
        """Verifies the steps of collecting adb logcat on an AndroidDevice
        object, including various function calls and the expected behaviors of
        the calls.
        """
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        ad.adb_logcat_param = '-b radio'
        expected_msg = '.* No ongoing adb logcat collection found.'
        # Expect error if stop is called before start.
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.stop_adb_logcat()
        ad.start_adb_logcat()
        # Verify start did the correct operations.
        self.assertTrue(ad._adb_logcat_process)
        expected_log_path = os.path.join(logging.log_path,
                                         'AndroidDevice%s' % ad.serial,
                                         'adblog,fakemodel,%s.txt' % ad.serial)
        creat_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
        adb_cmd = '"adb" -s %s logcat -v threadtime -b radio >> %s'
        start_proc_mock.assert_called_with(
            adb_cmd % (ad.serial, '"%s"' % expected_log_path), shell=True)
        self.assertEqual(ad.adb_logcat_file_path, expected_log_path)

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.utils.start_standing_subprocess', return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_change_log_path(self, stop_proc_mock,
                                           start_proc_mock, FastbootProxy,
                                           MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.start_adb_logcat()
        ad.stop_adb_logcat()
        old_path = ad.log_path
        new_log_path = tempfile.mkdtemp()
        ad.log_path = new_log_path
        self.assertTrue(os.path.exists(new_log_path))
        self.assertFalse(os.path.exists(old_path))

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.utils.start_standing_subprocess', return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_change_log_path_no_log_exists(
            self, stop_proc_mock, start_proc_mock, FastbootProxy,
            MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        old_path = ad.log_path
        new_log_path = tempfile.mkdtemp()
        ad.log_path = new_log_path
        self.assertTrue(os.path.exists(new_log_path))
        self.assertFalse(os.path.exists(old_path))

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch(
        'mobly.utils.start_standing_subprocess', return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_change_log_path_with_service(
            self, stop_proc_mock, start_proc_mock, creat_dir_mock,
            FastbootProxy, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.start_adb_logcat()
        new_log_path = tempfile.mkdtemp()
        expected_msg = '.* Cannot change `log_path` when there is service running.'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.log_path = new_log_path

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch(
        'mobly.utils.start_standing_subprocess', return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_change_log_path_with_existing_file(
            self, stop_proc_mock, start_proc_mock, creat_dir_mock,
            FastbootProxy, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        new_log_path = tempfile.mkdtemp()
        new_file_path = os.path.join(new_log_path, 'file.txt')
        with io.open(new_file_path, 'w', encoding='utf-8') as f:
            f.write(u'hahah.')
        expected_msg = '.* Logs already exist .*'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.log_path = new_log_path

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch(
        'mobly.utils.start_standing_subprocess', return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_update_serial(self, stop_proc_mock, start_proc_mock,
                                         creat_dir_mock, FastbootProxy,
                                         MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.update_serial('2')
        self.assertEqual(ad.serial, '2')
        self.assertEqual(ad.debug_tag, ad.serial)
        self.assertEqual(ad.adb.serial, ad.serial)
        self.assertEqual(ad.fastboot.serial, ad.serial)

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch(
        'mobly.utils.start_standing_subprocess', return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_update_serial_with_service_running(
            self, stop_proc_mock, start_proc_mock, creat_dir_mock,
            FastbootProxy, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.start_adb_logcat()
        expected_msg = '.* Cannot change device serial number when there is service running.'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.update_serial('2')

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.utils.start_standing_subprocess', return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch(
        'mobly.logger.get_log_line_timestamp',
        return_value=MOCK_ADB_LOGCAT_END_TIME)
    def test_AndroidDevice_cat_adb_log(self, mock_timestamp_getter,
                                       stop_proc_mock, start_proc_mock,
                                       FastbootProxy, MockAdbProxy):
        """Verifies that AndroidDevice.cat_adb_log loads the correct adb log
        file, locates the correct adb log lines within the given time range,
        and writes the lines to the correct output file.
        """
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        # Direct the log path of the ad to a temp dir to avoid racing.
        ad._log_path_base = self.tmp_dir
        # Expect error if attempted to cat adb log before starting adb logcat.
        expected_msg = ('.* Attempting to cat adb log when none'
                        ' has been collected.')
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.cat_adb_log('some_test', MOCK_ADB_LOGCAT_BEGIN_TIME)
        ad.start_adb_logcat()
        utils.create_dir(ad.log_path)
        mock_adb_log_path = os.path.join(ad.log_path, 'adblog,%s,%s.txt' %
                                         (ad.model, ad.serial))
        with io.open(mock_adb_log_path, 'w', encoding='utf-8') as f:
            f.write(MOCK_ADB_LOGCAT)
        ad.cat_adb_log('some_test', MOCK_ADB_LOGCAT_BEGIN_TIME)
        cat_file_path = os.path.join(
            ad.log_path, 'AdbLogExcerpts',
            ('some_test,02-29 14-02-20.123,%s,%s.txt') % (ad.model, ad.serial))
        with io.open(cat_file_path, 'r', encoding='utf-8') as f:
            actual_cat = f.read()
        self.assertEqual(actual_cat, ''.join(MOCK_ADB_LOGCAT_CAT_RESULT))
        # Stops adb logcat.
        ad.stop_adb_logcat()

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.utils.start_standing_subprocess', return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch(
        'mobly.logger.get_log_line_timestamp',
        return_value=MOCK_ADB_LOGCAT_END_TIME)
    def test_AndroidDevice_cat_adb_log_with_unicode(
            self, mock_timestamp_getter, stop_proc_mock, start_proc_mock,
            FastbootProxy, MockAdbProxy):
        """Verifies that AndroidDevice.cat_adb_log loads the correct adb log
        file, locates the correct adb log lines within the given time range,
        and writes the lines to the correct output file.
        """
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        # Direct the log path of the ad to a temp dir to avoid racing.
        ad._log_path_base = self.tmp_dir
        # Expect error if attempted to cat adb log before starting adb logcat.
        expected_msg = ('.* Attempting to cat adb log when none'
                        ' has been collected.')
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.cat_adb_log('some_test', MOCK_ADB_LOGCAT_BEGIN_TIME)
        ad.start_adb_logcat()
        utils.create_dir(ad.log_path)
        mock_adb_log_path = os.path.join(ad.log_path, 'adblog,%s,%s.txt' %
                                         (ad.model, ad.serial))
        with io.open(mock_adb_log_path, 'w', encoding='utf-8') as f:
            f.write(MOCK_ADB_UNICODE_LOGCAT)
        ad.cat_adb_log('some_test', MOCK_ADB_LOGCAT_BEGIN_TIME)
        cat_file_path = os.path.join(
            ad.log_path, 'AdbLogExcerpts',
            ('some_test,02-29 14-02-20.123,%s,%s.txt') % (ad.model, ad.serial))
        with io.open(cat_file_path, 'r', encoding='utf-8') as f:
            actual_cat = f.read()
        self.assertEqual(actual_cat,
                         ''.join(MOCK_ADB_UNICODE_LOGCAT_CAT_RESULT))
        # Stops adb logcat.
        ad.stop_adb_logcat()

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock.MagicMock())
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice__enable_logpersist_with_logpersist(
            self, MockFastboot, MockAdbProxy):
        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprop.return_value = 'userdebug'
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': True,
            'logpersist.stop': True, }[command]
        ad = android_device.AndroidDevice(serial=mock_serial)
        ad._enable_logpersist()
        mock_adb_proxy.shell.assert_has_calls([
            mock.call('logpersist.stop --clear'),
            mock.call('logpersist.start'),
        ])

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock.MagicMock())
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice__enable_logpersist_with_missing_all_logpersist(
            self, MockFastboot, MockAdbProxy):
        def adb_shell_helper(command):
            if command == 'logpersist.start':
                raise MOCK_LOGPERSIST_START_MISSING_ADB_ERROR
            elif command == 'logpersist.stop --clear':
                raise MOCK_LOGPERSIST_STOP_MISSING_ADB_ERROR
            else:
                return ''

        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprop.return_value = 'userdebug'
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': False,
            'logpersist.stop': False, }[command]
        mock_adb_proxy.shell.side_effect = adb_shell_helper
        ad = android_device.AndroidDevice(serial=mock_serial)
        ad._enable_logpersist()

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock.MagicMock())
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice__enable_logpersist_with_missing_logpersist_stop(
            self, MockFastboot, MockAdbProxy):
        def adb_shell_helper(command):
            if command == 'logpersist.stop --clear':
                raise MOCK_LOGPERSIST_STOP_MISSING_ADB_ERROR
            else:
                return ''

        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprop.return_value = 'userdebug'
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': True,
            'logpersist.stop': False, }[command]
        mock_adb_proxy.shell.side_effect = adb_shell_helper
        ad = android_device.AndroidDevice(serial=mock_serial)
        ad._enable_logpersist()

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock.MagicMock())
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice__enable_logpersist_with_missing_logpersist_start(
            self, MockFastboot, MockAdbProxy):
        def adb_shell_helper(command):
            if command == 'logpersist.start':
                raise MOCK_LOGPERSIST_START_MISSING_ADB_ERROR
            else:
                return ''

        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprop.return_value = 'userdebug'
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': False,
            'logpersist.stop': True, }[command]
        mock_adb_proxy.shell.side_effect = adb_shell_helper
        ad = android_device.AndroidDevice(serial=mock_serial)
        ad._enable_logpersist()

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet(self, MockGetPort, MockSnippetClient,
                                        MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        self.assertTrue(hasattr(ad, 'snippet'))

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_failure(
            self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        client = mock.MagicMock()
        client.start_app_and_connect.side_effect = Exception(
            'Something went wrong.')
        MockSnippetClient.return_value = client
        with self.assertRaisesRegex(Exception, 'Something went wrong.'):
            ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        client.stop_app.assert_called_once_with()

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_precheck_failure(
            self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        client = mock.MagicMock()
        client.start_app_and_connect.side_effect = snippet_client.AppStartPreCheckError(
            mock.MagicMock, 'Something went wrong in precheck.')
        MockSnippetClient.return_value = client
        with self.assertRaisesRegex(snippet_client.AppStartPreCheckError,
                                    'Something went wrong in precheck.'):
            ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        client.stop_app.assert_not_called()

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_fail_cleanup_also_fail(
            self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        client = mock.MagicMock()
        client.start_app_and_connect.side_effect = Exception(
            'Something went wrong in start app.')
        client.stop_app.side_effect = Exception('Stop app also failed.')
        MockSnippetClient.return_value = client
        with self.assertRaisesRegex(Exception,
                                    'Something went wrong in start app.'):
            ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        client.stop_app.assert_called_once_with()

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient',
        return_value=MockSnippetClient)
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_dup_package(
            self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        expected_msg = ('Snippet package "%s" has already been loaded under '
                        'name "snippet".') % MOCK_SNIPPET_PACKAGE_NAME
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.load_snippet('snippet2', MOCK_SNIPPET_PACKAGE_NAME)

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient',
        return_value=MockSnippetClient)
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_dup_snippet_name(
            self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        expected_msg = ('Attribute "%s" is already registered with package '
                        '"%s", it cannot be used again.') % (
                            'snippet', MOCK_SNIPPET_PACKAGE_NAME)
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME + 'haha')

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_dup_attribute_name(
            self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        expected_msg = ('Attribute "%s" already exists, please use a different'
                        ' name') % 'adb'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.load_snippet('adb', MOCK_SNIPPET_PACKAGE_NAME)

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_start_app_fails(
            self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
        """Verifies that the correct exception is raised if start app failed.

        It's possible that the `stop_app` call as part of the start app failure
        teardown also fails. So we want the exception from the start app
        failure.
        """
        expected_e = Exception('start failed.')
        MockSnippetClient.start_app_and_connect = mock.Mock(
            side_effect=expected_e)
        MockSnippetClient.stop_app = mock.Mock(
            side_effect=Exception('stop failed.'))
        ad = android_device.AndroidDevice(serial='1')
        try:
            ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        except Exception as e:
            assertIs(e, expected_e)

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_unload_snippet(self, MockGetPort, MockSnippetClient,
                                          MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        ad.unload_snippet('snippet')
        self.assertFalse(hasattr(ad, 'snippet'))
        with self.assertRaisesRegex(
                android_device.SnippetError,
                '<AndroidDevice|1> No snippet registered with name "snippet"'):
            ad.unload_snippet('snippet')
        # Loading the same snippet again should succeed
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        self.assertTrue(hasattr(ad, 'snippet'))

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_snippet_cleanup(
            self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial='1')
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        ad.stop_services()
        self.assertFalse(hasattr(ad, 'snippet'))

    @mock.patch(
        'mobly.controllers.android_device_lib.adb.AdbProxy',
        return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch(
        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
        return_value=mock_android_device.MockFastbootProxy('1'))
    def test_AndroidDevice_debug_tag(self, MockFastboot, MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        self.assertEqual(ad.debug_tag, '1')
        try:
            raise android_device.DeviceError(ad, 'Something')
        except android_device.DeviceError as e:
            self.assertEqual('<AndroidDevice|1> Something', str(e))
        # Verify that debug tag's setter updates the debug prefix correctly.
        ad.debug_tag = 'Mememe'
        try:
            raise android_device.DeviceError(ad, 'Something')
        except android_device.DeviceError as e:
            self.assertEqual('<AndroidDevice|Mememe> Something', str(e))
        # Verify that repr is changed correctly.
        try:
            raise Exception(ad, 'Something')
        except Exception as e:
            self.assertEqual("(<AndroidDevice|Mememe>, 'Something')", str(e))
Example #13
0
class AndroidDeviceTest(unittest.TestCase):
    """This test class has unit tests for the implementation of everything
    under mobly.controllers.android_device.
    """
    def setUp(self):
        # Set log_path to logging since mobly logger setup is not called.
        if not hasattr(logging, "log_path"):
            setattr(logging, "log_path", "/tmp/logs")
        # Creates a temp dir to be used by tests in this test class.
        self.tmp_dir = tempfile.mkdtemp()

    def tearDown(self):
        """Removes the temp dir.
        """
        shutil.rmtree(self.tmp_dir)

    # Tests for android_device module functions.
    # These tests use mock AndroidDevice instances.

    @mock.patch.object(android_device,
                       "get_all_instances",
                       new=mock_android_device.get_all_instances)
    @mock.patch.object(android_device,
                       "list_adb_devices",
                       new=mock_android_device.list_adb_devices)
    @mock.patch.object(android_device,
                       "list_adb_devices_by_usb_id",
                       new=mock_android_device.list_adb_devices)
    def test_create_with_pickup_all(self):
        pick_all_token = android_device.ANDROID_DEVICE_PICK_ALL_TOKEN
        actual_ads = android_device.create(pick_all_token)
        for actual, expected in zip(actual_ads,
                                    mock_android_device.get_mock_ads(5)):
            self.assertEqual(actual.serial, expected.serial)

    @mock.patch.object(android_device,
                       "get_instances",
                       new=mock_android_device.get_instances)
    @mock.patch.object(android_device,
                       "list_adb_devices",
                       new=mock_android_device.list_adb_devices)
    @mock.patch.object(android_device,
                       "list_adb_devices_by_usb_id",
                       new=mock_android_device.list_adb_devices)
    def test_create_with_string_list(self):
        string_list = [u'1', '2']
        actual_ads = android_device.create(string_list)
        for actual_ad, expected_serial in zip(actual_ads, ['1', '2']):
            self.assertEqual(actual_ad.serial, expected_serial)

    @mock.patch.object(android_device,
                       "get_instances_with_configs",
                       new=mock_android_device.get_instances_with_configs)
    @mock.patch.object(android_device,
                       "list_adb_devices",
                       new=mock_android_device.list_adb_devices)
    @mock.patch.object(android_device,
                       "list_adb_devices_by_usb_id",
                       new=mock_android_device.list_adb_devices)
    def test_create_with_dict_list(self):
        string_list = [{'serial': '1'}, {'serial': '2'}]
        actual_ads = android_device.create(string_list)
        for actual_ad, expected_serial in zip(actual_ads, ['1', '2']):
            self.assertEqual(actual_ad.serial, expected_serial)

    @mock.patch.object(android_device,
                       "get_instances_with_configs",
                       new=mock_android_device.get_instances_with_configs)
    @mock.patch.object(android_device,
                       "list_adb_devices",
                       new=mock_android_device.list_adb_devices)
    @mock.patch.object(android_device,
                       "list_adb_devices_by_usb_id",
                       return_value=['usb:1'])
    def test_create_with_usb_id(self, mock_list_adb_devices_by_usb_id):
        string_list = [{'serial': '1'}, {'serial': '2'}, {'serial': 'usb:1'}]
        actual_ads = android_device.create(string_list)
        for actual_ad, expected_serial in zip(actual_ads, ['1', '2', 'usb:1']):
            self.assertEqual(actual_ad.serial, expected_serial)

    def test_create_with_empty_config(self):
        expected_msg = android_device.ANDROID_DEVICE_EMPTY_CONFIG_MSG
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            android_device.create([])

    def test_create_with_not_list_config(self):
        expected_msg = android_device.ANDROID_DEVICE_NOT_LIST_CONFIG_MSG
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            android_device.create("HAHA")

    def test_create_with_no_valid_config(self):
        expected_msg = "No valid config found in: .*"
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            android_device.create([1])

    def test_get_device_success_with_serial(self):
        ads = mock_android_device.get_mock_ads(5)
        expected_serial = '0'
        ad = android_device.get_device(ads, serial=expected_serial)
        self.assertEqual(ad.serial, expected_serial)

    def test_get_device_success_with_serial_and_extra_field(self):
        ads = mock_android_device.get_mock_ads(5)
        expected_serial = '1'
        expected_h_port = 5555
        ads[1].h_port = expected_h_port
        ad = android_device.get_device(ads,
                                       serial=expected_serial,
                                       h_port=expected_h_port)
        self.assertEqual(ad.serial, expected_serial)
        self.assertEqual(ad.h_port, expected_h_port)

    def test_get_device_no_match(self):
        ads = mock_android_device.get_mock_ads(5)
        expected_msg = ("Could not find a target device that matches condition"
                        ": {'serial': 5}.")
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad = android_device.get_device(ads, serial=len(ads))

    def test_get_device_too_many_matches(self):
        ads = mock_android_device.get_mock_ads(5)
        target_serial = ads[1].serial = ads[0].serial
        expected_msg = "More than one device matched: \['0', '0'\]"
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            android_device.get_device(ads, serial=target_serial)

    def test_start_services_on_ads(self):
        """Makes sure when an AndroidDevice fails to start some services, all
        AndroidDevice objects get cleaned up.
        """
        msg = "Some error happened."
        ads = mock_android_device.get_mock_ads(3)
        ads[0].start_services = mock.MagicMock()
        ads[0].stop_services = mock.MagicMock()
        ads[1].start_services = mock.MagicMock()
        ads[1].stop_services = mock.MagicMock()
        ads[2].start_services = mock.MagicMock(
            side_effect=android_device.Error(msg))
        ads[2].stop_services = mock.MagicMock()
        with self.assertRaisesRegex(android_device.Error, msg):
            android_device._start_services_on_ads(ads)
        ads[0].stop_services.assert_called_once_with()
        ads[1].stop_services.assert_called_once_with()
        ads[2].stop_services.assert_called_once_with()

    # Tests for android_device.AndroidDevice class.
    # These tests mock out any interaction with the OS and real android device
    # in AndroidDeivce.

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    def test_AndroidDevice_instantiation(self, MockFastboot, MockAdbProxy):
        """Verifies the AndroidDevice object's basic attributes are correctly
        set after instantiation.
        """
        mock_serial = 1
        ad = android_device.AndroidDevice(serial=mock_serial)
        self.assertEqual(ad.serial, 1)
        self.assertEqual(ad.model, "fakemodel")
        self.assertIsNone(ad._adb_logcat_process)
        self.assertIsNone(ad.adb_logcat_file_path)
        expected_lp = os.path.join(logging.log_path,
                                   "AndroidDevice%s" % mock_serial)
        self.assertEqual(ad.log_path, expected_lp)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    def test_AndroidDevice_build_info(self, MockFastboot, MockAdbProxy):
        """Verifies the AndroidDevice object's basic attributes are correctly
        set after instantiation.
        """
        ad = android_device.AndroidDevice(serial=1)
        build_info = ad.build_info
        self.assertEqual(build_info["build_id"], "AB42")
        self.assertEqual(build_info["build_type"], "userdebug")

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch('mobly.utils.create_dir')
    def test_AndroidDevice_take_bug_report(self, create_dir_mock,
                                           FastbootProxy, MockAdbProxy):
        """Verifies AndroidDevice.take_bug_report calls the correct adb command
        and writes the bugreport file to the correct path.
        """
        mock_serial = 1
        ad = android_device.AndroidDevice(serial=mock_serial)
        ad.take_bug_report("test_something", "sometime")
        expected_path = os.path.join(logging.log_path,
                                     "AndroidDevice%s" % ad.serial,
                                     "BugReports")
        create_dir_mock.assert_called_with(expected_path)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1, fail_br=True))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch('mobly.utils.create_dir')
    def test_AndroidDevice_take_bug_report_fail(self, create_dir_mock,
                                                FastbootProxy, MockAdbProxy):
        """Verifies AndroidDevice.take_bug_report writes out the correct message
        when taking bugreport fails.
        """
        mock_serial = 1
        ad = android_device.AndroidDevice(serial=mock_serial)
        expected_msg = ".* Failed to take bugreport."
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.take_bug_report("test_something", "sometime")

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(
                    1, fail_br_before_N=True))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch('mobly.utils.create_dir')
    def test_AndroidDevice_take_bug_report_fallback(self, create_dir_mock,
                                                    FastbootProxy,
                                                    MockAdbProxy):
        """Verifies AndroidDevice.take_bug_report falls back to traditional
        bugreport on builds that do not have bugreportz.
        """
        mock_serial = 1
        ad = android_device.AndroidDevice(serial=mock_serial)
        ad.take_bug_report("test_something", "sometime")
        expected_path = os.path.join(logging.log_path,
                                     "AndroidDevice%s" % ad.serial,
                                     "BugReports")
        create_dir_mock.assert_called_with(expected_path)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value="process")
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_take_logcat(self, stop_proc_mock, start_proc_mock,
                                       creat_dir_mock, FastbootProxy,
                                       MockAdbProxy):
        """Verifies the steps of collecting adb logcat on an AndroidDevice
        object, including various function calls and the expected behaviors of
        the calls.
        """
        mock_serial = 1
        ad = android_device.AndroidDevice(serial=mock_serial)
        expected_msg = ".* No ongoing adb logcat collection found."
        # Expect error if stop is called before start.
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.stop_adb_logcat()
        ad.start_adb_logcat()
        # Verify start did the correct operations.
        self.assertTrue(ad._adb_logcat_process)
        expected_log_path = os.path.join(logging.log_path,
                                         "AndroidDevice%s" % ad.serial,
                                         "adblog,fakemodel,%s.txt" % ad.serial)
        creat_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
        adb_cmd = '"adb" -s %s logcat -v threadtime  >> %s'
        start_proc_mock.assert_called_with(adb_cmd %
                                           (ad.serial, expected_log_path),
                                           shell=True)
        self.assertEqual(ad.adb_logcat_file_path, expected_log_path)
        expected_msg = (
            'Logcat thread is already running, cannot start another'
            ' one.')
        # Expect error if start is called back to back.
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.start_adb_logcat()
        # Verify stop did the correct operations.
        ad.stop_adb_logcat()
        stop_proc_mock.assert_called_with("process")
        self.assertIsNone(ad._adb_logcat_process)
        self.assertEqual(ad.adb_logcat_file_path, expected_log_path)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value="process")
    @mock.patch('mobly.utils.stop_standing_subprocess')
    def test_AndroidDevice_take_logcat_with_user_param(self, stop_proc_mock,
                                                       start_proc_mock,
                                                       creat_dir_mock,
                                                       FastbootProxy,
                                                       MockAdbProxy):
        """Verifies the steps of collecting adb logcat on an AndroidDevice
        object, including various function calls and the expected behaviors of
        the calls.
        """
        mock_serial = 1
        ad = android_device.AndroidDevice(serial=mock_serial)
        ad.adb_logcat_param = "-b radio"
        expected_msg = '.* No ongoing adb logcat collection found.'
        # Expect error if stop is called before start.
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.stop_adb_logcat()
        ad.start_adb_logcat()
        # Verify start did the correct operations.
        self.assertTrue(ad._adb_logcat_process)
        expected_log_path = os.path.join(logging.log_path,
                                         "AndroidDevice%s" % ad.serial,
                                         "adblog,fakemodel,%s.txt" % ad.serial)
        creat_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
        adb_cmd = '"adb" -s %s logcat -v threadtime -b radio >> %s'
        start_proc_mock.assert_called_with(adb_cmd %
                                           (ad.serial, expected_log_path),
                                           shell=True)
        self.assertEqual(ad.adb_logcat_file_path, expected_log_path)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value="process")
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch('mobly.logger.get_log_line_timestamp',
                return_value=MOCK_ADB_LOGCAT_END_TIME)
    def test_AndroidDevice_cat_adb_log(self, mock_timestamp_getter,
                                       stop_proc_mock, start_proc_mock,
                                       FastbootProxy, MockAdbProxy):
        """Verifies that AndroidDevice.cat_adb_log loads the correct adb log
        file, locates the correct adb log lines within the given time range,
        and writes the lines to the correct output file.
        """
        mock_serial = 1
        ad = android_device.AndroidDevice(serial=mock_serial)
        # Expect error if attempted to cat adb log before starting adb logcat.
        expected_msg = (".* Attempting to cat adb log when none"
                        " has been collected.")
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.cat_adb_log("some_test", MOCK_ADB_LOGCAT_BEGIN_TIME)
        ad.start_adb_logcat()
        # Direct the log path of the ad to a temp dir to avoid racing.
        ad.log_path = os.path.join(self.tmp_dir, ad.log_path)
        mock_adb_log_path = os.path.join(
            ad.log_path, "adblog,%s,%s.txt" % (ad.model, ad.serial))
        with open(mock_adb_log_path, 'w') as f:
            f.write(MOCK_ADB_LOGCAT)
        ad.cat_adb_log("some_test", MOCK_ADB_LOGCAT_BEGIN_TIME)
        cat_file_path = os.path.join(
            ad.log_path, "AdbLogExcerpts",
            ("some_test,02-29 14:02:20.123,%s,%s.txt") % (ad.model, ad.serial))
        with open(cat_file_path, 'r') as f:
            actual_cat = f.read()
        self.assertEqual(actual_cat, ''.join(MOCK_ADB_LOGCAT_CAT_RESULT))
        # Stops adb logcat.
        ad.stop_adb_logcat()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet(self, MockGetPort, MockSnippetClient,
                                        MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial=1)
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        self.assertTrue(hasattr(ad, 'snippet'))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient',
        return_value=MockSnippetClient)
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_dup_package(self, MockGetPort,
                                                    MockSnippetClient,
                                                    MockFastboot,
                                                    MockAdbProxy):
        ad = android_device.AndroidDevice(serial=1)
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        expected_msg = ('Snippet package "%s" has already been loaded under '
                        'name "snippet".') % MOCK_SNIPPET_PACKAGE_NAME
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.load_snippet('snippet2', MOCK_SNIPPET_PACKAGE_NAME)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient',
        return_value=MockSnippetClient)
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_dup_snippet_name(
            self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial=1)
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        expected_msg = ('Attribute "%s" is already registered with package '
                        '"%s", it cannot be used again.') % (
                            'snippet', MOCK_SNIPPET_PACKAGE_NAME)
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME + 'haha')

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_load_snippet_dup_attribute_name(
            self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
        ad = android_device.AndroidDevice(serial=1)
        expected_msg = ('Attribute "%s" already exists, please use a different'
                        ' name') % 'adb'
        with self.assertRaisesRegex(android_device.Error, expected_msg):
            ad.load_snippet('adb', MOCK_SNIPPET_PACKAGE_NAME)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    @mock.patch(
        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
    @mock.patch('mobly.utils.get_available_host_port')
    def test_AndroidDevice_snippet_cleanup(self, MockGetPort,
                                           MockSnippetClient, MockFastboot,
                                           MockAdbProxy):
        ad = android_device.AndroidDevice(serial=1)
        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
        ad.stop_services()
        self.assertFalse(hasattr(ad, 'snippet'))

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy(1))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy(1))
    def test_AndroidDevice_debug_tag(self, MockFastboot, MockAdbProxy):
        mock_serial = 1
        ad = android_device.AndroidDevice(serial=mock_serial)
        self.assertEqual(ad.debug_tag, 1)
        try:
            raise android_device.DeviceError(ad, 'Something')
        except android_device.DeviceError as e:
            self.assertEqual('<AndroidDevice|1> Something', str(e))
        # Verify that debug tag's setter updates the debug prefix correctly.
        ad.debug_tag = 'Mememe'
        try:
            raise android_device.DeviceError(ad, 'Something')
        except android_device.DeviceError as e:
            self.assertEqual('<AndroidDevice|Mememe> Something', str(e))
        # Verify that repr is changed correctly.
        try:
            raise Exception(ad, 'Something')
        except Exception as e:
            self.assertEqual("(<AndroidDevice|Mememe>, 'Something')", str(e))
Example #14
0
class LogcatTest(unittest.TestCase):
    """Tests for Logcat service and its integration with AndroidDevice."""
    def setUp(self):
        # Set log_path to logging since mobly logger setup is not called.
        if not hasattr(logging, 'log_path'):
            setattr(logging, 'log_path', '/tmp/logs')
        # Creates a temp dir to be used by tests in this test class.
        self.tmp_dir = tempfile.mkdtemp()

    def tearDown(self):
        """Removes the temp dir.
    """
        shutil.rmtree(self.tmp_dir)

    def AssertFileContains(self, content, file_path):
        with open(file_path, 'r') as f:
            output = f.read()
        self.assertIn(content, output)

    def AssertFileDoesNotContain(self, content, file_path):
        with open(file_path, 'r') as f:
            output = f.read()
        self.assertNotIn(content, output)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch.object(logcat.Logcat, '_open_logcat_file')
    @mock.patch('mobly.logger.get_log_file_timestamp')
    def test_start_and_stop(self, get_timestamp_mock, open_logcat_mock,
                            stop_proc_mock, start_proc_mock, create_dir_mock,
                            FastbootProxy, MockAdbProxy):
        """Verifies the steps of collecting adb logcat on an AndroidDevice
    object, including various function calls and the expected behaviors of
    the calls.
    """
        mock_serial = '1'
        get_timestamp_mock.return_value = '123'
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service.start()
        # Verify start did the correct operations.
        self.assertTrue(logcat_service._adb_logcat_process)
        expected_log_path = os.path.join(
            logging.log_path, 'AndroidDevice%s' % ad.serial,
            'logcat,%s,fakemodel,123.txt' % ad.serial)
        create_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
        adb_cmd = ' "adb" -s %s logcat -v threadtime -T 1  >> %s'
        start_proc_mock.assert_called_with(
            adb_cmd % (ad.serial, '"%s" ' % expected_log_path), shell=True)
        self.assertEqual(logcat_service.adb_logcat_file_path,
                         expected_log_path)
        expected_msg = (
            'Logcat thread is already running, cannot start another'
            ' one.')
        # Expect error if start is called back to back.
        with self.assertRaisesRegex(logcat.Error, expected_msg):
            logcat_service.start()
        # Verify stop did the correct operations.
        logcat_service.stop()
        stop_proc_mock.assert_called_with('process')
        self.assertIsNone(logcat_service._adb_logcat_process)
        self.assertEqual(logcat_service.adb_logcat_file_path,
                         expected_log_path)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch.object(logcat.Logcat, '_open_logcat_file')
    def test_update_config(self, open_logcat_mock, stop_proc_mock,
                           start_proc_mock, create_dir_mock, FastbootProxy,
                           MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service.start()
        logcat_service.stop()
        new_log_params = '-a -b -c'
        new_file_path = 'some/path/log.txt'
        new_config = logcat.Config(logcat_params=new_log_params,
                                   output_file_path=new_file_path)
        logcat_service.update_config(new_config)
        logcat_service.start()
        self.assertTrue(logcat_service._adb_logcat_process)
        create_dir_mock.assert_has_calls([mock.call('some/path')])
        expected_adb_cmd = (
            ' "adb" -s 1 logcat -v threadtime -T 1 -a -b -c >> '
            '"some/path/log.txt" ')
        start_proc_mock.assert_called_with(expected_adb_cmd, shell=True)
        self.assertEqual(logcat_service.adb_logcat_file_path,
                         'some/path/log.txt')
        logcat_service.stop()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch.object(logcat.Logcat, '_open_logcat_file')
    def test_update_config_while_running(self, open_logcat_mock,
                                         stop_proc_mock, start_proc_mock,
                                         create_dir_mock, FastbootProxy,
                                         MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service.start()
        new_config = logcat.Config(logcat_params='-blah',
                                   output_file_path='some/path/file.txt')
        with self.assertRaisesRegex(
                logcat.Error,
                'Logcat thread is already running, cannot start another one'):
            logcat_service.update_config(new_config)
        self.assertTrue(logcat_service.is_alive)
        logcat_service.stop()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch.object(logcat.Logcat, '_open_logcat_file')
    @mock.patch(
        'mobly.controllers.android_device_lib.services.logcat.Logcat.clear_adb_log',
        return_value=mock_android_device.MockAdbProxy('1'))
    def test_pause_and_resume(self, clear_adb_mock, open_logcat_mock,
                              stop_proc_mock, start_proc_mock, create_dir_mock,
                              FastbootProxy, MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad, logcat.Config(clear_log=True))
        logcat_service.start()
        clear_adb_mock.assert_called_once_with()
        self.assertTrue(logcat_service.is_alive)
        logcat_service.pause()
        self.assertFalse(logcat_service.is_alive)
        stop_proc_mock.assert_called_with('process')
        self.assertIsNone(logcat_service._adb_logcat_process)
        clear_adb_mock.reset_mock()
        logcat_service.resume()
        self.assertTrue(logcat_service.is_alive)
        clear_adb_mock.assert_not_called()
        logcat_service.stop()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch(
        'mobly.controllers.android_device_lib.services.logcat.Logcat.clear_adb_log',
        return_value=mock_android_device.MockAdbProxy('1'))
    def test_logcat_service_create_output_excerpts(self, clear_adb_mock,
                                                   stop_proc_mock,
                                                   start_proc_mock,
                                                   FastbootProxy,
                                                   MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service._start()

        def _write_logcat_file_and_assert_excerpts_exists(
                logcat_file_content, test_begin_time, test_name):
            with open(logcat_service.adb_logcat_file_path, 'a') as f:
                f.write(logcat_file_content)
            test_output_dir = os.path.join(self.tmp_dir, test_name)
            mock_record = records.TestResultRecord(test_name)
            mock_record.begin_time = test_begin_time
            mock_record.signature = f'{test_name}-{test_begin_time}'
            test_run_info = runtime_test_info.RuntimeTestInfo(
                test_name, test_output_dir, mock_record)
            actual_path = logcat_service.create_output_excerpts(
                test_run_info)[0]
            expected_path = os.path.join(
                test_output_dir, '{test_name}-{test_begin_time}'.format(
                    test_name=test_name, test_begin_time=test_begin_time),
                'logcat,{mock_serial},fakemodel,{test_name}-{test_begin_time}.txt'
                .format(mock_serial=mock_serial,
                        test_name=test_name,
                        test_begin_time=test_begin_time))
            self.assertEqual(actual_path, expected_path)
            self.assertTrue(os.path.exists(expected_path))
            return expected_path

        # Generate logs before the file pointer is created.
        # This message will not be captured in the excerpt.
        NOT_IN_EXCERPT = 'Not in excerpt.\n'
        with open(logcat_service.adb_logcat_file_path, 'a') as f:
            f.write(NOT_IN_EXCERPT)
        # With the file pointer created, generate logs and make an excerpt.
        logcat_service._open_logcat_file()
        FILE_CONTENT = 'Some log.\n'
        expected_path1 = _write_logcat_file_and_assert_excerpts_exists(
            logcat_file_content=FILE_CONTENT,
            test_begin_time=123,
            test_name='test_foo',
        )
        self.AssertFileContains(FILE_CONTENT, expected_path1)
        self.AssertFileDoesNotContain(NOT_IN_EXCERPT, expected_path1)
        # Generate some new logs and do another excerpt.
        FILE_CONTENT = 'Some more logs!!!\n'
        expected_path2 = _write_logcat_file_and_assert_excerpts_exists(
            logcat_file_content=FILE_CONTENT,
            test_begin_time=456,
            test_name='test_bar',
        )
        self.AssertFileContains(FILE_CONTENT, expected_path2)
        self.AssertFileDoesNotContain(FILE_CONTENT, expected_path1)
        # Simulate devices accidentally go offline, logcat service stopped.
        logcat_service.stop()
        FILE_CONTENT = 'Whatever logs\n'
        expected_path3 = _write_logcat_file_and_assert_excerpts_exists(
            logcat_file_content=FILE_CONTENT,
            test_begin_time=789,
            test_name='test_offline',
        )
        self.assertEqual(os.stat(expected_path3).st_size, 0)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    @mock.patch('mobly.utils.create_dir')
    @mock.patch('mobly.utils.start_standing_subprocess',
                return_value='process')
    @mock.patch('mobly.utils.stop_standing_subprocess')
    @mock.patch.object(logcat.Logcat, '_open_logcat_file')
    @mock.patch('mobly.logger.get_log_file_timestamp')
    def test_take_logcat_with_extra_params(self, get_timestamp_mock,
                                           open_logcat_mock, stop_proc_mock,
                                           start_proc_mock, create_dir_mock,
                                           FastbootProxy, MockAdbProxy):
        """Verifies the steps of collecting adb logcat on an AndroidDevice
    object, including various function calls and the expected behaviors of
    the calls.
    """
        mock_serial = '1'
        get_timestamp_mock.return_value = '123'
        ad = android_device.AndroidDevice(serial=mock_serial)
        configs = logcat.Config()
        configs.logcat_params = '-b radio'
        logcat_service = logcat.Logcat(ad, configs)
        logcat_service.start()
        # Verify start did the correct operations.
        self.assertTrue(logcat_service._adb_logcat_process)
        expected_log_path = os.path.join(
            logging.log_path, 'AndroidDevice%s' % ad.serial,
            'logcat,%s,fakemodel,123.txt' % ad.serial)
        create_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
        adb_cmd = ' "adb" -s %s logcat -v threadtime -T 1 -b radio >> %s'
        start_proc_mock.assert_called_with(
            adb_cmd % (ad.serial, '"%s" ' % expected_log_path), shell=True)
        self.assertEqual(logcat_service.adb_logcat_file_path,
                         expected_log_path)
        logcat_service.stop()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock_android_device.MockAdbProxy('1'))
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test_instantiation(self, MockFastboot, MockAdbProxy):
        """Verifies the AndroidDevice object's basic attributes are correctly
    set after instantiation.
    """
        mock_serial = 1
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        self.assertIsNone(logcat_service._adb_logcat_process)
        self.assertIsNone(logcat_service.adb_logcat_file_path)

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock.MagicMock())
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test__enable_logpersist_with_logpersist(self, MockFastboot,
                                                MockAdbProxy):
        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprops.return_value = {
            'ro.build.id': 'AB42',
            'ro.build.type': 'userdebug',
            'ro.debuggable': '1',
        }
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': True,
            'logpersist.stop': True,
        }[command]
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service._enable_logpersist()
        mock_adb_proxy.shell.assert_has_calls([
            mock.call('logpersist.stop --clear'),
            mock.call('logpersist.start'),
        ])

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock.MagicMock())
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test__enable_logpersist_with_user_build_device(self, MockFastboot,
                                                       MockAdbProxy):
        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprops.return_value = {
            'ro.build.id': 'AB42',
            'ro.build.type': 'user',
            'ro.debuggable': '0',
        }
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': True,
            'logpersist.stop': True,
        }[command]
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service._enable_logpersist()
        mock_adb_proxy.shell.assert_not_called()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock.MagicMock())
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test__enable_logpersist_with_missing_all_logpersist(
            self, MockFastboot, MockAdbProxy):
        def adb_shell_helper(command):
            if command == 'logpersist.start':
                raise MOCK_LOGPERSIST_START_MISSING_ADB_ERROR
            elif command == 'logpersist.stop --clear':
                raise MOCK_LOGPERSIST_STOP_MISSING_ADB_ERROR
            else:
                return b''

        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprops.return_value = {
            'ro.build.id': 'AB42',
            'ro.build.type': 'userdebug',
            'ro.debuggable': '1',
        }
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': False,
            'logpersist.stop': False,
        }[command]
        mock_adb_proxy.shell.side_effect = adb_shell_helper
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service._enable_logpersist()
        mock_adb_proxy.shell.assert_not_called()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock.MagicMock())
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test__enable_logpersist_with_missing_logpersist_stop(
            self, MockFastboot, MockAdbProxy):
        def adb_shell_helper(command):
            if command == 'logpersist.stop --clear':
                raise MOCK_LOGPERSIST_STOP_MISSING_ADB_ERROR
            else:
                return b''

        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprops.return_value = {
            'ro.build.id': 'AB42',
            'ro.build.type': 'userdebug',
            'ro.debuggable': '1',
        }
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': True,
            'logpersist.stop': False,
        }[command]
        mock_adb_proxy.shell.side_effect = adb_shell_helper
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service._enable_logpersist()
        mock_adb_proxy.shell.assert_has_calls([
            mock.call('logpersist.stop --clear'),
        ])

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
                return_value=mock.MagicMock())
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test__enable_logpersist_with_missing_logpersist_start(
            self, MockFastboot, MockAdbProxy):
        def adb_shell_helper(command):
            if command == 'logpersist.start':
                raise MOCK_LOGPERSIST_START_MISSING_ADB_ERROR
            else:
                return b''

        mock_serial = '1'
        mock_adb_proxy = MockAdbProxy.return_value
        mock_adb_proxy.getprops.return_value = {
            'ro.build.id': 'AB42',
            'ro.build.type': 'userdebug',
            'ro.debuggable': '1',
        }
        mock_adb_proxy.has_shell_command.side_effect = lambda command: {
            'logpersist.start': False,
            'logpersist.stop': True,
        }[command]
        mock_adb_proxy.shell.side_effect = adb_shell_helper
        ad = android_device.AndroidDevice(serial=mock_serial)
        logcat_service = logcat.Logcat(ad)
        logcat_service._enable_logpersist()
        mock_adb_proxy.shell.assert_not_called()

    @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy')
    @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
                return_value=mock_android_device.MockFastbootProxy('1'))
    def test_clear_adb_log(self, MockFastboot, MockAdbProxy):
        mock_serial = '1'
        ad = android_device.AndroidDevice(serial=mock_serial)
        ad.adb.logcat = mock.MagicMock()
        ad.adb.logcat.side_effect = adb.AdbError(
            cmd='cmd',
            stdout=b'',
            stderr=b'failed to clear "main" log',
            ret_code=1)
        logcat_service = logcat.Logcat(ad)
        logcat_service.clear_adb_log()