120 lines
6.3 KiB
C
120 lines
6.3 KiB
C
/* Настоящие 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;
|
|
}
|