You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

119 lines
6.3 KiB

/* Настоящие transport/fragment/uasync, управляемый router без сетевых таймеров. */
#define etcp_router_conn_get test_router_conn_get
#define etcp_router_conn_send test_router_conn_send
#define etcp_router_tx_stats test_router_tx_stats
#define etcp_router_send_q_has_room test_router_send_q_has_room
#define etcp_router_resume_receive test_router_resume_receive
#include "../src/call/call_video_transport.c"
#undef etcp_router_conn_get
#undef etcp_router_conn_send
#undef etcp_router_tx_stats
#undef etcp_router_send_q_has_room
#undef etcp_router_resume_receive
#include <assert.h>
#include <stdio.h>
/* Один batch, без poll/timeouts: тест не может случайно продолжить TX таймером 50мс. */
extern void process_immediate_queue(struct UASYNC *ua);
static struct ETCP_ROUTER_CONN router;
static unsigned packets, received, room, resumed;
static uint64_t debt;
enum { FRAME_BYTES = 45001 };
static uint8_t output[FRAME_BYTES];
struct ETCP_ROUTER_CONN *test_router_conn_get(struct UTUN_INSTANCE *inst, uint64_t group, uint64_t peer, uint8_t svc) {
(void)inst; (void)group; (void)peer; assert(svc == ETCP_RT_ID_CALL_VIDEO); return &router;
}
int test_router_conn_send(struct ETCP_ROUTER_CONN *conn, const uint8_t *data, size_t len, int mode) {
assert(conn == &router && !mode);
struct call_video_frame f;
assert(!call_video_fragment_parse(data, len, &f));
assert(f.call_id == 7 && f.frame_id == 1 && f.size == FRAME_BYTES && f.offset == received);
assert(received + f.length <= sizeof(output));
memcpy(output + received, f.data, f.length); received += f.length; packets++; return 0;
}
int test_router_tx_stats(struct UTUN_INSTANCE *inst, uint64_t group, uint64_t peer, uint8_t svc,
uint64_t now_us, struct ETCP_ROUTER_TX_STATS *out) {
(void)inst; (void)group; (void)peer; (void)svc; (void)now_us;
*out = (struct ETCP_ROUTER_TX_STATS){.inflight_bytes = debt, .ready = 1}; return 0;
}
int test_router_send_q_has_room(struct UTUN_INSTANCE *inst, uint64_t group, uint64_t peer, uint8_t svc) {
(void)inst; (void)group; (void)peer; (void)svc; return room;
}
void test_router_resume_receive(struct UTUN_INSTANCE *inst, uint64_t group, uint64_t peer, uint8_t svc) {
(void)inst; (void)group; (void)peer; (void)svc; resumed++;
}
static struct call_video_transport *setup(struct call_session *s) {
struct call_video_transport *v = u_calloc(1, sizeof(*v)); assert(v);
v->session = s; s->video = v;
v->receiver = call_video_receiver_create(); assert(v->receiver);
v->have_peer = 1; v->local_revision = v->acknowledged_revision = 1;
v->local.encode[0] = (struct call_video_limit){1280, 720, 30, 1280 * 720 * 30, 4000000, CALL_VIDEO_BASELINE, 31};
v->peer.decode[0] = v->local.encode[0];
v->audio_end_pts = 1000000;
v->tx_count = 1; v->tx_bytes = FRAME_BYTES;
struct video_tx_frame *head = &v->tx[0];
head->data = u_malloc(FRAME_BYTES); assert(head->data);
for (unsigned i = 0; i < FRAME_BYTES; i++) head->data[i] = (uint8_t)(i * 17 + i / 257);
head->started_tb = get_time_tb();
head->frame = (struct call_video_frame){.call_id = 7, .frame_id = 1, .pts_us = 1000000,
.format = {1, 640, 480, 30, 0, CALL_VIDEO_H264, 31, 4000000, CALL_VIDEO_BASELINE}, .size = FRAME_BYTES};
packets = received = resumed = 0; room = 1; debt = 0;
memset(output, 0, sizeof(output)); return v;
}
static void check_complete(struct call_video_transport *v) {
assert(packets == 38 && received == FRAME_BYTES && !v->tx_count && !v->tx_bytes && !v->flush_pending);
for (unsigned i = 0; i < FRAME_BYTES; i++) assert(output[i] == (uint8_t)(i * 17 + i / 257));
}
static void cleanup(struct call_session *s) {
call_video_transport_stop(s); assert(!s->video && resumed == 1);
unsigned before = packets;
uasync_drain_immediate(s->ctx->inst->ua);
assert(packets == before);
assert(s->ctx->inst->ua->timer_alloc_count == s->ctx->inst->ua->timer_free_count);
}
int main(void) {
debug_set_level(DEBUG_LEVEL_WARN);
struct UASYNC *ua = uasync_create(); assert(ua);
struct UTUN_INSTANCE inst = {.ua = ua};
struct call_ctx ctx = {.inst = &inst};
struct call_session s = {.ctx = &ctx, .call_id = 7};
router.send_q = queue_new(ua, 0, 0, 0, "video_flush_test"); assert(router.send_q);
struct call_video_transport *v = setup(&s);
flush(v); assert(packets == 16 && v->flush_pending);
process_immediate_queue(ua); assert(packets == 32 && v->flush_pending);
process_immediate_queue(ua); check_complete(v); cleanup(&s);
puts("PASS: 45001-byte frame completes in bounded batches without audio or periodic timers; bytes preserved");
for (unsigned gate = 0; gate < 2; gate++) {
v = setup(&s); flush(v);
if (gate) debt = 1000000; else room = 0;
process_immediate_queue(ua); assert(packets == 16 && !v->flush_pending && v->flush_net_wait == 1);
process_immediate_queue(ua); assert(packets == 16);
room = 1; debt = 0; flush(v); uasync_drain_immediate(ua); check_complete(v); cleanup(&s);
}
puts("PASS: router room and inflight debt stop continuation without spinning; TX resumes exactly once");
v = setup(&s); v->audio_end_pts--;
flush(v); assert(!packets && !v->flush_pending);
call_video_audio_sent(&s, 1000000); uasync_drain_immediate(ua); check_complete(v); cleanup(&s);
v = setup(&s); v->acknowledged_revision = 0;
flush(v); assert(!packets && !v->flush_pending);
v->acknowledged_revision = 1; flush(v); uasync_drain_immediate(ua); check_complete(v); cleanup(&s);
puts("PASS: audio position and CAPS acknowledgement remain required");
v = setup(&s); flush(v);
call_video_audio_sent(&s, 1020000); assert(packets == 32);
process_immediate_queue(ua); check_complete(v); cleanup(&s);
puts("PASS: audio flush supersedes pending continuation without duplicate fragments");
v = setup(&s); flush(v); cleanup(&s); assert(packets == 16);
v = setup(&s); flush(v); discard_tx(v, "test_detach");
assert(!v->tx_count && !v->flush_pending && v->need_key);
uasync_drain_immediate(ua); assert(packets == 16); cleanup(&s);
puts("PASS: stop and discard cancel pending callbacks before freeing their owner/data");
queue_free(router.send_q); uasync_destroy(ua, 0); return 0;
}