mirror of
https://github.com/esphome/esphome.git
synced 2026-10-04 02:05:59 +00:00
Gate uploads on a built firmware, refuse unclaimed native builds, seed the CI toolchain cache from dev
The upload path now checks CORE.firmware_bin exists before invoking esptool and names the fix (compile first) instead of failing inside esptool. compile_program raises when a native toolchain resolved but no platform backend claimed the build rather than falling through to a mis-configured PlatformIO project. The addr2line failure warning now includes the exception so the cause is visible without debug logging. CI: the native toolchain cache is restore-only on PRs and seeded by a new dev-push job, mirroring seed-apt-cache; the key resolves the pinned core and toolchain versions from code instead of hashing framework.py. determine-jobs main() output tests now pin the esp8266_native pair.
This commit is contained in:
@@ -78,6 +78,17 @@ def mock_esp32_platformio_components_to_test() -> Generator[Mock, None, None]:
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_esp8266_native_components_to_test() -> Generator[Mock, None, None]:
|
||||
"""Mock esp8266_native_components_to_test from determine_jobs.
|
||||
|
||||
main() drives both the ``esp8266_native`` boolean output and the
|
||||
``esp8266_native_components`` CSV from this one function.
|
||||
"""
|
||||
with patch.object(determine_jobs, "esp8266_native_components_to_test") as mock:
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_determine_cpp_unit_tests() -> Generator[Mock, None, None]:
|
||||
"""Mock determine_cpp_unit_tests from helpers."""
|
||||
@@ -116,6 +127,7 @@ def test_main_all_tests_should_run(
|
||||
mock_should_run_import_time: Mock,
|
||||
mock_should_run_device_builder: Mock,
|
||||
mock_esp32_platformio_components_to_test: Mock,
|
||||
mock_esp8266_native_components_to_test: Mock,
|
||||
mock_changed_files: Mock,
|
||||
mock_determine_cpp_unit_tests: Mock,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
@@ -132,6 +144,7 @@ def test_main_all_tests_should_run(
|
||||
mock_should_run_import_time.return_value = True
|
||||
mock_should_run_device_builder.return_value = True
|
||||
mock_esp32_platformio_components_to_test.return_value = ["api", "esp32"]
|
||||
mock_esp8266_native_components_to_test.return_value = ["api", "logger"]
|
||||
mock_determine_cpp_unit_tests.return_value = (False, ["wifi", "api", "sensor"])
|
||||
|
||||
# Mock changed_files to return non-component files (to avoid memory impact)
|
||||
@@ -215,6 +228,8 @@ def test_main_all_tests_should_run(
|
||||
assert output["device_builder"] is True
|
||||
assert output["esp32_platformio"] is True
|
||||
assert output["esp32_platformio_components"] == "api,esp32"
|
||||
assert output["esp8266_native"] is True
|
||||
assert output["esp8266_native_components"] == "api,logger"
|
||||
assert output["changed_components"] == ["wifi", "api", "sensor"]
|
||||
# changed_components_with_tests will only include components that actually have test files
|
||||
assert "changed_components_with_tests" in output
|
||||
@@ -251,6 +266,7 @@ def test_main_no_tests_should_run(
|
||||
mock_should_run_import_time: Mock,
|
||||
mock_should_run_device_builder: Mock,
|
||||
mock_esp32_platformio_components_to_test: Mock,
|
||||
mock_esp8266_native_components_to_test: Mock,
|
||||
mock_changed_files: Mock,
|
||||
mock_determine_cpp_unit_tests: Mock,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
@@ -267,6 +283,7 @@ def test_main_no_tests_should_run(
|
||||
mock_should_run_import_time.return_value = False
|
||||
mock_should_run_device_builder.return_value = False
|
||||
mock_esp32_platformio_components_to_test.return_value = []
|
||||
mock_esp8266_native_components_to_test.return_value = []
|
||||
mock_determine_cpp_unit_tests.return_value = (False, [])
|
||||
|
||||
# Mock changed_files to return no component files
|
||||
@@ -309,6 +326,8 @@ def test_main_no_tests_should_run(
|
||||
assert output["device_builder"] is False
|
||||
assert output["esp32_platformio"] is False
|
||||
assert output["esp32_platformio_components"] == ""
|
||||
assert output["esp8266_native"] is False
|
||||
assert output["esp8266_native_components"] == ""
|
||||
assert output["changed_components"] == []
|
||||
assert output["changed_components_with_tests"] == []
|
||||
assert output["component_test_count"] == 0
|
||||
|
||||
@@ -7139,6 +7139,8 @@ def test_upload_using_esptool_arduino_toolchain(
|
||||
"""The native ESP8266 Arduino toolchain flashes CORE.firmware_bin at 0x0."""
|
||||
setup_core(platform=PLATFORM_ESP8266, tmp_path=tmp_path, name="test")
|
||||
CORE.toolchain = Toolchain.ARDUINO
|
||||
CORE.firmware_bin.parent.mkdir(parents=True, exist_ok=True)
|
||||
CORE.firmware_bin.touch()
|
||||
|
||||
config = {CONF_ESPHOME: {"platformio_options": {}}}
|
||||
result = upload_using_esptool(config, "/dev/ttyUSB0", None, None)
|
||||
@@ -7404,3 +7406,26 @@ def test_cli_toolchain_skips_the_validated_config_cache(tmp_path: Path) -> None:
|
||||
assert run_esphome(argv) == 2
|
||||
mock_cache.assert_not_called()
|
||||
mock_read.assert_called_once()
|
||||
|
||||
|
||||
def test_upload_using_esptool_native_missing_firmware_raises(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""A stale or absent firmware.bin fails by name instead of flashing air."""
|
||||
setup_core(platform=PLATFORM_ESP8266, tmp_path=tmp_path, name="test")
|
||||
CORE.toolchain = Toolchain.ARDUINO
|
||||
with pytest.raises(EsphomeError, match="compile the configuration first"):
|
||||
upload_using_esptool(
|
||||
{CONF_ESPHOME: {"platformio_options": {}}}, "/dev/ttyUSB0", None, None
|
||||
)
|
||||
|
||||
|
||||
def test_compile_program_unclaimed_native_toolchain_raises(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""A resolved native toolchain no platform backend claims must fail,
|
||||
never fall through to the PlatformIO project path."""
|
||||
setup_core(platform=PLATFORM_ESP32, tmp_path=tmp_path, name="test_device")
|
||||
CORE.toolchain = Toolchain.ARDUINO # esp32 has no arduino-native backend
|
||||
with pytest.raises(EsphomeError, match="no platform backend claimed"):
|
||||
compile_program(MockArgs(), {})
|
||||
|
||||
Reference in New Issue
Block a user