[ci] Run C++ unit tests when a component's Python or test override changes (#18706)

This commit is contained in:
J. Nick Koston
2026-08-24 12:18:17 -05:00
committed by GitHub
parent b8408e14b9
commit 49fc4be861
6 changed files with 202 additions and 43 deletions
+47 -23
View File
@@ -1150,17 +1150,41 @@ def filter_component_and_test_files(file_path: str) -> bool:
)
def filter_component_and_test_cpp_files(file_path: str) -> bool:
"""Check if a file is a C++ source file in component or test directories.
def filter_cpp_unit_test_files(file_path: str) -> bool:
"""Check if a file can affect a component's C++ unit test build.
Besides C++ sources, a component's Python code (defines, source file
filters, libraries) and the ``__init__.py`` manifest overrides under
``tests/components/<component>/`` decide what the host test binary
compiles and links. Other Python files under ``tests/components/``
(pytest conftest.py, fixtures) do not.
Args:
file_path: Path to check
Returns:
True if the file is a C++ source/header file in component or test directories
True if the file is a C++ or Python file in a component directory, or
a C++ file or ``__init__.py`` in a component test directory
"""
return file_path.endswith(CPP_FILE_EXTENSIONS) and file_path.startswith(
COMPONENT_AND_TESTS_PATHS
if file_path.startswith(ESPHOME_COMPONENTS_PATH):
return file_path.endswith(CPP_AND_PYTHON_FILE_EXTENSIONS)
if file_path.startswith(ESPHOME_TESTS_COMPONENTS_PATH):
return file_path.endswith(CPP_FILE_EXTENSIONS) or file_path.endswith(
"/__init__.py"
)
return False
def has_cpp_unit_tests(component: str, tests_dir: Path) -> bool:
"""Check if a component has C++ test or benchmark sources in ``tests_dir``.
Shared by CI job selection and the build itself
(``build_helpers.filter_components_with_files``) so both agree on
which components have something to build.
"""
component_dir = tests_dir / component
return component_dir.is_dir() and (
any(component_dir.glob("*.cpp")) or any(component_dir.glob("*.h"))
)
@@ -1486,41 +1510,41 @@ def base_python_changed(files: list[str]) -> bool:
def get_cpp_changed_components(files: list[str]) -> list[str]:
"""Get components that have changed C++ files or tests.
"""Get components whose C++ unit tests are affected by changed files.
This function analyzes a list of changed files and determines which components
are affected. It handles two scenarios:
1. Test files changed (tests/components/<component>/*.cpp):
1. Test files changed (tests/components/<component>/*.cpp or __init__.py):
- Adds the component to the affected list
- Only that component needs to be tested
2. Component C++ files changed (esphome/components/<component>/*):
2. Component files changed (esphome/components/<component>/*.cpp or *.py):
- Adds the component to the affected list
- Also adds all components that depend on this component (recursively)
- This ensures that changes propagate to dependent components
Python files count because a component's Python code decides which
sources and defines end up in the host test build. Components without
C++ test sources are dropped so CI does not schedule the job for nothing.
Args:
files: List of file paths to analyze (should be C++ files)
files: List of changed file paths; irrelevant ones are ignored
Returns:
Sorted list of component names that need C++ unit tests run
"""
components_graph = create_components_graph()
tests_dir = Path(root_path) / ESPHOME_TESTS_COMPONENTS_PATH
affected: set[str] = set()
for file in files:
if not file.endswith(CPP_FILE_EXTENSIONS):
if not filter_cpp_unit_test_files(file):
continue
if file.startswith(ESPHOME_TESTS_COMPONENTS_PATH):
parts = file.split("/")
if len(parts) >= 4:
component_dir = Path(ESPHOME_TESTS_COMPONENTS_PATH) / parts[2]
if component_dir.is_dir():
affected.add(parts[2])
elif file.startswith(ESPHOME_COMPONENTS_PATH):
parts = file.split("/")
if len(parts) >= 4:
component = parts[2]
affected.update(find_children_of_component(components_graph, component))
affected.add(component)
return sorted(affected)
parts = file.split("/")
if len(parts) < 4:
continue
component = parts[2]
affected.add(component)
if file.startswith(ESPHOME_COMPONENTS_PATH):
affected.update(find_children_of_component(components_graph, component))
return sorted(c for c in affected if has_cpp_unit_tests(c, tests_dir))