[core] Extract the shared idedata and size-summary helpers into build_helpers

This commit is contained in:
J. Nick Koston
2026-08-22 21:58:41 -05:00
parent f0651e5c9b
commit dada0f2c2b
21 changed files with 973 additions and 472 deletions
+2 -110
View File
@@ -140,7 +140,7 @@ def test_get_idedata_generates_and_caches(setup_core: Path) -> None:
compile_commands.write_text("[]")
with patch(
"esphome.espidf.idedata.idedata_from_build",
"esphome.build_helpers.idedata.idedata_from_build",
return_value={"cxx_path": "g++"},
) as mock_transform:
result = toolchain.get_idedata()
@@ -151,114 +151,6 @@ def test_get_idedata_generates_and_caches(setup_core: Path) -> None:
assert json.loads(cache.read_text()) == {"cxx_path": "g++", "prog_path": prog_path}
def test_get_idedata_uses_cache_when_valid(setup_core: Path) -> None:
"""A cache at least as new as the compile DB is reused without regenerating."""
compile_commands, cache = _setup_build(setup_core)
compile_commands.parent.mkdir(parents=True, exist_ok=True)
compile_commands.write_text("[]")
cache.parent.mkdir(parents=True, exist_ok=True)
cache.write_text('{"cc_path": "cached-gcc", "cxx_path": "cached"}')
cc_mtime = compile_commands.stat().st_mtime
os.utime(cache, (cc_mtime + 1, cc_mtime + 1))
with patch("esphome.espidf.idedata.idedata_from_build") as mock_transform:
result = toolchain.get_idedata()
mock_transform.assert_not_called()
assert result == {"cc_path": "cached-gcc", "cxx_path": "cached"}
def test_get_idedata_regenerates_cache_without_cc_path(setup_core: Path) -> None:
"""A cache predating cc_path is rebuilt even though it is newer.
Such a cache stays newer than the compile DB forever, so consumers that
derive the binutils paths from cc_path would keep failing on it.
"""
compile_commands, cache = _setup_build(setup_core)
compile_commands.parent.mkdir(parents=True, exist_ok=True)
compile_commands.write_text("[]")
cache.parent.mkdir(parents=True, exist_ok=True)
cache.write_text('{"cxx_path": "cached"}')
cc_mtime = compile_commands.stat().st_mtime
os.utime(cache, (cc_mtime + 1, cc_mtime + 1))
with patch(
"esphome.espidf.idedata.idedata_from_build",
return_value={"cc_path": "gcc", "cxx_path": "g++"},
) as mock_transform:
result = toolchain.get_idedata()
mock_transform.assert_called_once()
assert result["cc_path"] == "gcc"
def test_get_idedata_regenerates_when_compile_commands_newer(setup_core: Path) -> None:
"""A compile DB newer than the cache forces regeneration."""
compile_commands, cache = _setup_build(setup_core)
cache.parent.mkdir(parents=True, exist_ok=True)
cache.write_text('{"cxx_path": "stale"}')
compile_commands.parent.mkdir(parents=True, exist_ok=True)
compile_commands.write_text("[]")
cache_mtime = cache.stat().st_mtime
os.utime(compile_commands, (cache_mtime + 1, cache_mtime + 1))
with patch(
"esphome.espidf.idedata.idedata_from_build",
return_value={"cxx_path": "fresh"},
) as mock_transform:
result = toolchain.get_idedata()
mock_transform.assert_called_once()
assert result == {"cxx_path": "fresh", "prog_path": str(toolchain.get_elf_path())}
@pytest.mark.parametrize("cached", ['"cc_path is a string"', "[]", "42"])
def test_get_idedata_regenerates_on_non_dict_cache(
setup_core: Path, cached: str
) -> None:
"""A newer cache holding valid JSON that is not an object is regenerated.
A bare string would otherwise pass the cc_path check by substring and be
handed to consumers expecting a dict.
"""
compile_commands, cache = _setup_build(setup_core)
compile_commands.parent.mkdir(parents=True, exist_ok=True)
compile_commands.write_text("[]")
cache.parent.mkdir(parents=True, exist_ok=True)
cache.write_text(cached)
cc_mtime = compile_commands.stat().st_mtime
os.utime(cache, (cc_mtime + 1, cc_mtime + 1))
with patch(
"esphome.espidf.idedata.idedata_from_build",
return_value={"cc_path": "gcc", "cxx_path": "g++"},
) as mock_transform:
result = toolchain.get_idedata()
mock_transform.assert_called_once()
assert isinstance(result, dict)
def test_get_idedata_regenerates_on_corrupted_cache(setup_core: Path) -> None:
"""An unparseable (but newer) cache falls back to regeneration."""
compile_commands, cache = _setup_build(setup_core)
compile_commands.parent.mkdir(parents=True, exist_ok=True)
compile_commands.write_text("[]")
cache.parent.mkdir(parents=True, exist_ok=True)
cache.write_text("{not json")
cc_mtime = compile_commands.stat().st_mtime
os.utime(cache, (cc_mtime + 1, cc_mtime + 1))
with patch(
"esphome.espidf.idedata.idedata_from_build",
return_value={"cxx_path": "regen"},
) as mock_transform:
result = toolchain.get_idedata()
mock_transform.assert_called_once()
assert result == {"cxx_path": "regen", "prog_path": str(toolchain.get_elf_path())}
def test_get_idedata_prog_path_points_at_firmware_elf(setup_core: Path) -> None:
"""The idedata exposes prog_path (the ELF) so consumers like build-action
can locate firmware.factory.bin / firmware.ota.bin as its siblings."""
@@ -267,7 +159,7 @@ def test_get_idedata_prog_path_points_at_firmware_elf(setup_core: Path) -> None:
compile_commands.write_text("[]")
with patch(
"esphome.espidf.idedata.idedata_from_build",
"esphome.build_helpers.idedata.idedata_from_build",
return_value={"cxx_path": "g++"},
):
result = toolchain.get_idedata()