From 0dd15956754aa732168dfa6a32f9bc4ecf38c732 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Mon, 21 Dec 2020 08:51:22 -0800 Subject: [PATCH 1/3] Add 'exec()' function --- adb_shell/adb_device.py | 30 ++++++++++++++++++++++++++++++ adb_shell/adb_device_async.py | 30 ++++++++++++++++++++++++++++++ tests/test_adb_device.py | 28 ++++++++++++++++++++++++++++ tests/test_adb_device_async.py | 25 +++++++++++++++++++++++++ 4 files changed, 113 insertions(+) diff --git a/adb_shell/adb_device.py b/adb_shell/adb_device.py index 7a38778..8354859 100644 --- a/adb_shell/adb_device.py +++ b/adb_shell/adb_device.py @@ -365,6 +365,36 @@ def _streaming_service(self, service, command, transport_timeout_s=None, read_ti for line in stream: yield line + def exec(self, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None, decode=True): + """Send an ADB ``exec-out`` command to the device. + + https://www.linux-magazine.com/Issues/2017/195/Ask-Klaus + + Parameters + ---------- + command : str + The exec-out command that will be sent + transport_timeout_s : float, None + Timeout in seconds for sending and receiving packets, or ``None``; see :meth:`BaseTransport.bulk_read() ` + and :meth:`BaseTransport.bulk_write() ` + read_timeout_s : float + The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`AdbDevice._read` + timeout_s : float, None + The total time in seconds to wait for the ADB command to finish + decode : bool + Whether to decode the output to utf8 before returning + + Returns + ------- + bytes, str + The output of the ADB shell command as a string if ``decode`` is True, otherwise as bytes. + + """ + if not self.available: + raise exceptions.AdbConnectionError("ADB command not sent because a connection to the device has not been established. (Did you call `AdbDevice.connect()`?)") + + return self._service(b'exec', command.encode('utf8'), transport_timeout_s, read_timeout_s, timeout_s, decode) + def root(self, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None): """Gain root access. diff --git a/adb_shell/adb_device_async.py b/adb_shell/adb_device_async.py index a73ef5d..bed807b 100644 --- a/adb_shell/adb_device_async.py +++ b/adb_shell/adb_device_async.py @@ -360,6 +360,36 @@ async def _streaming_service(self, service, command, transport_timeout_s=None, r async for line in stream: yield line + async def exec(self, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None, decode=True): + """Send an ADB ``exec-out`` command to the device. + + https://www.linux-magazine.com/Issues/2017/195/Ask-Klaus + + Parameters + ---------- + command : str + The exec-out command that will be sent + transport_timeout_s : float, None + Timeout in seconds for sending and receiving packets, or ``None``; see :meth:`BaseTransport.bulk_read() ` + and :meth:`BaseTransport.bulk_write() ` + read_timeout_s : float + The total time in seconds to wait for a ``b'CLSE'`` or ``b'OKAY'`` command in :meth:`AdbDevice._read` + timeout_s : float, None + The total time in seconds to wait for the ADB command to finish + decode : bool + Whether to decode the output to utf8 before returning + + Returns + ------- + bytes, str + The output of the ADB shell command as a string if ``decode`` is True, otherwise as bytes. + + """ + if not self.available: + raise exceptions.AdbConnectionError("ADB command not sent because a connection to the device has not been established. (Did you call `AdbDevice.connect()`?)") + + return await self._service(b'exec', command.encode('utf8'), transport_timeout_s, read_timeout_s, timeout_s, decode) + async def root(self, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None): """Gain root access. diff --git a/tests/test_adb_device.py b/tests/test_adb_device.py index 5475da8..df47823 100644 --- a/tests/test_adb_device.py +++ b/tests/test_adb_device.py @@ -48,6 +48,12 @@ def tearDown(self): self.assertFalse(self.device._transport._bulk_read) def test_adb_connection_error(self): + with self.assertRaises(exceptions.AdbConnectionError): + self.device.exec('FAIL') + + with self.assertRaises(exceptions.AdbConnectionError): + self.device.root() + with self.assertRaises(exceptions.AdbConnectionError): self.device.shell('FAIL') @@ -490,6 +496,28 @@ def test_root(self): self.device.root() patch_service.assert_called_once() + + # ======================================================================= # + # # + # `exec` test # + # # + # ======================================================================= # + def test_exec(self): + self.assertTrue(self.device.connect()) + + # Provide the `bulk_read` return values + self.device._transport._bulk_read = join_messages(AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00'), + AdbMessage(command=constants.WRTE, arg0=1, arg1=1, data=b'TEST'), + AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'')) + + self.device._transport._bulk_read = b''.join([b'OKAY\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', + b'WRTE\x14\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00J\x01\x00\x00\xa8\xad\xab\xba', + b'TEST\n', + b'', + b'CLSE\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba']) + + self.assertEqual(self.device.exec("echo 'TEST'"), "TEST\n") + # ======================================================================= # # # # `filesync` tests # diff --git a/tests/test_adb_device_async.py b/tests/test_adb_device_async.py index f884b74..748cc64 100644 --- a/tests/test_adb_device_async.py +++ b/tests/test_adb_device_async.py @@ -51,6 +51,12 @@ def tearDown(self): @awaiter async def test_adb_connection_error(self): + with self.assertRaises(exceptions.AdbConnectionError): + await self.device.exec('FAIL') + + with self.assertRaises(exceptions.AdbConnectionError): + await self.device.root() + with self.assertRaises(exceptions.AdbConnectionError): await self.device.shell('FAIL') @@ -525,6 +531,25 @@ async def test_root(self): patch_service.assert_called_once() + # ======================================================================= # + # # + # `exec` test # + # # + # ======================================================================= # + @awaiter + async def test_exec(self): + self.assertTrue(await self.device.connect()) + + # Provide the `bulk_read` return values + self.device._transport._bulk_read = b''.join([b'OKAY\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', + b'WRTE\x14\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00J\x01\x00\x00\xa8\xad\xab\xba', + b'TEST\n', + b'', + b'CLSE\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba']) + + self.assertEqual(await self.device.exec("echo 'TEST'"), "TEST\n") + + # ======================================================================= # # # # `filesync` tests # From 93a0e78e218bbd0e8f02732e7fbac7f582854c9f Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Mon, 21 Dec 2020 08:58:19 -0800 Subject: [PATCH 2/3] Python 2 compatibility fix: 'exec' -> 'exec_out' --- adb_shell/adb_device.py | 2 +- adb_shell/adb_device_async.py | 2 +- tests/test_adb_device.py | 4 ++-- tests/test_adb_device_async.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/adb_shell/adb_device.py b/adb_shell/adb_device.py index 8354859..b468ac9 100644 --- a/adb_shell/adb_device.py +++ b/adb_shell/adb_device.py @@ -365,7 +365,7 @@ def _streaming_service(self, service, command, transport_timeout_s=None, read_ti for line in stream: yield line - def exec(self, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None, decode=True): + def exec_out(self, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None, decode=True): """Send an ADB ``exec-out`` command to the device. https://www.linux-magazine.com/Issues/2017/195/Ask-Klaus diff --git a/adb_shell/adb_device_async.py b/adb_shell/adb_device_async.py index bed807b..440a08c 100644 --- a/adb_shell/adb_device_async.py +++ b/adb_shell/adb_device_async.py @@ -360,7 +360,7 @@ async def _streaming_service(self, service, command, transport_timeout_s=None, r async for line in stream: yield line - async def exec(self, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None, decode=True): + async def exec_out(self, command, transport_timeout_s=None, read_timeout_s=constants.DEFAULT_READ_TIMEOUT_S, timeout_s=None, decode=True): """Send an ADB ``exec-out`` command to the device. https://www.linux-magazine.com/Issues/2017/195/Ask-Klaus diff --git a/tests/test_adb_device.py b/tests/test_adb_device.py index df47823..68978f6 100644 --- a/tests/test_adb_device.py +++ b/tests/test_adb_device.py @@ -49,7 +49,7 @@ def tearDown(self): def test_adb_connection_error(self): with self.assertRaises(exceptions.AdbConnectionError): - self.device.exec('FAIL') + self.device.exec_out('FAIL') with self.assertRaises(exceptions.AdbConnectionError): self.device.root() @@ -516,7 +516,7 @@ def test_exec(self): b'', b'CLSE\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba']) - self.assertEqual(self.device.exec("echo 'TEST'"), "TEST\n") + self.assertEqual(self.device.exec_out("echo 'TEST'"), "TEST\n") # ======================================================================= # # # diff --git a/tests/test_adb_device_async.py b/tests/test_adb_device_async.py index 748cc64..2deabcc 100644 --- a/tests/test_adb_device_async.py +++ b/tests/test_adb_device_async.py @@ -52,7 +52,7 @@ def tearDown(self): @awaiter async def test_adb_connection_error(self): with self.assertRaises(exceptions.AdbConnectionError): - await self.device.exec('FAIL') + await self.device.exec_out('FAIL') with self.assertRaises(exceptions.AdbConnectionError): await self.device.root() @@ -547,7 +547,7 @@ async def test_exec(self): b'', b'CLSE\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba']) - self.assertEqual(await self.device.exec("echo 'TEST'"), "TEST\n") + self.assertEqual(await self.device.exec_out("echo 'TEST'"), "TEST\n") # ======================================================================= # From 2c4935e36fdab30d83c5179b14154617d4266820 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Mon, 21 Dec 2020 22:27:05 -0800 Subject: [PATCH 3/3] Minor fixes --- adb_shell/adb_device.py | 2 +- adb_shell/adb_device_async.py | 2 +- tests/test_adb_device.py | 4 ++-- tests/test_adb_device_async.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/adb_shell/adb_device.py b/adb_shell/adb_device.py index b468ac9..8b5b25e 100644 --- a/adb_shell/adb_device.py +++ b/adb_shell/adb_device.py @@ -387,7 +387,7 @@ def exec_out(self, command, transport_timeout_s=None, read_timeout_s=constants.D Returns ------- bytes, str - The output of the ADB shell command as a string if ``decode`` is True, otherwise as bytes. + The output of the ADB exec-out command as a string if ``decode`` is True, otherwise as bytes. """ if not self.available: diff --git a/adb_shell/adb_device_async.py b/adb_shell/adb_device_async.py index 440a08c..d09c7be 100644 --- a/adb_shell/adb_device_async.py +++ b/adb_shell/adb_device_async.py @@ -382,7 +382,7 @@ async def exec_out(self, command, transport_timeout_s=None, read_timeout_s=const Returns ------- bytes, str - The output of the ADB shell command as a string if ``decode`` is True, otherwise as bytes. + The output of the ADB exec-out command as a string if ``decode`` is True, otherwise as bytes. """ if not self.available: diff --git a/tests/test_adb_device.py b/tests/test_adb_device.py index 68978f6..537d88d 100644 --- a/tests/test_adb_device.py +++ b/tests/test_adb_device.py @@ -499,10 +499,10 @@ def test_root(self): # ======================================================================= # # # - # `exec` test # + # `exec_out` test # # # # ======================================================================= # - def test_exec(self): + def test_exec_out(self): self.assertTrue(self.device.connect()) # Provide the `bulk_read` return values diff --git a/tests/test_adb_device_async.py b/tests/test_adb_device_async.py index 2deabcc..1a95337 100644 --- a/tests/test_adb_device_async.py +++ b/tests/test_adb_device_async.py @@ -533,11 +533,11 @@ async def test_root(self): # ======================================================================= # # # - # `exec` test # + # `exec_out` test # # # # ======================================================================= # @awaiter - async def test_exec(self): + async def test_exec_out(self): self.assertTrue(await self.device.connect()) # Provide the `bulk_read` return values