Repository: google/mobly

Issue:
Add timeout to bug report util functions
It is possible for the adb `bugreport` cmd to never return.
E.g. we have seen the bugreport cmd stuck for over an hour on `LenovoA7010`.

`AndroidDevice.take_bug_report` should probably have a default timeout set to prevent the test from getting stuck forever.


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
@@ -836,8 +836,10 @@ def start_adb_logcat(self, clear_log=True):
             extra_params = self.adb_logcat_param
         except AttributeError:
             extra_params = ''
-        cmd = '"%s" -s %s logcat -v threadtime %s >> "%s"' % (
-            adb.ADB, self.serial, extra_params, logcat_file_path)
+        cmd = '"%s" -s %s logcat -v threadtime %s >> "%s"' % (adb.ADB,
+                                                              self.serial,
+                                                              extra_params,
+                                                              logcat_file_path)
         process = utils.start_standing_subprocess(cmd, shell=True)
         self._adb_logcat_process = process
         self.adb_logcat_file_path = logcat_file_path
@@ -853,12 +855,14 @@ def stop_adb_logcat(self):
         utils.stop_standing_subprocess(self._adb_logcat_process)
         self._adb_logcat_process = None
 
-    def take_bug_report(self, test_name, begin_time):
+    def take_bug_report(self, test_name, begin_time, timetout=300):
         """Takes a bug report on the device and stores it in a file.
 
         Args:
             test_name: Name of the test method that triggered this bug report.
             begin_time: Timestamp of when the test started.
+            timeout: float, the number of seconds to wait for bugreport to
+                complete, default is 5min.
         """
         new_br = True
         try:
@@ -881,7 +885,8 @@ def take_bug_report(self, test_name, begin_time):
         self.wait_for_boot_completion()
         self.log.info('Taking bugreport for %s.', test_name)
         if new_br:
-            out = self.adb.shell('bugreportz').decode('utf-8')
+            out = self.adb.shell(
+                'bugreportz', timeout=timetout).decode('utf-8')
             if not out.startswith('OK'):
                 raise DeviceError(self, 'Failed to take bugreport: %s' % out)
             br_out_path = out.split(':')[1].strip()
@@ -889,7 +894,8 @@ def take_bug_report(self, test_name, begin_time):
         else:
             # shell=True as this command redirects the stdout to a local file
             # using shell redirection.
-            self.adb.bugreport(' > %s' % full_out_path, shell=True)
+            self.adb.bugreport(
+                ' > %s' % full_out_path, shell=True, timeout=timetout)
         self.log.info('Bugreport for %s taken at %s.', test_name,
                       full_out_path)
 


Test patch:
diff --git a/tests/lib/mock_android_device.py b/tests/lib/mock_android_device.py
--- a/tests/lib/mock_android_device.py
+++ b/tests/lib/mock_android_device.py
@@ -68,7 +68,7 @@ def __init__(self, serial, fail_br=False, fail_br_before_N=False):
         self.fail_br = fail_br
         self.fail_br_before_N = fail_br_before_N
 
-    def shell(self, params):
+    def shell(self, params, timeout=None):
         if params == "id -u":
             return b"root"
         elif params == "bugreportz":
@@ -90,7 +90,7 @@ def getprop(self, params):
         elif params == "sys.boot_completed":
             return "1"
 
-    def bugreport(self, args, shell=False):
+    def bugreport(self, args, shell=False, timeout=None):
         expected = os.path.join(logging.log_path,
                                 'AndroidDevice%s' % self.serial, 'BugReports',
                                 'test_something,sometime,%s' % self.serial)