blob: 5b63a64f765ccb7658467a42d28c0dd48493752b [file] [log] [blame]
# Lint as: python2, python3
# Copyright 2022 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import unittest
from unittest.mock import ANY, MagicMock, call, patch
from autotest_lib.client.common_lib import error
from autotest_lib.server import utils
from autotest_lib.server.cros.tradefed import adb
class MockHost:
"""Mock Host object."""
def __init__(self, hostname='hostname', port='22'):
self.hostname = hostname
self.port = port
def ssh_command(self, options):
"""Returns a mock SSH command."""
return f'ssh {options} {self.hostname}'
class AdbTest(unittest.TestCase):
"""Tests for Adb."""
def test_add_paths(self):
instance = adb.Adb()
instance.add_path('/some/install/path')
instance.add_path('/another/directory')
self.assertEqual(set(['/some/install/path', '/another/directory']),
instance.get_paths())
def test_set_create_tunnel(self):
mock_tunnel = MagicMock()
mock_ctx = MagicMock()
mock_tunnel.create.return_value = mock_ctx
a = adb.Adb()
a.set_tunnel(mock_tunnel)
with a.create_tunnel():
mock_tunnel.create.assert_called_once()
mock_ctx.__enter__.assert_called_once()
mock_ctx.__exit__.assert_called_once()
def test_get_adb_target(self):
mock_tunnel = MagicMock()
mock_host = MockHost()
a = adb.Adb()
a.set_tunnel(mock_tunnel)
with a.create_tunnel():
a.get_adb_target(mock_host)
mock_tunnel.create.assert_called_once()
mock_tunnel.get_adb_target.assert_called_once_with(mock_host)
def test_get_adb_targets(self):
mock_tunnel = MagicMock()
mock_host1 = MockHost()
mock_host2 = MockHost()
mock_host3 = MockHost()
a = adb.Adb()
a.set_tunnel(mock_tunnel)
with a.create_tunnel():
a.get_adb_targets([mock_host1, mock_host2, mock_host3])
mock_tunnel.create.assert_called_once()
mock_tunnel.get_adb_target.assert_has_calls([
call(mock_host1),
call(mock_host2),
call(mock_host3),
])
@patch('autotest_lib.server.utils.run')
def test_run(self, mock_run):
instance = adb.Adb()
instance.add_path('/some/install/path')
mock_host = MockHost('123.76.0.29', 3467)
instance.run(mock_host, args=('some', 'command'), timeout=240)
mock_run.assert_called_with('adb',
args=('-L', 'tcp:localhost:5037', '-s',
'123.76.0.29:3467', 'some',
'command'),
timeout=240,
extra_paths=['/some/install/path'])
@patch('autotest_lib.server.utils.run')
def test_run_without_host(self, mock_run):
instance = adb.Adb()
instance.add_path('/some/install/path')
instance.run(None, args=('some', 'command'), timeout=240)
mock_run.assert_called_with('adb',
args=('-L', 'tcp:localhost:5037', 'some',
'command'),
timeout=240,
extra_paths=['/some/install/path'])
@patch('autotest_lib.server.utils.run')
@patch('random.randint', return_value=12345)
def test_pick_random_port(self, mock_randint, mock_run):
mock_run.return_value = utils.CmdResult(
exit_status=0,
stderr='daemon started successfully')
instance = adb.Adb()
instance.pick_random_port(max_retries=1, start_timeout=3)
mock_randint.assert_called()
mock_run.assert_called_once_with('adb',
args=('-L', 'tcp:localhost:12345',
'start-server'),
extra_paths=[],
timeout=3,
verbose=True)
self.assertEqual(instance.get_port(), 12345)
@patch('autotest_lib.server.utils.run')
@patch('random.randint')
def test_pick_random_port_with_retry(self, mock_randint, mock_run):
mock_randint.side_effect = [12340, 12341, 12342, 12343]
mock_run.side_effect = [
# 1st try: port occupied by ADB server
utils.CmdResult(exit_status=0),
# 2nd try: port occupied by some process, got invalid response
error.CmdError(command=None, result_obj=None),
# 3rd try: port occupied by some process, got no response
error.CmdTimeoutError(command=None, result_obj=None),
# 4th try: success
utils.CmdResult(exit_status=0,
stderr='daemon started successfully'),
]
instance = adb.Adb()
# Try 4 times; should succeed on 4th try
instance.pick_random_port(max_retries=4)
self.assertEqual(instance.get_port(), 12343)
class NullAdbTunnelTest(unittest.TestCase):
"""Tests for NullAdbTunnel."""
def test_create(self):
t = adb.NullAdbTunnel()
with t.create():
pass
def test_get_adb_target_ipv4(self):
mock_host = MockHost('123.76.0.29', 3467)
t = adb.NullAdbTunnel()
target = t.get_adb_target(mock_host)
self.assertEqual(target, '123.76.0.29:3467')
def test_get_adb_target_ipv6(self):
mock_host = MockHost('2409::3', 1234)
t = adb.NullAdbTunnel()
target = t.get_adb_target(mock_host)
self.assertEqual(target, '[2409::3]:1234')
# Verify that a host name works.
def test_get_adb_target_hostname(self):
mock_host = MockHost('some.hostname.cros', 4792)
t = adb.NullAdbTunnel()
target = t.get_adb_target(mock_host)
self.assertEqual(target, 'some.hostname.cros:4792')
class SshAdbTunnelTest(unittest.TestCase):
"""Tests for SshAdbTunnel."""
@patch('autotest_lib.server.utils.BgJob')
@patch('autotest_lib.server.utils.nuke_subprocess')
@patch('autotest_lib.server.utils.join_bg_jobs')
def test_basic(self, mock_join_bg_jobs, mock_nuke_subprocess, mock_bgjob):
host1 = MockHost('host1')
host2 = MockHost('host2')
hosts = [host1, host2]
mock_jobs = [MagicMock(), MagicMock()]
mock_bgjob.side_effect = mock_jobs
t = adb.SshAdbTunnel(hosts)
with t.create():
mock_bgjob.assert_has_calls([
call('ssh -v -N -L9222:localhost:22 host1',
nickname=ANY,
stderr_level=ANY,
stdout_tee=ANY,
stderr_tee=ANY),
call('ssh -v -N -L9223:localhost:22 host2',
nickname=ANY,
stderr_level=ANY,
stdout_tee=ANY,
stderr_tee=ANY),
])
target1 = t.get_adb_target(host1)
self.assertEqual(target1, 'localhost:9222')
target2 = t.get_adb_target(host2)
self.assertEqual(target2, 'localhost:9223')
mock_nuke_subprocess.assert_has_calls([
call(mock_jobs[0].sp),
call(mock_jobs[1].sp),
])
mock_join_bg_jobs.assert_called_once_with(mock_jobs)