From 321a9d198668e4bfb963236f9863106ab375ea64 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Mon, 20 Aug 2018 21:50:51 -0700 Subject: [PATCH 1/8] Fix python3-ism with byte v. str (copied from @rustyhowell) --- adb/adb_protocol.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/adb/adb_protocol.py b/adb/adb_protocol.py index 9654e51..4ff28c7 100644 --- a/adb/adb_protocol.py +++ b/adb/adb_protocol.py @@ -302,6 +302,11 @@ def Connect(cls, usb, banner=b'notadb', rsa_keys=None, auth_timeout_ms=100): InvalidResponseError: When the device does authentication in an unexpected way. """ + # In py3, convert unicode to bytes. In py2, convert str to bytes. + # It's later joined into a byte string, so in py2, this ends up kind of being a no-op. + if isinstance(banner, str): + banner = bytearray(banner, 'utf-8') + msg = cls( command=b'CNXN', arg0=VERSION, arg1=MAX_ADB_DATA, data=b'host::%s\0' % banner) From 922359ce2aced9b7adb357bbf45d813869a38d25 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Mon, 20 Aug 2018 22:11:11 -0700 Subject: [PATCH 2/8] Changed binary strings to regular strings --- adb/adb_commands.py | 2 +- adb/common.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/adb/adb_commands.py b/adb/adb_commands.py index 667b713..e88aa88 100644 --- a/adb/adb_commands.py +++ b/adb/adb_commands.py @@ -127,7 +127,7 @@ def ConnectDevice(self, port_path=None, serial=None, default_timeout_ms=None, ** # If there isnt a handle override (used by tests), build one here if 'handle' in kwargs: self._handle = kwargs.pop('handle') - elif serial and b':' in serial: + elif serial and ':' in serial: self._handle = common.TcpHandle(serial, timeout_ms=default_timeout_ms) else: self._handle = common.UsbHandle.FindAndOpen( diff --git a/adb/common.py b/adb/common.py index a68414f..0e4bfd6 100644 --- a/adb/common.py +++ b/adb/common.py @@ -298,8 +298,8 @@ def __init__(self, serial, timeout_ms=None): Host may be an IP address or a host name. """ - if b':' in serial: - (host, port) = serial.split(b':') + if ':' in serial: + host, port = serial.split(':') else: host = serial port = 5555 From 66e11401200ad76650d1ca4008edf0730ebced49 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Mon, 20 Aug 2018 22:59:48 -0700 Subject: [PATCH 3/8] Version bump to 1.3.0.dev --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f476438..9bf62c0 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ setup( name = 'adb', packages = ['adb'], - version = '1.3.0', + version = '1.3.0.dev', author = 'Fahrzin Hemmati', author_email = 'fahhem@gmail.com', maintainer = 'Fahrzin Hemmati', From 64e073da7afdd5d8eb397932044d8d842e1da6c3 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Wed, 22 Aug 2018 22:45:02 -0700 Subject: [PATCH 4/8] If 'serial' is bytes, convert it to unicode --- adb/adb_commands.py | 15 ++++++++++----- adb/common.py | 4 ++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/adb/adb_commands.py b/adb/adb_commands.py index e88aa88..f3667c8 100644 --- a/adb/adb_commands.py +++ b/adb/adb_commands.py @@ -127,12 +127,17 @@ def ConnectDevice(self, port_path=None, serial=None, default_timeout_ms=None, ** # If there isnt a handle override (used by tests), build one here if 'handle' in kwargs: self._handle = kwargs.pop('handle') - elif serial and ':' in serial: - self._handle = common.TcpHandle(serial, timeout_ms=default_timeout_ms) else: - self._handle = common.UsbHandle.FindAndOpen( - DeviceIsAvailable, port_path=port_path, serial=serial, - timeout_ms=default_timeout_ms) + # if necessary, convert serial to a unicode string + if isinstance(serial, (bytes, bytearray)): + serial = serial.decode('utf-8') + + if serial and ':' in serial: + self._handle = common.TcpHandle(serial, timeout_ms=default_timeout_ms) + else: + self._handle = common.UsbHandle.FindAndOpen( + DeviceIsAvailable, port_path=port_path, serial=serial, + timeout_ms=default_timeout_ms) self._Connect(**kwargs) diff --git a/adb/common.py b/adb/common.py index 0e4bfd6..840c697 100644 --- a/adb/common.py +++ b/adb/common.py @@ -298,6 +298,10 @@ def __init__(self, serial, timeout_ms=None): Host may be an IP address or a host name. """ + # if necessary, convert serial to a unicode string + if isinstance(serial, (bytes, bytearray)): + serial = serial.decode('utf-8') + if ':' in serial: host, port = serial.split(':') else: From 7e8e11b7b8247df22f948830d0844e256305de69 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Fri, 24 Aug 2018 16:33:54 -0700 Subject: [PATCH 5/8] Version bump to 1.3.0.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9bf62c0..e4d2d9d 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ setup( name = 'adb', packages = ['adb'], - version = '1.3.0.dev', + version = '1.3.0.1', author = 'Fahrzin Hemmati', author_email = 'fahhem@gmail.com', maintainer = 'Fahrzin Hemmati', From d8f64fc26c1ecfd498a9137514283737636700e1 Mon Sep 17 00:00:00 2001 From: tuxuser <462620+tuxuser@users.noreply.github.com> Date: Wed, 3 Oct 2018 18:40:20 +0200 Subject: [PATCH 6/8] Add tests for TcpHandle and AdbCommands.ConnectDevice --- test/adb_test.py | 35 +++++++++++++++++++++++++++++++++-- test/common_stub.py | 11 +++++++++-- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/test/adb_test.py b/test/adb_test.py index bdbfce5..960ef84 100755 --- a/test/adb_test.py +++ b/test/adb_test.py @@ -20,7 +20,7 @@ from adb import adb_commands from adb import adb_protocol -from adb.usb_exceptions import TcpTimeoutException +from adb.usb_exceptions import TcpTimeoutException, DeviceNotFoundError import common_stub @@ -78,7 +78,6 @@ def _Connect(cls, usb): class AdbTest(BaseAdbTest): - @classmethod def _ExpectCommand(cls, service, command, *responses): usb = common_stub.StubUsb() @@ -97,6 +96,12 @@ def testConnect(self): dev = adb_commands.AdbCommands() dev.ConnectDevice(handle=usb, banner=BANNER) + def testConnectSerialString(self): + dev = common_stub.StubAdbCommands() + + with self.assertRaises(DeviceNotFoundError): + dev.ConnectDevice(serial='/dev/ttyS99') + def testSmallResponseShell(self): command = b'keepin it real' response = 'word.' @@ -276,5 +281,31 @@ def testTcpTimeout(self): command, timeout_ms=timeout_ms) + +class TcpHandleTest(unittest.TestCase): + def testInitWithHost(self): + tcp = common_stub.StubTcp('10.11.12.13') + + self.assertEqual('10.11.12.13:5555', tcp._serial_number) + self.assertEqual(None, tcp._timeout_ms) + + def testInitWithHostAndPort(self): + tcp = common_stub.StubTcp('10.11.12.13:5678') + + self.assertEqual('10.11.12.13:5678', tcp._serial_number) + self.assertEqual(None, tcp._timeout_ms) + + def testInitWithTimeout(self): + tcp = common_stub.StubTcp('10.0.0.2', timeout_ms=234.5) + + self.assertEqual('10.0.0.2:5555', tcp._serial_number) + self.assertEqual(234.5, tcp._timeout_ms) + + def testInitWithTimeoutInt(self): + tcp = common_stub.StubTcp('10.0.0.2', timeout_ms=234) + + self.assertEqual('10.0.0.2:5555', tcp._serial_number) + self.assertEqual(234.0, tcp._timeout_ms) + if __name__ == '__main__': unittest.main() diff --git a/test/common_stub.py b/test/common_stub.py index e2a2e3e..32fa45e 100644 --- a/test/common_stub.py +++ b/test/common_stub.py @@ -5,7 +5,8 @@ import string import sys import time -from adb.usb_exceptions import TcpTimeoutException +from adb.usb_exceptions import TcpTimeoutException +from adb.adb_commands import AdbCommands PRINTABLE_DATA = set(string.printable) - set(string.whitespace) @@ -94,5 +95,11 @@ def BulkRead(self, length, if b'i_need_a_timeout' in data: self._alarm_sounder(timeout_ms) time.sleep(2*self._return_seconds(timeout_ms)) - return bytearray(data) + return bytearray(data) + +class StubAdbCommands(AdbCommands): + """AdbCommands stub""" + def _Connect(self, banner=None, **kwargs): + """Simply let Connect pass, so we can test param parsing""" + pass From 5d2db7aaaab81c955597c2d10c19260fad407697 Mon Sep 17 00:00:00 2001 From: tuxuser <462620+tuxuser@users.noreply.github.com> Date: Wed, 3 Oct 2018 19:10:12 +0200 Subject: [PATCH 7/8] Base TcpStub and UsbStub from the real class --- adb/common.py | 18 +++++-- test/adb_test.py | 10 ++-- test/common_stub.py | 108 +++++++++++++++++++++++++----------------- test/fastboot_test.py | 2 +- 4 files changed, 84 insertions(+), 54 deletions(-) diff --git a/adb/common.py b/adb/common.py index a68414f..b0be81b 100644 --- a/adb/common.py +++ b/adb/common.py @@ -298,15 +298,23 @@ def __init__(self, serial, timeout_ms=None): Host may be an IP address or a host name. """ + if b':' in serial: - (host, port) = serial.split(b':') + (self.host, self.port) = serial.split(b':') else: - host = serial - port = 5555 - self._serial_number = '%s:%s' % (host, port) + self.host = serial + self.port = 5555 + + self._connection = None + self._serial_number = '%s:%s' % (self.host, self.port) self._timeout_ms = float(timeout_ms) if timeout_ms else None + + self._connect() + + def _connect(self): timeout = self.TimeoutSeconds(self._timeout_ms) - self._connection = socket.create_connection((host, port), timeout=timeout) + self._connection = socket.create_connection((self.host, self.port), + timeout=timeout) if timeout: self._connection.setblocking(0) diff --git a/test/adb_test.py b/test/adb_test.py index 960ef84..01130cb 100755 --- a/test/adb_test.py +++ b/test/adb_test.py @@ -80,7 +80,7 @@ def _Connect(cls, usb): class AdbTest(BaseAdbTest): @classmethod def _ExpectCommand(cls, service, command, *responses): - usb = common_stub.StubUsb() + usb = common_stub.StubUsb(device=None, setting=None) cls._ExpectConnection(usb) cls._ExpectOpen(usb, b'%s:%s\0' % (service, command)) @@ -90,7 +90,7 @@ def _ExpectCommand(cls, service, command, *responses): return usb def testConnect(self): - usb = common_stub.StubUsb() + usb = common_stub.StubUsb(device=None, setting=None) self._ExpectConnection(usb) dev = adb_commands.AdbCommands() @@ -201,7 +201,7 @@ def _MakeWriteSyncPacket(cls, command, data=b'', size=None): @classmethod def _ExpectSyncCommand(cls, write_commands, read_commands): - usb = common_stub.StubUsb() + usb = common_stub.StubUsb(device=None, setting=None) cls._ExpectConnection(usb) cls._ExpectOpen(usb, b'sync:\0') @@ -251,7 +251,7 @@ class TcpTimeoutAdbTest(BaseAdbTest): @classmethod def _ExpectCommand(cls, service, command, *responses): - tcp = common_stub.StubTcp() + tcp = common_stub.StubTcp('10.0.0.123') cls._ExpectConnection(tcp) cls._ExpectOpen(tcp, b'%s:%s\0' % (service, command)) @@ -267,7 +267,7 @@ def _run_shell(self, cmd, timeout_ms=None): dev.Shell(cmd, timeout_ms=timeout_ms) def testConnect(self): - tcp = common_stub.StubTcp() + tcp = common_stub.StubTcp('10.0.0.123') self._ExpectConnection(tcp) dev = adb_commands.AdbCommands() dev.ConnectDevice(handle=tcp, banner=BANNER) diff --git a/test/common_stub.py b/test/common_stub.py index 32fa45e..427ce99 100644 --- a/test/common_stub.py +++ b/test/common_stub.py @@ -5,6 +5,7 @@ import string import sys import time +from adb.common import TcpHandle, UsbHandle from adb.usb_exceptions import TcpTimeoutException from adb.adb_commands import AdbCommands @@ -17,33 +18,23 @@ def _Dotify(data): return ''.join(char if char in PRINTABLE_DATA else '.' for char in data) -class StubUsb(object): - """UsbHandle stub.""" - - def __init__(self): +class StubHandleBase(object): + def __init__(self, timeout_ms, is_tcp=False): self.written_data = [] self.read_data = [] - self.timeout_ms = 0 + self.is_tcp = is_tcp + self.timeout_ms = timeout_ms - def BulkWrite(self, data, unused_timeout_ms=None): - expected_data = self.written_data.pop(0) - if isinstance(data, bytearray): - data = bytes(data) - if not isinstance(data, bytes): - data = data.encode('utf8') - if expected_data != data: - raise ValueError('Expected %s (%s) got %s (%s)' % ( - binascii.hexlify(expected_data), _Dotify(expected_data), - binascii.hexlify(data), _Dotify(data))) + def _signal_handler(self, signum, frame): + raise TcpTimeoutException('End of time') - def BulkRead(self, length, - timeout_ms=None): # pylint: disable=unused-argument - data = self.read_data.pop(0) - if length < len(data): - raise ValueError( - 'Overflow packet length. Read %d bytes, got %d bytes: %s', - length, len(data)) - return bytearray(data) + def _return_seconds(self, time_ms): + return (float(time_ms)/1000) if time_ms else 0 + + def _alarm_sounder(self, timeout_ms): + signal.signal(signal.SIGALRM, self._signal_handler) + signal.setitimer(signal.ITIMER_REAL, + self._return_seconds(timeout_ms)) def ExpectWrite(self, data): if not isinstance(data, bytes): @@ -55,22 +46,6 @@ def ExpectRead(self, data): data = data.encode('utf8') self.read_data.append(data) - def Timeout(self, timeout_ms): - return timeout_ms if timeout_ms is not None else self.timeout_ms - -class StubTcp(StubUsb): - - def _signal_handler(self, signum, frame): - raise TcpTimeoutException('End of time') - - def _return_seconds(self, time_ms): - return (float(time_ms)/1000) if time_ms else 0 - - def _alarm_sounder(self, timeout_ms): - signal.signal(signal.SIGALRM, self._signal_handler) - signal.setitimer(signal.ITIMER_REAL, - self._return_seconds(timeout_ms)) - def BulkWrite(self, data, timeout_ms=None): expected_data = self.written_data.pop(0) if isinstance(data, bytearray): @@ -81,8 +56,8 @@ def BulkWrite(self, data, timeout_ms=None): raise ValueError('Expected %s (%s) got %s (%s)' % ( binascii.hexlify(expected_data), _Dotify(expected_data), binascii.hexlify(data), _Dotify(data))) - if b'i_need_a_timeout' in data: - self._alarm_sounder(timeout_ms) + if self.is_tcp and b'i_need_a_timeout' in data: + self._alarm_sounder(timeout_ms) time.sleep(2*self._return_seconds(timeout_ms)) def BulkRead(self, length, @@ -92,11 +67,58 @@ def BulkRead(self, length, raise ValueError( 'Overflow packet length. Read %d bytes, got %d bytes: %s', length, len(data)) - if b'i_need_a_timeout' in data: - self._alarm_sounder(timeout_ms) + if self.is_tcp and b'i_need_a_timeout' in data: + self._alarm_sounder(timeout_ms) time.sleep(2*self._return_seconds(timeout_ms)) return bytearray(data) + def Timeout(self, timeout_ms): + return timeout_ms if timeout_ms is not None else self.timeout_ms + + +class StubUsb(UsbHandle): + """UsbHandle stub.""" + def __init__(self, device, setting, usb_info=None, timeout_ms=None): + super(StubUsb, self).__init__(device, setting, usb_info, timeout_ms) + self.stub_base = StubHandleBase(0) + + def ExpectWrite(self, data): + return self.stub_base.ExpectWrite(data) + + def ExpectRead(self, data): + return self.stub_base.ExpectRead(data) + + def BulkWrite(self, data, unused_timeout_ms=None): + return self.stub_base.BulkWrite(data, unused_timeout_ms) + + def BulkRead(self, length, timeout_ms=None): + return self.stub_base.BulkRead(length, timeout_ms) + + def Timeout(self, timeout_ms): + return self.stub_base.Timeout(timeout_ms) + + +class StubTcp(TcpHandle): + def __init__(self, serial, timeout_ms=None): + """TcpHandle stub.""" + super(StubTcp, self).__init__(serial, timeout_ms) + self.stub_base = StubHandleBase(0, is_tcp=True) + + def ExpectWrite(self, data): + return self.stub_base.ExpectWrite(data) + + def ExpectRead(self, data): + return self.stub_base.ExpectRead(data) + + def BulkWrite(self, data, unused_timeout_ms=None): + return self.stub_base.BulkWrite(data, unused_timeout_ms) + + def BulkRead(self, length, timeout_ms=None): + return self.stub_base.BulkRead(length, timeout_ms) + + def Timeout(self, timeout_ms): + return self.stub_base.Timeout(timeout_ms) + class StubAdbCommands(AdbCommands): """AdbCommands stub""" diff --git a/test/fastboot_test.py b/test/fastboot_test.py index 32c96fa..58ccced 100755 --- a/test/fastboot_test.py +++ b/test/fastboot_test.py @@ -26,7 +26,7 @@ class FastbootTest(unittest.TestCase): def setUp(self): - self.usb = common_stub.StubUsb() + self.usb = common_stub.StubUsb(device=None, setting=None) @staticmethod def _SumLengths(items): From 2b3481276557f2f5d8849c2c6a4ee0937fab0015 Mon Sep 17 00:00:00 2001 From: tuxuser <462620+tuxuser@users.noreply.github.com> Date: Wed, 3 Oct 2018 19:17:44 +0200 Subject: [PATCH 8/8] Mocking for AdbCommands().ConnectDevice test --- setup.py | 2 +- test/adb_test.py | 10 +++++++--- test/common_stub.py | 12 ++++-------- tox.ini | 1 + 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/setup.py b/setup.py index e4d2d9d..f476438 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ setup( name = 'adb', packages = ['adb'], - version = '1.3.0.1', + version = '1.3.0', author = 'Fahrzin Hemmati', author_email = 'fahhem@gmail.com', maintainer = 'Fahrzin Hemmati', diff --git a/test/adb_test.py b/test/adb_test.py index 01130cb..0ce1ead 100755 --- a/test/adb_test.py +++ b/test/adb_test.py @@ -17,7 +17,10 @@ from io import BytesIO import struct import unittest +from mock import mock + +from adb import common from adb import adb_commands from adb import adb_protocol from adb.usb_exceptions import TcpTimeoutException, DeviceNotFoundError @@ -97,10 +100,11 @@ def testConnect(self): dev.ConnectDevice(handle=usb, banner=BANNER) def testConnectSerialString(self): - dev = common_stub.StubAdbCommands() + dev = adb_commands.AdbCommands() - with self.assertRaises(DeviceNotFoundError): - dev.ConnectDevice(serial='/dev/ttyS99') + with mock.patch.object(common.UsbHandle, 'FindAndOpen', return_value=None): + with mock.patch.object(adb_commands.AdbCommands, '_Connect', return_value=None): + dev.ConnectDevice(serial='/dev/invalidHandle') def testSmallResponseShell(self): command = b'keepin it real' diff --git a/test/common_stub.py b/test/common_stub.py index 427ce99..f993ef1 100644 --- a/test/common_stub.py +++ b/test/common_stub.py @@ -5,9 +5,10 @@ import string import sys import time +from mock import mock + from adb.common import TcpHandle, UsbHandle from adb.usb_exceptions import TcpTimeoutException -from adb.adb_commands import AdbCommands PRINTABLE_DATA = set(string.printable) - set(string.whitespace) @@ -101,6 +102,8 @@ def Timeout(self, timeout_ms): class StubTcp(TcpHandle): def __init__(self, serial, timeout_ms=None): """TcpHandle stub.""" + self._connect = mock.MagicMock(return_value=None) + super(StubTcp, self).__init__(serial, timeout_ms) self.stub_base = StubHandleBase(0, is_tcp=True) @@ -118,10 +121,3 @@ def BulkRead(self, length, timeout_ms=None): def Timeout(self, timeout_ms): return self.stub_base.Timeout(timeout_ms) - - -class StubAdbCommands(AdbCommands): - """AdbCommands stub""" - def _Connect(self, banner=None, **kwargs): - """Simply let Connect pass, so we can test param parsing""" - pass diff --git a/tox.ini b/tox.ini index 4880a55..0f9881a 100644 --- a/tox.ini +++ b/tox.ini @@ -12,5 +12,6 @@ envlist = deps = pytest pytest-cov + mock usedevelop = True commands = py.test --cov adb test