Uninstall apk if installed before re-installing
PiperOrigin-RevId: 575106519
diff --git a/ui_automator/android_device.py b/ui_automator/android_device.py
new file mode 100644
index 0000000..5d1f224
--- /dev/null
+++ b/ui_automator/android_device.py
@@ -0,0 +1,48 @@
+"""Android device related methods which extend from UI Automator.
+
+Android device class is defined in `mobly.controller`.
+"""
+
+from mobly import utils
+from mobly.controllers import android_device
+
+
+def is_apk_installed(
+ device: android_device.AndroidDevice, package_name: str
+) -> bool:
+ """Checks if given package is already installed on the Android device.
+
+ Args:
+ device: An AndroidDevice object.
+ package_name: The Android app package name.
+
+ Raises:
+ adb.AdbError: When executing adb command fails.
+
+ Returns:
+ True if package is installed. False otherwise.
+ """
+ out = device.adb.shell(['pm', 'list', 'package'])
+ return bool(utils.grep('^package:%s$' % package_name, out))
+
+
+def install_apk(device: android_device.AndroidDevice, apk_path: str) -> None:
+ """Installs required apk snippet to the given Android device.
+
+ Args:
+ device: An AndroidDevice object.
+ apk_path: The absolute file path where the apk is located.
+ """
+ device.adb.install(['-r', '-g', apk_path])
+
+
+def uninstall_apk(
+ device: android_device.AndroidDevice, package_name: str
+) -> None:
+ """Uninstalls required apk snippet on given Android device.
+
+ Args:
+ device: An AndroidDevice object.
+ package_name: The Android app package name.
+ """
+ device.adb.uninstall(package_name)
diff --git a/ui_automator/android_device_test.py b/ui_automator/android_device_test.py
new file mode 100644
index 0000000..9f1fd46
--- /dev/null
+++ b/ui_automator/android_device_test.py
@@ -0,0 +1,73 @@
+"""Unittest of android device extended methods."""
+import unittest
+from unittest import mock
+
+from mobly.controllers import android_device
+from mobly.controllers.android_device_lib import adb
+
+from ui_automator import android_device as ad
+
+
+class UIAutomatorTest(unittest.TestCase):
+
+ def setUp(self):
+ """This method will be run before each of the test methods in the class."""
+ super().setUp()
+ self.mock_android_device = mock.patch.object(
+ android_device, 'AndroidDevice'
+ ).start()
+
+ def test_is_apk_installed_returns_true_with_installed_apk(self):
+ self.mock_android_device.adb.shell.return_value = (
+ b'package:installed.apk\npackage:a.apk\npackage:b.apk\n'
+ )
+
+ is_apk_installed = ad.is_apk_installed(
+ self.mock_android_device, 'installed.apk'
+ )
+
+ self.assertTrue(is_apk_installed)
+
+ def test_is_apk_installed_returns_false_with_not_installed_apk(self):
+ self.mock_android_device.adb.shell.return_value = (
+ b'package:installed.apk\npackage:a.apk\npackage:b.apk\n'
+ )
+
+ is_apk_installed = ad.is_apk_installed(
+ self.mock_android_device, 'notinstalled.apk'
+ )
+
+ self.assertFalse(is_apk_installed)
+
+ def test_is_apk_installed_raises_an_error(self):
+ self.mock_android_device.adb.shell.side_effect = adb.AdbError(
+ cmd='adb.shell',
+ stderr=b'Run adb command failed.',
+ stdout=b'',
+ ret_code=1,
+ )
+
+ with self.assertRaisesRegex(adb.AdbError, r'Run adb command failed\.'):
+ ad.is_apk_installed(self.mock_android_device, 'fake.apk')
+
+ def test_install_apk_installs_apk_with_correct_path(self):
+ apk_path = '/path/to/fake.apk'
+
+ ad.install_apk(self.mock_android_device, apk_path)
+
+ self.mock_android_device.adb.install.assert_called_once_with(
+ ['-r', '-g', apk_path]
+ )
+
+ def test_uninstall_apk_uninstalls_apk_with_given_package_name(self):
+ package_to_be_uninstalled = 'notinstalled.apk'
+
+ ad.uninstall_apk(self.mock_android_device, package_to_be_uninstalled)
+
+ self.mock_android_device.adb.uninstall.assert_called_once_with(
+ package_to_be_uninstalled
+ )
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/ui_automator/ui_automator.py b/ui_automator/ui_automator.py
index fef9fea..63343c6 100644
--- a/ui_automator/ui_automator.py
+++ b/ui_automator/ui_automator.py
@@ -26,10 +26,10 @@
from absl import app
from absl import flags
import inflection
-from mobly import utils
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 errors
@@ -184,8 +184,10 @@
'No Android device connected to the host computer.'
)
- if not self._is_apk_installed(self._connected_device, _MOBLY_SNIPPET_APK):
- self._install_apk(self._connected_device, self._get_mbs_apk_path())
+ 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())
try:
self._connected_device.load_snippet(
@@ -255,35 +257,6 @@
f' device({self._connected_device.device_info["serial"]}).'
) from e
- def _is_apk_installed(
- self, device: android_device.AndroidDevice, package_name: str
- ) -> bool:
- """Checks if given package is already installed on the Android device.
-
- Args:
- device: An AndroidDevice object.
- package_name: The Android app package name.
-
- Raises:
- adb.AdbError: When executing adb command fails.
-
- Returns:
- True if package is installed. False otherwise.
- """
- out = device.adb.shell(['pm', 'list', 'package'])
- return bool(utils.grep('^package:%s$' % package_name, out))
-
- def _install_apk(
- self, device: android_device.AndroidDevice, apk_path: str
- ) -> None:
- """Installs required apk snippet to the given Android device.
-
- Args:
- device: An AndroidDevice object.
- apk_path: The absolute file path where the apk is located.
- """
- device.adb.install(['-r', '-g', apk_path])
-
def _get_mbs_apk_path(self) -> str:
return os.path.join(
os.path.dirname(os.path.abspath(__file__)),
diff --git a/ui_automator/ui_automator_test.py b/ui_automator/ui_automator_test.py
index 74617a2..707264a 100644
--- a/ui_automator/ui_automator_test.py
+++ b/ui_automator/ui_automator_test.py
@@ -184,11 +184,14 @@
self.mock_android_device.adb.install.assert_called_once_with(
['-r', '-g', '/path/to/android/app/snippet-0.0.0.apk']
)
+ self.mock_android_device.adb.uninstall.assert_not_called()
mock_dirname.assert_called_once()
@mock.patch.object(android_device, 'get_all_instances', autospec=True)
- @mock.patch.object(os.path, 'dirname', autospec=True)
- def test_load_snippet_should_not_install_apk_when_apk_is_installed(
+ @mock.patch.object(
+ os.path, 'dirname', autospec=True, return_value='/path/to/'
+ )
+ def test_load_snippet_uninstalls_apk_before_installing_it_when_installed(
self, mock_dirname, mock_get_all_instances
):
self.mock_android_device.adb.shell.return_value = (
@@ -199,8 +202,13 @@
self.ui_automator.load_snippet()
- self.mock_android_device.adb.install.assert_not_called()
- mock_dirname.assert_not_called()
+ self.mock_android_device.adb.uninstall.assert_called_with(
+ 'com.chip.interop.moblysnippet'
+ )
+ self.mock_android_device.adb.install.assert_called_once_with(
+ ['-r', '-g', '/path/to/android/app/snippet-0.0.0.apk']
+ )
+ mock_dirname.assert_called_once()
@mock.patch.object(ui_automator.UIAutomator, 'load_device')
@mock.patch.object(ui_automator.UIAutomator, 'load_snippet')