Repository: google/mobly

Issue:
`_execute_and_process_stdout` should log cmd



Discussion:

Resolving patch:
diff --git a/mobly/controllers/android_device_lib/adb.py b/mobly/controllers/android_device_lib/adb.py
--- a/mobly/controllers/android_device_lib/adb.py
+++ b/mobly/controllers/android_device_lib/adb.py
@@ -203,6 +203,7 @@ def _execute_and_process_stdout(self, args, shell, handler):
             stderr=subprocess.PIPE,
             shell=shell,
             bufsize=1)
+        out = '[elided, processed via handler]'
         try:
             while proc.poll() is None:
                 line = proc.stdout.readline()
@@ -211,16 +212,19 @@ def _execute_and_process_stdout(self, args, shell, handler):
                 else:
                     break
         finally:
-            (_, err) = proc.communicate()
+            (unexpected_out, err) = proc.communicate()
+            if unexpected_out:
+                out = '[unexpected stdout] %s' % unexpected_out
+                for line in unexpected_out.splitlines():
+                    handler(line)
+
         ret = proc.returncode
+        logging.debug('cmd: %s, stdout: %s, stderr: %s, ret: %s',
+                      cli_cmd_to_string(args), out, err, ret)
         if ret == 0:
             return err
         else:
-            raise AdbError(
-                cmd=args,
-                stdout='[elided, processed via handler]',
-                stderr=err,
-                ret_code=ret)
+            raise AdbError(cmd=args, stdout=out, stderr=err, ret_code=ret)
 
     def _construct_adb_cmd(self, raw_name, args, shell):
         """Constructs an adb command with arguments for a subprocess call.


Test patch:
diff --git a/tests/mobly/controllers/android_device_lib/adb_test.py b/tests/mobly/controllers/android_device_lib/adb_test.py
--- a/tests/mobly/controllers/android_device_lib/adb_test.py
+++ b/tests/mobly/controllers/android_device_lib/adb_test.py
@@ -76,8 +76,7 @@ def _mock_execute_and_process_stdout_process(self, mock_popen):
         mock_popen.return_value.stdout.readline.side_effect = ['']
 
         mock_proc.communicate = mock.Mock(
-            return_value=(MOCK_DEFAULT_STDOUT.encode('utf-8'),
-                          MOCK_DEFAULT_STDERR.encode('utf-8')))
+            return_value=('', MOCK_DEFAULT_STDERR.encode('utf-8')))
         mock_proc.returncode = 0
         return mock_popen
 
@@ -150,6 +149,57 @@ def test_execute_and_process_stdout_reads_stdout(self, mock_popen):
         mock_handler.assert_any_call('1')
         mock_handler.assert_any_call('2')
 
+    @mock.patch('mobly.controllers.android_device_lib.adb.subprocess.Popen')
+    def test_execute_and_process_stdout_reads_unexpected_stdout(
+            self, mock_popen):
+        unexpected_stdout = MOCK_DEFAULT_STDOUT.encode('utf-8')
+
+        self._mock_execute_and_process_stdout_process(mock_popen)
+        mock_handler = mock.MagicMock()
+        mock_popen.return_value.communicate = mock.Mock(
+            return_value=(unexpected_stdout, MOCK_DEFAULT_STDERR.encode(
+                'utf-8')))
+
+        err = adb.AdbProxy()._execute_and_process_stdout(
+            ['fake_cmd'], shell=False, handler=mock_handler)
+        self.assertEqual(mock_handler.call_count, 1)
+        mock_handler.assert_called_with(unexpected_stdout)
+
+    @mock.patch('mobly.controllers.android_device_lib.adb.subprocess.Popen')
+    @mock.patch('logging.debug')
+    def test_execute_and_process_stdout_logs_cmd(self, mock_debug_logger,
+                                                 mock_popen):
+        raw_expected_stdout = ''
+        expected_stdout = '[elided, processed via handler]'
+        expected_stderr = MOCK_DEFAULT_STDERR.encode('utf-8')
+        self._mock_execute_and_process_stdout_process(mock_popen)
+        mock_popen.return_value.communicate = mock.Mock(
+            return_value=(raw_expected_stdout, expected_stderr))
+
+        err = adb.AdbProxy()._execute_and_process_stdout(
+            ['fake_cmd'], shell=False, handler=mock.MagicMock())
+        mock_debug_logger.assert_called_with(
+            'cmd: %s, stdout: %s, stderr: %s, ret: %s', 'fake_cmd',
+            expected_stdout, expected_stderr, 0)
+
+    @mock.patch('mobly.controllers.android_device_lib.adb.subprocess.Popen')
+    @mock.patch('logging.debug')
+    def test_execute_and_process_stdout_logs_cmd_with_unexpected_stdout(
+            self, mock_debug_logger, mock_popen):
+        raw_expected_stdout = MOCK_DEFAULT_STDOUT.encode('utf-8')
+        expected_stdout = '[unexpected stdout] %s' % raw_expected_stdout
+        expected_stderr = MOCK_DEFAULT_STDERR.encode('utf-8')
+
+        self._mock_execute_and_process_stdout_process(mock_popen)
+        mock_popen.return_value.communicate = mock.Mock(
+            return_value=(raw_expected_stdout, expected_stderr))
+
+        err = adb.AdbProxy()._execute_and_process_stdout(
+            ['fake_cmd'], shell=False, handler=mock.MagicMock())
+        mock_debug_logger.assert_called_with(
+            'cmd: %s, stdout: %s, stderr: %s, ret: %s', 'fake_cmd',
+            expected_stdout, expected_stderr, 0)
+
     @mock.patch('mobly.controllers.android_device_lib.adb.subprocess.Popen')
     def test_execute_and_process_stdout_when_cmd_exits(self, mock_popen):
         self._mock_execute_and_process_stdout_process(mock_popen)