From a85c3237de3b3d62b6a8bfc475005cc07239d97a Mon Sep 17 00:00:00 2001 From: Bluetooth Devices Bot Date: Fri, 15 May 2026 10:07:04 +0000 Subject: [PATCH 1/3] test(send_reply): cover generic exception, send_error, no-exception paths Closes three coverage gaps in `SendReply`: - non-DBusError exceptions converted to SERVICE_ERROR replies (was untested) - the public `send_error()` method (was untested) - the no-exception branch returning False without sending anything `src/dbus_fast/send_reply.py` now hits 100% line and branch coverage. --- tests/test_send_reply.py | 112 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/tests/test_send_reply.py b/tests/test_send_reply.py index d794dd45..47b3cdbc 100644 --- a/tests/test_send_reply.py +++ b/tests/test_send_reply.py @@ -61,6 +61,118 @@ def _setup_socket(self) -> None: mock_message_bus._finalize(None) +def test_send_reply_generic_exception() -> None: + """Non-DBusError exceptions become SERVICE_ERROR replies.""" + + messages: list[Message] = [] + + class MockClosable: + def close(self) -> None: + pass + + class MockBus(BaseMessageBus): + def send(self, msg: Message) -> None: + messages.append(msg) + + def send_message(self, msg: Message) -> None: + messages.append(msg) + + def _setup_socket(self) -> None: + self._sock = MockClosable() # type: ignore + self._stream = MockClosable() # type: ignore + + with patch("socket.socket.connect"): + mock_message_bus = MockBus() + mock_message = Message( + path="/test/path", interface="test.interface", member="test_member", serial=1 + ) + send_reply = SendReply(mock_message_bus, mock_message) + + with send_reply: + raise RuntimeError("boom") + + assert len(messages) == 1 + assert messages[0].message_type == MessageType.ERROR + assert messages[0].error_name == ErrorType.SERVICE_ERROR.value + assert messages[0].reply_serial == 1 + assert "boom" in messages[0].body[0] + + mock_message_bus.disconnect() + mock_message_bus._finalize(None) + + +def test_send_reply_send_error() -> None: + """send_error() routes an exception through the same error reply path.""" + + messages: list[Message] = [] + + class MockClosable: + def close(self) -> None: + pass + + class MockBus(BaseMessageBus): + def send(self, msg: Message) -> None: + messages.append(msg) + + def send_message(self, msg: Message) -> None: + messages.append(msg) + + def _setup_socket(self) -> None: + self._sock = MockClosable() # type: ignore + self._stream = MockClosable() # type: ignore + + with patch("socket.socket.connect"): + mock_message_bus = MockBus() + mock_message = Message( + path="/test/path", interface="test.interface", member="test_member", serial=1 + ) + send_reply = SendReply(mock_message_bus, mock_message) + + send_reply.send_error(DBusError(ErrorType.FAILED, "explicit failure", None)) + + assert len(messages) == 1 + assert messages[0].message_type == MessageType.ERROR + assert messages[0].error_name == "org.freedesktop.DBus.Error.Failed" + assert messages[0].reply_serial == 1 + + mock_message_bus.disconnect() + mock_message_bus._finalize(None) + + +def test_send_reply_no_exception_returns_false() -> None: + """Exiting with no exception must not suppress and must not send a reply.""" + + messages: list[Message] = [] + + class MockClosable: + def close(self) -> None: + pass + + class MockBus(BaseMessageBus): + def send(self, msg: Message) -> None: + messages.append(msg) + + def send_message(self, msg: Message) -> None: + messages.append(msg) + + def _setup_socket(self) -> None: + self._sock = MockClosable() # type: ignore + self._stream = MockClosable() # type: ignore + + with patch("socket.socket.connect"): + mock_message_bus = MockBus() + mock_message = Message( + path="/test/path", interface="test.interface", member="test_member", serial=1 + ) + send_reply = SendReply(mock_message_bus, mock_message) + + assert send_reply._exit(None, None, None) is False + assert messages == [] + + mock_message_bus.disconnect() + mock_message_bus._finalize(None) + + def test_send_reply_happy_path() -> None: """Test that SendReply sends a message.""" From 3745abb65c6c4915bb465ab286d59c8a19b984cc Mon Sep 17 00:00:00 2001 From: Bluetooth Devices Bot Date: Fri, 15 May 2026 13:19:32 +0000 Subject: [PATCH 2/3] test(send_reply): extract shared MockBus fixture per review --- tests/test_send_reply.py | 162 ++++++++++----------------------------- 1 file changed, 40 insertions(+), 122 deletions(-) diff --git a/tests/test_send_reply.py b/tests/test_send_reply.py index 47b3cdbc..195ac778 100644 --- a/tests/test_send_reply.py +++ b/tests/test_send_reply.py @@ -22,9 +22,10 @@ def mock_address() -> Generator[None, None, None]: os.environ["DBUS_SESSION_BUS_ADDRESS"] = original_address -def test_send_reply_exception() -> None: - """Test that SendReply sends an error message when DBusError is raised.""" - +@pytest.fixture +def send_reply_setup() -> ( + Generator[tuple[BaseMessageBus, Message, list[Message]], None, None] +): messages: list[Message] = [] class MockClosable: @@ -43,11 +44,23 @@ def _setup_socket(self) -> None: self._stream = MockClosable() # type: ignore with patch("socket.socket.connect"): - mock_message_bus = MockBus() - mock_message = Message( + bus = MockBus() + msg = Message( path="/test/path", interface="test.interface", member="test_member", serial=1 ) - send_reply = SendReply(mock_message_bus, mock_message) + try: + yield bus, msg, messages + finally: + bus.disconnect() + bus._finalize(None) + + +def test_send_reply_exception( + send_reply_setup: tuple[BaseMessageBus, Message, list[Message]], +) -> None: + """Test that SendReply sends an error message when DBusError is raised.""" + bus, msg, messages = send_reply_setup + send_reply = SendReply(bus, msg) with send_reply: raise DBusError(ErrorType.DISCONNECTED, "Disconnected", None) @@ -57,36 +70,13 @@ def _setup_socket(self) -> None: assert messages[0].error_name == "org.freedesktop.DBus.Error.Disconnected" assert messages[0].reply_serial == 1 - mock_message_bus.disconnect() - mock_message_bus._finalize(None) - -def test_send_reply_generic_exception() -> None: +def test_send_reply_generic_exception( + send_reply_setup: tuple[BaseMessageBus, Message, list[Message]], +) -> None: """Non-DBusError exceptions become SERVICE_ERROR replies.""" - - messages: list[Message] = [] - - class MockClosable: - def close(self) -> None: - pass - - class MockBus(BaseMessageBus): - def send(self, msg: Message) -> None: - messages.append(msg) - - def send_message(self, msg: Message) -> None: - messages.append(msg) - - def _setup_socket(self) -> None: - self._sock = MockClosable() # type: ignore - self._stream = MockClosable() # type: ignore - - with patch("socket.socket.connect"): - mock_message_bus = MockBus() - mock_message = Message( - path="/test/path", interface="test.interface", member="test_member", serial=1 - ) - send_reply = SendReply(mock_message_bus, mock_message) + bus, msg, messages = send_reply_setup + send_reply = SendReply(bus, msg) with send_reply: raise RuntimeError("boom") @@ -97,36 +87,13 @@ def _setup_socket(self) -> None: assert messages[0].reply_serial == 1 assert "boom" in messages[0].body[0] - mock_message_bus.disconnect() - mock_message_bus._finalize(None) - -def test_send_reply_send_error() -> None: +def test_send_reply_send_error( + send_reply_setup: tuple[BaseMessageBus, Message, list[Message]], +) -> None: """send_error() routes an exception through the same error reply path.""" - - messages: list[Message] = [] - - class MockClosable: - def close(self) -> None: - pass - - class MockBus(BaseMessageBus): - def send(self, msg: Message) -> None: - messages.append(msg) - - def send_message(self, msg: Message) -> None: - messages.append(msg) - - def _setup_socket(self) -> None: - self._sock = MockClosable() # type: ignore - self._stream = MockClosable() # type: ignore - - with patch("socket.socket.connect"): - mock_message_bus = MockBus() - mock_message = Message( - path="/test/path", interface="test.interface", member="test_member", serial=1 - ) - send_reply = SendReply(mock_message_bus, mock_message) + bus, msg, messages = send_reply_setup + send_reply = SendReply(bus, msg) send_reply.send_error(DBusError(ErrorType.FAILED, "explicit failure", None)) @@ -135,77 +102,28 @@ def _setup_socket(self) -> None: assert messages[0].error_name == "org.freedesktop.DBus.Error.Failed" assert messages[0].reply_serial == 1 - mock_message_bus.disconnect() - mock_message_bus._finalize(None) - -def test_send_reply_no_exception_returns_false() -> None: +def test_send_reply_no_exception_returns_false( + send_reply_setup: tuple[BaseMessageBus, Message, list[Message]], +) -> None: """Exiting with no exception must not suppress and must not send a reply.""" - - messages: list[Message] = [] - - class MockClosable: - def close(self) -> None: - pass - - class MockBus(BaseMessageBus): - def send(self, msg: Message) -> None: - messages.append(msg) - - def send_message(self, msg: Message) -> None: - messages.append(msg) - - def _setup_socket(self) -> None: - self._sock = MockClosable() # type: ignore - self._stream = MockClosable() # type: ignore - - with patch("socket.socket.connect"): - mock_message_bus = MockBus() - mock_message = Message( - path="/test/path", interface="test.interface", member="test_member", serial=1 - ) - send_reply = SendReply(mock_message_bus, mock_message) + bus, msg, messages = send_reply_setup + send_reply = SendReply(bus, msg) assert send_reply._exit(None, None, None) is False assert messages == [] - mock_message_bus.disconnect() - mock_message_bus._finalize(None) - -def test_send_reply_happy_path() -> None: +def test_send_reply_happy_path( + send_reply_setup: tuple[BaseMessageBus, Message, list[Message]], +) -> None: """Test that SendReply sends a message.""" - - messages: list[Message] = [] - - class MockClosable: - def close(self) -> None: - pass - - class MockBus(BaseMessageBus): - def send(self, msg: Message) -> None: - messages.append(msg) - - def send_message(self, msg: Message) -> None: - messages.append(msg) - - def _setup_socket(self) -> None: - self._sock = MockClosable() # type: ignore - self._stream = MockClosable() # type: ignore - - with patch("socket.socket.connect"): - mock_message_bus = MockBus() - mock_message = Message( - path="/test/path", interface="test.interface", member="test_member", serial=1 - ) - send_reply = SendReply(mock_message_bus, mock_message) + bus, msg, messages = send_reply_setup + send_reply = SendReply(bus, msg) with send_reply as reply: - reply(mock_message) + reply(msg) assert len(messages) == 1 assert messages[0].message_type == MessageType.METHOD_CALL assert messages[0].error_name is None - - mock_message_bus.disconnect() - mock_message_bus._finalize(None) From 94ba368124267c92e6d6f767f2c07e43da0f80cf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 13:19:45 +0000 Subject: [PATCH 3/3] chore(pre-commit.ci): auto fixes --- tests/test_send_reply.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_send_reply.py b/tests/test_send_reply.py index 195ac778..408ca996 100644 --- a/tests/test_send_reply.py +++ b/tests/test_send_reply.py @@ -23,9 +23,9 @@ def mock_address() -> Generator[None, None, None]: @pytest.fixture -def send_reply_setup() -> ( - Generator[tuple[BaseMessageBus, Message, list[Message]], None, None] -): +def send_reply_setup() -> Generator[ + tuple[BaseMessageBus, Message, list[Message]], None, None +]: messages: list[Message] = [] class MockClosable: