Improve port picking logic. (#717)
* Do not check for adb ports if adb is not available.
* Replace the usage of the deprecated API.
diff --git a/mobly/controllers/android_device_lib/adb.py b/mobly/controllers/android_device_lib/adb.py
index 1814d19..11539fb 100644
--- a/mobly/controllers/android_device_lib/adb.py
+++ b/mobly/controllers/android_device_lib/adb.py
@@ -93,6 +93,19 @@
utils.cli_cmd_to_string(self.cmd), self.timeout)
+def is_adb_available():
+ """Checks if adb is available as a command line tool.
+
+ Returns:
+ True if adb binary is available in console, False otherwise.
+ """
+ ret, out, err = utils.run_command('which adb', shell=True)
+ clean_out = out.decode('utf-8').strip()
+ if clean_out:
+ return True
+ return False
+
+
def list_occupied_adb_ports():
"""Lists all the host ports occupied by adb forward.
diff --git a/mobly/utils.py b/mobly/utils.py
index 5d63c1d..5aca4a7 100644
--- a/mobly/utils.py
+++ b/mobly/utils.py
@@ -517,12 +517,15 @@
"""
# Only import adb module if needed.
from mobly.controllers.android_device_lib import adb
+ port = portpicker.pick_unused_port()
+ if not adb.is_adb_available():
+ return port
for _ in range(MAX_PORT_ALLOCATION_RETRY):
- port = portpicker.PickUnusedPort()
# Make sure adb is not using this port so we don't accidentally
# interrupt ongoing runs by trying to bind to the port.
if port not in adb.list_occupied_adb_ports():
return port
+ port = portpicker.pick_unused_port()
raise Error('Failed to find available port after {} retries'.format(
MAX_PORT_ALLOCATION_RETRY))
diff --git a/tests/mobly/controllers/android_device_lib/adb_test.py b/tests/mobly/controllers/android_device_lib/adb_test.py
index e97eaf2..099cff0 100755
--- a/tests/mobly/controllers/android_device_lib/adb_test.py
+++ b/tests/mobly/controllers/android_device_lib/adb_test.py
@@ -86,6 +86,17 @@
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'))
diff --git a/tests/mobly/utils_test.py b/tests/mobly/utils_test.py
index f047827..3b499a6 100755
--- a/tests/mobly/utils_test.py
+++ b/tests/mobly/utils_test.py
@@ -40,6 +40,7 @@
from tests.lib import multiple_subclasses_module
MOCK_AVAILABLE_PORT = 5
+ADB_MODULE_PACKAGE_NAME = 'mobly.controllers.android_device_lib.adb'
class UtilsTest(unittest.TestCase):
@@ -418,24 +419,34 @@
utils.create_dir(self.tmp_dir)
self.assertTrue(os.path.exists(self.tmp_dir))
- @mock.patch('mobly.controllers.android_device_lib.adb.list_occupied_adb_ports'
- )
- @mock.patch('portpicker.PickUnusedPort', return_value=MOCK_AVAILABLE_PORT)
- def test_get_available_port_positive(self, mock_list_occupied_adb_ports,
+ @mock.patch(f'{ADB_MODULE_PACKAGE_NAME}.is_adb_available', return_value=True)
+ @mock.patch(f'{ADB_MODULE_PACKAGE_NAME}.list_occupied_adb_ports')
+ @mock.patch('portpicker.pick_unused_port', return_value=MOCK_AVAILABLE_PORT)
+ def test_get_available_port_positive(self, mock_is_adb_available,
+ mock_list_occupied_adb_ports,
mock_pick_unused_port):
self.assertEqual(utils.get_available_host_port(), MOCK_AVAILABLE_PORT)
- @mock.patch(
- 'mobly.controllers.android_device_lib.adb.list_occupied_adb_ports',
- return_value=[MOCK_AVAILABLE_PORT])
- @mock.patch('portpicker.PickUnusedPort', return_value=MOCK_AVAILABLE_PORT)
- def test_get_available_port_negative(self, mock_list_occupied_adb_ports,
+ @mock.patch(f'{ADB_MODULE_PACKAGE_NAME}.is_adb_available', return_value=False)
+ @mock.patch(f'{ADB_MODULE_PACKAGE_NAME}.list_occupied_adb_ports')
+ @mock.patch('portpicker.pick_unused_port', return_value=MOCK_AVAILABLE_PORT)
+ def test_get_available_port_positive_no_adb(self, mock_is_adb_available,
+ mock_list_occupied_adb_ports,
+ mock_pick_unused_port):
+ self.assertEqual(utils.get_available_host_port(), MOCK_AVAILABLE_PORT)
+ mock_list_occupied_adb_ports.assert_not_called()
+
+ @mock.patch(f'{ADB_MODULE_PACKAGE_NAME}.is_adb_available', return_value=True)
+ @mock.patch(f'{ADB_MODULE_PACKAGE_NAME}.list_occupied_adb_ports',
+ return_value=[MOCK_AVAILABLE_PORT])
+ @mock.patch('portpicker.pick_unused_port', return_value=MOCK_AVAILABLE_PORT)
+ def test_get_available_port_negative(self, mock_is_adb_available,
+ mock_list_occupied_adb_ports,
mock_pick_unused_port):
with self.assertRaisesRegex(utils.Error, 'Failed to find.* retries'):
utils.get_available_host_port()
- @mock.patch('mobly.controllers.android_device_lib.adb.list_occupied_adb_ports'
- )
+ @mock.patch(f'{ADB_MODULE_PACKAGE_NAME}.list_occupied_adb_ports')
def test_get_available_port_returns_free_port(self,
mock_list_occupied_adb_ports):
"""Verifies logic to pick a free port on the host.