[bluetooth_proxy] Deliver scanner state through the hub callback (#18175)

This commit is contained in:
J. Nick Koston
2026-08-08 15:59:49 -05:00
committed by GitHub
parent 04384e0f5b
commit 8c74e3d5ef
9 changed files with 136 additions and 61 deletions
@@ -103,17 +103,17 @@ def test_esp32_tracker_handler_counts(
assert get_define_value("ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT") is None
def test_esp32_bluetooth_proxy_requests_scanner_state_slot(
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 one scanner state slot and a client slot per
connection (three by default with active: true); advertisements arrive
through the hub raw callback, so no listener slot exists."""
"""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 or scanner-state slot exists."""
generate_main(component_config_path("esp32_bluetooth_proxy.yaml"))
assert (
get_define_value("ESPHOME_ESP32_BLE_TRACKER_SCANNER_STATE_LISTENER_COUNT")
== "1"
is None
)
assert get_define_value("ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT") is None
assert get_define_value("ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT") == "3"
@@ -0,0 +1,52 @@
#include <gtest/gtest.h>
#include <cstdint>
#include "esphome/components/ble_device_base/ble_hub.h"
namespace esphome::ble_device_base::testing {
// Pins the ScannerStateCallback slot semantics, mirroring test_raw_callback:
// a default-constructed slot is "no subscriber", a set slot delivers the
// state, and a new registration replaces the old.
namespace {
struct CapturingSubscriber {
ScannerState last{ScannerState::IDLE};
int calls{0};
static void trampoline(void *self, ScannerState state) {
auto *sub = static_cast<CapturingSubscriber *>(self);
sub->last = state;
sub->calls++;
}
};
} // namespace
TEST(ScannerStateCallback, DefaultConstructedSlotIsNotSet) {
const ScannerStateCallback callback{};
EXPECT_FALSE(callback.is_set());
}
TEST(ScannerStateCallback, SubscriberSeesState) {
CapturingSubscriber subscriber;
ScannerStateCallback callback{&subscriber, CapturingSubscriber::trampoline};
ASSERT_TRUE(callback.is_set());
callback.invoke(ScannerState::RUNNING);
EXPECT_EQ(subscriber.calls, 1);
EXPECT_EQ(subscriber.last, ScannerState::RUNNING);
}
TEST(ScannerStateCallback, NewSubscriberReplacesOld) {
CapturingSubscriber first;
CapturingSubscriber second;
ScannerStateCallback callback{&first, CapturingSubscriber::trampoline};
callback = {&second, CapturingSubscriber::trampoline};
callback.invoke(ScannerState::STOPPED);
EXPECT_EQ(first.calls, 0);
EXPECT_EQ(second.calls, 1);
EXPECT_EQ(second.last, ScannerState::STOPPED);
}
} // namespace esphome::ble_device_base::testing