blob: 689f4939a8ea49ccc0f13e09af5f9d94a70b031b [file] [log] [blame]
# Copyright 2017 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import collections
import io
import subprocess
import unittest
from unittest import mock
from mobly.controllers.android_device_lib import adb
# Mock parameters for instrumentation.
MOCK_INSTRUMENTATION_PACKAGE = 'com.my.instrumentation.tests'
MOCK_INSTRUMENTATION_RUNNER = 'com.my.instrumentation.runner'
MOCK_INSTRUMENTATION_OPTIONS = collections.OrderedDict(
[
('option1', 'value1'),
('option2', 'value2'),
]
)
# Mock android instrumentation commands.
MOCK_BASIC_INSTRUMENTATION_COMMAND = (
'am instrument -r -w com.my'
'.instrumentation.tests/com.android'
'.common.support.test.runner'
'.AndroidJUnitRunner'
)
MOCK_RUNNER_INSTRUMENTATION_COMMAND = (
'am instrument -r -w com.my'
'.instrumentation.tests/com.my'
'.instrumentation.runner'
)
MOCK_OPTIONS_INSTRUMENTATION_COMMAND = (
'am instrument -r -w -e option1 value1'
' -e option2 value2 com.my'
'.instrumentation.tests/com.android'
'.common.support.test.runner'
'.AndroidJUnitRunner'
)
# Mock root command outputs.
MOCK_ROOT_SUCCESS_OUTPUT = 'adbd is already running as root'
MOCK_ROOT_ERROR_OUTPUT = 'adb: unable to connect for root: closed'.encode(
'utf-8'
)
# Mock Shell Command
MOCK_SHELL_COMMAND = 'ls'
MOCK_COMMAND_OUTPUT = '/system/bin/ls'.encode('utf-8')
MOCK_DEFAULT_STDOUT = 'out'
MOCK_DEFAULT_STDERR = 'err'
MOCK_DEFAULT_COMMAND_OUTPUT = MOCK_DEFAULT_STDOUT.encode('utf-8')
MOCK_ADB_SHELL_COMMAND_CHECK = 'adb shell command -v ls'
class AdbTest(unittest.TestCase):
"""Unit tests for mobly.controllers.android_device_lib.adb."""
def _mock_execute_and_process_stdout_process(self, mock_popen):
# the created proc object in adb._execute_and_process_stdout()
mock_proc = mock.Mock()
mock_popen.return_value = mock_proc
mock_popen.return_value.stdout.readline.side_effect = ['']
mock_proc.communicate = mock.Mock(
return_value=('', MOCK_DEFAULT_STDERR.encode('utf-8'))
)
mock_proc.returncode = 0
return mock_popen
@mock.patch('mobly.utils.run_command')
def test_is_adb_available(self, mock_run_command):
mock_run_command.return_value = (
0,
'/usr/local/bin/adb\n'.encode('utf-8'),
''.encode('utf-8'),
)
self.assertTrue(adb.is_adb_available())
@mock.patch('mobly.utils.run_command')
def test_is_adb_available_negative(self, mock_run_command):
mock_run_command.return_value = (0, ''.encode('utf-8'), ''.encode('utf-8'))
self.assertFalse(adb.is_adb_available())
@mock.patch('mobly.utils.run_command')
def test_exec_cmd_no_timeout_success(self, mock_run_command):
mock_run_command.return_value = (
0,
MOCK_DEFAULT_STDOUT.encode('utf-8'),
MOCK_DEFAULT_STDERR.encode('utf-8'),
)
out = adb.AdbProxy()._exec_cmd(
['fake_cmd'], shell=False, timeout=None, stderr=None
)
self.assertEqual(MOCK_DEFAULT_STDOUT, out.decode('utf-8'))
mock_run_command.assert_called_with(['fake_cmd'], shell=False, timeout=None)
@mock.patch('mobly.utils.run_command')
def test_exec_cmd_error_with_serial(self, mock_run_command):
# Return 1 for retcode for error.
mock_run_command.return_value = (
1,
MOCK_DEFAULT_STDOUT.encode('utf-8'),
MOCK_DEFAULT_STDERR.encode('utf-8'),
)
mock_serial = 'ABCD1234'
with self.assertRaisesRegex(
adb.AdbError, 'Error executing adb cmd .*'
) as context:
adb.AdbProxy(mock_serial).fake_cmd()
self.assertEqual(context.exception.serial, mock_serial)
self.assertIn(mock_serial, context.exception.cmd)
@mock.patch('mobly.utils.run_command')
def test_exec_cmd_error_without_serial(self, mock_run_command):
# Return 1 for retcode for error.
mock_run_command.return_value = (
1,
MOCK_DEFAULT_STDOUT.encode('utf-8'),
MOCK_DEFAULT_STDERR.encode('utf-8'),
)
with self.assertRaisesRegex(
adb.AdbError, 'Error executing adb cmd .*'
) as context:
adb.AdbProxy()._exec_cmd(
['fake_cmd'], shell=False, timeout=None, stderr=None
)
self.assertFalse(context.exception.serial)
mock_run_command.assert_called_with(['fake_cmd'], shell=False, timeout=None)
@mock.patch('mobly.utils.run_command')
def test_exec_cmd_with_timeout_success(self, mock_run_command):
mock_run_command.return_value = (
0,
MOCK_DEFAULT_STDOUT.encode('utf-8'),
MOCK_DEFAULT_STDERR.encode('utf-8'),
)
out = adb.AdbProxy()._exec_cmd(
['fake_cmd'], shell=False, timeout=1, stderr=None
)
self.assertEqual(MOCK_DEFAULT_STDOUT, out.decode('utf-8'))
mock_run_command.assert_called_with(['fake_cmd'], shell=False, timeout=1)
@mock.patch('mobly.utils.run_command')
def test_exec_cmd_timed_out(self, mock_run_command):
mock_run_command.side_effect = subprocess.TimeoutExpired(
cmd='mock_command', timeout=0.01
)
mock_serial = '1234Abcd'
with self.assertRaisesRegex(
adb.AdbTimeoutError,
'Timed out executing command "adb -s 1234Abcd fake-cmd" after 0.01s.',
) as context:
adb.AdbProxy(mock_serial).fake_cmd(timeout=0.01)
self.assertEqual(context.exception.serial, mock_serial)
self.assertIn(mock_serial, context.exception.cmd)
@mock.patch('mobly.utils.run_command')
def test_exec_cmd_timed_out_without_serial(self, mock_run_command):
mock_run_command.side_effect = subprocess.TimeoutExpired(
cmd='mock_command', timeout=0.01
)
with self.assertRaisesRegex(
adb.AdbTimeoutError,
'Timed out executing command "adb fake-cmd" after 0.01s.',
):
adb.AdbProxy().fake_cmd(timeout=0.01)
def test_exec_cmd_with_negative_timeout_value(self):
with self.assertRaisesRegex(
ValueError, 'Timeout is not a positive value: -1'
):
adb.AdbProxy()._exec_cmd(
['fake_cmd'], shell=False, timeout=-1, stderr=None
)
@mock.patch('mobly.controllers.android_device_lib.adb.subprocess.Popen')
def test_execute_and_process_stdout_reads_stdout(self, mock_popen):
self._mock_execute_and_process_stdout_process(mock_popen)
mock_popen.return_value.stdout.readline.side_effect = ['1', '2', '']
mock_handler = mock.MagicMock()
err = adb.AdbProxy()._execute_and_process_stdout(
['fake_cmd'], shell=False, handler=mock_handler
)
self.assertEqual(mock_handler.call_count, 2)
mock_handler.assert_any_call('1')
mock_handler.assert_any_call('2')
@mock.patch('mobly.controllers.android_device_lib.adb.subprocess.Popen')
def test_execute_and_process_stdout_reads_unexpected_stdout(self, mock_popen):
unexpected_stdout = MOCK_DEFAULT_STDOUT.encode('utf-8')
self._mock_execute_and_process_stdout_process(mock_popen)
mock_handler = mock.MagicMock()
mock_popen.return_value.communicate = mock.Mock(
return_value=(unexpected_stdout, MOCK_DEFAULT_STDERR.encode('utf-8'))
)
err = adb.AdbProxy()._execute_and_process_stdout(
['fake_cmd'], shell=False, handler=mock_handler
)
self.assertEqual(mock_handler.call_count, 1)
mock_handler.assert_called_with(unexpected_stdout)
@mock.patch('mobly.controllers.android_device_lib.adb.subprocess.Popen')
@mock.patch('logging.debug')
def test_execute_and_process_stdout_logs_cmd(
self, mock_debug_logger, mock_popen
):
raw_expected_stdout = ''
expected_stdout = '[elided, processed via handler]'
expected_stderr = MOCK_DEFAULT_STDERR.encode('utf-8')
self._mock_execute_and_process_stdout_process(mock_popen)
mock_popen.return_value.communicate = mock.Mock(
return_value=(raw_expected_stdout, expected_stderr)
)
err = adb.AdbProxy()._execute_and_process_stdout(
['fake_cmd'], shell=False, handler=mock.MagicMock()
)
mock_debug_logger.assert_called_with(
'cmd: %s, stdout: %s, stderr: %s, ret: %s',
'fake_cmd',
expected_stdout,
expected_stderr,
0,
)
@mock.patch('mobly.controllers.android_device_lib.adb.subprocess.Popen')
@mock.patch('logging.debug')
def test_execute_and_process_stdout_logs_cmd_with_unexpected_stdout(
self, mock_debug_logger, mock_popen
):
raw_expected_stdout = MOCK_DEFAULT_STDOUT.encode('utf-8')
expected_stdout = '[unexpected stdout] %s' % raw_expected_stdout
expected_stderr = MOCK_DEFAULT_STDERR.encode('utf-8')
self._mock_execute_and_process_stdout_process(mock_popen)
mock_popen.return_value.communicate = mock.Mock(
return_value=(raw_expected_stdout, expected_stderr)
)
err = adb.AdbProxy()._execute_and_process_stdout(
['fake_cmd'], shell=False, handler=mock.MagicMock()
)
mock_debug_logger.assert_called_with(
'cmd: %s, stdout: %s, stderr: %s, ret: %s',
'fake_cmd',
expected_stdout,
expected_stderr,
0,
)
@mock.patch('mobly.controllers.android_device_lib.adb.subprocess.Popen')
def test_execute_and_process_stdout_despite_cmd_exits(self, mock_popen):
self._mock_execute_and_process_stdout_process(mock_popen)
mock_popen.return_value.poll.side_effect = [None, 0]
mock_popen.return_value.stdout.readline.side_effect = ['1', '2', '3', '']
mock_handler = mock.MagicMock()
err = adb.AdbProxy()._execute_and_process_stdout(
['fake_cmd'], shell=False, handler=mock_handler
)
self.assertEqual(mock_handler.call_count, 3)
mock_handler.assert_any_call('1')
mock_handler.assert_any_call('2')
mock_handler.assert_any_call('3')
@mock.patch('mobly.controllers.android_device_lib.adb.subprocess.Popen')
def test_execute_and_process_stdout_when_cmd_eof(self, mock_popen):
self._mock_execute_and_process_stdout_process(mock_popen)
mock_popen.return_value.stdout.readline.side_effect = ['1', '2', '3', '']
mock_handler = mock.MagicMock()
err = adb.AdbProxy()._execute_and_process_stdout(
['fake_cmd'], shell=False, handler=mock_handler
)
self.assertEqual(mock_handler.call_count, 3)
mock_handler.assert_any_call('1')
mock_handler.assert_any_call('2')
mock_handler.assert_any_call('3')
@mock.patch('mobly.controllers.android_device_lib.adb.subprocess.Popen')
def test_execute_and_process_stdout_returns_stderr(self, mock_popen):
self._mock_execute_and_process_stdout_process(mock_popen)
err = adb.AdbProxy()._execute_and_process_stdout(
['fake_cmd'], shell=False, handler=mock.MagicMock()
)
self.assertEqual(MOCK_DEFAULT_STDERR, err.decode('utf-8'))
@mock.patch('mobly.controllers.android_device_lib.adb.subprocess.Popen')
def test_execute_and_process_stdout_raises_adb_error(self, mock_popen):
self._mock_execute_and_process_stdout_process(mock_popen)
mock_popen.return_value.returncode = 1
with self.assertRaisesRegex(adb.AdbError, 'Error executing adb cmd .*'):
err = adb.AdbProxy()._execute_and_process_stdout(
['fake_cmd'], shell=False, handler=mock.MagicMock()
)
@mock.patch('mobly.controllers.android_device_lib.adb.subprocess.Popen')
def test_execute_and_process_stdout_when_handler_crash(self, mock_popen):
self._mock_execute_and_process_stdout_process(mock_popen)
mock_popen.return_value.stdout.readline.side_effect = ['1', '2', '3', '']
mock_handler = mock.MagicMock()
mock_handler.side_effect = ['', TypeError('fake crash'), '', '']
with self.assertRaisesRegex(TypeError, 'fake crash'):
err = adb.AdbProxy()._execute_and_process_stdout(
['fake_cmd'], shell=False, handler=mock_handler
)
mock_popen.return_value.communicate.assert_called_once_with()
def test_construct_adb_cmd(self):
adb_cmd = adb.AdbProxy()._construct_adb_cmd('shell', 'arg1', shell=False)
self.assertEqual(adb_cmd, ['adb', 'shell', 'arg1'])
def test_construct_adb_cmd_with_one_command(self):
adb_cmd = adb.AdbProxy()._construct_adb_cmd(
'shell ls /asdafsfd/asdf-asfd/asa', [], shell=False
)
self.assertEqual(adb_cmd, ['adb', 'shell ls /asdafsfd/asdf-asfd/asa'])
def test_construct_adb_cmd_with_one_arg_command(self):
adb_cmd = adb.AdbProxy()._construct_adb_cmd(
'shell', 'ls /asdafsfd/asdf-asfd/asa', shell=False
)
self.assertEqual(adb_cmd, ['adb', 'shell', 'ls /asdafsfd/asdf-asfd/asa'])
def test_construct_adb_cmd_with_one_arg_command_list(self):
adb_cmd = adb.AdbProxy()._construct_adb_cmd(
'shell', ['ls /asdafsfd/asdf-asfd/asa'], shell=False
)
self.assertEqual(adb_cmd, ['adb', 'shell', 'ls /asdafsfd/asdf-asfd/asa'])
def test_construct_adb_cmd_with_special_characters(self):
adb_cmd = adb.AdbProxy()._construct_adb_cmd(
'shell', ['a b', '"blah"', r'\/\/'], shell=False
)
self.assertEqual(adb_cmd, ['adb', 'shell', 'a b', '"blah"', r'\/\/'])
def test_construct_adb_cmd_with_serial(self):
adb_cmd = adb.AdbProxy('12345')._construct_adb_cmd(
'shell', 'arg1', shell=False
)
self.assertEqual(adb_cmd, ['adb', '-s', '12345', 'shell', 'arg1'])
def test_construct_adb_cmd_with_list(self):
adb_cmd = adb.AdbProxy()._construct_adb_cmd(
'shell', ['arg1', 'arg2'], shell=False
)
self.assertEqual(adb_cmd, ['adb', 'shell', 'arg1', 'arg2'])
def test_construct_adb_cmd_with_serial_with_list(self):
adb_cmd = adb.AdbProxy('12345')._construct_adb_cmd(
'shell', ['arg1', 'arg2'], shell=False
)
self.assertEqual(adb_cmd, ['adb', '-s', '12345', 'shell', 'arg1', 'arg2'])
def test_construct_adb_cmd_with_shell_true(self):
adb_cmd = adb.AdbProxy()._construct_adb_cmd(
'shell', 'arg1 arg2', shell=True
)
self.assertEqual(adb_cmd, '"adb" shell arg1 arg2')
def test_construct_adb_cmd_with_shell_true_with_one_command(self):
adb_cmd = adb.AdbProxy()._construct_adb_cmd(
'shell ls /asdafsfd/asdf-asfd/asa', [], shell=True
)
self.assertEqual(adb_cmd, '"adb" shell ls /asdafsfd/asdf-asfd/asa ')
def test_construct_adb_cmd_with_shell_true_with_one_arg_command(self):
adb_cmd = adb.AdbProxy()._construct_adb_cmd(
'shell', 'ls /asdafsfd/asdf-asfd/asa', shell=True
)
self.assertEqual(adb_cmd, '"adb" shell ls /asdafsfd/asdf-asfd/asa')
def test_construct_adb_cmd_with_shell_true_with_one_arg_command_list(self):
adb_cmd = adb.AdbProxy()._construct_adb_cmd(
'shell', ['ls /asdafsfd/asdf-asfd/asa'], shell=True
)
self.assertEqual(adb_cmd, '"adb" shell \'ls /asdafsfd/asdf-asfd/asa\'')
def test_construct_adb_cmd_with_shell_true_with_auto_quotes(self):
adb_cmd = adb.AdbProxy()._construct_adb_cmd(
'shell', ['a b', '"blah"', r'\/\/'], shell=True
)
self.assertEqual(adb_cmd, "\"adb\" shell 'a b' '\"blah\"' '\\/\\/'")
def test_construct_adb_cmd_with_shell_true_with_serial(self):
adb_cmd = adb.AdbProxy('12345')._construct_adb_cmd(
'shell', 'arg1 arg2', shell=True
)
self.assertEqual(adb_cmd, '"adb" -s "12345" shell arg1 arg2')
def test_construct_adb_cmd_with_shell_true_with_list(self):
adb_cmd = adb.AdbProxy()._construct_adb_cmd(
'shell', ['arg1', 'arg2'], shell=True
)
self.assertEqual(adb_cmd, '"adb" shell arg1 arg2')
def test_construct_adb_cmd_with_shell_true_with_serial_with_list(self):
adb_cmd = adb.AdbProxy('12345')._construct_adb_cmd(
'shell', ['arg1', 'arg2'], shell=True
)
self.assertEqual(adb_cmd, '"adb" -s "12345" shell arg1 arg2')
def test_exec_adb_cmd(self):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
mock_exec_cmd.return_value = MOCK_DEFAULT_COMMAND_OUTPUT
adb.AdbProxy().shell(['arg1', 'arg2'])
mock_exec_cmd.assert_called_once_with(
['adb', 'shell', 'arg1', 'arg2'],
shell=False,
timeout=None,
stderr=None,
)
def test_exec_adb_cmd_with_shell_true(self):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
mock_exec_cmd.return_value = MOCK_DEFAULT_COMMAND_OUTPUT
adb.AdbProxy().shell('arg1 arg2', shell=True)
mock_exec_cmd.assert_called_once_with(
'"adb" shell arg1 arg2', shell=True, timeout=None, stderr=None
)
def test_exec_adb_cmd_formats_command(self):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
with mock.patch.object(
adb.AdbProxy, '_construct_adb_cmd'
) as mock_construct_adb_cmd:
mock_adb_cmd = mock.MagicMock()
mock_adb_args = mock.MagicMock()
mock_construct_adb_cmd.return_value = mock_adb_cmd
mock_exec_cmd.return_value = MOCK_DEFAULT_COMMAND_OUTPUT
adb.AdbProxy().shell(mock_adb_args)
mock_construct_adb_cmd.assert_called_once_with(
'shell', mock_adb_args, shell=False
)
mock_exec_cmd.assert_called_once_with(
mock_adb_cmd, shell=False, timeout=None, stderr=None
)
def test_exec_adb_cmd_formats_command_with_shell_true(self):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
with mock.patch.object(
adb.AdbProxy, '_construct_adb_cmd'
) as mock_construct_adb_cmd:
mock_adb_cmd = mock.MagicMock()
mock_adb_args = mock.MagicMock()
mock_construct_adb_cmd.return_value = mock_adb_cmd
adb.AdbProxy().shell(mock_adb_args, shell=True)
mock_construct_adb_cmd.assert_called_once_with(
'shell', mock_adb_args, shell=True
)
mock_exec_cmd.assert_called_once_with(
mock_adb_cmd, shell=True, timeout=None, stderr=None
)
def test_execute_adb_and_process_stdout_formats_command(self):
with mock.patch.object(
adb.AdbProxy, '_execute_and_process_stdout'
) as mock_execute_and_process_stdout:
with mock.patch.object(
adb.AdbProxy, '_construct_adb_cmd'
) as mock_construct_adb_cmd:
mock_adb_cmd = mock.MagicMock()
mock_adb_args = mock.MagicMock()
mock_handler = mock.MagicMock()
mock_construct_adb_cmd.return_value = mock_adb_cmd
adb.AdbProxy()._execute_adb_and_process_stdout(
'shell', mock_adb_args, shell=False, handler=mock_handler
)
mock_construct_adb_cmd.assert_called_once_with(
'shell', mock_adb_args, shell=False
)
mock_execute_and_process_stdout.assert_called_once_with(
mock_adb_cmd, shell=False, handler=mock_handler
)
@mock.patch('mobly.utils.run_command')
def test_exec_adb_cmd_with_stderr_pipe(self, mock_run_command):
mock_run_command.return_value = (
0,
MOCK_DEFAULT_STDOUT.encode('utf-8'),
MOCK_DEFAULT_STDERR.encode('utf-8'),
)
stderr_redirect = io.BytesIO()
out = adb.AdbProxy().shell('arg1 arg2', shell=True, stderr=stderr_redirect)
self.assertEqual(MOCK_DEFAULT_STDOUT, out.decode('utf-8'))
self.assertEqual(
MOCK_DEFAULT_STDERR, stderr_redirect.getvalue().decode('utf-8')
)
@mock.patch('mobly.utils.run_command')
def test_connect_success(self, mock_run_command):
mock_address = 'localhost:1234'
mock_run_command.return_value = (
0,
f'connected to {mock_address}'.encode('utf-8'),
MOCK_DEFAULT_STDERR.encode('utf-8'),
)
out = adb.AdbProxy().connect(mock_address)
self.assertEqual('connected to localhost:1234', out.decode('utf-8'))
@mock.patch('mobly.utils.run_command')
def test_connect_already_connected(self, mock_run_command):
mock_address = 'localhost:1234'
mock_run_command.return_value = (
0,
f'already connected to {mock_address}'.encode('utf-8'),
MOCK_DEFAULT_STDERR.encode('utf-8'),
)
out = adb.AdbProxy().connect(mock_address)
self.assertEqual('already connected to localhost:1234', out.decode('utf-8'))
@mock.patch('mobly.utils.run_command')
def test_connect_fail(self, mock_run_command):
mock_address = 'localhost:1234'
mock_run_command.return_value = (
0,
'Connection refused\n'.encode('utf-8'),
MOCK_DEFAULT_STDERR.encode('utf-8'),
)
with self.assertRaisesRegex(
adb.AdbError, 'Error executing adb cmd "connect localhost:1234".'
):
out = adb.AdbProxy().connect(mock_address)
def test_getprop(self):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
mock_exec_cmd.return_value = b'blah'
self.assertEqual(adb.AdbProxy().getprop('haha'), 'blah')
mock_exec_cmd.assert_called_once_with(
['adb', 'shell', 'getprop', 'haha'],
shell=False,
stderr=None,
timeout=adb.DEFAULT_GETPROP_TIMEOUT_SEC,
)
def test_getprop_custom_timeout(self):
timeout_s = adb.DEFAULT_GETPROP_TIMEOUT_SEC * 2
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
mock_exec_cmd.return_value = b'blah'
self.assertEqual(
adb.AdbProxy().getprop('haha', timeout=timeout_s), 'blah'
)
mock_exec_cmd.assert_called_once_with(
['adb', 'shell', 'getprop', 'haha'],
shell=False,
stderr=None,
timeout=timeout_s,
)
def test__parse_getprop_output_special_values(self):
mock_adb_output = (
b'[selinux.restorecon_recursive]: [/data/misc_ce/10]\n'
b'[selinux.abc]: [key: value]\n' # "key: value" as value
b'[persist.sys.boot.reason.history]: [reboot,adb,1558549857\n'
b'reboot,factory_reset,1558483886\n' # multi-line value
b'reboot,1558483823]\n'
b'[persist.something]: [haha\n'
b']\n'
b'[[wrapped.key]]: [[wrapped value]]\n'
b'[persist.byte]: [J\xaa\x8bb\xab\x9dP\x0f]\n' # non-decodable
)
parsed_props = adb.AdbProxy()._parse_getprop_output(mock_adb_output)
expected_output = {
'persist.sys.boot.reason.history': (
'reboot,adb,1558549857\nreboot,factory_reset,1558483886\n'
'reboot,1558483823'
),
'selinux.abc': 'key: value',
'persist.something': 'haha\n',
'selinux.restorecon_recursive': '/data/misc_ce/10',
'[wrapped.key]': '[wrapped value]',
'persist.byte': 'JbP\x0f',
}
self.assertEqual(parsed_props, expected_output)
def test__parse_getprop_output_malformat_output(self):
mock_adb_output = ( # Malformat
b'[selinux.restorecon_recursive][/data/misc_ce/10]\n'
b'[persist.sys.boot.reason]: [reboot,adb,1558549857]\n'
b'[persist.something]: [haha]\n'
)
parsed_props = adb.AdbProxy()._parse_getprop_output(mock_adb_output)
expected_output = {
'persist.sys.boot.reason': 'reboot,adb,1558549857',
'persist.something': 'haha',
}
self.assertEqual(parsed_props, expected_output)
def test__parse_getprop_output_special_line_separator(self):
mock_adb_output = ( # Malformat
b'[selinux.restorecon_recursive][/data/misc_ce/10]\r\n'
b'[persist.sys.boot.reason]: [reboot,adb,1558549857]\r\n'
b'[persist.something]: [haha]\r\n'
)
parsed_props = adb.AdbProxy()._parse_getprop_output(mock_adb_output)
expected_output = {
'persist.sys.boot.reason': 'reboot,adb,1558549857',
'persist.something': 'haha',
}
self.assertEqual(parsed_props, expected_output)
@mock.patch('time.sleep', return_value=mock.MagicMock())
def test_getprops(self, mock_sleep):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
mock_exec_cmd.return_value = (
b'\n[sendbug.preferred.domain]: [google.com]\n'
b'[sys.uidcpupower]: []\n'
b'[sys.wifitracing.started]: [1]\n'
b'[telephony.lteOnCdmaDevice]: [1]\n\n'
)
actual_output = adb.AdbProxy().getprops(
[
'sys.wifitracing.started', # "numeric" value
'sys.uidcpupower', # empty value
'sendbug.preferred.domain', # string value
'nonExistentProp',
]
)
self.assertEqual(
actual_output,
{
'sys.wifitracing.started': '1',
'sys.uidcpupower': '',
'sendbug.preferred.domain': 'google.com',
},
)
mock_exec_cmd.assert_called_once_with(
['adb', 'shell', 'getprop'],
shell=False,
stderr=None,
timeout=adb.DEFAULT_GETPROP_TIMEOUT_SEC,
)
mock_sleep.assert_not_called()
@mock.patch('time.sleep', return_value=mock.MagicMock())
def test_getprops_when_empty_string_randomly_returned(self, mock_sleep):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
mock_exec_cmd.side_effect = [
b'',
b'\n[ro.build.id]: [AB42]\n[ro.build.type]: [userdebug]\n\n',
]
actual_output = adb.AdbProxy().getprops(['ro.build.id'])
self.assertEqual(
actual_output,
{
'ro.build.id': 'AB42',
},
)
self.assertEqual(mock_exec_cmd.call_count, 2)
mock_exec_cmd.assert_called_with(
['adb', 'shell', 'getprop'],
shell=False,
stderr=None,
timeout=adb.DEFAULT_GETPROP_TIMEOUT_SEC,
)
self.assertEqual(mock_sleep.call_count, 1)
mock_sleep.assert_called_with(1)
@mock.patch('time.sleep', return_value=mock.MagicMock())
def test_getprops_when_empty_string_always_returned(self, mock_sleep):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
mock_exec_cmd.return_value = b''
actual_output = adb.AdbProxy().getprops(['ro.build.id'])
self.assertEqual(actual_output, {})
self.assertEqual(mock_exec_cmd.call_count, 3)
mock_exec_cmd.assert_called_with(
['adb', 'shell', 'getprop'],
shell=False,
stderr=None,
timeout=adb.DEFAULT_GETPROP_TIMEOUT_SEC,
)
self.assertEqual(mock_sleep.call_count, 2)
mock_sleep.assert_called_with(1)
def test_forward(self):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
adb.AdbProxy().forward(['tcp:12345', 'tcp:98765'])
mock_exec_cmd.assert_called_with(
['adb', 'forward', 'tcp:12345', 'tcp:98765'],
shell=False,
timeout=None,
stderr=None,
)
def test_reverse(self):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
adb.AdbProxy().reverse(['tcp:12345', 'tcp:98765'])
mock_exec_cmd.assert_called_with(
['adb', 'reverse', 'tcp:12345', 'tcp:98765'],
shell=False,
timeout=None,
stderr=None,
)
def test_instrument_without_parameters(self):
"""Verifies the AndroidDevice object's instrument command is correct in
the basic case.
"""
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
output = adb.AdbProxy().instrument(MOCK_INSTRUMENTATION_PACKAGE)
mock_exec_cmd.assert_called_once_with(
['adb', 'shell', MOCK_BASIC_INSTRUMENTATION_COMMAND],
shell=False,
timeout=None,
stderr=None,
)
self.assertEqual(output, mock_exec_cmd.return_value)
def test_instrument_with_runner(self):
"""Verifies the AndroidDevice object's instrument command is correct
with a runner specified.
"""
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
stdout = adb.AdbProxy().instrument(
MOCK_INSTRUMENTATION_PACKAGE, runner=MOCK_INSTRUMENTATION_RUNNER
)
mock_exec_cmd.assert_called_once_with(
['adb', 'shell', MOCK_RUNNER_INSTRUMENTATION_COMMAND],
shell=False,
timeout=None,
stderr=None,
)
self.assertEqual(stdout, mock_exec_cmd.return_value)
def test_instrument_with_options(self):
"""Verifies the AndroidDevice object's instrument command is correct
with options.
"""
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
stdout = adb.AdbProxy().instrument(
MOCK_INSTRUMENTATION_PACKAGE, options=MOCK_INSTRUMENTATION_OPTIONS
)
mock_exec_cmd.assert_called_once_with(
['adb', 'shell', MOCK_OPTIONS_INSTRUMENTATION_COMMAND],
shell=False,
timeout=None,
stderr=None,
)
self.assertEqual(stdout, mock_exec_cmd.return_value)
def test_instrument_with_handler(self):
"""Verifies the AndroidDevice object's instrument command is correct
with a handler passed in.
"""
def mock_handler(raw_line):
pass
with mock.patch.object(
adb.AdbProxy, '_execute_and_process_stdout'
) as mock_execute_and_process_stdout:
stderr = adb.AdbProxy().instrument(
MOCK_INSTRUMENTATION_PACKAGE, handler=mock_handler
)
mock_execute_and_process_stdout.assert_called_once_with(
['adb', 'shell', MOCK_BASIC_INSTRUMENTATION_COMMAND],
shell=False,
handler=mock_handler,
)
self.assertEqual(stderr, mock_execute_and_process_stdout.return_value)
def test_instrument_with_handler_with_runner(self):
"""Verifies the AndroidDevice object's instrument command is correct
with a handler passed in and a runner specified.
"""
def mock_handler(raw_line):
pass
with mock.patch.object(
adb.AdbProxy, '_execute_and_process_stdout'
) as mock_execute_and_process_stdout:
stderr = adb.AdbProxy().instrument(
MOCK_INSTRUMENTATION_PACKAGE,
runner=MOCK_INSTRUMENTATION_RUNNER,
handler=mock_handler,
)
mock_execute_and_process_stdout.assert_called_once_with(
['adb', 'shell', MOCK_RUNNER_INSTRUMENTATION_COMMAND],
shell=False,
handler=mock_handler,
)
self.assertEqual(stderr, mock_execute_and_process_stdout.return_value)
def test_instrument_with_handler_with_options(self):
"""Verifies the AndroidDevice object's instrument command is correct
with a handler passed in and options.
"""
def mock_handler(raw_line):
pass
with mock.patch.object(
adb.AdbProxy, '_execute_and_process_stdout'
) as mock_execute_and_process_stdout:
stderr = adb.AdbProxy().instrument(
MOCK_INSTRUMENTATION_PACKAGE,
options=MOCK_INSTRUMENTATION_OPTIONS,
handler=mock_handler,
)
mock_execute_and_process_stdout.assert_called_once_with(
['adb', 'shell', MOCK_OPTIONS_INSTRUMENTATION_COMMAND],
shell=False,
handler=mock_handler,
)
self.assertEqual(stderr, mock_execute_and_process_stdout.return_value)
@mock.patch.object(adb.AdbProxy, '_exec_cmd')
def test_root_success(self, mock_exec_cmd):
mock_exec_cmd.return_value = MOCK_ROOT_SUCCESS_OUTPUT
output = adb.AdbProxy().root()
mock_exec_cmd.assert_called_once_with(
['adb', 'root'], shell=False, timeout=None, stderr=None
)
self.assertEqual(output, MOCK_ROOT_SUCCESS_OUTPUT)
@mock.patch('time.sleep', return_value=mock.MagicMock())
@mock.patch.object(adb.AdbProxy, '_exec_cmd')
def test_root_success_with_retry(self, mock_exec_cmd, mock_sleep):
mock_exec_cmd.side_effect = [
adb.AdbError('adb root', '', MOCK_ROOT_ERROR_OUTPUT, 1),
MOCK_ROOT_SUCCESS_OUTPUT,
]
output = adb.AdbProxy().root()
mock_exec_cmd.assert_called_with(
['adb', 'root'], shell=False, timeout=None, stderr=None
)
self.assertEqual(output, MOCK_ROOT_SUCCESS_OUTPUT)
self.assertEqual(mock_sleep.call_count, 1)
mock_sleep.assert_called_with(10)
@mock.patch('time.sleep', return_value=mock.MagicMock())
@mock.patch.object(adb.AdbProxy, '_exec_cmd')
def test_root_raises_adb_error_when_all_retries_failed(
self, mock_exec_cmd, mock_sleep
):
mock_exec_cmd.side_effect = adb.AdbError(
'adb root', '', MOCK_ROOT_ERROR_OUTPUT, 1
)
expected_msg = (
'Error executing adb cmd "adb root". ret: 1, stdout: , stderr: %s'
% MOCK_ROOT_ERROR_OUTPUT
)
with self.assertRaisesRegex(adb.AdbError, expected_msg):
adb.AdbProxy().root()
mock_exec_cmd.assert_called_with(
['adb', 'root'], shell=False, timeout=None, stderr=None
)
self.assertEqual(mock_sleep.call_count, adb.ADB_ROOT_RETRY_ATTEMPTS - 1)
mock_sleep.assert_has_calls([mock.call(10), mock.call(20)])
def test_has_shell_command_called_correctly(self):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
mock_exec_cmd.return_value = MOCK_DEFAULT_COMMAND_OUTPUT
adb.AdbProxy().has_shell_command(MOCK_SHELL_COMMAND)
mock_exec_cmd.assert_called_once_with(
['adb', 'shell', 'command', '-v', MOCK_SHELL_COMMAND],
shell=False,
timeout=None,
stderr=None,
)
def test_has_shell_command_with_existing_command(self):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
mock_exec_cmd.return_value = MOCK_COMMAND_OUTPUT
self.assertTrue(adb.AdbProxy().has_shell_command(MOCK_SHELL_COMMAND))
def test_has_shell_command_with_missing_command_on_older_devices(self):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
mock_exec_cmd.return_value = MOCK_DEFAULT_COMMAND_OUTPUT
mock_exec_cmd.side_effect = adb.AdbError(
MOCK_ADB_SHELL_COMMAND_CHECK, '', '', 0
)
self.assertFalse(adb.AdbProxy().has_shell_command(MOCK_SHELL_COMMAND))
def test_has_shell_command_with_missing_command_on_newer_devices(self):
with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
mock_exec_cmd.return_value = MOCK_DEFAULT_COMMAND_OUTPUT
mock_exec_cmd.side_effect = adb.AdbError(
MOCK_ADB_SHELL_COMMAND_CHECK, '', '', 1
)
self.assertFalse(adb.AdbProxy().has_shell_command(MOCK_SHELL_COMMAND))
@mock.patch.object(adb.AdbProxy, 'getprop')
@mock.patch.object(adb.AdbProxy, '_exec_cmd')
def test_current_user_id_25_and_above(self, mock_exec_cmd, mock_getprop):
mock_getprop.return_value = b'25'
mock_exec_cmd.return_value = b'123'
user_id = adb.AdbProxy().current_user_id
mock_exec_cmd.assert_called_once_with(
['adb', 'shell', 'am', 'get-current-user'],
shell=False,
stderr=None,
timeout=None,
)
self.assertEqual(user_id, 123)
@mock.patch.object(adb.AdbProxy, 'getprop')
@mock.patch.object(adb.AdbProxy, '_exec_cmd')
def test_current_user_id_between_21_and_24(self, mock_exec_cmd, mock_getprop):
mock_getprop.return_value = b'23'
mock_exec_cmd.return_value = (
b'Users:\n'
b'UserInfo{123:Owner:13} serialNo=0\n'
b'Created: <unknown>\n'
b'Last logged in: +1h22m12s497ms ago\n'
b'UserInfo{456:Owner:14} serialNo=0\n'
b'Created: <unknown>\n'
b'Last logged in: +1h01m12s497ms ago\n'
)
user_id = adb.AdbProxy().current_user_id
mock_exec_cmd.assert_called_once_with(
['adb', 'shell', 'dumpsys', 'user'],
shell=False,
stderr=None,
timeout=None,
)
self.assertEqual(user_id, 123)
if __name__ == '__main__':
unittest.main()