Rewrite logcat's create_output_excerpts to avoid dangling excerpts. (#658)
diff --git a/mobly/controllers/android_device_lib/services/logcat.py b/mobly/controllers/android_device_lib/services/logcat.py
index 2582750..0d3c94d 100644
--- a/mobly/controllers/android_device_lib/services/logcat.py
+++ b/mobly/controllers/android_device_lib/services/logcat.py
@@ -11,11 +11,10 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-import copy
import io
import logging
import os
-import shutil
+import time
from mobly import logger as mobly_logger
from mobly import utils
@@ -23,6 +22,8 @@
from mobly.controllers.android_device_lib import errors
from mobly.controllers.android_device_lib.services import base_service
+CREATE_LOGCAT_FILE_TIMEOUT_SEC = 5
+
class Error(errors.ServiceError):
"""Root error type for logcat service."""
@@ -62,6 +63,7 @@
super(Logcat, self).__init__(android_device, configs)
self._ad = android_device
self._adb_logcat_process = None
+ self._adb_logcat_file_obj = None
self.adb_logcat_file_path = None
# Logcat service uses a single config obj, using singular internal
# name: `_config`.
@@ -104,12 +106,12 @@
.. deprecated:: 1.10
Use :func:`create_output_excerpts` instead.
+ This copies logcat lines from self.adb_logcat_file_path to an excerpt
+ file, starting from the location where the previous excerpt ended.
+
To use this feature, call this method at the end of: `setup_class`,
`teardown_test`, and `teardown_class`.
- This moves the current content of `self.adb_logcat_file_path` to the
- log directory specific to the current test.
-
Args:
current_test_info: `self.current_test_info` in a Mobly test.
"""
@@ -118,8 +120,8 @@
def create_output_excerpts(self, test_info):
"""Convenient method for creating excerpts of adb logcat.
- This moves the current content of `self.adb_logcat_file_path` to the
- log directory specific to the current test.
+ This copies logcat lines from self.adb_logcat_file_path to an excerpt
+ file, starting from the location where the previous excerpt ended.
Call this method at the end of: `setup_class`, `teardown_test`, and
`teardown_class`.
@@ -130,15 +132,19 @@
Returns:
List of strings, the absolute paths to excerpt files.
"""
- self.pause()
dest_path = test_info.output_path
utils.create_dir(dest_path)
filename = self._ad.generate_filename(self.OUTPUT_FILE_TYPE, test_info,
'txt')
excerpt_file_path = os.path.join(dest_path, filename)
- shutil.move(self.adb_logcat_file_path, excerpt_file_path)
+ with io.open(excerpt_file_path, 'w', encoding='utf-8',
+ errors='replace') as out:
+ while True:
+ line = self._adb_logcat_file_obj.readline()
+ if not line:
+ break
+ out.write(line)
self._ad.log.debug('logcat excerpt created at: %s', excerpt_file_path)
- self.resume()
return [excerpt_file_path]
@property
@@ -239,6 +245,30 @@
self._config, new_config)
self._config = new_config
+ def _open_logcat_file(self):
+ """Create a file object that points to the beginning of the logcat file.
+ Wait for the logcat file to be created by the subprocess if it doesn't
+ exist.
+ """
+ if not self._adb_logcat_file_obj:
+ start_time = time.time()
+ while not os.path.exists(self.adb_logcat_file_path):
+ if time.time() > start_time + CREATE_LOGCAT_FILE_TIMEOUT_SEC:
+ raise Error(
+ self._ad,
+ 'Timeout while waiting for logcat file to be created.')
+ time.sleep(1)
+ self._adb_logcat_file_obj = io.open(
+ self.adb_logcat_file_path, 'r', encoding='utf-8',
+ errors='replace')
+ self._adb_logcat_file_obj.seek(0, os.SEEK_END)
+
+ def _close_logcat_file(self):
+ """Closes and resets the logcat file object, if it exists."""
+ if self._adb_logcat_file_obj:
+ self._adb_logcat_file_obj.close()
+ self._adb_logcat_file_obj = None
+
def start(self):
"""Starts a standing adb logcat collection.
@@ -248,25 +278,33 @@
if self._config.clear_log:
self.clear_adb_log()
self._start()
+ self._open_logcat_file()
def _start(self):
"""The actual logic of starting logcat."""
self._enable_logpersist()
- logcat_file_path = self._config.output_file_path
- if not logcat_file_path:
+ if self._config.output_file_path:
+ self._close_logcat_file()
+ self.adb_logcat_file_path = self._config.output_file_path
+ if not self.adb_logcat_file_path:
f_name = self._ad.generate_filename(self.OUTPUT_FILE_TYPE,
extension_name='txt')
logcat_file_path = os.path.join(self._ad.log_path, f_name)
- utils.create_dir(os.path.dirname(logcat_file_path))
- cmd = '"%s" -s %s logcat -v threadtime %s >> "%s"' % (
+ self.adb_logcat_file_path = logcat_file_path
+ utils.create_dir(os.path.dirname(self.adb_logcat_file_path))
+ cmd = '"%s" -s %s logcat -v threadtime -T 1 %s >> "%s"' % (
adb.ADB, self._ad.serial, self._config.logcat_params,
- logcat_file_path)
+ self.adb_logcat_file_path)
process = utils.start_standing_subprocess(cmd, shell=True)
self._adb_logcat_process = process
- self.adb_logcat_file_path = logcat_file_path
def stop(self):
"""Stops the adb logcat service."""
+ self._close_logcat_file()
+ self._stop()
+
+ def _stop(self):
+ """Stops the background process for logcat."""
if not self._adb_logcat_process:
return
try:
@@ -281,17 +319,8 @@
Note: the service is unable to collect the logs when paused, if more
logs are generated on the device than the device's log buffer can hold,
some logs would be lost.
-
- Clears cached adb content, so that when the service resumes, we don't
- duplicate what's in the device's log buffer already. This helps
- situations like USB off.
"""
- self.stop()
- # Clears cached adb content, so that the next time logcat is started,
- # we won't produce duplicated logs to log file.
- # This helps disconnection that caused by, e.g., USB off; at the
- # cost of losing logs at disconnection caused by reboot.
- self.clear_adb_log()
+ self._stop()
def resume(self):
"""Resumes a paused logcat service."""
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 7b2217d..f394239 100755
--- a/tests/mobly/controllers/android_device_lib/services/logcat_test.py
+++ b/tests/mobly/controllers/android_device_lib/services/logcat_test.py
@@ -97,10 +97,11 @@
@mock.patch('mobly.utils.start_standing_subprocess',
return_value='process')
@mock.patch('mobly.utils.stop_standing_subprocess')
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
@mock.patch('mobly.logger.get_log_file_timestamp')
- def test_start_and_stop(self, get_timestamp_mock, stop_proc_mock,
- start_proc_mock, create_dir_mock, FastbootProxy,
- MockAdbProxy):
+ def test_start_and_stop(self, get_timestamp_mock, open_logcat_mock,
+ stop_proc_mock, start_proc_mock, create_dir_mock,
+ FastbootProxy, MockAdbProxy):
"""Verifies the steps of collecting adb logcat on an AndroidDevice
object, including various function calls and the expected behaviors of
the calls.
@@ -116,7 +117,7 @@
logging.log_path, 'AndroidDevice%s' % ad.serial,
'logcat,%s,fakemodel,123.txt' % ad.serial)
create_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
- adb_cmd = '"adb" -s %s logcat -v threadtime >> %s'
+ adb_cmd = '"adb" -s %s logcat -v threadtime -T 1 >> %s'
start_proc_mock.assert_called_with(
adb_cmd % (ad.serial, '"%s"' % expected_log_path), shell=True)
self.assertEqual(logcat_service.adb_logcat_file_path,
@@ -142,8 +143,10 @@
@mock.patch('mobly.utils.start_standing_subprocess',
return_value='process')
@mock.patch('mobly.utils.stop_standing_subprocess')
- def test_update_config(self, stop_proc_mock, start_proc_mock,
- create_dir_mock, FastbootProxy, MockAdbProxy):
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
+ def test_update_config(self, open_logcat_mock, stop_proc_mock,
+ start_proc_mock, create_dir_mock, FastbootProxy,
+ MockAdbProxy):
mock_serial = '1'
ad = android_device.AndroidDevice(serial=mock_serial)
logcat_service = logcat.Logcat(ad)
@@ -157,11 +160,12 @@
logcat_service.start()
self.assertTrue(logcat_service._adb_logcat_process)
create_dir_mock.assert_has_calls([mock.call('some/path')])
- expected_adb_cmd = ('"adb" -s 1 logcat -v threadtime -a -b -c >> '
+ expected_adb_cmd = ('"adb" -s 1 logcat -v threadtime -T 1 -a -b -c >> '
'"some/path/log.txt"')
start_proc_mock.assert_called_with(expected_adb_cmd, shell=True)
self.assertEqual(logcat_service.adb_logcat_file_path,
'some/path/log.txt')
+ logcat_service.stop()
@mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
return_value=mock_android_device.MockAdbProxy('1'))
@@ -171,9 +175,10 @@
@mock.patch('mobly.utils.start_standing_subprocess',
return_value='process')
@mock.patch('mobly.utils.stop_standing_subprocess')
- def test_update_config_while_running(self, stop_proc_mock, start_proc_mock,
- create_dir_mock, FastbootProxy,
- MockAdbProxy):
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
+ def test_update_config_while_running(self, open_logcat_mock, stop_proc_mock,
+ start_proc_mock, create_dir_mock,
+ FastbootProxy, MockAdbProxy):
mock_serial = '1'
ad = android_device.AndroidDevice(serial=mock_serial)
logcat_service = logcat.Logcat(ad)
@@ -185,6 +190,7 @@
'Logcat thread is already running, cannot start another one'):
logcat_service.update_config(new_config)
self.assertTrue(logcat_service.is_alive)
+ logcat_service.stop()
@mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
return_value=mock_android_device.MockAdbProxy('1'))
@@ -194,12 +200,13 @@
@mock.patch('mobly.utils.start_standing_subprocess',
return_value='process')
@mock.patch('mobly.utils.stop_standing_subprocess')
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
@mock.patch(
'mobly.controllers.android_device_lib.services.logcat.Logcat.clear_adb_log',
return_value=mock_android_device.MockAdbProxy('1'))
- def test_pause_and_resume(self, clear_adb_mock, stop_proc_mock,
- start_proc_mock, create_dir_mock, FastbootProxy,
- MockAdbProxy):
+ def test_pause_and_resume(self, clear_adb_mock, open_logcat_mock,
+ stop_proc_mock, start_proc_mock, create_dir_mock,
+ FastbootProxy, MockAdbProxy):
mock_serial = '1'
ad = android_device.AndroidDevice(serial=mock_serial)
logcat_service = logcat.Logcat(ad, logcat.Config(clear_log=True))
@@ -214,56 +221,7 @@
logcat_service.resume()
self.assertTrue(logcat_service.is_alive)
clear_adb_mock.assert_not_called()
-
- @mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
- return_value=mock_android_device.MockAdbProxy('1'))
- @mock.patch('mobly.controllers.android_device_lib.fastboot.FastbootProxy',
- return_value=mock_android_device.MockFastbootProxy('1'))
- @mock.patch('mobly.utils.start_standing_subprocess',
- return_value='process')
- @mock.patch('mobly.utils.stop_standing_subprocess')
- @mock.patch(
- 'mobly.controllers.android_device_lib.services.logcat.Logcat.clear_adb_log',
- return_value=mock_android_device.MockAdbProxy('1'))
- def test_logcat_service_create_excerpt(self, clear_adb_mock,
- stop_proc_mock, start_proc_mock,
- FastbootProxy, MockAdbProxy):
- mock_serial = '1'
- ad = android_device.AndroidDevice(serial=mock_serial)
- logcat_service = logcat.Logcat(ad)
- logcat_service.start()
- FILE_CONTENT = 'Some log.\n'
- with open(logcat_service.adb_logcat_file_path, 'w') as f:
- f.write(FILE_CONTENT)
- test_output_dir = os.path.join(self.tmp_dir, 'test_foo')
- mock_record = mock.MagicMock()
- mock_record.begin_time = 123
- test_run_info = runtime_test_info.RuntimeTestInfo(
- 'test_foo', test_output_dir, mock_record)
- actual_path1 = logcat_service.create_output_excerpts(test_run_info)[0]
- expected_path1 = os.path.join(test_output_dir, 'test_foo-123',
- 'logcat,1,fakemodel,test_foo-123.txt')
- self.assertEqual(expected_path1, actual_path1)
- self.assertTrue(os.path.exists(expected_path1))
- self.AssertFileContains(FILE_CONTENT, expected_path1)
- self.assertFalse(os.path.exists(logcat_service.adb_logcat_file_path))
- # Generate some new logs and do another excerpt.
- FILE_CONTENT = 'Some more logs!!!\n'
- with open(logcat_service.adb_logcat_file_path, 'w') as f:
- f.write(FILE_CONTENT)
- test_output_dir = os.path.join(self.tmp_dir, 'test_bar')
- mock_record = mock.MagicMock()
- mock_record.begin_time = 456
- test_run_info = runtime_test_info.RuntimeTestInfo(
- 'test_bar', test_output_dir, mock_record)
- actual_path2 = logcat_service.create_output_excerpts(test_run_info)[0]
- expected_path2 = os.path.join(test_output_dir, 'test_bar-456',
- 'logcat,1,fakemodel,test_bar-456.txt')
- self.assertEqual(expected_path2, actual_path2)
- self.assertTrue(os.path.exists(expected_path2))
- self.AssertFileContains(FILE_CONTENT, expected_path2)
- self.AssertFileDoesNotContain(FILE_CONTENT, expected_path1)
- self.assertFalse(os.path.exists(logcat_service.adb_logcat_file_path))
+ logcat_service.stop()
@mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
return_value=mock_android_device.MockAdbProxy('1'))
@@ -283,9 +241,16 @@
mock_serial = '1'
ad = android_device.AndroidDevice(serial=mock_serial)
logcat_service = logcat.Logcat(ad)
- logcat_service.start()
+ logcat_service._start()
+ # Generate logs before the file pointer is created.
+ # This message will not be captured in the excerpt.
+ NOT_IN_EXCERPT = 'Not in excerpt.\n'
+ with open(logcat_service.adb_logcat_file_path, 'a') as f:
+ f.write(NOT_IN_EXCERPT)
+ # With the file pointer created, generate logs and make an excerpt.
+ logcat_service._open_logcat_file()
FILE_CONTENT = 'Some log.\n'
- with open(logcat_service.adb_logcat_file_path, 'w') as f:
+ with open(logcat_service.adb_logcat_file_path, 'a') as f:
f.write(FILE_CONTENT)
test_output_dir = os.path.join(self.tmp_dir, 'test_foo')
mock_record = mock.MagicMock()
@@ -298,10 +263,10 @@
self.assertEqual(actual_path1, expected_path1)
self.assertTrue(os.path.exists(expected_path1))
self.AssertFileContains(FILE_CONTENT, expected_path1)
- self.assertFalse(os.path.exists(logcat_service.adb_logcat_file_path))
+ self.AssertFileDoesNotContain(NOT_IN_EXCERPT, expected_path1)
# Generate some new logs and do another excerpt.
FILE_CONTENT = 'Some more logs!!!\n'
- with open(logcat_service.adb_logcat_file_path, 'w') as f:
+ with open(logcat_service.adb_logcat_file_path, 'a') as f:
f.write(FILE_CONTENT)
test_output_dir = os.path.join(self.tmp_dir, 'test_bar')
mock_record = mock.MagicMock()
@@ -315,7 +280,7 @@
self.assertTrue(os.path.exists(expected_path2))
self.AssertFileContains(FILE_CONTENT, expected_path2)
self.AssertFileDoesNotContain(FILE_CONTENT, expected_path1)
- self.assertFalse(os.path.exists(logcat_service.adb_logcat_file_path))
+ logcat_service.stop()
@mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
return_value=mock_android_device.MockAdbProxy('1'))
@@ -325,11 +290,12 @@
@mock.patch('mobly.utils.start_standing_subprocess',
return_value='process')
@mock.patch('mobly.utils.stop_standing_subprocess')
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
@mock.patch('mobly.logger.get_log_file_timestamp')
def test_take_logcat_with_extra_params(self, get_timestamp_mock,
- stop_proc_mock, start_proc_mock,
- create_dir_mock, FastbootProxy,
- MockAdbProxy):
+ open_logcat_mock, stop_proc_mock,
+ start_proc_mock, create_dir_mock,
+ FastbootProxy, MockAdbProxy):
"""Verifies the steps of collecting adb logcat on an AndroidDevice
object, including various function calls and the expected behaviors of
the calls.
@@ -347,11 +313,12 @@
logging.log_path, 'AndroidDevice%s' % ad.serial,
'logcat,%s,fakemodel,123.txt' % ad.serial)
create_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
- adb_cmd = '"adb" -s %s logcat -v threadtime -b radio >> %s'
+ adb_cmd = '"adb" -s %s logcat -v threadtime -T 1 -b radio >> %s'
start_proc_mock.assert_called_with(
adb_cmd % (ad.serial, '"%s"' % expected_log_path), shell=True)
self.assertEqual(logcat_service.adb_logcat_file_path,
expected_log_path)
+ logcat_service.stop()
@mock.patch('mobly.controllers.android_device_lib.adb.AdbProxy',
return_value=mock_android_device.MockAdbProxy('1'))
@@ -374,10 +341,12 @@
@mock.patch('mobly.utils.start_standing_subprocess',
return_value='process')
@mock.patch('mobly.utils.stop_standing_subprocess')
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
@mock.patch('mobly.logger.get_log_line_timestamp',
return_value=MOCK_ADB_LOGCAT_END_TIME)
- def test_cat_adb_log(self, mock_timestamp_getter, stop_proc_mock,
- start_proc_mock, FastbootProxy, MockAdbProxy):
+ def test_cat_adb_log(self, mock_timestamp_getter, open_logcat_mock,
+ stop_proc_mock, start_proc_mock, FastbootProxy,
+ MockAdbProxy):
"""Verifies that AndroidDevice.cat_adb_log loads the correct adb log
file, locates the correct adb log lines within the given time range,
and writes the lines to the correct output file.
@@ -413,11 +382,13 @@
@mock.patch('mobly.utils.start_standing_subprocess',
return_value='process')
@mock.patch('mobly.utils.stop_standing_subprocess')
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
@mock.patch('mobly.logger.get_log_line_timestamp',
return_value=MOCK_ADB_LOGCAT_END_TIME)
def test_cat_adb_log_with_unicode(self, mock_timestamp_getter,
- stop_proc_mock, start_proc_mock,
- FastbootProxy, MockAdbProxy):
+ open_logcat_mock, stop_proc_mock,
+ start_proc_mock, FastbootProxy,
+ MockAdbProxy):
"""Verifies that AndroidDevice.cat_adb_log loads the correct adb log
file, locates the correct adb log lines within the given time range,
and writes the lines to the correct output file.
diff --git a/tests/mobly/controllers/android_device_test.py b/tests/mobly/controllers/android_device_test.py
index 6308833..22aa17c 100755
--- a/tests/mobly/controllers/android_device_test.py
+++ b/tests/mobly/controllers/android_device_test.py
@@ -855,9 +855,10 @@
@mock.patch('mobly.utils.start_standing_subprocess',
return_value='process')
@mock.patch('mobly.utils.stop_standing_subprocess')
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
def test_AndroidDevice_change_log_path_with_service(
- self, stop_proc_mock, start_proc_mock, creat_dir_mock,
- FastbootProxy, MockAdbProxy):
+ self, open_logcat_mock, stop_proc_mock, start_proc_mock,
+ creat_dir_mock, FastbootProxy, MockAdbProxy):
ad = android_device.AndroidDevice(serial='1')
ad.services.logcat.start()
new_log_path = tempfile.mkdtemp()
@@ -911,9 +912,10 @@
@mock.patch('mobly.utils.start_standing_subprocess',
return_value='process')
@mock.patch('mobly.utils.stop_standing_subprocess')
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
def test_AndroidDevice_update_serial_with_service_running(
- self, stop_proc_mock, start_proc_mock, creat_dir_mock,
- FastbootProxy, MockAdbProxy):
+ self, open_logcat_mock, stop_proc_mock, start_proc_mock,
+ creat_dir_mock, FastbootProxy, MockAdbProxy):
ad = android_device.AndroidDevice(serial='1')
ad.services.logcat.start()
expected_msg = '.* Cannot change device serial number when there is service running.'
@@ -1053,7 +1055,8 @@
@mock.patch(
'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
@mock.patch('mobly.utils.get_available_host_port')
- def test_AndroidDevice_snippet_cleanup(self, MockGetPort,
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
+ def test_AndroidDevice_snippet_cleanup(self, open_logcat_mock, MockGetPort,
MockSnippetClient, MockFastboot,
MockAdbProxy):
ad = android_device.AndroidDevice(serial='1')
@@ -1093,9 +1096,11 @@
@mock.patch('mobly.utils.start_standing_subprocess',
return_value='process')
@mock.patch('mobly.utils.stop_standing_subprocess')
- def test_AndroidDevice_handle_usb_disconnect(self, stop_proc_mock,
- start_proc_mock,
- FastbootProxy, MockAdbProxy):
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
+ def test_AndroidDevice_handle_usb_disconnect(self, open_logcat_mock,
+ stop_proc_mock,
+ start_proc_mock, FastbootProxy,
+ MockAdbProxy):
class MockService(base_service.BaseService):
def __init__(self, device, configs=None):
self._alive = False
@@ -1137,8 +1142,10 @@
@mock.patch('mobly.utils.start_standing_subprocess',
return_value='process')
@mock.patch('mobly.utils.stop_standing_subprocess')
- def test_AndroidDevice_handle_reboot(self, stop_proc_mock, start_proc_mock,
- FastbootProxy, MockAdbProxy):
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
+ def test_AndroidDevice_handle_reboot(self, open_logcat_mock, stop_proc_mock,
+ start_proc_mock, FastbootProxy,
+ MockAdbProxy):
class MockService(base_service.BaseService):
def __init__(self, device, configs=None):
self._alive = False
@@ -1180,9 +1187,10 @@
@mock.patch('mobly.utils.start_standing_subprocess',
return_value='process')
@mock.patch('mobly.utils.stop_standing_subprocess')
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
def test_AndroidDevice_handle_reboot_changes_build_info(
- self, stop_proc_mock, start_proc_mock, FastbootProxy,
- MockAdbProxy):
+ self, open_logcat_mock, stop_proc_mock, start_proc_mock,
+ FastbootProxy, MockAdbProxy):
ad = android_device.AndroidDevice(serial='1')
with ad.handle_reboot():
ad.adb.mock_properties['ro.build.type'] = 'user'
@@ -1199,9 +1207,10 @@
@mock.patch('mobly.utils.start_standing_subprocess',
return_value='process')
@mock.patch('mobly.utils.stop_standing_subprocess')
+ @mock.patch.object(logcat.Logcat, '_open_logcat_file')
def test_AndroidDevice_handle_reboot_changes_build_info_with_caching(
- self, stop_proc_mock, start_proc_mock, FastbootProxy,
- MockAdbProxy):
+ self, open_logcat_mock, stop_proc_mock, start_proc_mock,
+ FastbootProxy, MockAdbProxy):
ad = android_device.AndroidDevice(serial='1') # Call getprops 1.
rootable_states = [ad.is_rootable]
with ad.handle_reboot():