From 616672a4ff55b4894eeffcd34a064b6854d4c5e1 Mon Sep 17 00:00:00 2001 From: Minghao Li Date: Wed, 10 Dec 2025 16:46:42 +0800 Subject: [PATCH 1/8] Initial commit: Changing snippet identifier to pacakge+user_id --- .../services/snippet_management_service.py | 44 +++---- .../android_device_lib/snippet_client_v2.py | 24 ++-- .../snippet_management_service_test.py | 109 ++++++++++++++++-- .../snippet_client_v2_test.py | 5 +- .../mobly/controllers/android_device_test.py | 12 +- 5 files changed, 149 insertions(+), 45 deletions(-) diff --git a/mobly/controllers/android_device_lib/services/snippet_management_service.py b/mobly/controllers/android_device_lib/services/snippet_management_service.py index 7da1b23d..4165fe26 100644 --- a/mobly/controllers/android_device_lib/services/snippet_management_service.py +++ b/mobly/controllers/android_device_lib/services/snippet_management_service.py @@ -69,31 +69,33 @@ def add_snippet_client(self, name, package, config=None): class for supported configurations. Raises: - Error, if a duplicated name or package is passed in. + Error: if a duplicated name is passed in, or the same snippet has been + registered. """ # Should not load snippet with the same name more than once. if name in self._snippet_clients: raise Error( self, - 'Name "%s" is already registered with package "%s", it cannot ' - 'be used again.' % (name, self._snippet_clients[name].client.package), + 'Name "%s" is already registered with snippet "%s", it cannot ' + 'be used again.' % (name, self._snippet_clients[name].identifier), ) - # Should not load the same snippet package more than once. + # Should not load snippets with the same identifier more than once. + new_client = snippet_client_v2.SnippetClientV2( + package=package, + ad=self._device, + config=config, + ) for snippet_name, client in self._snippet_clients.items(): - if package == client.package: + if new_client.identifier == client.identifier: + del new_client raise Error( self, - 'Snippet package "%s" has already been loaded under name "%s".' - % (package, snippet_name), + 'Snippet "%s" has already been loaded under name "%s".' + % (client.identifier, snippet_name), ) - client = snippet_client_v2.SnippetClientV2( - package=package, - ad=self._device, - config=config, - ) - client.initialize() - self._snippet_clients[name] = client + new_client.initialize() + self._snippet_clients[name] = new_client def remove_snippet_client(self, name): """Removes a snippet client from management. @@ -113,24 +115,24 @@ def start(self): """Starts all the snippet clients under management.""" for client in self._snippet_clients.values(): if not client.is_alive: - self._device.log.debug('Starting SnippetClient<%s>.', client.package) + self._device.log.debug('Starting SnippetClient<%s>.', client.identifier) client.initialize() else: self._device.log.debug( 'Not startng SnippetClient<%s> because it is already alive.', - client.package, + client.identifier, ) def stop(self): """Stops all the snippet clients under management.""" for client in self._snippet_clients.values(): if client.is_alive: - self._device.log.debug('Stopping SnippetClient<%s>.', client.package) + self._device.log.debug('Stopping SnippetClient<%s>.', client.identifier) client.stop() else: self._device.log.debug( 'Not stopping SnippetClient<%s> because it is not alive.', - client.package, + client.identifier, ) def pause(self): @@ -140,18 +142,18 @@ def pause(self): allocated in `resume`. """ for client in self._snippet_clients.values(): - self._device.log.debug('Pausing SnippetClient<%s>.', client.package) + self._device.log.debug('Pausing SnippetClient<%s>.', client.identifier) client.close_connection() def resume(self): """Resumes all paused snippet clients.""" for client in self._snippet_clients.values(): if not client.is_alive: - self._device.log.debug('Resuming SnippetClient<%s>.', client.package) + self._device.log.debug('Resuming SnippetClient<%s>.', client.identifier) client.restore_server_connection() else: self._device.log.debug( - 'Not resuming SnippetClient<%s>.', client.package + 'Not resuming SnippetClient<%s>.', client.identifier ) def __getattr__(self, name): diff --git a/mobly/controllers/android_device_lib/snippet_client_v2.py b/mobly/controllers/android_device_lib/snippet_client_v2.py index ee0b2ecd..3c29f097 100644 --- a/mobly/controllers/android_device_lib/snippet_client_v2.py +++ b/mobly/controllers/android_device_lib/snippet_client_v2.py @@ -198,6 +198,11 @@ def user_id(self): self._user_id = self._adb.current_user_id return self._user_id + @property + def identifier(self): + """The identifier of this snippet client.""" + return f'{self.package}:user_{self.user_id}' + @property def is_alive(self): """Does the client have an active connection to the snippet server.""" @@ -286,8 +291,8 @@ def start_server(self): """ persists_shell_cmd = self._get_persisting_command() self.log.debug( - 'Snippet server for package %s is using protocol %d.%d', - self.package, + 'Snippet server for identifier %s is using protocol %d.%d', + self.identifier, _PROTOCOL_MAJOR_VERSION, _PROTOCOL_MINOR_VERSION, ) @@ -345,8 +350,8 @@ def _get_persisting_command(self): def _get_instrument_options_str(self): self.log.debug( - 'Got am instrument options in snippet client for package %s: %s', - self.package, + 'Got am instrument options in snippet client "%s": %s', + self.identifier, self._config.am_instrument_options, ) if not self._config.am_instrument_options: @@ -457,7 +462,7 @@ def create_socket_connection(self): self.log.debug( 'Snippet client is creating socket connection to the snippet server ' 'of %s through host port %d.', - self.package, + self.identifier, self.host_port, ) self._conn = socket.create_connection( @@ -699,11 +704,11 @@ def stop(self): android_device_lib_errors.DeviceError: if the server exited with errors on the device side. """ - self.log.debug('Stopping snippet package %s.', self.package) + self.log.debug('Stopping snippet client %s.', self.identifier) self.close_connection() self._stop_server() self._destroy_event_client() - self.log.debug('Snippet package %s stopped.', self.package) + self.log.debug('Snippet client %s stopped.', self.identifier) def close_connection(self): """Closes the connection to the snippet server on the device. @@ -810,8 +815,9 @@ def restore_server_connection(self, port=None): raise errors.ServerRestoreConnectionError( self._device, ( - f'Failed to restore server connection for {self.package} at ' - f'host port {self.host_port}, device port {self.device_port}.' + 'Failed to restore server connection for snippet' + f' {self.identifier} at host port {self.host_port}, device port' + f' {self.device_port}.' ), ) from e diff --git a/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py b/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py index b6431d54..3e0ce22a 100755 --- a/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py +++ b/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py @@ -12,18 +12,62 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging import unittest from unittest import mock +from mobly.snippet import client_base from mobly.controllers.android_device_lib import snippet_client_v2 from mobly.controllers.android_device_lib.services import snippet_management_service MOCK_PACKAGE = 'com.mock.package' +MOCK_PACKAGE2 = 'com.mock.package2' SNIPPET_CLIENT_V2_CLASS_PATH = ( 'mobly.controllers.android_device_lib.snippet_client_v2.SnippetClientV2' ) +class MockSnippetClientV2(client_base.ClientBase): + + def __init__(self, package, ad, config=None): + self.user_id = ( + config.user_id + if config is not None and config.user_id is not None + else ad.adb.current_user_id + ) + self.package = package + self.identifier = f'{self.package}:user_{self.user_id}' + self.log = logging + + # Override abstract methods so this class can be instantiated. + def before_starting_server(self): + pass + + def start_server(self): + pass + + def make_connection(self): + pass + + def restore_server_connection(self, port=None): + pass + + def check_server_proc_running(self): + pass + + def send_rpc_request(self, request): + pass + + def handle_callback(self, callback_id, ret_value, rpc_func_name): + pass + + def stop(self): + pass + + def close_connection(self): + pass + + class SnippetManagementServiceTest(unittest.TestCase): """Tests for the snippet management service.""" @@ -99,27 +143,72 @@ def test_add_snippet_client_dup_name(self, _): ) manager.add_snippet_client('foo', MOCK_PACKAGE) msg = ( - '.* Name "foo" is already registered with package ".*", it ' + '.* Name "foo" is already registered with snippet ".*", it ' 'cannot be used again.' ) with self.assertRaisesRegex(snippet_management_service.Error, msg): manager.add_snippet_client('foo', MOCK_PACKAGE + 'ha') - @mock.patch(SNIPPET_CLIENT_V2_CLASS_PATH) - def test_add_snippet_client_dup_package(self, mock_class): - mock_client = mock_class.return_value - mock_client.package = MOCK_PACKAGE - manager = snippet_management_service.SnippetManagementService( - mock.MagicMock() - ) + @mock.patch(SNIPPET_CLIENT_V2_CLASS_PATH, new=MockSnippetClientV2) + def test_add_snippet_client_different_pacakge(self): + mock_device = mock.MagicMock() + manager = snippet_management_service.SnippetManagementService(mock_device) + manager.add_snippet_client('foo', MOCK_PACKAGE) + + try: + manager.add_snippet_client('bar', MOCK_PACKAGE2) + except snippet_management_service.Error as e: + self.fail( + 'Should not fail when loading snippets with the different package' + ) + + @mock.patch(SNIPPET_CLIENT_V2_CLASS_PATH, new=MockSnippetClientV2) + def test_add_snippet_client_dup_package_and_none_as_snippet_config(self): + user_id = 2 + mock_adb = mock.MagicMock(current_user_id=user_id) + mock_device = mock.MagicMock(adb=mock_adb) + manager = snippet_management_service.SnippetManagementService(mock_device) manager.add_snippet_client('foo', MOCK_PACKAGE) msg = ( - 'Snippet package "com.mock.package" has already been loaded ' - 'under name "foo".' + f'Snippet "com.mock.package:user_{user_id}" has already been loaded' + ' under name "foo".' ) with self.assertRaisesRegex(snippet_management_service.Error, msg): manager.add_snippet_client('bar', MOCK_PACKAGE) + @mock.patch(SNIPPET_CLIENT_V2_CLASS_PATH, new=MockSnippetClientV2) + def test_add_snippet_client_dup_package_and_user_id(self): + user_id = 2 + config = snippet_client_v2.Config(user_id=user_id) + mock_adb = mock.MagicMock(current_user_id=user_id) + mock_device = mock.MagicMock(adb=mock_adb) + manager = snippet_management_service.SnippetManagementService(mock_device) + manager.add_snippet_client('foo', MOCK_PACKAGE, config=config) + msg = ( + f'Snippet "com.mock.package:user_{user_id}" has already been loaded' + ' under name "foo".' + ) + with self.assertRaisesRegex(snippet_management_service.Error, msg): + manager.add_snippet_client('bar', MOCK_PACKAGE, config=config) + + @mock.patch(SNIPPET_CLIENT_V2_CLASS_PATH, new=MockSnippetClientV2) + def test_add_snippet_client_dup_package_with_different_user_id(self): + old_user_id = 2 + new_user_id = 3 + old_config = snippet_client_v2.Config(user_id=old_user_id) + new_config = snippet_client_v2.Config(user_id=new_user_id) + mock_adb = mock.MagicMock(current_user_id=new_user_id) + mock_device = mock.MagicMock(adb=mock_adb) + manager = snippet_management_service.SnippetManagementService(mock_device) + manager.add_snippet_client('foo', MOCK_PACKAGE, old_config) + try: + manager.add_snippet_client('bar', MOCK_PACKAGE, new_config) + except snippet_management_service.Error as e: + self.fail( + 'Should not fail when loading snippets with the same package but' + ' different user ID.' + ) + @mock.patch(SNIPPET_CLIENT_V2_CLASS_PATH) def test_remove_snippet_client(self, mock_class): mock_client = mock.MagicMock() diff --git a/tests/mobly/controllers/android_device_lib/snippet_client_v2_test.py b/tests/mobly/controllers/android_device_lib/snippet_client_v2_test.py index 13f57821..6e42a363 100644 --- a/tests/mobly/controllers/android_device_lib/snippet_client_v2_test.py +++ b/tests/mobly/controllers/android_device_lib/snippet_client_v2_test.py @@ -1343,8 +1343,9 @@ def test_restore_event_client( with self.assertRaisesRegex( errors.ServerRestoreConnectionError, ( - f'Failed to restore server connection for {MOCK_PACKAGE_NAME} at ' - f'host port {host_port_3}, device port {MOCK_DEVICE_PORT}' + 'Failed to restore server connection for snippet' + f' {MOCK_PACKAGE_NAME}:user_{MOCK_USER_ID} at host port' + f' {host_port_3}, device port {MOCK_DEVICE_PORT}' ), ): self.client.restore_server_connection() diff --git a/tests/mobly/controllers/android_device_test.py b/tests/mobly/controllers/android_device_test.py index 640f28c4..8f59c96b 100755 --- a/tests/mobly/controllers/android_device_test.py +++ b/tests/mobly/controllers/android_device_test.py @@ -1552,14 +1552,20 @@ def test_AndroidDevice_getattr( return_value=MockSnippetClient, ) @mock.patch('mobly.utils.get_available_host_port') - def test_AndroidDevice_load_snippet_dup_package( + def test_AndroidDevice_load_snippet_dup_identifier( self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy ): ad = android_device.AndroidDevice(serial='1') + ad.adb.current_user_id = 1 + identifier = f'{MOCK_SNIPPET_PACKAGE_NAME}:user_1' + MockSnippetClient.return_value.identifier = identifier + MockSnippetClient.get_identifier.side_effect = ( + lambda pacakge, user_id: f'{pacakge}:user_{user_id}' + ) ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME) expected_msg = ( - 'Snippet package "%s" has already been loaded under name "snippet".' - ) % MOCK_SNIPPET_PACKAGE_NAME + f'Snippet "{identifier}" has already been loaded under name "snippet".' + ) with self.assertRaisesRegex(android_device.Error, expected_msg): ad.load_snippet('snippet2', MOCK_SNIPPET_PACKAGE_NAME) From ac2f2ab1e25c22826966971443a30b08544fc7ec Mon Sep 17 00:00:00 2001 From: Minghao Li Date: Thu, 11 Dec 2025 16:07:59 +0800 Subject: [PATCH 2/8] fix comment --- .../android_device_lib/snippet_client_v2.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mobly/controllers/android_device_lib/snippet_client_v2.py b/mobly/controllers/android_device_lib/snippet_client_v2.py index 3c29f097..5bbf54eb 100644 --- a/mobly/controllers/android_device_lib/snippet_client_v2.py +++ b/mobly/controllers/android_device_lib/snippet_client_v2.py @@ -200,7 +200,15 @@ def user_id(self): @property def identifier(self): - """The identifier of this snippet client.""" + """The unique identifier of this snippet client. + + This property serves as the singular key for the snippet client, ensuring + that every loaded snippet client possesses a distinct identifier. + + This identifier is constructed by combining the client's package name + with the user ID. The user ID is needed since it's allowed to load snippets + with the same package for different Android users. + """ return f'{self.package}:user_{self.user_id}' @property From a1bc22e8c0885064d2c050bb070f1157f11be6da Mon Sep 17 00:00:00 2001 From: Minghao Li Date: Thu, 11 Dec 2025 16:24:56 +0800 Subject: [PATCH 3/8] fix comments --- .../android_device_lib/services/snippet_management_service.py | 4 ++-- mobly/controllers/android_device_lib/snippet_client_v2.py | 2 +- .../controllers/android_device_lib/snippet_client_v2_test.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mobly/controllers/android_device_lib/services/snippet_management_service.py b/mobly/controllers/android_device_lib/services/snippet_management_service.py index 4165fe26..6e2d639c 100644 --- a/mobly/controllers/android_device_lib/services/snippet_management_service.py +++ b/mobly/controllers/android_device_lib/services/snippet_management_service.py @@ -69,8 +69,8 @@ def add_snippet_client(self, name, package, config=None): class for supported configurations. Raises: - Error: if a duplicated name is passed in, or the same snippet has been - registered. + Error: if a duplicated name is passed in, or a snippet instance with the + same identifier has already been registered. """ # Should not load snippet with the same name more than once. if name in self._snippet_clients: diff --git a/mobly/controllers/android_device_lib/snippet_client_v2.py b/mobly/controllers/android_device_lib/snippet_client_v2.py index 5bbf54eb..1532089e 100644 --- a/mobly/controllers/android_device_lib/snippet_client_v2.py +++ b/mobly/controllers/android_device_lib/snippet_client_v2.py @@ -209,7 +209,7 @@ def identifier(self): with the user ID. The user ID is needed since it's allowed to load snippets with the same package for different Android users. """ - return f'{self.package}:user_{self.user_id}' + return f'{self.package}@user_id[{self.user_id}]' @property def is_alive(self): diff --git a/tests/mobly/controllers/android_device_lib/snippet_client_v2_test.py b/tests/mobly/controllers/android_device_lib/snippet_client_v2_test.py index 6e42a363..f2e72215 100644 --- a/tests/mobly/controllers/android_device_lib/snippet_client_v2_test.py +++ b/tests/mobly/controllers/android_device_lib/snippet_client_v2_test.py @@ -1344,7 +1344,7 @@ def test_restore_event_client( errors.ServerRestoreConnectionError, ( 'Failed to restore server connection for snippet' - f' {MOCK_PACKAGE_NAME}:user_{MOCK_USER_ID} at host port' + f' {MOCK_PACKAGE_NAME}@user_id\[{MOCK_USER_ID}\] at host port' f' {host_port_3}, device port {MOCK_DEVICE_PORT}' ), ): From 8790d52d516b3221f03f376857e64275b9f83453 Mon Sep 17 00:00:00 2001 From: Minghao Li Date: Thu, 11 Dec 2025 16:41:11 +0800 Subject: [PATCH 4/8] fix comment --- .../android_device_lib/services/snippet_management_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobly/controllers/android_device_lib/services/snippet_management_service.py b/mobly/controllers/android_device_lib/services/snippet_management_service.py index 6e2d639c..8a793e49 100644 --- a/mobly/controllers/android_device_lib/services/snippet_management_service.py +++ b/mobly/controllers/android_device_lib/services/snippet_management_service.py @@ -69,8 +69,8 @@ def add_snippet_client(self, name, package, config=None): class for supported configurations. Raises: - Error: if a duplicated name is passed in, or a snippet instance with the - same identifier has already been registered. + Error: if a duplicated name is passed in, or the same package has + already been registered under the same Android user ID. """ # Should not load snippet with the same name more than once. if name in self._snippet_clients: From 7c49e007ef050eece33601baf99d4f39bb0b3095 Mon Sep 17 00:00:00 2001 From: Minghao Li Date: Thu, 11 Dec 2025 17:41:01 +0800 Subject: [PATCH 5/8] fix error messages --- .../services/snippet_management_service.py | 10 ++++++---- .../android_device_lib/snippet_client_v2.py | 6 +++--- .../services/snippet_management_service_test.py | 12 ++++++------ .../android_device_lib/snippet_client_v2_test.py | 4 ++-- tests/mobly/controllers/android_device_test.py | 14 +++++++++----- 5 files changed, 26 insertions(+), 20 deletions(-) diff --git a/mobly/controllers/android_device_lib/services/snippet_management_service.py b/mobly/controllers/android_device_lib/services/snippet_management_service.py index 8a793e49..b3a889cb 100644 --- a/mobly/controllers/android_device_lib/services/snippet_management_service.py +++ b/mobly/controllers/android_device_lib/services/snippet_management_service.py @@ -76,8 +76,9 @@ class for supported configurations. if name in self._snippet_clients: raise Error( self, - 'Name "%s" is already registered with snippet "%s", it cannot ' - 'be used again.' % (name, self._snippet_clients[name].identifier), + f'Name "{name}" is already registered with snippet' + f' "{self._snippet_clients[name].identifier}", the same name' + ' cannot be used again.', ) # Should not load snippets with the same identifier more than once. new_client = snippet_client_v2.SnippetClientV2( @@ -90,8 +91,9 @@ class for supported configurations. del new_client raise Error( self, - 'Snippet "%s" has already been loaded under name "%s".' - % (client.identifier, snippet_name), + f'Snippet "{client.package}" has already been registered for user' + f' id {client.user_id} under name "{name}". The same pacakge cannot' + ' be loaded again for the same user.', ) new_client.initialize() diff --git a/mobly/controllers/android_device_lib/snippet_client_v2.py b/mobly/controllers/android_device_lib/snippet_client_v2.py index 1532089e..675b0a5c 100644 --- a/mobly/controllers/android_device_lib/snippet_client_v2.py +++ b/mobly/controllers/android_device_lib/snippet_client_v2.py @@ -823,9 +823,9 @@ def restore_server_connection(self, port=None): raise errors.ServerRestoreConnectionError( self._device, ( - 'Failed to restore server connection for snippet' - f' {self.identifier} at host port {self.host_port}, device port' - f' {self.device_port}.' + 'Failed to restore server connection of the snippet package' + f' {self.package} for user id {self.user_id} at host port' + f' {self.host_port}, device port {self.device_port}.' ), ) from e diff --git a/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py b/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py index 3e0ce22a..a1e2840c 100755 --- a/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py +++ b/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py @@ -36,7 +36,7 @@ def __init__(self, package, ad, config=None): else ad.adb.current_user_id ) self.package = package - self.identifier = f'{self.package}:user_{self.user_id}' + self.identifier = f'{self.package}@user_id[{self.user_id}]' self.log = logging # Override abstract methods so this class can be instantiated. @@ -143,7 +143,7 @@ def test_add_snippet_client_dup_name(self, _): ) manager.add_snippet_client('foo', MOCK_PACKAGE) msg = ( - '.* Name "foo" is already registered with snippet ".*", it ' + '.* Name "foo" is already registered with snippet ".*", the same name ' 'cannot be used again.' ) with self.assertRaisesRegex(snippet_management_service.Error, msg): @@ -170,8 +170,8 @@ def test_add_snippet_client_dup_package_and_none_as_snippet_config(self): manager = snippet_management_service.SnippetManagementService(mock_device) manager.add_snippet_client('foo', MOCK_PACKAGE) msg = ( - f'Snippet "com.mock.package:user_{user_id}" has already been loaded' - ' under name "foo".' + f'Snippet "com.mock.package" has already been registered for user id' + f' {user_id} under name "foo"' ) with self.assertRaisesRegex(snippet_management_service.Error, msg): manager.add_snippet_client('bar', MOCK_PACKAGE) @@ -185,8 +185,8 @@ def test_add_snippet_client_dup_package_and_user_id(self): manager = snippet_management_service.SnippetManagementService(mock_device) manager.add_snippet_client('foo', MOCK_PACKAGE, config=config) msg = ( - f'Snippet "com.mock.package:user_{user_id}" has already been loaded' - ' under name "foo".' + f'Snippet "com.mock.package" has already been registered for user id' + f' {user_id} under name "foo"' ) with self.assertRaisesRegex(snippet_management_service.Error, msg): manager.add_snippet_client('bar', MOCK_PACKAGE, config=config) diff --git a/tests/mobly/controllers/android_device_lib/snippet_client_v2_test.py b/tests/mobly/controllers/android_device_lib/snippet_client_v2_test.py index f2e72215..7dc8a976 100644 --- a/tests/mobly/controllers/android_device_lib/snippet_client_v2_test.py +++ b/tests/mobly/controllers/android_device_lib/snippet_client_v2_test.py @@ -1343,8 +1343,8 @@ def test_restore_event_client( with self.assertRaisesRegex( errors.ServerRestoreConnectionError, ( - 'Failed to restore server connection for snippet' - f' {MOCK_PACKAGE_NAME}@user_id\[{MOCK_USER_ID}\] at host port' + 'Failed to restore server connection of the snippet package' + f' {MOCK_PACKAGE_NAME} for user id {MOCK_USER_ID} at host port' f' {host_port_3}, device port {MOCK_DEVICE_PORT}' ), ): diff --git a/tests/mobly/controllers/android_device_test.py b/tests/mobly/controllers/android_device_test.py index 8f59c96b..9a03473e 100755 --- a/tests/mobly/controllers/android_device_test.py +++ b/tests/mobly/controllers/android_device_test.py @@ -1556,15 +1556,19 @@ def test_AndroidDevice_load_snippet_dup_identifier( self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy ): ad = android_device.AndroidDevice(serial='1') - ad.adb.current_user_id = 1 - identifier = f'{MOCK_SNIPPET_PACKAGE_NAME}:user_1' - MockSnippetClient.return_value.identifier = identifier + user_id = 1 + ad.adb.current_user_id = user_id + MockSnippetClient.return_value.user_id = user_id + MockSnippetClient.return_value.identifier = ( + f'{MOCK_SNIPPET_PACKAGE_NAME}@user_id[{user_id}]' + ) MockSnippetClient.get_identifier.side_effect = ( - lambda pacakge, user_id: f'{pacakge}:user_{user_id}' + lambda pacakge, user_id: f'{pacakge}@user_id[{user_id}]' ) ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME) expected_msg = ( - f'Snippet "{identifier}" has already been loaded under name "snippet".' + f'Snippet "{MOCK_SNIPPET_PACKAGE_NAME}" has already been registered for' + f' user id {user_id}' ) with self.assertRaisesRegex(android_device.Error, expected_msg): ad.load_snippet('snippet2', MOCK_SNIPPET_PACKAGE_NAME) From a80455724f1a4881fb5986b25551ba6683c08d72 Mon Sep 17 00:00:00 2001 From: Minghao Li Date: Thu, 11 Dec 2025 17:45:19 +0800 Subject: [PATCH 6/8] fix ut --- .../services/snippet_management_service.py | 4 ++-- .../services/snippet_management_service_test.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mobly/controllers/android_device_lib/services/snippet_management_service.py b/mobly/controllers/android_device_lib/services/snippet_management_service.py index b3a889cb..9892ef87 100644 --- a/mobly/controllers/android_device_lib/services/snippet_management_service.py +++ b/mobly/controllers/android_device_lib/services/snippet_management_service.py @@ -92,8 +92,8 @@ class for supported configurations. raise Error( self, f'Snippet "{client.package}" has already been registered for user' - f' id {client.user_id} under name "{name}". The same pacakge cannot' - ' be loaded again for the same user.', + f' id {client.user_id} under name "{snippet_name}". The same' + ' pacakge cannot be registered again for the same user.', ) new_client.initialize() diff --git a/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py b/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py index a1e2840c..106f558b 100755 --- a/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py +++ b/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py @@ -171,7 +171,8 @@ def test_add_snippet_client_dup_package_and_none_as_snippet_config(self): manager.add_snippet_client('foo', MOCK_PACKAGE) msg = ( f'Snippet "com.mock.package" has already been registered for user id' - f' {user_id} under name "foo"' + f' {user_id} under name "foo". The same pacakge cannot be registered' + ' again for the same user.' ) with self.assertRaisesRegex(snippet_management_service.Error, msg): manager.add_snippet_client('bar', MOCK_PACKAGE) @@ -186,7 +187,8 @@ def test_add_snippet_client_dup_package_and_user_id(self): manager.add_snippet_client('foo', MOCK_PACKAGE, config=config) msg = ( f'Snippet "com.mock.package" has already been registered for user id' - f' {user_id} under name "foo"' + f' {user_id} under name "foo". The same pacakge cannot be registered' + ' again for the same user.' ) with self.assertRaisesRegex(snippet_management_service.Error, msg): manager.add_snippet_client('bar', MOCK_PACKAGE, config=config) From d2177b6c23ccafb6522d8dbf08ffcabe85174a17 Mon Sep 17 00:00:00 2001 From: Minghao Li Date: Wed, 17 Dec 2025 18:14:10 +0800 Subject: [PATCH 7/8] fix comments --- .../services/snippet_management_service.py | 21 ++++++++++--------- .../android_device_lib/snippet_client_v2.py | 16 ++++++++------ .../snippet_management_service_test.py | 10 ++++----- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/mobly/controllers/android_device_lib/services/snippet_management_service.py b/mobly/controllers/android_device_lib/services/snippet_management_service.py index 9892ef87..60ee8f14 100644 --- a/mobly/controllers/android_device_lib/services/snippet_management_service.py +++ b/mobly/controllers/android_device_lib/services/snippet_management_service.py @@ -76,8 +76,9 @@ class for supported configurations. if name in self._snippet_clients: raise Error( self, - f'Name "{name}" is already registered with snippet' - f' "{self._snippet_clients[name].identifier}", the same name' + f'Name "{name}" is already registered with package' + f' "{self._snippet_clients[name].package}" for user ID' + f' "{self._snippet_clients[name].user_id}", the same name' ' cannot be used again.', ) # Should not load snippets with the same identifier more than once. @@ -93,7 +94,7 @@ class for supported configurations. self, f'Snippet "{client.package}" has already been registered for user' f' id {client.user_id} under name "{snippet_name}". The same' - ' pacakge cannot be registered again for the same user.', + ' package cannot be registered again for the same user.', ) new_client.initialize() @@ -117,24 +118,24 @@ def start(self): """Starts all the snippet clients under management.""" for client in self._snippet_clients.values(): if not client.is_alive: - self._device.log.debug('Starting SnippetClient<%s>.', client.identifier) + self._device.log.debug('Starting SnippetClient<%s>.', str(client)) client.initialize() else: self._device.log.debug( 'Not startng SnippetClient<%s> because it is already alive.', - client.identifier, + str(client), ) def stop(self): """Stops all the snippet clients under management.""" for client in self._snippet_clients.values(): if client.is_alive: - self._device.log.debug('Stopping SnippetClient<%s>.', client.identifier) + self._device.log.debug('Stopping SnippetClient<%s>.', str(client)) client.stop() else: self._device.log.debug( 'Not stopping SnippetClient<%s> because it is not alive.', - client.identifier, + str(client), ) def pause(self): @@ -144,18 +145,18 @@ def pause(self): allocated in `resume`. """ for client in self._snippet_clients.values(): - self._device.log.debug('Pausing SnippetClient<%s>.', client.identifier) + self._device.log.debug('Pausing SnippetClient<%s>.', str(client)) client.close_connection() def resume(self): """Resumes all paused snippet clients.""" for client in self._snippet_clients.values(): if not client.is_alive: - self._device.log.debug('Resuming SnippetClient<%s>.', client.identifier) + self._device.log.debug('Resuming SnippetClient<%s>.', str(client)) client.restore_server_connection() else: self._device.log.debug( - 'Not resuming SnippetClient<%s>.', client.identifier + 'Not resuming SnippetClient<%s>.', str(client) ) def __getattr__(self, name): diff --git a/mobly/controllers/android_device_lib/snippet_client_v2.py b/mobly/controllers/android_device_lib/snippet_client_v2.py index 675b0a5c..5c816e51 100644 --- a/mobly/controllers/android_device_lib/snippet_client_v2.py +++ b/mobly/controllers/android_device_lib/snippet_client_v2.py @@ -216,6 +216,10 @@ def is_alive(self): """Does the client have an active connection to the snippet server.""" return self._conn is not None + def __repr__(self): + return self.identifier + + def before_starting_server(self): """Performs the preparation steps before starting the remote server. @@ -299,8 +303,8 @@ def start_server(self): """ persists_shell_cmd = self._get_persisting_command() self.log.debug( - 'Snippet server for identifier %s is using protocol %d.%d', - self.identifier, + 'Snippet server for %s is using protocol %d.%d', + str(self), _PROTOCOL_MAJOR_VERSION, _PROTOCOL_MINOR_VERSION, ) @@ -359,7 +363,7 @@ def _get_persisting_command(self): def _get_instrument_options_str(self): self.log.debug( 'Got am instrument options in snippet client "%s": %s', - self.identifier, + str(self), self._config.am_instrument_options, ) if not self._config.am_instrument_options: @@ -470,7 +474,7 @@ def create_socket_connection(self): self.log.debug( 'Snippet client is creating socket connection to the snippet server ' 'of %s through host port %d.', - self.identifier, + str(self), self.host_port, ) self._conn = socket.create_connection( @@ -712,11 +716,11 @@ def stop(self): android_device_lib_errors.DeviceError: if the server exited with errors on the device side. """ - self.log.debug('Stopping snippet client %s.', self.identifier) + self.log.debug('Stopping snippet client %s.', str(self)) self.close_connection() self._stop_server() self._destroy_event_client() - self.log.debug('Snippet client %s stopped.', self.identifier) + self.log.debug('Snippet client %s stopped.', str(self)) def close_connection(self): """Closes the connection to the snippet server on the device. diff --git a/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py b/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py index 106f558b..f2af7325 100755 --- a/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py +++ b/tests/mobly/controllers/android_device_lib/services/snippet_management_service_test.py @@ -143,14 +143,14 @@ def test_add_snippet_client_dup_name(self, _): ) manager.add_snippet_client('foo', MOCK_PACKAGE) msg = ( - '.* Name "foo" is already registered with snippet ".*", the same name ' - 'cannot be used again.' + '.* Name "foo" is already registered with package ".*" for user ID .*,' + ' the same name cannot be used again.' ) with self.assertRaisesRegex(snippet_management_service.Error, msg): manager.add_snippet_client('foo', MOCK_PACKAGE + 'ha') @mock.patch(SNIPPET_CLIENT_V2_CLASS_PATH, new=MockSnippetClientV2) - def test_add_snippet_client_different_pacakge(self): + def test_add_snippet_client_different_package(self): mock_device = mock.MagicMock() manager = snippet_management_service.SnippetManagementService(mock_device) manager.add_snippet_client('foo', MOCK_PACKAGE) @@ -171,7 +171,7 @@ def test_add_snippet_client_dup_package_and_none_as_snippet_config(self): manager.add_snippet_client('foo', MOCK_PACKAGE) msg = ( f'Snippet "com.mock.package" has already been registered for user id' - f' {user_id} under name "foo". The same pacakge cannot be registered' + f' {user_id} under name "foo". The same package cannot be registered' ' again for the same user.' ) with self.assertRaisesRegex(snippet_management_service.Error, msg): @@ -187,7 +187,7 @@ def test_add_snippet_client_dup_package_and_user_id(self): manager.add_snippet_client('foo', MOCK_PACKAGE, config=config) msg = ( f'Snippet "com.mock.package" has already been registered for user id' - f' {user_id} under name "foo". The same pacakge cannot be registered' + f' {user_id} under name "foo". The same package cannot be registered' ' again for the same user.' ) with self.assertRaisesRegex(snippet_management_service.Error, msg): From 99883e9a93af9343189a400a56fabd339508f824 Mon Sep 17 00:00:00 2001 From: Minghao Li Date: Wed, 17 Dec 2025 18:16:39 +0800 Subject: [PATCH 8/8] fix format --- .../services/snippet_management_service.py | 6 ++---- mobly/controllers/android_device_lib/snippet_client_v2.py | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/mobly/controllers/android_device_lib/services/snippet_management_service.py b/mobly/controllers/android_device_lib/services/snippet_management_service.py index 60ee8f14..2e1a604b 100644 --- a/mobly/controllers/android_device_lib/services/snippet_management_service.py +++ b/mobly/controllers/android_device_lib/services/snippet_management_service.py @@ -78,7 +78,7 @@ class for supported configurations. self, f'Name "{name}" is already registered with package' f' "{self._snippet_clients[name].package}" for user ID' - f' "{self._snippet_clients[name].user_id}", the same name' + f' {self._snippet_clients[name].user_id}, the same name' ' cannot be used again.', ) # Should not load snippets with the same identifier more than once. @@ -155,9 +155,7 @@ def resume(self): self._device.log.debug('Resuming SnippetClient<%s>.', str(client)) client.restore_server_connection() else: - self._device.log.debug( - 'Not resuming SnippetClient<%s>.', str(client) - ) + self._device.log.debug('Not resuming SnippetClient<%s>.', str(client)) def __getattr__(self, name): client = self.get_snippet_client(name) diff --git a/mobly/controllers/android_device_lib/snippet_client_v2.py b/mobly/controllers/android_device_lib/snippet_client_v2.py index 5c816e51..7963fd50 100644 --- a/mobly/controllers/android_device_lib/snippet_client_v2.py +++ b/mobly/controllers/android_device_lib/snippet_client_v2.py @@ -219,7 +219,6 @@ def is_alive(self): def __repr__(self): return self.identifier - def before_starting_server(self): """Performs the preparation steps before starting the remote server.