diff --git a/esphome/espidf/bootloader.py b/esphome/espidf/bootloader.py index 65237a371ac..b0d58d1a981 100644 --- a/esphome/espidf/bootloader.py +++ b/esphome/espidf/bootloader.py @@ -75,9 +75,13 @@ def _compute_enabled() -> bool: _LOGGER.info("IDF changed its bootloader macro; building in-tree") return False return True - except (OSError, KeyError, ValueError, EsphomeError) as err: + except (OSError, EsphomeError) as err: _LOGGER.info("Bootloader cache disabled: %s", err) return False + except (KeyError, ValueError) as err: + # More likely a regression than the environment; still fail safe. + _LOGGER.warning("Bootloader cache disabled: %s", err) + return False def _snapshot_blocks_cache(sdkconfig_path: Path) -> bool: diff --git a/tests/unit_tests/test_espidf_bootloader.py b/tests/unit_tests/test_espidf_bootloader.py index 2a86e76b21c..c570b4325f6 100644 --- a/tests/unit_tests/test_espidf_bootloader.py +++ b/tests/unit_tests/test_espidf_bootloader.py @@ -118,14 +118,25 @@ def test_enabled_readonly_tools_prefix_disables(tmp_path: Path) -> None: assert bootloader._compute_enabled() is False -def test_enabled_swallows_errors_as_disabled(tmp_path: Path) -> None: - """Any failure while deciding must read as disabled, never raise.""" +@pytest.mark.parametrize( + ("err", "level"), + [(OSError("boom"), "INFO"), (ValueError("bad"), "WARNING")], + ids=["environment", "regression"], +) +def test_enabled_swallows_errors_as_disabled( + tmp_path: Path, caplog: pytest.LogCaptureFixture, err: Exception, level: str +) -> None: + """Any failure while deciding must read as disabled, never raise; + likely regressions log louder than environmental errors.""" _write_snapshot("CONFIG_APP_REPRODUCIBLE_BUILD=y\n") with ( _tools_prefix(tmp_path), - patch.object(toolchain, "_get_idf_path", side_effect=OSError("boom")), + patch.object(toolchain, "_get_idf_path", side_effect=err), + caplog.at_level("INFO"), ): assert bootloader._compute_enabled() is False + record = next(r for r in caplog.records if "cache disabled" in r.message) + assert record.levelname == level def test_bootloader_cache_enabled_is_cached_per_run() -> None: