mirror of
https://github.com/esphome/esphome.git
synced 2026-10-05 10:31:29 +00:00
[ci] Run C++ unit tests when a component's Python or test override changes (#18706)
This commit is contained in:
@@ -1214,6 +1214,56 @@ def test_count_changed_cpp_files_with_branch() -> None:
|
||||
mock_changed.assert_called_once_with("release")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("changed_files", "expected"),
|
||||
[
|
||||
# Core C++ change runs everything
|
||||
(["esphome/core/helpers.cpp"], (True, [])),
|
||||
# Core Python change runs everything too
|
||||
(["esphome/core/config.py"], (True, [])),
|
||||
# Component C++ change: component plus dependents with C++ tests
|
||||
(["esphome/components/time/posix_tz.cpp"], (False, ["sntp", "time"])),
|
||||
# Component Python change shapes the host build (defines, source
|
||||
# filters), so it must trigger the same tests as a C++ change
|
||||
(["esphome/components/time/__init__.py"], (False, ["sntp", "time"])),
|
||||
# Nothing to build when no selected component has C++ tests
|
||||
(["esphome/components/homeassistant/__init__.py"], (False, [])),
|
||||
# Test manifest override changes only that component
|
||||
(["tests/components/time/__init__.py"], (False, ["time"])),
|
||||
# Test source change only that component
|
||||
(["tests/components/time/posix_tz.cpp"], (False, ["time"])),
|
||||
# pytest files and YAML build tests do not affect the test binary
|
||||
(["tests/components/socket/conftest.py"], (False, [])),
|
||||
(["tests/components/time/test.esp32-idf.yaml"], (False, [])),
|
||||
(["README.md", "script/helpers.py"], (False, [])),
|
||||
([], (False, [])),
|
||||
],
|
||||
)
|
||||
def test_determine_cpp_unit_tests(
|
||||
changed_files: list[str],
|
||||
expected: tuple[bool, list[str]],
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test which C++ unit tests a set of changed files selects."""
|
||||
tests_dir = tmp_path / "tests" / "components"
|
||||
for component in ("time", "sntp"):
|
||||
(tests_dir / component).mkdir(parents=True)
|
||||
(tests_dir / component / f"{component}.cpp").write_text("")
|
||||
(tests_dir / "homeassistant").mkdir()
|
||||
(tests_dir / "socket").mkdir()
|
||||
monkeypatch.setattr(helpers, "root_path", str(tmp_path))
|
||||
with (
|
||||
patch.object(determine_jobs, "changed_files", return_value=changed_files),
|
||||
patch.object(
|
||||
helpers,
|
||||
"create_components_graph",
|
||||
return_value={"time": ["homeassistant", "sntp"]},
|
||||
),
|
||||
):
|
||||
assert determine_jobs.determine_cpp_unit_tests() == expected
|
||||
|
||||
|
||||
def test_main_filters_components_without_tests(
|
||||
mock_determine_integration_tests: Mock,
|
||||
mock_should_run_clang_tidy: Mock,
|
||||
|
||||
@@ -2031,3 +2031,92 @@ def test_get_changed_files_from_command_gh_failure_keeps_stderr() -> None:
|
||||
pytest.raises(Exception, match="maximum number of changed files"),
|
||||
):
|
||||
_get_changed_files_from_command(["gh", "pr", "diff", "123", "--name-only"])
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("file_path", "expected"),
|
||||
[
|
||||
("esphome/components/time/posix_tz.cpp", True),
|
||||
("esphome/components/time/posix_tz.h", True),
|
||||
("esphome/components/time/__init__.py", True),
|
||||
("esphome/components/sntp/time.py", True),
|
||||
("tests/components/time/posix_tz.cpp", True),
|
||||
("tests/components/time/__init__.py", True),
|
||||
# Platform override: tests/components/<component>/<domain>/__init__.py
|
||||
("tests/components/template/sensor/__init__.py", True),
|
||||
# pytest-only files do not shape the C++ test binary
|
||||
("tests/components/socket/conftest.py", False),
|
||||
("tests/components/socket/test_socket.py", False),
|
||||
("tests/components/time/test.esp32-idf.yaml", False),
|
||||
("esphome/core/time.cpp", False),
|
||||
("esphome/config.py", False),
|
||||
("script/helpers.py", False),
|
||||
("README.md", False),
|
||||
],
|
||||
)
|
||||
def test_filter_cpp_unit_test_files(file_path: str, expected: bool) -> None:
|
||||
"""Test which changed files can affect a component's C++ unit test build."""
|
||||
assert helpers.filter_cpp_unit_test_files(file_path) is expected
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cpp_unit_test_tree(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
|
||||
"""Fake repo root where time, sntp and api have C++ unit tests.
|
||||
|
||||
homeassistant depends on time but has no C++ tests, so it must be
|
||||
dropped from the selection; socket has only pytest files.
|
||||
"""
|
||||
tests_dir = tmp_path / "tests" / "components"
|
||||
for component in ("time", "sntp", "api"):
|
||||
(tests_dir / component).mkdir(parents=True)
|
||||
(tests_dir / component / f"{component}.cpp").write_text("")
|
||||
(tests_dir / "homeassistant").mkdir()
|
||||
(tests_dir / "homeassistant" / "__init__.py").write_text("")
|
||||
(tests_dir / "socket").mkdir()
|
||||
(tests_dir / "socket" / "conftest.py").write_text("")
|
||||
monkeypatch.setattr(helpers, "root_path", str(tmp_path))
|
||||
monkeypatch.setattr(
|
||||
helpers,
|
||||
"create_components_graph",
|
||||
lambda: {"time": ["homeassistant", "sntp"]},
|
||||
)
|
||||
return tmp_path
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("files", "expected"),
|
||||
[
|
||||
# Component changes expand to dependents with C++ tests
|
||||
(["esphome/components/time/posix_tz.cpp"], ["sntp", "time"]),
|
||||
(["esphome/components/time/__init__.py"], ["sntp", "time"]),
|
||||
# Dependent without C++ tests is dropped
|
||||
(["esphome/components/homeassistant/__init__.py"], []),
|
||||
# Test changes select only that component
|
||||
(["tests/components/time/posix_tz.cpp"], ["time"]),
|
||||
(["tests/components/time/__init__.py"], ["time"]),
|
||||
(["tests/components/homeassistant/__init__.py"], []),
|
||||
(["tests/components/socket/conftest.py"], []),
|
||||
(["tests/components/time/test.esp32-idf.yaml"], []),
|
||||
(
|
||||
["esphome/components/time/__init__.py", "tests/components/api/api.cpp"],
|
||||
["api", "sntp", "time"],
|
||||
),
|
||||
([], []),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("cpp_unit_test_tree")
|
||||
def test_get_cpp_changed_components(files: list[str], expected: list[str]) -> None:
|
||||
"""Test that C++ and Python component changes select the right unit tests."""
|
||||
assert helpers.get_cpp_changed_components(files) == expected
|
||||
|
||||
|
||||
def test_get_cpp_changed_components_independent_of_cwd(
|
||||
cpp_unit_test_tree: Path,
|
||||
tmp_path_factory: pytest.TempPathFactory,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Test directories resolve against root_path, not the current directory."""
|
||||
monkeypatch.chdir(tmp_path_factory.mktemp("elsewhere"))
|
||||
assert helpers.get_cpp_changed_components(
|
||||
["tests/components/time/__init__.py"]
|
||||
) == ["time"]
|
||||
|
||||
Reference in New Issue
Block a user