mirror of
https://github.com/esphome/esphome.git
synced 2026-09-22 20:48:43 +00:00
make linux error reporting more helpful
This commit is contained in:
@@ -73,6 +73,7 @@ from esphome.const import (
|
||||
PLATFORM_RP2040,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError
|
||||
from esphome.util import BootselResult
|
||||
|
||||
|
||||
def strip_ansi_codes(text: str) -> str:
|
||||
@@ -872,7 +873,7 @@ def test_choose_upload_log_host_no_defaults_with_rp2040_bootsel(
|
||||
patch(
|
||||
"esphome.__main__._find_picotool", return_value=Path("/usr/bin/picotool")
|
||||
),
|
||||
patch("esphome.__main__.detect_rp2040_bootsel", return_value=1),
|
||||
patch("esphome.__main__.detect_rp2040_bootsel", return_value=BootselResult(1)),
|
||||
):
|
||||
result = choose_upload_log_host(
|
||||
default=None,
|
||||
@@ -895,7 +896,7 @@ def test_choose_upload_log_host_rp2040_no_device_shows_bootsel_help() -> None:
|
||||
patch(
|
||||
"esphome.__main__._find_picotool", return_value=Path("/usr/bin/picotool")
|
||||
),
|
||||
patch("esphome.__main__.detect_rp2040_bootsel", return_value=0),
|
||||
patch("esphome.__main__.detect_rp2040_bootsel", return_value=BootselResult(0)),
|
||||
pytest.raises(EsphomeError, match="BOOTSEL"),
|
||||
):
|
||||
choose_upload_log_host(
|
||||
@@ -920,7 +921,7 @@ def test_choose_upload_log_host_rp2040_bootsel_tip_with_ota(
|
||||
patch(
|
||||
"esphome.__main__._find_picotool", return_value=Path("/usr/bin/picotool")
|
||||
),
|
||||
patch("esphome.__main__.detect_rp2040_bootsel", return_value=0),
|
||||
patch("esphome.__main__.detect_rp2040_bootsel", return_value=BootselResult(0)),
|
||||
patch(
|
||||
"esphome.__main__.choose_prompt",
|
||||
return_value="192.168.1.100",
|
||||
@@ -949,7 +950,7 @@ def test_choose_upload_log_host_rp2040_bootsel_tip_with_serial_ports(
|
||||
"esphome.__main__._find_picotool",
|
||||
return_value=Path("/usr/bin/picotool"),
|
||||
),
|
||||
patch("esphome.__main__.detect_rp2040_bootsel", return_value=0),
|
||||
patch("esphome.__main__.detect_rp2040_bootsel", return_value=BootselResult(0)),
|
||||
caplog.at_level(logging.INFO, logger="esphome.__main__"),
|
||||
):
|
||||
choose_upload_log_host(
|
||||
@@ -960,6 +961,69 @@ def test_choose_upload_log_host_rp2040_bootsel_tip_with_serial_ports(
|
||||
assert "BOOTSEL" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_no_serial_ports")
|
||||
def test_choose_upload_log_host_rp2040_permission_error_no_options(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test permission warning shown when BOOTSEL device found but not accessible."""
|
||||
setup_core(platform=PLATFORM_RP2040)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"esphome.__main__._find_picotool", return_value=Path("/usr/bin/picotool")
|
||||
),
|
||||
patch(
|
||||
"esphome.__main__.detect_rp2040_bootsel",
|
||||
return_value=BootselResult(0, permission_error=True),
|
||||
),
|
||||
patch("esphome.__main__.sys.platform", "linux"),
|
||||
pytest.raises(EsphomeError, match="BOOTSEL"),
|
||||
caplog.at_level(logging.WARNING, logger="esphome.__main__"),
|
||||
):
|
||||
choose_upload_log_host(
|
||||
default=None,
|
||||
check_default=None,
|
||||
purpose=Purpose.UPLOADING,
|
||||
)
|
||||
|
||||
assert "USB permissions" in caplog.text
|
||||
assert "udev" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_no_serial_ports")
|
||||
def test_choose_upload_log_host_rp2040_permission_error_with_ota(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test permission warning shown with OTA fallback available."""
|
||||
setup_core(
|
||||
platform=PLATFORM_RP2040,
|
||||
config={CONF_OTA: [{CONF_PLATFORM: CONF_ESPHOME}]},
|
||||
address="192.168.1.100",
|
||||
)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"esphome.__main__._find_picotool", return_value=Path("/usr/bin/picotool")
|
||||
),
|
||||
patch(
|
||||
"esphome.__main__.detect_rp2040_bootsel",
|
||||
return_value=BootselResult(0, permission_error=True),
|
||||
),
|
||||
patch(
|
||||
"esphome.__main__.choose_prompt",
|
||||
return_value="192.168.1.100",
|
||||
),
|
||||
caplog.at_level(logging.WARNING, logger="esphome.__main__"),
|
||||
):
|
||||
choose_upload_log_host(
|
||||
default=None,
|
||||
check_default=None,
|
||||
purpose=Purpose.UPLOADING,
|
||||
)
|
||||
|
||||
assert "USB permissions" in caplog.text
|
||||
|
||||
|
||||
def test_choose_upload_log_host_no_bootsel_for_non_rp2040(
|
||||
mock_no_serial_ports: Mock,
|
||||
) -> None:
|
||||
@@ -997,7 +1061,7 @@ def test_choose_upload_log_host_rp2040_serial_and_bootsel(
|
||||
patch(
|
||||
"esphome.__main__._find_picotool", return_value=Path("/usr/bin/picotool")
|
||||
),
|
||||
patch("esphome.__main__.detect_rp2040_bootsel", return_value=1),
|
||||
patch("esphome.__main__.detect_rp2040_bootsel", return_value=BootselResult(1)),
|
||||
):
|
||||
choose_upload_log_host(
|
||||
default=None,
|
||||
|
||||
@@ -461,8 +461,9 @@ def test_detect_rp2040_bootsel_found() -> None:
|
||||
mock_result = MagicMock()
|
||||
mock_result.stdout = b"Device Information\n type: RP2040\n"
|
||||
with patch("esphome.util.subprocess.run", return_value=mock_result):
|
||||
count = util.detect_rp2040_bootsel("/usr/bin/picotool")
|
||||
assert count == 1
|
||||
result = util.detect_rp2040_bootsel("/usr/bin/picotool")
|
||||
assert result.device_count == 1
|
||||
assert result.permission_error is False
|
||||
|
||||
|
||||
def test_detect_rp2040_bootsel_multiple() -> None:
|
||||
@@ -470,8 +471,9 @@ def test_detect_rp2040_bootsel_multiple() -> None:
|
||||
mock_result = MagicMock()
|
||||
mock_result.stdout = b"type: RP2040\ntype: RP2350\n"
|
||||
with patch("esphome.util.subprocess.run", return_value=mock_result):
|
||||
count = util.detect_rp2040_bootsel("/usr/bin/picotool")
|
||||
assert count == 2
|
||||
result = util.detect_rp2040_bootsel("/usr/bin/picotool")
|
||||
assert result.device_count == 2
|
||||
assert result.permission_error is False
|
||||
|
||||
|
||||
def test_detect_rp2040_bootsel_none() -> None:
|
||||
@@ -480,16 +482,47 @@ def test_detect_rp2040_bootsel_none() -> None:
|
||||
mock_result.stdout = (
|
||||
b"No accessible RP2040/RP2350 devices in BOOTSEL mode were found.\n"
|
||||
)
|
||||
mock_result.stderr = b""
|
||||
with patch("esphome.util.subprocess.run", return_value=mock_result):
|
||||
count = util.detect_rp2040_bootsel("/usr/bin/picotool")
|
||||
assert count == 0
|
||||
result = util.detect_rp2040_bootsel("/usr/bin/picotool")
|
||||
assert result.device_count == 0
|
||||
assert result.permission_error is False
|
||||
|
||||
|
||||
def test_detect_rp2040_bootsel_permission_error() -> None:
|
||||
"""Test BOOTSEL detection with device found but not accessible."""
|
||||
mock_result = MagicMock()
|
||||
mock_result.stdout = (
|
||||
b"No accessible RP-series devices in BOOTSEL mode were found.\n"
|
||||
)
|
||||
mock_result.stderr = (
|
||||
b"RP2040 device at bus 5, address 24 appears to be in BOOTSEL mode, "
|
||||
b"but picotool was unable to connect. "
|
||||
b"Maybe try 'sudo' or check your permissions.\n"
|
||||
)
|
||||
with patch("esphome.util.subprocess.run", return_value=mock_result):
|
||||
result = util.detect_rp2040_bootsel("/usr/bin/picotool")
|
||||
assert result.device_count == 0
|
||||
assert result.permission_error is True
|
||||
|
||||
|
||||
def test_detect_rp2040_bootsel_libusb_access_error() -> None:
|
||||
"""Test BOOTSEL detection with LIBUSB_ERROR_ACCESS."""
|
||||
mock_result = MagicMock()
|
||||
mock_result.stdout = b""
|
||||
mock_result.stderr = b"LIBUSB_ERROR_ACCESS\n"
|
||||
with patch("esphome.util.subprocess.run", return_value=mock_result):
|
||||
result = util.detect_rp2040_bootsel("/usr/bin/picotool")
|
||||
assert result.device_count == 0
|
||||
assert result.permission_error is True
|
||||
|
||||
|
||||
def test_detect_rp2040_bootsel_oserror() -> None:
|
||||
"""Test BOOTSEL detection handles OSError."""
|
||||
with patch("esphome.util.subprocess.run", side_effect=OSError("not found")):
|
||||
count = util.detect_rp2040_bootsel("/usr/bin/picotool")
|
||||
assert count == 0
|
||||
result = util.detect_rp2040_bootsel("/usr/bin/picotool")
|
||||
assert result.device_count == 0
|
||||
assert result.permission_error is False
|
||||
|
||||
|
||||
def test_detect_rp2040_bootsel_timeout() -> None:
|
||||
@@ -498,5 +531,6 @@ def test_detect_rp2040_bootsel_timeout() -> None:
|
||||
"esphome.util.subprocess.run",
|
||||
side_effect=subprocess.TimeoutExpired("picotool", 10),
|
||||
):
|
||||
count = util.detect_rp2040_bootsel("/usr/bin/picotool")
|
||||
assert count == 0
|
||||
result = util.detect_rp2040_bootsel("/usr/bin/picotool")
|
||||
assert result.device_count == 0
|
||||
assert result.permission_error is False
|
||||
|
||||
Reference in New Issue
Block a user