Name the platform in the analyze-memory refusal, surface parser-shaped idedata failures, count builds per component under the no-tests flag

This commit is contained in:
J. Nick Koston
2026-08-23 19:38:12 -05:00
parent b9d40d2b1b
commit 9c2a3baaa9
4 changed files with 47 additions and 9 deletions
+3 -2
View File
@@ -2001,9 +2001,10 @@ def command_analyze_memory(args: ArgsProtocol, config: ConfigType) -> int:
native_toolchain = _native_toolchain_module()
if native_toolchain is None and not CORE.using_toolchain_platformio:
_LOGGER.error(
"analyze-memory is not supported with the '%s' toolchain; it "
"requires a PlatformIO, ESP-IDF, or native Arduino build",
"analyze-memory is not supported with the '%s' toolchain on %s; "
"it requires a PlatformIO, ESP-IDF, or native Arduino build",
CORE.toolchain.value if CORE.toolchain else "unresolved",
CORE.target_platform,
)
return 1
+7 -1
View File
@@ -48,7 +48,13 @@ def warn_if_idedata_missing(get_idedata: Callable[[], dict | None]) -> None:
"memory-analysis data will be unavailable for this build)",
err,
)
_LOGGER.debug("Idedata failure detail", exc_info=True)
if isinstance(err, (EsphomeError, OSError)):
# Routine environmental failures keep the detail at debug
_LOGGER.debug("Idedata failure detail", exc_info=True)
else:
# LookupError/ValueError/RuntimeError smell like a parsing bug;
# a permanently masked traceback would hide it on every build
_LOGGER.warning("Idedata failure detail", exc_info=True)
_LOGGER = logging.getLogger(__name__)
+15 -6
View File
@@ -1202,12 +1202,21 @@ def test_components(
toolchain=toolchain,
)
if fail_on_no_tests and not test_results:
# A green run that compiled nothing (renamed/removed test fixture,
# bad platform filter) must not pass CI. Opt-in: some legs (the
# esp32-ard smoke subset) legitimately match nothing.
print("No tests matched the requested components/platform")
return 1
if fail_on_no_tests:
# A green run that built nothing for a requested component (renamed
# fixture, missing base file, version-suffix mismatch) must not pass
# CI. Per component: an all-or-nothing check would let one silent
# component hide behind the others. Opt-in: some legs (the esp32-ard
# smoke subset) legitimately match nothing.
built = {c for r in test_results for c in r.components}
if silent := [
p for p in component_patterns if p and "*" not in p and p not in built
]:
print(f"No tests ran for requested component(s): {', '.join(silent)}")
return 1
if not test_results:
print("No tests matched the requested components/platform")
return 1
# Separate results into passed and failed
passed_results = [r for r in test_results if r.success]
@@ -251,6 +251,28 @@ def test_components_empty_match_fails_with_flag(
assert "No zz-none tests found" in capsys.readouterr().out
def test_components_component_with_no_base_file_fails_with_flag(
capsys: pytest.CaptureFixture[str],
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""A component whose fixture matches the platform but whose platform has
no base file builds nothing; under the flag that silent zero fails by
component name instead of hiding behind other components."""
monkeypatch.setattr(tbc, "get_platform_base_files", lambda base_dir: {})
rc = tbc.test_components(
["logger"],
"esp8266-ard",
"compile",
False,
enable_grouping=False,
fail_on_no_tests=True,
)
assert rc == 1
assert "No tests ran for requested component(s): logger" in (
capsys.readouterr().out
)
def test_components_empty_match_tolerated_without_flag() -> None:
"""The esp32-ard smoke leg deliberately builds only the subset with a
matching fixture; without the flag an empty match stays green."""