Skip to content

Commit 600d5da

Browse files
authored
Merge pull request #32 from bp100a/31-update-mockusbip-server-to-use-the-sockewrapper-class-for-greater-testability
31 update mockusbip server to use the sockewrapper class for greater testability
2 parents 9e1be85 + 61c88e2 commit 600d5da

File tree

5 files changed

+43
-13
lines changed

5 files changed

+43
-13
lines changed

CHANGES

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# CHANGES
2+
## [1.0.4] - 2024-09-06
3+
- for testability, MockUSBIP server uses the SocketWrapper class
4+
for socket abstraction, will allow injecting errors for testing
5+
26
## [1.0.3] - 2024-09-06
37
- refactoring for better test coverage of the Mock USBIP server
8+
- for testability, MockUSBIP server uses the SocketWrapper class
9+
for socket abstraction, will allow injecting errors for testing
410

511
## [1.0.2] - 2024-09-05
612
- refactor some code to increase test coverage of the usbip_client.py

coverage.svg

Lines changed: 3 additions & 3 deletions
Loading

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Repository = "https://github.com/bp100a/serial-usbipclient"
4848
name = "serial-usbipclient"
4949
authors = ["Harry J. Collins <[email protected]>"]
5050
maintainers = ["Harry J. Collins <[email protected]>"]
51-
version = "1.0.3"
51+
version = "1.0.4"
5252
description = "Simple serial connectivity to CDC (serial) USB devices exposed by a USBIPD service."
5353
readme = "README.md"
5454
package-mode = true

serial_usbipclient/socket_wrapper.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""wraps socket so we can abstract if for testing (dependency injection)"""
22
from socket import AddressFamily, SocketKind, socket
3+
from typing import Any
34

45
from typing_extensions import Buffer
56

@@ -46,3 +47,24 @@ def sendall(self, data: bytes) -> None:
4647
def recv(self, size: int) -> bytes:
4748
"""read from our socket"""
4849
return self._socket.recv(size)
50+
51+
def bind(self, address: tuple[str, int]) -> None:
52+
"""bind to the socket"""
53+
return self._socket.bind(address)
54+
55+
def listen(self, backlog: int):
56+
"""listen for connections"""
57+
return self._socket.listen(backlog)
58+
59+
def accept(self) -> tuple[socket, Any]:
60+
"""accept a connection"""
61+
return self._socket.accept()
62+
63+
def fileno(self) -> int:
64+
"""return the sockets file"""
65+
return self._socket.fileno()
66+
67+
@property
68+
def raw_socket(self) -> socket:
69+
"""get the underlying socket"""
70+
return self._socket

serial_usbipclient/tests/mock_usbip.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
UrbSetupPacket, URBStandardDeviceRequest)
3535
from serial_usbipclient.protocol.usb_descriptors import DescriptorType
3636
from serial_usbipclient.protocol.usbip_defs import BasicCommands, Direction
37+
from serial_usbipclient.socket_wrapper import SocketWrapper
3738

3839
LOGGER: logging.Logger = logging.getLogger(__name__)
3940

@@ -423,12 +424,13 @@ class DebugCommands(StrEnum):
423424
NO_WRITE_RESPONSE = 'no-write-response' # write does not return acknowledgement
424425
NO_READ_RESPONSE = 'no-read-response' # suppress an expected read
425426

426-
def __init__(self, host: str, port: int):
427+
def __init__(self, host: str, port: int, socket_class: type = SocketWrapper):
427428
"""set up our instance"""
428429
self.host: str = host
429430
self.port: int = port
430431
self.logger: logging.Logger = LOGGER
431-
self.server_socket: Optional[socket.socket] = None
432+
self.server_socket: Optional[SocketWrapper] = None
433+
self._socket_class: type = socket_class
432434
self.thread: Optional[Thread] = Thread(name=f'mock-usbip@{self.host}:{self.port}', target=self.run_server, daemon=True)
433435
self.event: Event = Event()
434436
self._is_windows: bool = platform.system() == 'Windows'
@@ -676,7 +678,7 @@ def wait_for_message(self, conn: Optional[USBIPServerClient] = None) -> tuple[US
676678
if self._wakeup is None or self.server_socket is None:
677679
raise ValueError("neither the wakeup or server socket can be empty!")
678680

679-
rlist: list[socket.socket | USBIPServerClient] = [self._wakeup.listener, self.server_socket]
681+
rlist: list[socket.socket | USBIPServerClient] = [self._wakeup.listener, self.server_socket.raw_socket]
680682
if conn:
681683
if conn not in self._clients:
682684
self._clients.append(conn)
@@ -693,7 +695,7 @@ def wait_for_message(self, conn: Optional[USBIPServerClient] = None) -> tuple[US
693695
if socket_read == self._wakeup.listener: # time to bail
694696
self.logger.info("Wakeup!")
695697
raise OrderlyExit("wakeup!")
696-
elif socket_read == self.server_socket: # someone is knocking
698+
elif socket_read == self.server_socket.raw_socket: # someone is knocking
697699
self.logger.info(f"wait_for_message(): accept() {self.server_socket=}")
698700
new_conn, address = self.server_socket.accept() # accept new connection
699701
client: USBIPServerClient = USBIPServerClient(connection=new_conn, address=address)
@@ -750,12 +752,12 @@ def read_message(self, client: Optional[USBIPServerClient] = None) -> tuple[USBI
750752

751753
def run_server(self):
752754
"""standup the server, start listening"""
753-
self.server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
754-
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
755-
self.server_socket.bind((self.host, self.port))
756-
self.server_socket.settimeout(None) # so our accept() will block
755+
self.server_socket = self._socket_class(socket.AF_INET, socket.SOCK_STREAM)
756+
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # type: ignore[union-attr]
757+
self.server_socket.bind((self.host, self.port)) # type: ignore[union-attr]
758+
self.server_socket.settimeout(None) # type: ignore[union-attr] # accept should block
757759

758-
self.server_socket.listen(1) # only allow one connection (testing)
760+
self.server_socket.listen(1) # type: ignore[union-attr] # only allow one connection (testing)
759761
self.event.set()
760762
self.logger.info("\nmock USBIP server started @%s:%s", self.host, self.port)
761763
try:

0 commit comments

Comments
 (0)