Check apk version to decide whether to uninstall it PiperOrigin-RevId: 579057715
diff --git a/ui_automator/android_device.py b/ui_automator/android_device.py index 5d1f224..c517377 100644 --- a/ui_automator/android_device.py +++ b/ui_automator/android_device.py
@@ -46,3 +46,28 @@ package_name: The Android app package name. """ device.adb.uninstall(package_name) + + +def is_apk_version_correct( + device: android_device.AndroidDevice, package_name: str, version: str +) -> bool: + """Checks if the version of package is given version on the Android device. + + Args: + device: An AndroidDevice object. + package_name: The Android app package name. + version: The specific version of app package. + + Returns: + True if the version of package matches given version. False otherwise. + + Raises: + adb.AdbError: When executing adb command fails. + """ + grep_version_name = utils.grep( + 'versionName', device.adb.shell(['dumpsys', 'package', package_name]) + ) + + return ( + bool(grep_version_name) and grep_version_name[0].split('=')[1] == version + )
diff --git a/ui_automator/android_device_test.py b/ui_automator/android_device_test.py index 9f1fd46..595f680 100644 --- a/ui_automator/android_device_test.py +++ b/ui_automator/android_device_test.py
@@ -1,4 +1,5 @@ """Unittest of android device extended methods.""" + import unittest from unittest import mock @@ -68,6 +69,35 @@ package_to_be_uninstalled ) + def test_is_apk_version_correct_returns_true(self): + self.mock_android_device.adb.shell.return_value = b'versionName=1.2.3\n' + + self.assertTrue( + ad.is_apk_version_correct( + self.mock_android_device, 'installed.apk', '1.2.3' + ) + ) + + def test_is_apk_version_correct_returns_false_with_not_installed_apk(self): + self.mock_android_device.adb.shell.return_value = ( + b'Unable to find package\n' + ) + + self.assertFalse( + ad.is_apk_version_correct( + self.mock_android_device, 'notinstalled.apk', '1.2.3' + ) + ) + + def test_is_apk_version_correct_returns_false_with_incorrect_version(self): + self.mock_android_device.adb.shell.return_value = b'versionName=1.2.4\n' + + self.assertFalse( + ad.is_apk_version_correct( + self.mock_android_device, 'installed.apk', '1.2.3' + ) + ) + if __name__ == '__main__': unittest.main()
diff --git a/ui_automator/ui_automator.py b/ui_automator/ui_automator.py index bdea2d1..a9edc5b 100644 --- a/ui_automator/ui_automator.py +++ b/ui_automator/ui_automator.py
@@ -29,9 +29,11 @@ from mobly.controllers import android_device from mobly.controllers.android_device_lib import adb from mobly.snippet import errors as snippet_errors -from ui_automator import android_device as ad +from ui_automator import android_device as ad from ui_automator import errors +from ui_automator import version + _GOOGLE_HOME_APP = { 'id': 'gha', @@ -184,10 +186,17 @@ 'No Android device connected to the host computer.' ) - if ad.is_apk_installed(self._connected_device, _MOBLY_SNIPPET_APK): - ad.uninstall_apk(self._connected_device, _MOBLY_SNIPPET_APK) - - ad.install_apk(self._connected_device, self._get_mbs_apk_path()) + is_apk_installed = ad.is_apk_installed( + self._connected_device, _MOBLY_SNIPPET_APK + ) + if not is_apk_installed or not ad.is_apk_version_correct( + self._connected_device, + _MOBLY_SNIPPET_APK, + version.VERSION, + ): + if is_apk_installed: + ad.uninstall_apk(self._connected_device, _MOBLY_SNIPPET_APK) + ad.install_apk(self._connected_device, self._get_mbs_apk_path()) try: self._connected_device.load_snippet(
diff --git a/ui_automator/ui_automator_test.py b/ui_automator/ui_automator_test.py index 8063411..4de7691 100644 --- a/ui_automator/ui_automator_test.py +++ b/ui_automator/ui_automator_test.py
@@ -25,6 +25,7 @@ from ui_automator import errors from ui_automator import ui_automator +from ui_automator import version _FAKE_MATTER_DEVICE_NAME = 'fake-matter-device-name' _FAKE_GHA_ROOM = 'Office' @@ -191,12 +192,33 @@ @mock.patch.object( os.path, 'dirname', autospec=True, return_value='/path/to/' ) - def test_load_snippet_uninstalls_apk_before_installing_it_when_installed( + def test_load_snippet_should_not_install_apk_when_correct_apk_installed( self, mock_dirname, mock_get_all_instances ): - self.mock_android_device.adb.shell.return_value = ( - b'package:com.chip.interop.moblysnippet' - ) + self.mock_android_device.adb.shell.side_effect = [ + b'package:com.chip.interop.moblysnippet\n', + f'versionName={version.VERSION}'.encode('utf-8'), + ] + mock_get_all_instances.return_value = [self.mock_android_device] + self.ui_automator.load_device() + + self.ui_automator.load_snippet() + + self.mock_android_device.adb.install.assert_not_called() + self.mock_android_device.adb.uninstall.assert_not_called() + mock_dirname.assert_not_called() + + @mock.patch.object(android_device, 'get_all_instances', autospec=True) + @mock.patch.object( + os.path, 'dirname', autospec=True, return_value='/path/to/' + ) + def test_load_snippet_uninstalls_apk_before_installing_it_with_incorrect_apk( + self, mock_dirname, mock_get_all_instances + ): + self.mock_android_device.adb.shell.side_effect = [ + b'package:com.chip.interop.moblysnippet\n', + b'versionName=fake.version', + ] mock_get_all_instances.return_value = [self.mock_android_device] self.ui_automator.load_device() @@ -210,6 +232,28 @@ ) mock_dirname.assert_called_once() + @mock.patch.object(android_device, 'get_all_instances', autospec=True) + @mock.patch.object( + os.path, 'dirname', autospec=True, return_value='/path/to/' + ) + def test_load_snippet_installs_apk_when_no_apk_installed( + self, mock_dirname, mock_get_all_instances + ): + self.mock_android_device.adb.shell.side_effect = [ + b'package:installed.apk\n', + b'Unable to find package\n', + ] + mock_get_all_instances.return_value = [self.mock_android_device] + self.ui_automator.load_device() + + self.ui_automator.load_snippet() + + self.mock_android_device.adb.uninstall.assert_not_called() + self.mock_android_device.adb.install.assert_called_once_with( + ['-r', '-g', '/path/to/android/app/snippet-0.1.0.apk'] + ) + mock_dirname.assert_called_once() + @mock.patch.object(ui_automator.UIAutomator, 'load_device') @mock.patch.object(ui_automator.UIAutomator, 'load_snippet') def test_get_android_device_ready_raises_an_error_when_load_device_throws_an_error(