diff --git a/.github/actions/cache-arduino8266/action.yml b/.github/actions/cache-arduino8266/action.yml deleted file mode 100644 index affe62b5aa2..00000000000 --- a/.github/actions/cache-arduino8266/action.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Cache Arduino ESP8266 -description: > - Resolve the pinned Arduino core and xtensa toolchain versions and cache the - native ESP8266 install (~110 MB framework + toolchain; no ccache store, the - seed job saves before any compile runs). Exports - ESPHOME_ARDUINO8266_PREFIX to the job so every later step installs into - the cached path; the Python venv must already be restored. Mirrors - cache-esp-idf: only dev-branch pushes write the shared cache, everything - else restores. -runs: - using: composite - steps: - - name: Resolve the native toolchain cache key - # Versions are pinned in code, not a hashable file; resolve them so a - # bump changes the cache key. Assignment form so errexit catches a - # resolver failure. - id: version - shell: bash - run: | - # One owner for the install prefix: exported here and referenced by - # the cache steps below via env, so the caller's install and the - # cached path cannot diverge. - echo "ESPHOME_ARDUINO8266_PREFIX=$HOME/.esphome-arduino8266" >> "$GITHUB_ENV" - . venv/bin/activate - key=$(python -c 'from esphome.components.esp8266 import RECOMMENDED_ARDUINO_FRAMEWORK_VERSION as f; from esphome.arduino8266.framework import TOOLCHAIN_VERSION as t; print(f"{f}-{t}")') - [ -n "$key" ] || exit 1 - echo "key=$key" >> "$GITHUB_OUTPUT" - - name: Cache the native toolchain (write on dev) - if: github.ref == 'refs/heads/dev' - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: ${{ env.ESPHOME_ARDUINO8266_PREFIX }} - key: ${{ runner.os }}-esp8266-native-${{ steps.version.outputs.key }} - - name: Restore the native toolchain (off dev) - if: github.ref != 'refs/heads/dev' - uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: ${{ env.ESPHOME_ARDUINO8266_PREFIX }} - key: ${{ runner.os }}-esp8266-native-${{ steps.version.outputs.key }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 11d073e7e32..2343f8c5baf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,8 +102,6 @@ jobs: device-builder: ${{ steps.determine.outputs.device-builder }} esp32-platformio: ${{ steps.determine.outputs.esp32-platformio }} esp32-platformio-components: ${{ steps.determine.outputs.esp32-platformio-components }} - esp8266-native: ${{ steps.determine.outputs.esp8266-native }} - esp8266-native-components: ${{ steps.determine.outputs.esp8266-native-components }} changed-components: ${{ steps.determine.outputs.changed-components }} changed-components-with-tests: ${{ steps.determine.outputs.changed-components-with-tests }} directly-changed-components-with-tests: ${{ steps.determine.outputs.directly-changed-components-with-tests }} @@ -167,8 +165,6 @@ jobs: echo "device-builder=$(echo "$output" | jq -r '.device_builder')" >> $GITHUB_OUTPUT echo "esp32-platformio=$(echo "$output" | jq -r '.esp32_platformio')" >> $GITHUB_OUTPUT echo "esp32-platformio-components=$(echo "$output" | jq -r '.esp32_platformio_components')" >> $GITHUB_OUTPUT - echo "esp8266-native=$(echo "$output" | jq -r '.esp8266_native')" >> $GITHUB_OUTPUT - echo "esp8266-native-components=$(echo "$output" | jq -r '.esp8266_native_components')" >> $GITHUB_OUTPUT echo "changed-components=$(echo "$output" | jq -c '.changed_components')" >> $GITHUB_OUTPUT echo "changed-components-with-tests=$(echo "$output" | jq -c '.changed_components_with_tests')" >> $GITHUB_OUTPUT echo "directly-changed-components-with-tests=$(echo "$output" | jq -c '.directly_changed_components_with_tests')" >> $GITHUB_OUTPUT @@ -187,32 +183,6 @@ jobs: path: .temp/components_graph.json key: components-graph-${{ hashFiles('esphome/components/**/*.py') }} - seed-esp8266-native-cache: - name: Seed the esp8266 native toolchain cache - runs-on: ubuntu-24.04 - 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. - if: github.event_name == 'push' && github.ref == 'refs/heads/dev' - timeout-minutes: 15 - steps: - - name: Check out code from GitHub - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Restore Python - uses: ./.github/actions/restore-python - with: - python-version: ${{ env.DEFAULT_PYTHON }} - cache-key: ${{ needs.common.outputs.cache-key }} - - name: Cache the native toolchain - uses: ./.github/actions/cache-arduino8266 - - name: Install the native toolchain - run: | - . venv/bin/activate - python -c "from esphome.arduino8266.framework import check_and_install; from esphome.components.esp8266 import RECOMMENDED_ARDUINO_FRAMEWORK_VERSION; check_and_install(RECOMMENDED_ARDUINO_FRAMEWORK_VERSION)" - ci-custom: name: Run script/ci-custom runs-on: ubuntu-24.04 @@ -1267,7 +1237,7 @@ jobs: # compile validates config first, so a separate config pass is # redundant for this smoke test. ESP-IDF framework via PlatformIO: - python3 script/test_build_components.py -e compile -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain platformio --fail-on-no-tests + python3 script/test_build_components.py -e compile -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain platformio echo "" echo "ESP-IDF-via-PlatformIO build passed! Starting Arduino smoke test..." @@ -1276,40 +1246,6 @@ jobs: # Arduino framework via PlatformIO (only components with an esp32-ard test are built): python3 script/test_build_components.py -e compile -t esp32-ard -c "$TEST_COMPONENTS" -f --toolchain platformio - test-esp8266-native: - name: Test esp8266 components with the native toolchain - runs-on: ubuntu-24.04 - needs: - - common - - determine-jobs - if: github.event_name == 'pull_request' && needs.determine-jobs.outputs.esp8266-native == 'true' - env: - # Computed by script/determine-jobs.py (ESP8266_NATIVE_TEST_COMPONENTS) - TEST_COMPONENTS: ${{ needs.determine-jobs.outputs.esp8266-native-components }} - steps: - - name: Check out code from GitHub - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - - name: Restore Python - uses: ./.github/actions/restore-python - with: - python-version: ${{ env.DEFAULT_PYTHON }} - cache-key: ${{ needs.common.outputs.cache-key }} - - - name: Cache the native toolchain - uses: ./.github/actions/cache-arduino8266 - - - name: Run native toolchain compile test - run: | - . venv/bin/activate - - echo "Testing components: $TEST_COMPONENTS" - echo "" - - # ESP8266 Arduino built directly (no PlatformIO); compile validates - # config first, so a separate config pass is redundant. - python3 script/test_build_components.py -e compile -t esp8266-ard -c "$TEST_COMPONENTS" -f --toolchain arduino --fail-on-no-tests - device-builder: name: Test downstream esphome/device-builder runs-on: ubuntu-24.04 @@ -1672,7 +1608,6 @@ jobs: needs: - common - seed-apt-cache - - seed-esp8266-native-cache - determine-jobs - ci-custom - pylint @@ -1688,7 +1623,6 @@ jobs: - clang-tidy-esp32-variants - test-build-components-split - test-esp32-platformio - - test-esp8266-native - device-builder - memory-impact-target-branch - memory-impact-pr-branch diff --git a/script/determine-jobs.py b/script/determine-jobs.py index f0cbbe63ceb..f5412af21d6 100755 --- a/script/determine-jobs.py +++ b/script/determine-jobs.py @@ -50,7 +50,6 @@ from __future__ import annotations import argparse from collections import Counter -from collections.abc import Callable from enum import StrEnum from functools import cache import json @@ -532,51 +531,37 @@ ESP32_PLATFORMIO_TRIGGER_PATH_PREFIXES = ("esphome/platformio/",) # - esphome/build_gen/platformio.py -- the PlatformIO build generator # - script/test_build_components.py -- the harness the job invokes # - .github/workflows/ci.yml -- the job's own definition -# Shared by every toolchain smoke-test job: the harness it invokes and the -# workflow that defines it -_SMOKE_HARNESS_TRIGGER_FILES = frozenset( +ESP32_PLATFORMIO_TRIGGER_FILES = frozenset( { + "esphome/build_gen/platformio.py", "script/test_build_components.py", ".github/workflows/ci.yml", } ) -ESP32_PLATFORMIO_TRIGGER_FILES = _SMOKE_HARNESS_TRIGGER_FILES | { - "esphome/build_gen/platformio.py", -} - - -def _path_or_file_trigger( - files: list[str], - trigger_files: frozenset[str], - trigger_prefixes: tuple[str, ...], -) -> bool: - """Whether any changed file matches the given infrastructure triggers.""" - return any( - file in trigger_files or file.startswith(trigger_prefixes) for file in files - ) - - -@cache -def _cached_components_closure(files: tuple[str, ...]) -> frozenset[str]: - """Dependency closure of the changed components; cached because the - walk is expensive and every smoke-test job asks for the same list.""" - component_files = [f for f in files if filter_component_and_test_files(f)] - return frozenset(get_components_with_dependencies(component_files, True)) - def _esp32_platformio_path_or_file_trigger(files: list[str]) -> bool: """Whether any changed file is a PlatformIO infrastructure / harness trigger.""" - return _path_or_file_trigger( - files, ESP32_PLATFORMIO_TRIGGER_FILES, ESP32_PLATFORMIO_TRIGGER_PATH_PREFIXES - ) + for file in files: + if file in ESP32_PLATFORMIO_TRIGGER_FILES: + return True + if any( + file.startswith(prefix) for prefix in ESP32_PLATFORMIO_TRIGGER_PATH_PREFIXES + ): + return True + return False def _esp_idf_infra_changed(files: list[str]) -> bool: """Whether any changed file is ESP-IDF build/runner infrastructure.""" - return _path_or_file_trigger( - files, ESP_IDF_INFRA_TRIGGER_FILES, ESP_IDF_INFRA_TRIGGER_PATH_PREFIXES - ) + for file in files: + if file in ESP_IDF_INFRA_TRIGGER_FILES: + return True + if any( + file.startswith(prefix) for prefix in ESP_IDF_INFRA_TRIGGER_PATH_PREFIXES + ): + return True + return False def esp32_platformio_components_to_test(branch: str | None = None) -> list[str]: @@ -614,23 +599,15 @@ def esp32_platformio_components_to_test(branch: str | None = None) -> list[str]: Returns: Sorted list of component names to compile. """ - return _toolchain_components_to_test( - branch, ESP32_PLATFORMIO_TEST_COMPONENTS, _esp32_platformio_path_or_file_trigger - ) - - -def _toolchain_components_to_test( - branch: str | None, - test_set: frozenset[str], - infra_trigger: Callable[[list[str]], bool], -) -> list[str]: - """The shared narrowing rule for the per-toolchain smoke-test jobs.""" files = changed_files(branch) - if core_changed(files) or infra_trigger(files): - return sorted(test_set) + if core_changed(files) or _esp32_platformio_path_or_file_trigger(files): + return sorted(ESP32_PLATFORMIO_TEST_COMPONENTS) - return sorted(test_set & _cached_components_closure(tuple(files))) + component_files = [f for f in files if filter_component_and_test_files(f)] + changed = get_components_with_dependencies(component_files, True) + + return sorted(ESP32_PLATFORMIO_TEST_COMPONENTS & set(changed)) def should_run_esp32_platformio(branch: str | None = None) -> bool: @@ -651,78 +628,6 @@ def should_run_esp32_platformio(branch: str | None = None) -> bool: return bool(esp32_platformio_components_to_test(branch)) -# The `--toolchain arduino` smoke-test set: covers the core, the bundled and -# converted registry libraries, and the waveform path. -ESP8266_NATIVE_TEST_COMPONENTS = frozenset( - { - "esp8266", - "api", - "web_server", - "captive_portal", - "mqtt", - "esp8266_pwm", - "neopixelbus", - "bme280_i2c", - "uart", - } -) - -# Infrastructure whose changes always trigger the native ESP8266 -# compile test -ESP8266_NATIVE_TRIGGER_PATH_PREFIXES = ( - "esphome/arduino8266/", - "esphome/arduino/", - "esphome/build_helpers/", -) -# Shared library-conversion modules every native build imports; espidf-only -# infra (build_gen/espidf.py) deliberately stays out of the esp8266 set. -_NATIVE_SHARED_TRIGGER_FILES = frozenset( - { - "esphome/framework_helpers.py", - "esphome/platformio/library.py", - "esphome/platformio/extra_script.py", - } -) -# Tripwire: the shared modules must stay in the ESP-IDF trigger set too -# (now defined in clang_tidy_hash), or its smoke test silently skips them -assert _NATIVE_SHARED_TRIGGER_FILES <= ESP_IDF_INFRA_TRIGGER_FILES -ESP8266_NATIVE_TRIGGER_FILES = ( - _NATIVE_SHARED_TRIGGER_FILES - | _SMOKE_HARNESS_TRIGGER_FILES - | { - "esphome/build_gen/arduino8266.py", - "esphome/build_gen/build_tool.py", - "esphome/components/esp8266/build_surgery.py", - "esphome/components/esp8266/boards.py", - "esphome/platformio/registry.py", - # esp8266/__init__.py imports copy_ccache_script from it - "esphome/platformio/toolchain.py", - ".github/actions/cache-arduino8266/action.yml", - } -) - - -def _esp8266_native_path_or_file_trigger(files: list[str]) -> bool: - """Whether any changed file is native-ESP8266 infrastructure / harness.""" - # base_python_changed covers the top-level esphome/*.py modules the - # native backend imports directly (framework_helpers, helpers, writer, - # __main__); without it a change there would silently skip this job. - # base_python_changed is deliberately broad (any top-level esphome/*.py) - # as belt-and-braces while the backend is new; narrow it to the modules - # the backend imports once the toolchain has soaked a few releases - return base_python_changed(files) or _path_or_file_trigger( - files, ESP8266_NATIVE_TRIGGER_FILES, ESP8266_NATIVE_TRIGGER_PATH_PREFIXES - ) - - -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 - ) - - def determine_cpp_unit_tests( branch: str | None = None, ) -> tuple[bool, list[str]]: @@ -1321,8 +1226,6 @@ def main() -> None: run_device_builder = True esp32_platformio_components = sorted(ESP32_PLATFORMIO_TEST_COMPONENTS) run_esp32_platformio = True - esp8266_native_components = sorted(ESP8266_NATIVE_TEST_COMPONENTS) - run_esp8266_native = True else: integration_run_all, integration_test_files = determine_integration_tests( args.branch @@ -1334,8 +1237,6 @@ def main() -> None: run_device_builder = should_run_device_builder(args.branch) esp32_platformio_components = esp32_platformio_components_to_test(args.branch) run_esp32_platformio = bool(esp32_platformio_components) - esp8266_native_components = esp8266_native_components_to_test(args.branch) - run_esp8266_native = bool(esp8266_native_components) run_integration, integration_test_buckets = _compute_integration_test_buckets( integration_run_all, integration_test_files ) @@ -1531,8 +1432,6 @@ def main() -> None: "device_builder": run_device_builder, "esp32_platformio": run_esp32_platformio, "esp32_platformio_components": ",".join(esp32_platformio_components), - "esp8266_native": run_esp8266_native, - "esp8266_native_components": ",".join(esp8266_native_components), "changed_components": changed_components, "changed_components_with_tests": changed_components_with_tests, "directly_changed_components_with_tests": list(directly_changed_with_tests), diff --git a/script/test_build_components.py b/script/test_build_components.py index de93968d4c7..ddd8a6a67d7 100755 --- a/script/test_build_components.py +++ b/script/test_build_components.py @@ -1027,7 +1027,6 @@ def test_components( isolated_components: set[str] | None = None, base_only: bool = False, toolchain: str | None = None, - fail_on_no_tests: bool = False, ) -> int: """Test components with optional intelligent grouping. @@ -1062,32 +1061,20 @@ def test_components( # toolchain build. include_validate = esphome_command != "compile" - # Find all component tests; remember which components each pattern - # (wildcards included) matched, for the deferred no-tests accounting + # Find all component tests all_tests = {} - pattern_components: dict[str, set[str]] = {} for pattern in component_patterns: # Skip empty patterns (happens when components list is empty string) if not pattern: continue - found = find_component_tests( - tests_dir, pattern, base_only, include_validate=include_validate + all_tests.update( + find_component_tests( + tests_dir, pattern, base_only, include_validate=include_validate + ) ) - pattern_components[pattern] = set(found) - all_tests.update(found) - - # A blank pattern list would slide into the reference-baseline - # fallback and exit green while building nothing - if fail_on_no_tests and not any(component_patterns): - print("No components requested (blank component list)") - return 1 - - if fail_on_no_tests and not all_tests: - # Nothing matched: fail before the synthetic baseline spends a - # compile reporting success on nothing - print(f"No components found matching: {component_patterns}") - return 1 + # If no components found, build a reference configuration for baseline comparison + # Create a synthetic "empty" component test that will build just the base config if not all_tests: print(f"No components found matching: {component_patterns}") print( @@ -1191,23 +1178,6 @@ def test_components( toolchain=toolchain, ) - silent: list[str] = [] - if fail_on_no_tests: - # A green run that built nothing for a requested pattern must not - # pass CI. Per pattern so one silent pattern cannot hide behind - # the others; opt-in because some legs legitimately match nothing; - # deferred past the summary so reproduce commands still print. - built = {c for r in test_results for c in r.components} - # A pattern is silent when it matched no fixture, or when none of - # its matched components produced a build (wildcards included) - silent = [ - p - for p in component_patterns - if p and not (pattern_components.get(p, set()) & built) - ] - if silent: - print(f"No tests ran for requested pattern(s): {', '.join(silent)}") - # Separate results into passed and failed passed_results = [r for r in test_results if r.success] failed_results = [r for r in test_results if not r.success] @@ -1239,7 +1209,7 @@ def test_components( if os.environ.get("GITHUB_STEP_SUMMARY"): write_github_summary(test_results, toolchain=toolchain) - if failed_results or silent: + if failed_results: return 1 return 0 @@ -1294,12 +1264,6 @@ def main() -> int: "--toolchain", help="Select toolchain for compiling.", ) - parser.add_argument( - "--fail-on-no-tests", - action="store_true", - help="Exit non-zero when no test matched (for CI legs whose " - "components must all have fixtures)", - ) args = parser.parse_args() @@ -1318,7 +1282,6 @@ def main() -> int: continue_on_fail=args.continue_on_fail, enable_grouping=not args.no_grouping, isolated_components=isolated_components, - fail_on_no_tests=args.fail_on_no_tests, base_only=args.base_only, toolchain=args.toolchain, ) diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index 990d7d19a5e..49718219698 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -78,17 +78,6 @@ 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.""" @@ -117,7 +106,6 @@ def clear_determine_jobs_caches() -> None: """Clear all cached functions before each test.""" determine_jobs._is_clang_tidy_full_scan.cache_clear() determine_jobs._component_has_tests.cache_clear() - determine_jobs._cached_components_closure.cache_clear() def test_main_all_tests_should_run( @@ -128,7 +116,6 @@ 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], @@ -145,7 +132,6 @@ 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) @@ -222,8 +208,6 @@ 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 @@ -260,7 +244,6 @@ 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], @@ -277,7 +260,6 @@ 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 @@ -320,8 +302,6 @@ 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 @@ -3171,78 +3151,6 @@ def test_memory_impact_elf_layouts_are_found(tmp_path: Path) -> None: assert find_elf_path(build_path) == elf, f"{platform} ELF not found" -@pytest.mark.parametrize( - "changed", - [ - "esphome/arduino8266/framework.py", - "esphome/build_gen/arduino8266.py", - "esphome/components/esp8266/build_surgery.py", - # Shared modules the native build depends on - "esphome/build_helpers/idedata.py", - "esphome/platformio/library.py", - # Top-level esphome/*.py modules the backend imports directly - "esphome/framework_helpers.py", - "esphome/writer.py", - # esp8266/__init__.py imports copy_ccache_script from it - "esphome/platformio/toolchain.py", - # The composite cache action must not ship unexercised - ".github/actions/cache-arduino8266/action.yml", - ], -) -def test_esp8266_native_components_full_list_on_infra_change(changed: str) -> None: - """Native-ESP8266 infrastructure changes run the full test list.""" - with ( - patch.object(determine_jobs, "changed_files", return_value=[changed]), - patch.object( - determine_jobs, - "get_components_with_dependencies", - return_value=["wifi"], - ), - ): - result = determine_jobs.esp8266_native_components_to_test() - assert result == sorted(determine_jobs.ESP8266_NATIVE_TEST_COMPONENTS) - - -@pytest.mark.parametrize( - ("changed_files", "dependency_closure", "expected"), - [ - # Tested component changed -- narrow to the intersection. - ( - ["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"], - [], - ), - # espidf infrastructure is not an esp8266-native trigger; the - # native backend depends on esphome/build_helpers/ instead. - (["esphome/build_gen/espidf.py"], [], []), - (["esphome/espidf/toolchain.py"], [], []), - (["README.md"], [], []), - ], -) -def test_esp8266_native_components_to_test_narrowing( - changed_files: list[str], - dependency_closure: list[str], - expected: list[str], -) -> None: - """Component changes narrow the native-ESP8266 test list.""" - with ( - patch.object(determine_jobs, "changed_files", return_value=changed_files), - patch.object( - determine_jobs, - "get_components_with_dependencies", - return_value=dependency_closure, - ), - ): - result = determine_jobs.esp8266_native_components_to_test() - assert result == expected - - def test_compute_integration_test_buckets_no_durations_full_fanout() -> None: """Without recorded durations the fan-out stays at the maximum.""" files = [f"tests/integration/test_{i:03d}.py" for i in range(15)] diff --git a/tests/script/test_test_build_components.py b/tests/script/test_test_build_components.py index 1d21e5d943e..74e150380c5 100644 --- a/tests/script/test_test_build_components.py +++ b/tests/script/test_test_build_components.py @@ -236,98 +236,3 @@ def test_run_grouped_test_closes_group_when_subprocess_raises( ) assert "::endgroup::" in capsys.readouterr().out - - -def test_components_empty_match_fails_with_flag( - capsys: pytest.CaptureFixture[str], -) -> None: - """Under --fail-on-no-tests, a real component filtered to a platform - with no matching test file must not pass CI as a green zero-component - compile.""" - rc = tbc.test_components( - ["logger"], - "zz-none", - "compile", - False, - enable_grouping=False, - fail_on_no_tests=True, - ) - assert rc == 1 - assert "No tests ran for requested pattern(s): logger" 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 pattern(s): logger" in (capsys.readouterr().out) - - -def test_components_blank_list_fails_with_flag( - capsys: pytest.CaptureFixture[str], -) -> None: - """A fully blank component list must not slide into the baseline - fallback and exit green under the flag.""" - rc = tbc.test_components( - [""], "esp8266-ard", "compile", False, fail_on_no_tests=True - ) - assert rc == 1 - assert "blank component list" in capsys.readouterr().out - - -def test_components_wildcard_no_match_fails_with_flag( - capsys: pytest.CaptureFixture[str], -) -> None: - """A wildcard matching nothing must not degrade to the synthetic - baseline build and exit green under the flag.""" - rc = tbc.test_components( - ["zz_no_such*"], - "esp8266-ard", - "compile", - False, - enable_grouping=False, - fail_on_no_tests=True, - ) - assert rc == 1 - assert "No components found matching" 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.""" - assert ( - tbc.test_components( - ["logger"], "zz-none", "compile", False, enable_grouping=False - ) - == 0 - ) - - -def test_components_unknown_component_fails_with_flag( - capsys: pytest.CaptureFixture[str], -) -> None: - """A renamed smoke-test component must shrink coverage loudly, not fall - into the reference-baseline build.""" - rc = tbc.test_components( - ["no_such_component_xyz"], - "esp8266-ard", - "compile", - False, - enable_grouping=False, - fail_on_no_tests=True, - ) - assert rc == 1 - assert "No components found matching" in capsys.readouterr().out