diff --git a/tests/component_tests/ble_device_base/test_hub_binding.py b/tests/component_tests/ble_device_base/test_hub_binding.py index 3dc71daf58..a3bd4a7b12 100644 --- a/tests/component_tests/ble_device_base/test_hub_binding.py +++ b/tests/component_tests/ble_device_base/test_hub_binding.py @@ -202,3 +202,36 @@ def test_add_service_uuid_dispatches_by_width(monkeypatch: pytest.MonkeyPatch) - assert "0x00,0xff,0xee,0xdd" in emitted[2] with pytest.raises(ValueError, match="Unsupported UUID format"): ble_device_base.add_service_uuid(var, "123") + + +def test_hub_alias_ladder_mirrors_tracker_defines() -> None: + """Adding a BLE chip is a three-place invariant: the tracker's to_code + emits USE__BLE_TRACKER, ble_hub_impl.h has a matching alias arm, and + defines.h mirrors the define for static analysis. Pin all three so a + missing piece fails here instead of as a distant C++ error.""" + import re + + components = Path(ble_device_base.__file__).parent.parent + impl = (components / "ble_device_base" / "ble_hub_impl.h").read_text() + arm_defines = set(re.findall(r"defined\((USE_\w+_BLE_TRACKER)\)", impl)) + assert arm_defines, "alias ladder has no tracker arms - parse stale?" + + defines_h = (components.parent / "core" / "defines.h").read_text() + for define in arm_defines: + chip = define.removeprefix("USE_").removesuffix("_BLE_TRACKER").lower() + tracker_init = (components / f"{chip}_ble_tracker" / "__init__.py").read_text() + assert f'cg.add_define("{define}")' in tracker_init, ( + f"{define} has an alias arm but {chip}_ble_tracker never emits it" + ) + assert f"#define {define}" in defines_h, ( + f"{define} missing from the defines.h static-analysis mirror" + ) + + # Reverse direction: every tracker emitting the define has an alias arm. + for init in components.glob("*_ble_tracker/__init__.py"): + for define in re.findall( + r'cg\.add_define\("(USE_\w+_BLE_TRACKER)"\)', init.read_text() + ): + assert define in arm_defines, ( + f"{init.parent.name} emits {define} but ble_hub_impl.h has no alias arm" + )