Extend build info (#599)
diff --git a/mobly/controllers/android_device.py b/mobly/controllers/android_device.py
index 386603a..75d8cf3 100644
--- a/mobly/controllers/android_device.py
+++ b/mobly/controllers/android_device.py
@@ -716,9 +716,23 @@
'info.')
return
info = {}
- build_info = self.adb.getprops(['ro.build.id', 'ro.build.type'])
+ build_info = self.adb.getprops([
+ 'ro.build.id',
+ 'ro.build.type',
+ 'ro.build.version.codename',
+ 'ro.build.version.sdk',
+ 'ro.build.product',
+ 'ro.debuggable',
+ 'ro.product.name',
+ ])
info['build_id'] = build_info['ro.build.id']
info['build_type'] = build_info['ro.build.type']
+ info['build_version_codename'] = build_info.get(
+ 'ro.build.version.codename', '')
+ info['build_version_sdk'] = build_info.get('ro.build.version.sdk', '')
+ info['build_product'] = build_info.get('ro.build.product', '')
+ info['debuggable'] = build_info.get('ro.debuggable', '')
+ info['product_name'] = build_info.get('ro.product.name', '')
return info
@property
@@ -740,7 +754,7 @@
@property
def is_rootable(self):
- return self.adb.getprop('ro.debuggable') == '1'
+ return self.build_info['debuggable'] == '1'
@property
def model(self):
@@ -757,10 +771,10 @@
if len(tokens) > 1:
return tokens[1].lower()
return None
- model = self.adb.getprop('ro.build.product').lower()
+ model = self.build_info['build_product'].lower()
if model == 'sprout':
return model
- return self.adb.getprop('ro.product.name').lower()
+ return self.build_info['product_name'].lower()
def load_config(self, config):
"""Add attributes to the AndroidDevice object based on config.
diff --git a/mobly/controllers/android_device_lib/jsonrpc_client_base.py b/mobly/controllers/android_device_lib/jsonrpc_client_base.py
index 1cd6840..abe0bbb 100644
--- a/mobly/controllers/android_device_lib/jsonrpc_client_base.py
+++ b/mobly/controllers/android_device_lib/jsonrpc_client_base.py
@@ -327,8 +327,8 @@
def disable_hidden_api_blacklist(self):
"""If necessary and possible, disables hidden api blacklist."""
- version_codename = self._ad.adb.getprop('ro.build.version.codename')
- sdk_version = int(self._ad.adb.getprop('ro.build.version.sdk'))
+ version_codename = self._ad.build_info['build_version_codename']
+ sdk_version = int(self._ad.build_info['build_version_sdk'])
# we check version_codename in addition to sdk_version because P builds
# in development report sdk_version 27, but still enforce the blacklist.
if self._ad.is_rootable and (sdk_version >= 28
diff --git a/tests/lib/mock_android_device.py b/tests/lib/mock_android_device.py
index dd1ae15..57527b4 100755
--- a/tests/lib/mock_android_device.py
+++ b/tests/lib/mock_android_device.py
@@ -28,6 +28,7 @@
'ro.build.version.codename': 'Z',
'ro.build.version.sdk': '28',
'ro.product.name': 'FakeModel',
+ 'ro.debuggable': '1',
'sys.boot_completed': "1",
}
diff --git a/tests/mobly/controllers/android_device_lib/services/logcat_test.py b/tests/mobly/controllers/android_device_lib/services/logcat_test.py
index 10e6cf3..b7467fd 100755
--- a/tests/mobly/controllers/android_device_lib/services/logcat_test.py
+++ b/tests/mobly/controllers/android_device_lib/services/logcat_test.py
@@ -57,11 +57,11 @@
# Mock AdbError for missing logpersist scripts
MOCK_LOGPERSIST_STOP_MISSING_ADB_ERROR = adb.AdbError(
- 'logpersist.stop --clear', '',
+ 'logpersist.stop --clear', b'',
'/system/bin/sh: logpersist.stop: not found', 0)
MOCK_LOGPERSIST_START_MISSING_ADB_ERROR = adb.AdbError(
- 'logpersist.start --clear', '',
- '/system/bin/sh: logpersist.stop: not found', 0)
+ 'logpersist.start --clear', b'',
+ b'/system/bin/sh: logpersist.stop: not found', 0)
class LogcatTest(unittest.TestCase):
@@ -367,8 +367,11 @@
MockAdbProxy):
mock_serial = '1'
mock_adb_proxy = MockAdbProxy.return_value
- # Set getprop to return '1' to indicate the device is rootable.
- mock_adb_proxy.getprop.return_value = '1'
+ mock_adb_proxy.getprops.return_value = {
+ 'ro.build.id': 'AB42',
+ 'ro.build.type': 'userdebug',
+ 'ro.debuggable': '1',
+ }
mock_adb_proxy.has_shell_command.side_effect = lambda command: {
'logpersist.start': True,
'logpersist.stop': True, }[command]
@@ -386,6 +389,29 @@
@mock.patch(
'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
return_value=mock_android_device.MockFastbootProxy('1'))
+ def test__enable_logpersist_with_user_build_device(self, MockFastboot,
+ MockAdbProxy):
+ mock_serial = '1'
+ mock_adb_proxy = MockAdbProxy.return_value
+ mock_adb_proxy.getprops.return_value = {
+ 'ro.build.id': 'AB42',
+ 'ro.build.type': 'user',
+ 'ro.debuggable': '0',
+ }
+ mock_adb_proxy.has_shell_command.side_effect = lambda command: {
+ 'logpersist.start': True,
+ 'logpersist.stop': True, }[command]
+ ad = android_device.AndroidDevice(serial=mock_serial)
+ logcat_service = logcat.Logcat(ad)
+ logcat_service._enable_logpersist()
+ mock_adb_proxy.shell.assert_not_called()
+
+ @mock.patch(
+ 'mobly.controllers.android_device_lib.adb.AdbProxy',
+ return_value=mock.MagicMock())
+ @mock.patch(
+ 'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
+ return_value=mock_android_device.MockFastbootProxy('1'))
def test__enable_logpersist_with_missing_all_logpersist(
self, MockFastboot, MockAdbProxy):
def adb_shell_helper(command):
@@ -394,11 +420,15 @@
elif command == 'logpersist.stop --clear':
raise MOCK_LOGPERSIST_STOP_MISSING_ADB_ERROR
else:
- return ''
+ return b''
mock_serial = '1'
mock_adb_proxy = MockAdbProxy.return_value
- mock_adb_proxy.getprop.return_value = 'userdebug'
+ mock_adb_proxy.getprops.return_value = {
+ 'ro.build.id': 'AB42',
+ 'ro.build.type': 'userdebug',
+ 'ro.debuggable': '1',
+ }
mock_adb_proxy.has_shell_command.side_effect = lambda command: {
'logpersist.start': False,
'logpersist.stop': False, }[command]
@@ -406,6 +436,7 @@
ad = android_device.AndroidDevice(serial=mock_serial)
logcat_service = logcat.Logcat(ad)
logcat_service._enable_logpersist()
+ mock_adb_proxy.shell.assert_not_called()
@mock.patch(
'mobly.controllers.android_device_lib.adb.AdbProxy',
@@ -419,11 +450,15 @@
if command == 'logpersist.stop --clear':
raise MOCK_LOGPERSIST_STOP_MISSING_ADB_ERROR
else:
- return ''
+ return b''
mock_serial = '1'
mock_adb_proxy = MockAdbProxy.return_value
- mock_adb_proxy.getprop.return_value = 'userdebug'
+ mock_adb_proxy.getprops.return_value = {
+ 'ro.build.id': 'AB42',
+ 'ro.build.type': 'userdebug',
+ 'ro.debuggable': '1',
+ }
mock_adb_proxy.has_shell_command.side_effect = lambda command: {
'logpersist.start': True,
'logpersist.stop': False, }[command]
@@ -431,6 +466,9 @@
ad = android_device.AndroidDevice(serial=mock_serial)
logcat_service = logcat.Logcat(ad)
logcat_service._enable_logpersist()
+ mock_adb_proxy.shell.assert_has_calls([
+ mock.call('logpersist.stop --clear'),
+ ])
@mock.patch(
'mobly.controllers.android_device_lib.adb.AdbProxy',
@@ -442,11 +480,15 @@
if command == 'logpersist.start':
raise MOCK_LOGPERSIST_START_MISSING_ADB_ERROR
else:
- return ''
+ return b''
mock_serial = '1'
mock_adb_proxy = MockAdbProxy.return_value
- mock_adb_proxy.getprop.return_value = 'userdebug'
+ mock_adb_proxy.getprops.return_value = {
+ 'ro.build.id': 'AB42',
+ 'ro.build.type': 'userdebug',
+ 'ro.debuggable': '1',
+ }
mock_adb_proxy.has_shell_command.side_effect = lambda command: {
'logpersist.start': False,
'logpersist.stop': True, }[command]
@@ -454,6 +496,7 @@
ad = android_device.AndroidDevice(serial=mock_serial)
logcat_service = logcat.Logcat(ad)
logcat_service._enable_logpersist()
+ mock_adb_proxy.shell.assert_not_called()
@mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy')
@mock.patch(
diff --git a/tests/mobly/controllers/android_device_lib/sl4a_client_test.py b/tests/mobly/controllers/android_device_lib/sl4a_client_test.py
index e3db6a4..e786b03 100755
--- a/tests/mobly/controllers/android_device_lib/sl4a_client_test.py
+++ b/tests/mobly/controllers/android_device_lib/sl4a_client_test.py
@@ -66,6 +66,11 @@
installed_packages=['com.googlecode.android_scripting'])
ad = mock.Mock()
ad.adb = adb_proxy
+ ad.build_info = {
+ 'build_version_codename':
+ ad.adb.getprop('ro.build.version.codename'),
+ 'build_version_sdk': ad.adb.getprop('ro.build.version.sdk'),
+ }
return sl4a_client.Sl4aClient(ad=ad)
def _setup_mock_instrumentation_cmd(self, mock_start_standing_subprocess,
diff --git a/tests/mobly/controllers/android_device_lib/snippet_client_test.py b/tests/mobly/controllers/android_device_lib/snippet_client_test.py
index 8b12a9f..d60ee46 100755
--- a/tests/mobly/controllers/android_device_lib/snippet_client_test.py
+++ b/tests/mobly/controllers/android_device_lib/snippet_client_test.py
@@ -452,6 +452,11 @@
MOCK_PACKAGE_NAME)])
ad = mock.Mock()
ad.adb = adb_proxy
+ ad.build_info = {
+ 'build_version_codename':
+ ad.adb.getprop('ro.build.version.codename'),
+ 'build_version_sdk': ad.adb.getprop('ro.build.version.sdk'),
+ }
return snippet_client.SnippetClient(package=MOCK_PACKAGE_NAME, ad=ad)
def _setup_mock_instrumentation_cmd(self, mock_start_standing_subprocess,
diff --git a/tests/mobly/controllers/android_device_test.py b/tests/mobly/controllers/android_device_test.py
index 03fcd3a..ae81ae1 100755
--- a/tests/mobly/controllers/android_device_test.py
+++ b/tests/mobly/controllers/android_device_test.py
@@ -272,6 +272,68 @@
build_info = ad.build_info
self.assertEqual(build_info['build_id'], 'AB42')
self.assertEqual(build_info['build_type'], 'userdebug')
+ self.assertEqual(build_info['build_version_codename'], 'Z')
+ self.assertEqual(build_info['build_version_sdk'], '28')
+ self.assertEqual(build_info['build_product'], 'FakeModel')
+ self.assertEqual(build_info['product_name'], 'FakeModel')
+ self.assertEqual(build_info['debuggable'], '1')
+
+ @mock.patch(
+ 'mobly.controllers.android_device_lib.adb.AdbProxy',
+ return_value=mock_android_device.MockAdbProxy(
+ '1',
+ mock_properties={
+ 'ro.build.id': 'AB42',
+ 'ro.build.type': 'userdebug',
+ }))
+ @mock.patch(
+ 'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
+ return_value=mock_android_device.MockFastbootProxy('1'))
+ def test_AndroidDevice_build_info_with_minimal_properties(
+ self, MockFastboot, MockAdbProxy):
+ ad = android_device.AndroidDevice(serial='1')
+ build_info = ad.build_info
+ self.assertEqual(build_info['build_id'], 'AB42')
+ self.assertEqual(build_info['build_type'], 'userdebug')
+ self.assertEqual(build_info['build_version_codename'], '')
+ self.assertEqual(build_info['build_version_sdk'], '')
+ self.assertEqual(build_info['build_product'], '')
+ self.assertEqual(build_info['product_name'], '')
+ self.assertEqual(build_info['debuggable'], '')
+
+ @mock.patch(
+ 'mobly.controllers.android_device_lib.adb.AdbProxy',
+ return_value=mock_android_device.MockAdbProxy(
+ '1',
+ mock_properties={
+ 'ro.build.id': 'AB42',
+ 'ro.build.type': 'userdebug',
+ 'ro.debuggable': '1',
+ }))
+ @mock.patch(
+ 'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
+ return_value=mock_android_device.MockFastbootProxy('1'))
+ def test_AndroidDevice_is_rootable_when_userdebug_device(
+ self, MockFastboot, MockAdbProxy):
+ ad = android_device.AndroidDevice(serial='1')
+ self.assertTrue(ad.is_rootable)
+
+ @mock.patch(
+ 'mobly.controllers.android_device_lib.adb.AdbProxy',
+ return_value=mock_android_device.MockAdbProxy(
+ '1',
+ mock_properties={
+ 'ro.build.id': 'AB42',
+ 'ro.build.type': 'user',
+ 'ro.debuggable': '0',
+ }))
+ @mock.patch(
+ 'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
+ return_value=mock_android_device.MockFastbootProxy('1'))
+ def test_AndroidDevice_is_rootable_when_user_device(
+ self, MockFastboot, MockAdbProxy):
+ ad = android_device.AndroidDevice(serial='1')
+ self.assertFalse(ad.is_rootable)
@mock.patch(
'mobly.controllers.android_device_lib.adb.AdbProxy',
@@ -904,9 +966,13 @@
return_value=mock_android_device.MockFastbootProxy('1'))
@mock.patch(
'mobly.controllers.android_device.AndroidDevice.is_boot_completed',
- side_effect=[False, False, adb.AdbTimeoutError(
- ['adb', 'shell', 'getprop sys.boot_completed'],
- timeout=5, serial=1), True])
+ side_effect=[
+ False, False,
+ adb.AdbTimeoutError(
+ ['adb', 'shell', 'getprop sys.boot_completed'],
+ timeout=5,
+ serial=1), True
+ ])
@mock.patch('time.sleep', return_value=None)
@mock.patch('time.time', side_effect=[0, 5, 10, 15, 20, 25, 30])
def test_AndroidDevice_wait_for_completion_completed(
@@ -918,7 +984,10 @@
ad.wait_for_boot_completion()
except (adb.AdbError, adb.AdbTimeoutError):
raised = True
- self.assertFalse(raised, 'adb.AdbError or adb.AdbTimeoutError exception raised but not handled.')
+ self.assertFalse(
+ raised,
+ 'adb.AdbError or adb.AdbTimeoutError exception raised but not handled.'
+ )
@mock.patch(
'mobly.controllers.android_device_lib.adb.AdbProxy',
@@ -928,9 +997,13 @@
return_value=mock_android_device.MockFastbootProxy('1'))
@mock.patch(
'mobly.controllers.android_device.AndroidDevice.is_boot_completed',
- side_effect=[False, False, adb.AdbTimeoutError(
- ['adb', 'shell', 'getprop sys.boot_completed'],
- timeout=5, serial=1), False, False, False, False])
+ side_effect=[
+ False, False,
+ adb.AdbTimeoutError(
+ ['adb', 'shell', 'getprop sys.boot_completed'],
+ timeout=5,
+ serial=1), False, False, False, False
+ ])
@mock.patch('time.sleep', return_value=None)
@mock.patch('time.time', side_effect=[0, 5, 10, 15, 20, 25, 30])
def test_AndroidDevice_wait_for_completion_never_boot(
@@ -943,7 +1016,10 @@
ad.wait_for_boot_completion(timeout=20)
except (adb.AdbError, adb.AdbTimeoutError):
raised = True
- self.assertFalse(raised, 'adb.AdbError or adb.AdbTimeoutError exception raised but not handled.')
+ self.assertFalse(
+ raised,
+ 'adb.AdbError or adb.AdbTimeoutError exception raised but not handled.'
+ )
if __name__ == '__main__':