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(