Files

131 lines
5.1 KiB
Python

"""Tests for the shared slot_counter codegen factory.
The factory is exercised end to end through the real controllers: a tracker
config must emit the platform's scan listener count define, and a
controller-only config must emit nothing so the guarded StaticVector storage
compiles out.
"""
from __future__ import annotations
from collections.abc import Callable
from pathlib import Path
import pytest
from esphome.core import CORE
from ..helpers import get_define_value
@pytest.mark.parametrize(
("config", "define"),
[
("bk72xx_tracker.yaml", "BK72XX_BLE_SCAN_LISTENER_COUNT"),
("rp2_tracker.yaml", "RP2040_BLE_SCAN_LISTENER_COUNT"),
],
)
def test_tracker_requests_one_slot(
config: str,
define: str,
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
"""The tracker's to_code requests a slot; the FINAL job emits the count.
The neutral listener count must stay absent from the same build: no BLE
consumer registered through register_ble_device().
"""
generate_main(component_config_path(config))
assert get_define_value(define) == "1"
assert get_define_value("ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT") is None
@pytest.mark.parametrize(
("config", "define"),
[
("bk72xx_controller_only.yaml", "BK72XX_BLE_SCAN_LISTENER_COUNT"),
("rp2_controller_only.yaml", "RP2040_BLE_SCAN_LISTENER_COUNT"),
],
)
def test_controller_only_emits_no_count(
config: str,
define: str,
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
"""No consumer, no define — the guarded listener storage compiles out."""
generate_main(component_config_path(config))
assert get_define_value(define) is None
def test_neutral_listener_count_emitted_when_requested() -> None:
"""Registering through register_ble_device() emits the neutral count.
No in-tree sensor registers through ble_device_base.register_ble_device()
yet (consumer migration is a follow-up), so the coroutine is driven with a
mock hub instead of a config; every tracker's #ifdef-guarded listener
storage keys on this define, and a broken emit path would compile the
storage out silently.
"""
import esphome.codegen as cg
from esphome.components import ble_device_base
from esphome.core import ID
hub_id = ID("hub", type=ble_device_base.BLEHub)
CORE.register_variable(hub_id, cg.MockObj("hub"))
CORE.add_job(
ble_device_base.register_ble_device,
cg.MockObj("listener"),
{ble_device_base.CONF_BLE_HUB_ID: hub_id},
)
CORE.flush_tasks()
assert get_define_value(ble_device_base.LISTENER_COUNT_DEFINE) == "1"
def test_esp32_tracker_handler_counts(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
"""A bare tracker registers its four esp32_ble handlers and nothing else."""
generate_main(component_config_path("esp32_tracker_only.yaml"))
assert get_define_value("ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT") == "1"
assert get_define_value("ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT") == "1"
assert get_define_value("ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT") == "1"
assert get_define_value("ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT") == "1"
assert get_define_value("ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT") is None
# No advertisement listener or client is registered, so both storages compile out.
assert get_define_value("ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT") is None
assert get_define_value("ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT") is None
def test_esp32_bluetooth_proxy_requests_client_slots_only(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
"""The proxy requests a client slot per connection (three by default with
active: true); advertisements and scanner state arrive through the hub
callbacks, so no listener slot exists."""
generate_main(component_config_path("esp32_bluetooth_proxy.yaml"))
assert get_define_value("ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT") is None
assert get_define_value("ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT") == "3"
# One neutral GATT backend slot per connection (the hub-model flip).
assert get_define_value("ESPHOME_BLE_GATT_CLIENT_COUNT") == "3"
def test_counts_reset_between_compiles(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
"""A second compile in the same process starts from zero.
The module level counters this change removes leaked across compiles in a
long lived host process (dashboard, device-builder), growing the handler
counts by one per compile and oversizing the StaticCallbackManager storage.
"""
generate_main(component_config_path("esp32_tracker_only.yaml"))
assert get_define_value("ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT") == "1"
CORE.reset()
generate_main(component_config_path("esp32_tracker_only.yaml"))
assert get_define_value("ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT") == "1"