Repository: google/mobly

Issue:
Do not crash upon `adb logcat -c` error
Android O has a bug that causes `adb logcat -c` cmd to fail in certain situations.
Let's not crash bc of this.


Discussion:

Resolving patch:
diff --git a/mobly/controllers/android_device.py b/mobly/controllers/android_device.py
--- a/mobly/controllers/android_device.py
+++ b/mobly/controllers/android_device.py
@@ -447,8 +447,7 @@ def __init__(self, serial=''):
         # logging.log_path only exists when this is used in an Mobly test run.
         self._log_path_base = getattr(logging, 'log_path', '/tmp/logs')
         self._log_path = os.path.join(
-            self._log_path_base,
-            'AndroidDevice%s' % self._normalized_serial)
+            self._log_path_base, 'AndroidDevice%s' % self._normalized_serial)
         self._debug_tag = self._serial
         self.log = AndroidDeviceLoggerAdapter(logging.getLogger(), {
             'tag': self.debug_tag
@@ -1035,7 +1034,16 @@ def start_adb_logcat(self, clear_log=True):
                 self,
                 'Logcat thread is already running, cannot start another one.')
         if clear_log:
-            self._clear_adb_log()
+            try:
+                self._clear_adb_log()
+            except adb.AdbError as e:
+                # On Android O, the clear command fails due to a known bug.
+                # Catching this so we don't crash from this Android issue.
+                if "failed to clear" in e.stderr:
+                    self.log.warning(
+                        'Encountered known Android error to clear logcat.')
+                else:
+                    raise
 
         self._enable_logpersist()
 


Test patch:
diff --git a/tests/mobly/controllers/android_device_test.py b/tests/mobly/controllers/android_device_test.py
--- a/tests/mobly/controllers/android_device_test.py
+++ b/tests/mobly/controllers/android_device_test.py
@@ -450,6 +450,40 @@ def test_AndroidDevice_take_logcat(self, stop_proc_mock, start_proc_mock,
         self.assertIsNone(ad._adb_logcat_process)
         self.assertEqual(ad.adb_logcat_file_path, expected_log_path)
 
+    @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.create_dir')
+    @mock.patch(
+        'mobly.utils.start_standing_subprocess', return_value='process')
+    @mock.patch('mobly.utils.stop_standing_subprocess')
+    @mock.patch(
+        'mobly.controllers.android_device.AndroidDevice._clear_adb_log')
+    def test_AndroidDevice_start_adb_logcat_clear_log_fails(
+            self, mock_clear_adb_log, stop_proc_mock, start_proc_mock,
+            creat_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.
+        """
+        mock_clear_adb_log.side_effect = adb.AdbError(
+            cmd='adb -s xx logcat -c',
+            stdout='',
+            stderr="failed to clear 'main' log",
+            ret_code=1)
+        mock_serial = '1'
+        ad = android_device.AndroidDevice(serial=mock_serial)
+        ad.start_adb_logcat()
+        # Verify start did the correct operations.
+        self.assertTrue(ad._adb_logcat_process)
+        expected_log_path = os.path.join(logging.log_path,
+                                         'AndroidDevice%s' % ad.serial,
+                                         'adblog,fakemodel,%s.txt' % ad.serial)
+        creat_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
+
     @mock.patch(
         'mobly.controllers.android_device_lib.adb.AdbProxy',
         return_value=mock_android_device.MockAdbProxy('1'))