/* Контракт packet_link: владение, занятость, объединение событий, RX pause и teardown внутри callbacks. */ #ifdef NDEBUG #undef NDEBUG #endif #include "../lib/packet_link_backend.h" #include "../lib/ll_queue.h" #include "../lib/u_async.h" #include "../lib/mem.h" #include "../lib/debug_config.h" #include #include struct fixture { struct packet_link* link; struct ll_entry* pending; int states, received, stopped, destroyed, in_send, close_state, close_rx, send_state, enabled; }; static struct ll_entry* packet(void) { struct ll_entry* p = ll_alloc_lldgram(8); assert(p); p->len = 8; p->dgram[0] = 42; return p; } static void free_packet(struct ll_entry* p) { queue_dgram_free(p); queue_entry_free(p); } static void publish(struct fixture* f, int ready) { const struct packet_link_state state = {PACKET_LINK_OPEN, 1500, ready, 0}; packet_link_backend_set_state(f->link, &state); } static int accept_packet(void* arg, struct ll_entry* p) { struct fixture* f = arg; assert(!f->pending && !f->stopped); f->in_send = 1; f->pending = p; publish(f, 0); f->in_send = 0; return PACKET_LINK_OK; } static void enable(void* arg, int enabled) { ((struct fixture*)arg)->enabled = enabled; } static void stop(void* arg) { struct fixture* f = arg; assert(!f->stopped); f->stopped = 1; if (f->pending) { free_packet(f->pending); f->pending = NULL; } } static void destroy(void* arg) { struct fixture* f = arg; assert(f->stopped && !f->destroyed); f->destroyed = 1; } static void changed(struct packet_link* link, void* arg) { struct fixture* f = arg; assert(!f->in_send && !f->stopped); f->states++; if (f->close_state) { packet_link_close(link); return; } if (f->send_state && packet_link_get_state(link).tx_ready) { f->send_state = 0; assert(packet_link_send(link, packet()) == PACKET_LINK_OK); } } static void received(struct packet_link* link, struct ll_entry* p, void* arg) { struct fixture* f = arg; assert(f->enabled && !f->in_send && !f->stopped); assert(p->dgram[0] == 42); f->received++; free_packet(p); if (f->close_rx) packet_link_close(link); } static void create(struct fixture* f, struct UASYNC* ua) { const struct packet_link_ops ops = {accept_packet, enable, stop, destroy}; const struct packet_link_callbacks callbacks = {changed, received}; const struct packet_link_state initial = {PACKET_LINK_OPEN, 1500, 1, 0}; f->link = packet_link_create(ua, &ops, f, &initial, &callbacks, f); assert(f->link); assert(!f->states && !f->enabled); } int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); size_t baseline = u_get_allocated_count(); struct UASYNC* ua = uasync_create(); assert(ua); struct fixture f = {0}; create(&f, ua); struct ll_entry* p = packet(); assert(packet_link_backend_receive(f.link, p) == PACKET_LINK_WOULD_BLOCK); packet_link_set_rx_enabled(f.link, 1); assert(packet_link_backend_receive(f.link, p) == PACKET_LINK_OK && f.received == 1); packet_link_set_rx_enabled(f.link, 0); p = packet(); assert(packet_link_backend_receive(f.link, p) == PACKET_LINK_WOULD_BLOCK); free_packet(p); p = ll_alloc_lldgram(1501); assert(p); p->len = 1501; assert(packet_link_send(f.link, p) == PACKET_LINK_TOO_LARGE); free_packet(p); p = packet(); assert(packet_link_send(f.link, p) == PACKET_LINK_OK && f.pending == p); assert(!packet_link_get_state(f.link).tx_ready && !f.states); p = packet(); assert(packet_link_send(f.link, p) == PACKET_LINK_WOULD_BLOCK && p->dgram[0] == 42); uasync_drain_immediate(ua); assert(f.states == 1); free_packet(f.pending); f.pending = NULL; publish(&f, 1); assert(packet_link_send(f.link, p) == PACKET_LINK_OK); uasync_drain_immediate(ua); assert(f.states == 2); packet_link_close(f.link); assert(f.stopped && !f.destroyed); uasync_drain_immediate(ua); assert(f.destroyed && f.states == 2); struct fixture cancel = {0}; create(&cancel, ua); packet_link_close(cancel.link); uasync_drain_immediate(ua); assert(!cancel.states && cancel.destroyed); struct fixture state = {.close_state = 1}; create(&state, ua); uasync_drain_immediate(ua); assert(state.states == 1 && state.destroyed); struct fixture rx = {.close_rx = 1}; create(&rx, ua); packet_link_set_rx_enabled(rx.link, 1); assert(packet_link_backend_receive(rx.link, packet()) == PACKET_LINK_OK); assert(rx.stopped && !rx.destroyed); uasync_drain_immediate(ua); assert(rx.destroyed && !rx.states); struct fixture nested = {.send_state = 1}; create(&nested, ua); uasync_drain_immediate(ua); assert(nested.states == 2 && nested.pending); struct packet_link_state closed = {PACKET_LINK_TERMINATED, 1500, 0, PACKET_LINK_ERROR}; packet_link_backend_set_state(nested.link, &closed); p = packet(); assert(packet_link_send(nested.link, p) == PACKET_LINK_CLOSED); free_packet(p); packet_link_close(nested.link); uasync_drain_immediate(ua); assert(nested.destroyed); uasync_destroy(ua, 0); assert(u_get_allocated_count() == baseline); puts("PASS packet_link: ownership, TX blocked/resume, deferred/coalesced state, RX pause, close in callbacks, no leaks"); return 0; }