100 lines
5.2 KiB
C
100 lines
5.2 KiB
C
/* Контракт 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 <assert.h>
|
|
#include <stdio.h>
|
|
|
|
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;
|
|
}
|