diff --git a/cli/src/mpftp/sidecar.py b/cli/src/mpftp/sidecar.py index c50dfb3..459fa9e 100644 --- a/cli/src/mpftp/sidecar.py +++ b/cli/src/mpftp/sidecar.py @@ -1159,7 +1159,10 @@ def _network_open_error(self, device: str, exc: BaseException) -> str: answers ping (lwIP has its own task), but WebREPL never replies, since the esp32 port only services its sockets from the VM's poll hook. So a silent WebREPL on a host that answers ping, or on a board that logged - in earlier in this session, is a busy board, and the user is told so. + in earlier, is a busy board, and the user is told so. When the TCP + connect itself timed out, though, the board may simply be off: then + ping decides even for a board that answered before, so a switched-off + board isn't sent to serial or the reset button. """ text = str(exc) if isinstance(exc, webrepl.WebReplAuthError): @@ -1172,7 +1175,9 @@ def _network_open_error(self, device: str, exc: BaseException) -> str: ) if not silent: return text - if "never answered the WebSocket upgrade" in text or device in self._answered: + if "never answered the WebSocket upgrade" in text: + return f"{device}: {wifiboard.STUCK_MESSAGE}" + if device in self._answered and "no answer from" not in text: return f"{device}: {wifiboard.STUCK_MESSAGE}" try: host = webrepl.parse_device(device)[0] diff --git a/cli/tests/test_wifi_access.py b/cli/tests/test_wifi_access.py index 1980724..036921a 100644 --- a/cli/tests/test_wifi_access.py +++ b/cli/tests/test_wifi_access.py @@ -375,14 +375,31 @@ def test_a_host_that_is_gone_keeps_the_plain_message(self): with mock.patch.object(webrepl, "host_answers_ping", return_value=False): self.assertEqual(s._network_open_error("ws://10.0.0.5", err), str(err)) - def test_a_board_that_answered_before_is_busy_without_asking_ping(self): + def test_a_board_that_answered_before_and_goes_quiet_after_connecting_is_busy(self): s = sidecar.Session() s._answered.add("ws://10.0.0.5") - err = webrepl.WebReplError("ws://10.0.0.5: no answer from 10.0.0.5:8266 in 5 s.") + err = webrepl.WebReplError("ws://10.0.0.5: no the password prompt within 5 s (got b'')") with mock.patch.object(webrepl, "host_answers_ping") as ping: self.assertIn(wifiboard.STUCK_MESSAGE, s._network_open_error("ws://10.0.0.5", err)) ping.assert_not_called() + def test_a_board_that_answered_before_and_is_now_off_is_not_called_busy(self): + # A saved board that's switched off: the TCP connect times out and + # ping gets no answer, so the plain "is the board on this network" + # message stands. + s = sidecar.Session() + s._answered.add("ws://10.0.0.5") + err = webrepl.WebReplError("ws://10.0.0.5: no answer from 10.0.0.5:8266 in 5 s.") + with mock.patch.object(webrepl, "host_answers_ping", return_value=False): + self.assertEqual(s._network_open_error("ws://10.0.0.5", err), str(err)) + + def test_a_board_that_answered_before_and_still_answers_ping_is_busy(self): + s = sidecar.Session() + s._answered.add("ws://10.0.0.5") + err = webrepl.WebReplError("ws://10.0.0.5: no answer from 10.0.0.5:8266 in 5 s.") + with mock.patch.object(webrepl, "host_answers_ping", return_value=True): + self.assertIn(wifiboard.STUCK_MESSAGE, s._network_open_error("ws://10.0.0.5", err)) + def test_a_refused_connection_is_not_called_busy(self): s = sidecar.Session() s._answered.add("ws://10.0.0.5")