From edf2f7c62a0299d9a63583bcc5c3869434585413 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 27 Aug 2026 23:54:39 -0500 Subject: [PATCH] Let print_summary own missing input handling --- esphome/espidf/size_summary.py | 16 ++---- esphome/espidf/toolchain.py | 7 +-- tests/unit_tests/test_size_summary.py | 78 +++++++++++---------------- 3 files changed, 37 insertions(+), 64 deletions(-) diff --git a/esphome/espidf/size_summary.py b/esphome/espidf/size_summary.py index 0d39a8b0e8..d4816872f2 100644 --- a/esphome/espidf/size_summary.py +++ b/esphome/espidf/size_summary.py @@ -72,9 +72,7 @@ def _find_app_partition_size(partitions_csv: Path) -> int: raise ValueError(f"No app+factory or app+ota_0 partition in {partitions_csv}") -def print_summary( - size_json: Path, partitions_csv: Path | None, firmware_bin: Path | None -) -> None: +def print_summary(size_json: Path, partitions_csv: Path, firmware_bin: Path) -> None: """Print PlatformIO-shaped RAM and Flash one-liners. Failures are non-fatal: the build has already succeeded, we just couldn't @@ -100,16 +98,10 @@ def print_summary( if ram_total and ram_used is not None: print_size_line("RAM", ram_used, ram_total) - if firmware_bin is None or partitions_csv is None: - return - try: - image_size = firmware_bin.stat().st_size - except OSError as e: - _LOGGER.debug("Skipping Flash summary: %s", e) - return try: + flash_used = firmware_bin.stat().st_size app_size = _find_app_partition_size(partitions_csv) - except ValueError as e: + except (OSError, ValueError) as e: _LOGGER.debug("Skipping Flash summary: %s", e) return - print_size_line("Flash", image_size, app_size) + print_size_line("Flash", flash_used, app_size) diff --git a/esphome/espidf/toolchain.py b/esphome/espidf/toolchain.py index a91659e2c2..9884084907 100644 --- a/esphome/espidf/toolchain.py +++ b/esphome/espidf/toolchain.py @@ -542,12 +542,7 @@ def run_compile(config, verbose: bool) -> int: if rc == 0: size_json = CORE.relative_build_path("build", "esp_idf_size.json") partitions = CORE.relative_build_path("partitions.csv") - firmware_bin = get_firmware_path() - print_summary( - size_json, - partitions if partitions.is_file() else None, - firmware_bin if firmware_bin.is_file() else None, - ) + print_summary(size_json, partitions, get_firmware_path()) return rc diff --git a/tests/unit_tests/test_size_summary.py b/tests/unit_tests/test_size_summary.py index a16585efa1..ff5c7b95c1 100644 --- a/tests/unit_tests/test_size_summary.py +++ b/tests/unit_tests/test_size_summary.py @@ -17,6 +17,16 @@ def _write_size_json(tmp_path: Path, data: dict) -> Path: return out +def _write_partitions(tmp_path: Path) -> Path: + """Drop a partitions.csv with a 0x1C0000 (1835008 byte) app slot.""" + out = tmp_path / "partitions.csv" + out.write_text( + "# name, type, subtype, offset, size, flags\n" + "app0, app, ota_0, 0x10000, 0x1C0000,\n" + ) + return out + + def _esp32_size_data() -> dict: """Synthetic json2 esp_idf_size.json for the original ESP32 (split IRAM/DRAM).""" return { @@ -77,12 +87,17 @@ def _s3_size_data() -> dict: } +def _print_summary_ram_only(tmp_path: Path, size_json: Path) -> None: + """Call print_summary with no partitions.csv or firmware bin on disk.""" + print_summary(size_json, tmp_path / "partitions.csv", tmp_path / "firmware.bin") + + def test_print_summary_esp32_uses_dram( tmp_path: Path, capsys: pytest.CaptureFixture[str] ) -> None: """Original ESP32: RAM = DRAM.used / DRAM.total.""" size_json = _write_size_json(tmp_path, _esp32_size_data()) - print_summary(size_json, partitions_csv=None, firmware_bin=None) + _print_summary_ram_only(tmp_path, size_json) out = capsys.readouterr().out assert "RAM:" in out assert "used 47332 bytes from 180736 bytes" in out @@ -93,7 +108,7 @@ def test_print_summary_s3_falls_back_to_diram( ) -> None: """ESP32-S3 with no DRAM entry falls back to DIRAM and reports raw region usage.""" size_json = _write_size_json(tmp_path, _s3_size_data()) - print_summary(size_json, partitions_csv=None, firmware_bin=None) + _print_summary_ram_only(tmp_path, size_json) out = capsys.readouterr().out assert "used 104999 bytes from 341760 bytes" in out @@ -106,18 +121,10 @@ def test_print_summary_skips_when_diram_total_collapses( tmp_path, { "version": "1.1", - "layout": [ - { - "name": "DIRAM", - "total": 0, - "used": 0, - "free": 0, - "parts": {}, - }, - ], + "layout": [{"name": "DIRAM", "total": 0, "used": 0}], }, ) - print_summary(size_json, partitions_csv=None, firmware_bin=None) + _print_summary_ram_only(tmp_path, size_json) out = capsys.readouterr().out assert "RAM:" not in out @@ -126,9 +133,7 @@ def test_print_summary_handles_missing_json( tmp_path: Path, capsys: pytest.CaptureFixture[str] ) -> None: """Missing size json is non-fatal and prints nothing.""" - print_summary( - tmp_path / "does_not_exist.json", partitions_csv=None, firmware_bin=None - ) + _print_summary_ram_only(tmp_path, tmp_path / "does_not_exist.json") assert capsys.readouterr().out == "" @@ -137,7 +142,7 @@ def test_print_summary_handles_no_layout( ) -> None: """A size json without ``layout`` still doesn't crash.""" size_json = _write_size_json(tmp_path, {"version": "1.1"}) - print_summary(size_json, partitions_csv=None, firmware_bin=None) + _print_summary_ram_only(tmp_path, size_json) assert capsys.readouterr().out == "" @@ -147,11 +152,7 @@ def test_print_summary_flash_line( """A partition table with an app row yields the Flash line in the exact padded shape script/ci_memory_impact_extract.py greps.""" size_json = _write_size_json(tmp_path, _esp32_size_data()) - partitions = tmp_path / "partitions.csv" - partitions.write_text( - "# name, type, subtype, offset, size, flags\n" - "app0, app, ota_0, 0x10000, 0x1C0000,\n" - ) + partitions = _write_partitions(tmp_path) firmware_bin = tmp_path / "firmware.bin" firmware_bin.write_bytes(b"\x00" * 827455) print_summary(size_json, partitions, firmware_bin) @@ -160,33 +161,18 @@ def test_print_summary_flash_line( assert "(used 827455 bytes from 1835008 bytes)" in out -def test_print_summary_skips_flash_when_bin_unreadable( - tmp_path: Path, capsys: pytest.CaptureFixture[str] +@pytest.mark.parametrize("missing", ["bin", "partitions"]) +def test_print_summary_skips_flash_on_missing_input( + missing: str, tmp_path: Path, capsys: pytest.CaptureFixture[str] ) -> None: - """A firmware bin that can't be stat'ed skips the Flash line, not the RAM line.""" + """A missing firmware bin or partitions.csv skips the Flash line, not the RAM line.""" size_json = _write_size_json(tmp_path, _esp32_size_data()) - partitions = tmp_path / "partitions.csv" - partitions.write_text( - "# name, type, subtype, offset, size, flags\n" - "app0, app, ota_0, 0x10000, 0x1C0000,\n" - ) - print_summary(size_json, partitions, tmp_path / "missing.bin") - out = capsys.readouterr().out - assert "RAM:" in out - assert "Flash:" not in out - - -def test_print_summary_skips_flash_without_bin( - tmp_path: Path, capsys: pytest.CaptureFixture[str] -) -> None: - """No firmware bin means the RAM line prints but the Flash line is skipped.""" - size_json = _write_size_json(tmp_path, _esp32_size_data()) - partitions = tmp_path / "partitions.csv" - partitions.write_text( - "# name, type, subtype, offset, size, flags\n" - "app0, app, ota_0, 0x10000, 0x1C0000,\n" - ) - print_summary(size_json, partitions, firmware_bin=None) + firmware_bin = tmp_path / "firmware.bin" + if missing == "bin": + _write_partitions(tmp_path) + else: + firmware_bin.write_bytes(b"\x00" * 16) + print_summary(size_json, tmp_path / "partitions.csv", firmware_bin) out = capsys.readouterr().out assert "RAM:" in out assert "Flash:" not in out