[bluetooth_connection] Explicit pairing for rp2 (#18166)

This commit is contained in:
J. Nick Koston
2026-08-08 09:51:12 -05:00
committed by GitHub
parent 9cb46aa584
commit 7218aa4803
11 changed files with 209 additions and 10 deletions
@@ -46,6 +46,16 @@ class MinimalConnection : public BLEGattConnection {
void release_services() override {}
};
TEST(BleGattClientContract, PairingDefaultsAreSafeForNonPairingBackends) {
// pair() defaults to not-connected and on_pairing_result() to a no-op, so
// a backend without pairing still answers the client through the dispatch.
MinimalConnection conn;
RecordingListener listener;
conn.set_listener(&listener);
EXPECT_EQ(conn.pair(), GATT_ERR_NOT_CONNECTED);
listener.on_pairing_result(0); // must not crash: default body
}
TEST(BleGattClientContract, MinimalImplementerCompilesAndRoutesEvents) {
MinimalConnection connection;
RecordingListener listener;
@@ -0,0 +1,18 @@
import esphome.codegen as cg
from tests.testing_helpers import ComponentManifestOverride
def override_manifest(manifest: ComponentManifestOverride) -> None:
# close_service_batch compiles only under BLUETOOTH_CONNECTION_HAS_GATT;
# emit the backend define so the host build exercises it.
async def to_code_testing(config):
# These defines are global to the merged host test binary; safe
# because no co-compiled test observes them.
cg.add_define("USE_BLE_GATT_CLIENT")
cg.add_define("USE_BLUETOOTH_PROXY")
cg.add_define("BLUETOOTH_PROXY_ADVERTISEMENT_BATCH_SIZE", 16)
cg.add_define("BLUETOOTH_PROXY_MAX_CONNECTIONS", 1)
manifest.to_code = to_code_testing
# The batcher sizes api protobuf messages.
manifest.dependencies = manifest.dependencies + ["api"]
@@ -0,0 +1,58 @@
#include "esphome/components/bluetooth_connection/bluetooth_connection.h"
#include <gtest/gtest.h>
#include "esphome/components/api/api_pb2.h"
namespace esphome::bluetooth_connection {
// The three cursor behaviors: a fitting service advances and continues, an
// overflowing batch with >1 service pops and retries it, and a single
// oversized service is force-advanced so the stream cannot wedge.
static void add_service(api::BluetoothGATTGetServicesResponse &resp, uint16_t characteristics) {
resp.services.emplace_back();
auto &svc = resp.services.back();
svc.handle = resp.services.size();
svc.uuid = {0x1234567890ABCDEFULL, 0xFEDCBA0987654321ULL};
svc.characteristics.init(characteristics);
for (uint16_t i = 0; i < characteristics; i++) {
auto &chr = svc.characteristics.emplace_back();
chr.handle = 100 + i;
chr.properties = 0x12;
chr.uuid = {0x1234567890ABCDEFULL, 0xFEDCBA0987654321ULL};
}
}
TEST(CloseServiceBatch, FittingServiceAdvancesAndContinues) {
api::BluetoothGATTGetServicesResponse resp;
add_service(resp, 1);
size_t current_size = 0;
int16_t cursor = 0;
EXPECT_EQ(close_service_batch(resp, current_size, cursor, 0, "AA:BB"), BatchClose::CONTINUE);
EXPECT_EQ(cursor, 1);
EXPECT_GT(current_size, 0u);
}
TEST(CloseServiceBatch, OverflowPopsAndRetriesWithoutAdvancing) {
api::BluetoothGATTGetServicesResponse resp;
add_service(resp, 1);
add_service(resp, 1);
size_t current_size = MAX_PACKET_SIZE - 10; // any service is bigger than 10 bytes
int16_t cursor = 5;
EXPECT_EQ(close_service_batch(resp, current_size, cursor, 0, "AA:BB"), BatchClose::SEND);
EXPECT_EQ(resp.services.size(), 1u); // popped for the next batch
EXPECT_EQ(cursor, 5); // not advanced: retried next batch
}
TEST(CloseServiceBatch, SingleOversizedServiceForceAdvances) {
api::BluetoothGATTGetServicesResponse resp;
add_service(resp, 60); // ~30 bytes per characteristic, far past the budget
ASSERT_GT(resp.services.back().calculate_size(), MAX_PACKET_SIZE);
size_t current_size = 0;
int16_t cursor = 7;
EXPECT_EQ(close_service_batch(resp, current_size, cursor, 0, "AA:BB"), BatchClose::SEND);
EXPECT_EQ(cursor, 8); // advanced despite not fitting, so the stream moves on
}
} // namespace esphome::bluetooth_connection