Files
utun2/lib/ble/tests/test_ble_transport.c
T
evgeny ba1c4cf160 Add cross-platform BLE transport and Bluetooth settings
Integrate shared C transport with desktop and Android frontends, add radio probes and detailed Windows diagnostics, and fix GUI build dependencies and early configuration startup errors.
2026-10-29 10:51:19 +01:00

127 lines
7.7 KiB
C

/* Два/три настоящих common transport с управляемым асинхронным GATT backend: проверяется весь lifecycle. */
#include "ble_backend.h"
#include "u_async.h"
#include "mem.h"
#include "debug_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CHECK(v) do { if (!(v)) { fprintf(stderr, "FAIL line=%d: %s\n", __LINE__, #v); exit(1); } } while (0)
struct fake_link { ble_link_id id, other; struct ble_backend* peer; uint8_t data[512]; size_t len; int pending; };
struct ble_backend { struct ble_transport* owner; struct UASYNC* ua; void* task; int index, started; struct fake_link links[16]; };
static struct ble_backend* backends[3];
static unsigned int mtu = 23;
static int hold, drop, reject_peer, ready_count, writable_count;
static ble_link_id incoming_ids[3];
static void run(void* arg);
static void schedule(struct ble_backend* b) { if (!b->task) b->task = uasync_call_soon(b->ua, b, run); CHECK(b->task); }
struct ble_backend* ble_backend_create(struct ble_transport* owner, struct UASYNC* ua, const struct ble_config* cfg) {
struct ble_backend* b = u_calloc(1, sizeof(*b)); CHECK(b); b->owner = owner; b->ua = ua; b->index = (int)cfg->node_id - 1;
backends[b->index] = b; schedule(b); return b;
}
void ble_backend_destroy(struct ble_backend* b) {
if (b->task) uasync_call_soon_cancel(b->ua, b->task);
backends[b->index] = NULL; u_free(b);
}
int ble_backend_advertise(struct ble_backend* b, const uint8_t* data, size_t len) { (void)b; (void)data; (void)len; return 0; }
static struct fake_link* slot(struct ble_backend* b, ble_link_id id) {
for (int i = 0; i < 16; i++) if (b->links[i].id == id) return &b->links[i];
return NULL;
}
int ble_backend_connect(struct ble_backend* b, ble_link_id id, const struct ble_endpoint* ep) {
struct ble_backend* p = backends[atoi(ep->id)]; CHECK(p);
struct fake_link *a = slot(b, 0), *c = slot(p, 0); CHECK(a && c);
struct ble_endpoint remote = {0}; snprintf(remote.id, sizeof(remote.id), "%d", b->index);
ble_link_id other = ble_backend_incoming(p->owner, &remote); CHECK(other);
*a = (struct fake_link){.id=id, .other=other, .peer=p};
*c = (struct fake_link){.id=other, .other=id, .peer=b};
incoming_ids[p->index] = other;
schedule(b); schedule(p); return 0;
}
void ble_backend_close(struct ble_backend* b, ble_link_id id) {
struct fake_link* f = slot(b, id); if (f) memset(f, 0, sizeof(*f));
}
int ble_backend_send(struct ble_backend* b, ble_link_id id, const uint8_t* data, size_t len) {
struct fake_link* f = slot(b, id); CHECK(f && !f->pending && len <= sizeof(f->data));
CHECK(len <= mtu - 3);
memcpy(f->data, data, len); f->len = len; f->pending = 1; schedule(b); return 0;
}
static void run(void* arg) {
struct ble_backend* b = arg; b->task = NULL;
if (!b->started) { b->started = 1; ble_backend_status(b->owner, 1, "fake ready"); }
for (int i = 0; i < 16; i++) {
struct fake_link* f = &b->links[i]; if (!f->id) continue;
if (!f->len) { f->len = 1; ble_backend_opened(b->owner, f->id, mtu); }
if (!f->pending || hold) continue;
ble_link_id id = f->id, other = f->other;
struct ble_backend* peer = f->peer;
uint8_t data[512]; size_t len = f->len; memcpy(data, f->data, len); f->pending = 0;
int dropped = data[0] == 3 && drop && --drop == 0;
if (!dropped && peer && slot(peer, other)) ble_backend_received(peer->owner, other, data, len);
if (slot(b, id)) ble_backend_sent(b->owner, id, 1);
}
}
static int validate(void* arg, ble_link_id id, uint64_t node) { (void)arg; (void)id; (void)node; return !reject_peer; }
static void connection(void* arg, ble_link_id id, uint64_t node, enum ble_state s, const char* reason) {
(void)arg; (void)id; (void)node; (void)reason; if (s == BLE_READY) ready_count++;
}
static void writable(void* arg, ble_link_id id) { (void)arg; (void)id; writable_count++; }
static void drain(struct UASYNC* ua) { for (int i = 0; i < 1000; i++) uasync_poll(ua, 0); }
static struct ll_entry* frame(unsigned int size, uint8_t value) {
struct ll_entry* e = ll_alloc_lldgram(size); CHECK(e); e->len = size; memset(e->dgram, value, size); return e;
}
static void release(struct ll_entry* e) { queue_dgram_free(e); queue_entry_free(e); }
static void exercise(unsigned int test_mtu) {
mtu = test_mtu; hold = drop = reject_peer = ready_count = writable_count = 0;
struct UASYNC* ua = uasync_create(); CHECK(ua);
struct ble_transport* t[3];
for (int i = 0; i < 3; i++) {
struct ble_config cfg = {0}; cfg.node_id = i + 1; cfg.company_id = 0xffff;
cfg.callbacks.validate = validate; cfg.callbacks.connection = connection; cfg.callbacks.writable = writable;
t[i] = ble_create(ua, &cfg); CHECK(t[i] && !ble_start(t[i]));
}
drain(ua);
struct ble_endpoint ep = {"1"};
ble_link_id a = ble_connect(t[0], &ep, 2); CHECK(a); drain(ua); CHECK(ready_count == 2);
ble_link_id b = incoming_ids[1];
CHECK(!ble_tx_put(t[0], a, frame(1500, 0x31))); CHECK(!ble_tx_put(t[1], b, frame(1499, 0x71))); drain(ua);
struct ll_entry* e = ble_rx_get(t[1], b); CHECK(e && e->len == 1500);
for (int i = 0; i < e->len; i++) CHECK(e->dgram[i] == 0x31);
release(e);
e = ble_rx_get(t[0], a); CHECK(e && e->len == 1499 && e->dgram[1498] == 0x71); release(e);
drop = 2; CHECK(!ble_tx_put(t[0], a, frame(1500, 0x44))); drain(ua); CHECK(!ble_rx_get(t[1], b));
CHECK(!ble_tx_put(t[0], a, frame(5, 0x55))); drain(ua); e = ble_rx_get(t[1], b); CHECK(e && e->len == 5); release(e);
hold = 1;
for (int i = 0; i < BLE_QUEUE_MAX; i++) CHECK(!ble_tx_put(t[0], a, frame(1, i)));
e = frame(1, 99); CHECK(ble_tx_put(t[0], a, e) == BLE_WOULD_BLOCK); CHECK(!e->owner && e->dgram[0] == 99); release(e);
hold = 0; schedule(backends[0]); drain(ua); CHECK(writable_count == 1);
for (int i = 0; i < BLE_QUEUE_MAX; i++) { e = ble_rx_get(t[1], b); CHECK(e && e->dgram[0] == i); release(e); }
CHECK(!ble_rx_get(t[1], b));
for (int i = 0; i < BLE_QUEUE_MAX + 1; i++) { CHECK(!ble_tx_put(t[0], a, frame(2, i))); drain(ua); }
for (int i = 0; i < BLE_QUEUE_MAX; i++) { e = ble_rx_get(t[1], b); CHECK(e && e->dgram[0] == i); release(e); }
CHECK(!ble_rx_get(t[1], b));
strcpy(ep.id, "2"); ble_link_id c = ble_connect(t[0], &ep, 3); CHECK(c); drain(ua); CHECK(ready_count == 4);
hold = 1; CHECK(!ble_tx_put(t[0], a, frame(1500, 8))); CHECK(!ble_tx_put(t[0], c, frame(1500, 9)));
ble_disconnect(t[0], a); hold = 0; schedule(backends[0]); drain(ua);
e = ble_rx_get(t[2], incoming_ids[2]); CHECK(e && e->len == 1500 && e->dgram[1499] == 9); release(e);
e = frame(1, 0); CHECK(ble_tx_put(t[0], a, e) == BLE_NOT_READY); release(e);
/* Отказ политики на третьем независимом направлении не затрагивает существующее соединение. */
reject_peer = 1; strcpy(ep.id, "2"); ble_link_id rejected = ble_connect(t[1], &ep, 3); CHECK(rejected); drain(ua);
CHECK(ready_count == 4);
e = frame(1, 0); CHECK(ble_tx_put(t[1], rejected, e) == BLE_NOT_READY); release(e);
ble_set_incoming(t[2], 0);
struct ble_endpoint denied = {"policy-test"};
CHECK(!ble_backend_incoming(t[2], &denied));
CHECK(!ble_rx_get(t[2], incoming_ids[2]));
ble_set_incoming(t[2], 1);
CHECK(ble_backend_incoming(t[2], &denied));
for (int i = 0; i < 3; i++) ble_destroy(t[i]);
drain(ua); uasync_destroy(ua, 0);
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
size_t before = u_get_allocated_count();
exercise(23); exercise(247); exercise(517);
CHECK(u_get_allocated_count() == before); puts("BLE transport tests PASS (MTU 23/247/517, duplex, loss, backpressure, peers, teardown)"); return 0;
}