[ci] Compile esp8266 component tests with the native toolchain (#19740)

This commit is contained in:
J. Nick Koston
2026-09-27 12:09:06 +02:00
committed by GitHub
parent 06d88e0381
commit b9dfae50c1
4 changed files with 120 additions and 24 deletions
+15 -14
View File
@@ -193,9 +193,10 @@ jobs:
needs:
- common
# PR-branch cache saves are invisible to other PRs, so dev pushes seed
# the shared entry test-esp8266-native restores. Only dev: the composite
# action saves nowhere else, so a beta/release push would download the
# toolchain and discard it.
# the shared entry the component matrix, the memory impact jobs and
# test-esp8266-native restore. Only dev: the composite action saves
# nowhere else, so a beta/release push would download the toolchain and
# discard it.
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
timeout-minutes: 15
steps:
@@ -1127,6 +1128,11 @@ jobs:
uses: ./.github/actions/cache-sdk-nrf
with:
restore-only: true
- name: Cache the native ESP8266 toolchain
# Only batches whose test platforms include esp8266; never saves
# here, it reuses the install the dev seed job cached.
if: matrix.batch.needs_arduino8266
uses: ./.github/actions/cache-arduino8266
- name: Validate and compile components with intelligent grouping
run: |
. venv/bin/activate
@@ -1479,12 +1485,9 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache platformio
if: steps.check-script.outputs.skip != 'true' && steps.check-tests.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true'
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }}
- name: Cache the native ESP8266 toolchain
if: steps.check-script.outputs.skip != 'true' && steps.check-tests.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true' && fromJSON(needs.determine-jobs.outputs.memory_impact).needs_arduino8266
uses: ./.github/actions/cache-arduino8266
- name: Build, compile, and analyze memory
if: steps.check-script.outputs.skip != 'true' && steps.check-tests.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true'
@@ -1569,11 +1572,9 @@ jobs:
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache platformio
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }}
- name: Cache the native ESP8266 toolchain
if: fromJSON(needs.determine-jobs.outputs.memory_impact).needs_arduino8266
uses: ./.github/actions/cache-arduino8266
- name: Build, compile, and analyze memory
id: extract
run: |
+21 -6
View File
@@ -724,11 +724,17 @@ def _esp8266_native_path_or_file_trigger(files: list[str]) -> bool:
def esp8266_native_components_to_test(branch: str | None = None) -> list[str]:
"""Subset of ``ESP8266_NATIVE_TEST_COMPONENTS`` the job needs to
compile (same narrowing as ``esp32_platformio_components_to_test``)."""
return _toolchain_components_to_test(
branch, ESP8266_NATIVE_TEST_COMPONENTS, _esp8266_native_path_or_file_trigger
)
"""The smoke set on a native-build change, nothing otherwise.
Unlike the esp32 PlatformIO job, this one does not narrow to the changed
components: the component matrix already compiles every esp8266 fixture
with this toolchain, so the only gap left is a change to the native build
itself that brings no component along.
"""
files = changed_files(branch)
if core_changed(files) or _esp8266_native_path_or_file_trigger(files):
return sorted(ESP8266_NATIVE_TEST_COMPONENTS)
return []
def determine_cpp_unit_tests(
@@ -1286,6 +1292,7 @@ def detect_memory_impact_config(
"components": compatible_components,
"platform": platform,
"use_merged_config": "true",
"needs_arduino8266": platform.startswith("esp8266"),
}
@@ -1516,12 +1523,20 @@ def main() -> None:
for batch in batches:
platforms: set[str] = set()
for component in batch:
platforms.update(get_component_test_platforms(component))
# Variants included: the compile stage builds them, so a
# component tested only by test-<variant>.<platform>.yaml
# still needs that platform's toolchain
platforms.update(
get_component_test_platforms(component, base_only=False)
)
component_test_batches.append(
{
"components": " ".join(batch),
"needs_idf": any(p.startswith("esp32") for p in platforms),
"needs_nrf": any(p.startswith("nrf52") for p in platforms),
"needs_arduino8266": any(
p.startswith("esp8266") for p in platforms
),
}
)
+83 -4
View File
@@ -250,6 +250,82 @@ def test_main_all_tests_should_run(
assert len(batch["components"]) > 0
assert isinstance(batch["needs_idf"], bool)
assert isinstance(batch["needs_nrf"], bool)
assert isinstance(batch["needs_arduino8266"], bool)
def test_main_batch_flags_count_variant_tests(
mock_determine_integration_tests: Mock,
mock_should_run_clang_tidy: Mock,
mock_should_run_clang_format: Mock,
mock_should_run_python_linters: Mock,
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],
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""The compile stage builds test-<variant>.<platform>.yaml too, so a
component tested on esp8266 only by a variant still needs the toolchain."""
monkeypatch.delenv("GITHUB_ACTIONS", raising=False)
mock_determine_integration_tests.return_value = (False, [])
mock_should_run_clang_tidy.return_value = False
mock_should_run_clang_format.return_value = False
mock_should_run_python_linters.return_value = False
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.return_value = ["esphome/components/safe_mode/__init__.py"]
def platforms(component: str, *, base_only: bool = True) -> set[str]:
return set() if base_only else {"esp8266-ard"}
with (
patch("sys.argv", ["determine-jobs.py"]),
patch.object(determine_jobs, "_is_clang_tidy_full_scan", return_value=False),
patch.object(
determine_jobs, "get_changed_components", return_value=["safe_mode"]
),
patch.object(
determine_jobs,
"filter_component_and_test_files",
side_effect=lambda f: f.startswith("esphome/components/"),
),
patch.object(
determine_jobs,
"get_components_with_dependencies",
return_value=["safe_mode"],
),
patch.object(determine_jobs, "_component_has_tests", return_value=True),
patch.object(
determine_jobs,
"detect_memory_impact_config",
return_value={"should_run": "false"},
),
patch.object(
determine_jobs,
"create_intelligent_batches",
return_value=([["safe_mode"]], {}),
),
patch.object(
determine_jobs, "get_component_test_platforms", side_effect=platforms
),
):
determine_jobs.main()
output = json.loads(capsys.readouterr().out)
assert output["component_test_batches"] == [
{
"components": "safe_mode",
"needs_idf": False,
"needs_nrf": False,
"needs_arduino8266": True,
}
]
def test_main_no_tests_should_run(
@@ -1530,6 +1606,7 @@ def test_detect_memory_impact_config_with_common_platform(tmp_path: Path) -> Non
assert set(result["components"]) == {"wifi", "api"}
assert result["platform"] == "esp32-idf" # Common platform
assert result["use_merged_config"] == "true"
assert result["needs_arduino8266"] is False
@pytest.mark.usefixtures("mock_target_branch_dev")
@@ -1634,6 +1711,8 @@ def test_detect_memory_impact_config_no_common_platform(tmp_path: Path) -> None:
assert result["platform"] == "esp8266-ard"
assert result["components"] == ["logger"]
assert result["use_merged_config"] == "true"
# The esp8266 build is native, so the job restores that toolchain
assert result["needs_arduino8266"] is True
@pytest.mark.usefixtures("mock_target_branch_dev")
@@ -3212,13 +3291,13 @@ def test_esp8266_native_components_full_list_on_infra_change(changed: str) -> No
@pytest.mark.parametrize(
("changed_files", "dependency_closure", "expected"),
[
# Tested component changed -- narrow to the intersection.
# A tested component alone does not schedule this job: the component
# matrix already compiles its esp8266 fixtures with this toolchain.
(
["esphome/components/mqtt/mqtt_client.cpp"],
["mqtt", "json"],
["mqtt"],
[],
),
# Components outside the test set return an empty list (job skipped).
(
["esphome/components/wifi/wifi_component.cpp"],
["wifi", "network"],
@@ -3236,7 +3315,7 @@ def test_esp8266_native_components_to_test_narrowing(
dependency_closure: list[str],
expected: list[str],
) -> None:
"""Component changes narrow the native-ESP8266 test list."""
"""Only a native-build change schedules the native-ESP8266 job."""
with (
patch.object(determine_jobs, "changed_files", return_value=changed_files),
patch.object(
@@ -4,6 +4,7 @@ esphome:
esp8266:
board: d1_mini_pro
toolchain: arduino
logger:
level: VERY_VERBOSE