Repository: google/mobly

Issue:
Snippet client is not py3 compatible.
Because of the byte string handling.
A couple examples:
1. >>> ad.load_snippet(name='spt', package='com.google.android.mobly.snippet.example1')
WARNING:root:[AndroidDevice|84B7N15A28012457] a bytes-like object is required, not 'str'

2. >>> ad.stop_services()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/angli/Developer/mobly/mobly/controllers/android_device.py", line 417, in stop_services
    self._terminate_jsonrpc_client(client)
  File "/Users/angli/Developer/mobly/mobly/controllers/android_device.py", line 576, in _terminate_jsonrpc_client
    client.stop_app()
  File "/Users/angli/Developer/mobly/mobly/controllers/android_device_lib/snippet_client.py", line 68, in stop_app
    if 'OK (0 tests)' not in out:
TypeError: a bytes-like object is required, not 'str'




Discussion:

Resolving patch:
diff --git a/mobly/controllers/android_device_lib/sl4a_client.py b/mobly/controllers/android_device_lib/sl4a_client.py
--- a/mobly/controllers/android_device_lib/sl4a_client.py
+++ b/mobly/controllers/android_device_lib/sl4a_client.py
@@ -13,7 +13,6 @@
 # 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.
-
 """JSON RPC interface to android scripting engine."""
 
 from mobly.controllers.android_device_lib import adb
@@ -28,10 +27,9 @@
 
 
 class Sl4aClient(jsonrpc_client_base.JsonRpcClientBase):
-
     def __init__(self, adb_proxy):
-      super(Sl4aClient, self).__init__(adb_proxy)
-      self.app_name = 'SL4A'
+        super(Sl4aClient, self).__init__(adb_proxy)
+        self.app_name = 'SL4A'
 
     def _do_start_app(self):
         """Overrides superclass."""
@@ -44,8 +42,9 @@ def stop_app(self):
     def _is_app_installed(self):
         """Overrides superclass."""
         try:
-            out = self._adb.shell("pm path com.googlecode.android_scripting")
-            return bool(out.strip())
+            out = self._adb.shell("pm path com.googlecode.android_scripting"
+                                  ).decode('utf-8').strip()
+            return bool(out)
         except adb.AdbError as e:
             if (e.ret_code == 1) and (not e.stdout) and (not e.stderr):
                 return False
@@ -56,8 +55,9 @@ def _is_app_running(self):
         # Grep for process with a preceding S which means it is truly started.
         try:
             out = self._adb.shell(
-                'ps | grep "S com.googlecode.android_scripting"')
-            return bool(out.strip())
+                'ps | grep "S com.googlecode.android_scripting"').decode(
+                    'utf-8').strip()
+            return bool(out)
         except adb.AdbError as e:
             if (e.ret_code == 1) and (not e.stdout) and (not e.stderr):
                 return False
diff --git a/mobly/controllers/android_device_lib/snippet_client.py b/mobly/controllers/android_device_lib/snippet_client.py
--- a/mobly/controllers/android_device_lib/snippet_client.py
+++ b/mobly/controllers/android_device_lib/snippet_client.py
@@ -13,7 +13,6 @@
 # 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.
-
 """JSON RPC interface to Mobly Snippet Lib."""
 import logging
 import socket
@@ -21,13 +20,11 @@
 from mobly.controllers.android_device_lib import adb
 from mobly.controllers.android_device_lib import jsonrpc_client_base
 
-_LAUNCH_CMD = (
-    'am instrument -e action start -e port {} '
-    '{}/com.google.android.mobly.snippet.SnippetRunner')
+_LAUNCH_CMD = ('am instrument -e action start -e port {} '
+               '{}/com.google.android.mobly.snippet.SnippetRunner')
 
-_STOP_CMD = (
-    'am instrument -w -e action stop '
-    '{}/com.google.android.mobly.snippet.SnippetRunner')
+_STOP_CMD = ('am instrument -w -e action stop '
+             '{}/com.google.android.mobly.snippet.SnippetRunner')
 
 
 class Error(Exception):
@@ -35,7 +32,6 @@ class Error(Exception):
 
 
 class SnippetClient(jsonrpc_client_base.JsonRpcClientBase):
-
     def __init__(self, package, port, adb_proxy):
         """Initialzies a SnippetClient.
   
@@ -64,7 +60,7 @@ def stop_app(self):
         """Overrides superclass."""
         cmd = _STOP_CMD.format(self._package)
         logging.info('Stopping snippet apk with: %s', cmd)
-        out = self._adb.shell(_STOP_CMD.format(self._package))
+        out = self._adb.shell(_STOP_CMD.format(self._package)).decode('utf-8')
         if 'OK (0 tests)' not in out:
             raise Error('Failed to stop existing apk. Unexpected output: ' +
                         out)
@@ -73,8 +69,8 @@ def _is_app_installed(self):
         """Overrides superclass."""
         try:
             out = self._adb.shell(
-              'pm list instrumentation | grep ^instrumentation:%s/' %
-              self._package)
+                'pm list instrumentation | grep ^instrumentation:%s/' %
+                self._package).decode('utf-8')
             return bool(out)
         except adb.AdbError as e:
             if (e.ret_code == 1) and (not e.stdout) and (not e.stderr):
@@ -89,7 +85,7 @@ def _is_app_running(self):
         # our destination port is alive.
         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         try:
-          result = sock.connect_ex(('127.0.0.1', self._port))
-          return result == 0
+            result = sock.connect_ex(('127.0.0.1', self._port))
+            return result == 0
         finally:
-          sock.close()
+            sock.close()


Test patch: