diff --git a/src/py_openocd_client/client.py b/src/py_openocd_client/client.py index abfabcb..b51f527 100644 --- a/src/py_openocd_client/client.py +++ b/src/py_openocd_client/client.py @@ -666,11 +666,24 @@ def exit(self) -> None: def shutdown(self) -> None: """ Shut down the OpenOCD process by sending the ``shutdown`` command to it. - Then terminate the connection. + PyOpenocd client also gets immediately disconnected from OpenOCD. """ - # OpenOCD's shutdown command returns a non-zero error code (which is expected). - # For that reason, throw=False is used. - self.cmd("shutdown", throw=False) + # Different OpenOCD versions respond to "shutdown" command differently: + # + # - OpenOCD 0.12.0 and older: + # The "shutdown" command results in a non-zero TCL return code, + # which can be obtained normally as for any other TCL command - + # e.g. via the "catch" command. + # + # - OpenOCD 0.13.0-dev and newer (from to commit "93f16eed4"): + # The "shutdown" command immediately ends the TCL processing and + # an empty response is sent back to the TCL client. + + # For the above reasons, send the shutdown command via raw_cmd() and: + # - don't wrap "shutdown" into any other TCL commands, + # - don't expect any particular response. + self.raw_cmd("shutdown") + self.disconnect() def raw_cmd(self, raw_cmd: str, timeout: Optional[float] = None) -> str: diff --git a/tests_unit/test_client_commands.py b/tests_unit/test_client_commands.py index a97fd6e..a042e26 100644 --- a/tests_unit/test_client_commands.py +++ b/tests_unit/test_client_commands.py @@ -451,6 +451,8 @@ def test_exit(ocd): def test_shutdown(ocd): ocd.disconnect = mock.Mock() + ocd.raw_cmd = mock.Mock() ocd.shutdown() - ocd.cmd.assert_called_once_with("shutdown", throw=False) + assert not ocd.cmd.called + ocd.raw_cmd.assert_called_once_with("shutdown") ocd.disconnect.assert_called_once()