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.
800 lines
36 KiB
800 lines
36 KiB
#include "u_async.h" |
|
#include "ll_queue.h" |
|
#include "pkt_normalizer.h" |
|
#include "u_async.h" |
|
#include "settings.h" |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <stdlib.h> |
|
#include <stdint.h> |
|
#include <assert.h> |
|
#include <time.h> |
|
|
|
#define TEST_ASSERT(cond, msg) \ |
|
do { \ |
|
if (!(cond)) { \ |
|
printf("FAIL: %s (line %d)\n", msg, __LINE__); \ |
|
return 1; \ |
|
} else { \ |
|
printf("PASS: %s\n", msg); \ |
|
} \ |
|
} while(0) |
|
|
|
/* Структура для хранения тестового пакета */ |
|
typedef struct { |
|
uint8_t* data; |
|
size_t len; |
|
} test_packet_t; |
|
|
|
#define MAX_TEST_PACKETS 20000 |
|
static test_packet_t sent_packets[MAX_TEST_PACKETS]; |
|
static test_packet_t received_packets[MAX_TEST_PACKETS]; |
|
static int sent_count = 0; |
|
static int received_count = 0; |
|
static uasync_t* test_ua = NULL; |
|
|
|
static void reset_test_data(void) { |
|
for (int i = 0; i < sent_count; i++) { |
|
free(sent_packets[i].data); |
|
sent_packets[i].data = NULL; |
|
} |
|
for (int i = 0; i < received_count; i++) { |
|
free(received_packets[i].data); |
|
received_packets[i].data = NULL; |
|
} |
|
sent_count = 0; |
|
received_count = 0; |
|
} |
|
|
|
static void add_sent_packet(const uint8_t* data, size_t len) { |
|
assert(sent_count < MAX_TEST_PACKETS); |
|
sent_packets[sent_count].data = malloc(len); |
|
assert(sent_packets[sent_count].data); |
|
memcpy(sent_packets[sent_count].data, data, len); |
|
sent_packets[sent_count].len = len; |
|
sent_count++; |
|
} |
|
|
|
static void add_received_packet(const uint8_t* data, size_t len) { |
|
assert(received_count < MAX_TEST_PACKETS); |
|
received_packets[received_count].data = malloc(len); |
|
assert(received_packets[received_count].data); |
|
memcpy(received_packets[received_count].data, data, len); |
|
received_packets[received_count].len = len; |
|
received_count++; |
|
} |
|
|
|
static int compare_packets(void) { |
|
if (sent_count != received_count) { |
|
printf("Packet count mismatch: sent=%d, received=%d\n", sent_count, received_count); |
|
return -1; |
|
} |
|
for (int i = 0; i < sent_count; i++) { |
|
if (sent_packets[i].len != received_packets[i].len) { |
|
printf("Packet %d length mismatch: sent=%zu, received=%zu\n", |
|
i, sent_packets[i].len, received_packets[i].len); |
|
return -1; |
|
} |
|
if (memcmp(sent_packets[i].data, received_packets[i].data, sent_packets[i].len) != 0) { |
|
printf("Packet %d data mismatch\n", i); |
|
return -1; |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
/* Создать тестовый пакет со случайным содержимым */ |
|
static ll_entry_t* create_test_packet(size_t data_size) { |
|
ll_entry_t* entry = queue_entry_new(data_size); |
|
if (!entry) { |
|
return NULL; |
|
} |
|
uint8_t* data = ll_entry_data(entry); |
|
for (size_t i = 0; i < data_size; i++) { |
|
data[i] = rand() & 0xFF; |
|
} |
|
return entry; |
|
} |
|
|
|
/* Коллбэк для пересылки пакетов от упаковщика к распаковщику */ |
|
static void forward_cb(ll_queue_t* q, ll_entry_t* unused, void* arg) { |
|
(void)unused; |
|
ll_queue_t* target = (ll_queue_t*)arg; |
|
while (queue_entry_count(q) > 0) { |
|
ll_entry_t* e = queue_entry_get(q); |
|
queue_entry_put(target, e); // Transfer ownership |
|
} |
|
queue_resume_callback(q); |
|
} |
|
|
|
/* Коллбэк для сбора полученных пакетов */ |
|
static void receive_cb(ll_queue_t* q, ll_entry_t* unused, void* arg) { |
|
(void)unused; |
|
(void)arg; |
|
while (queue_entry_count(q) > 0) { |
|
ll_entry_t* e = queue_entry_get(q); |
|
uint8_t* data = ll_entry_data(e); |
|
size_t len = ll_entry_size(e); |
|
add_received_packet(data, len); |
|
queue_entry_free(e); |
|
} |
|
queue_resume_callback(q); |
|
} |
|
|
|
/* Принудительно обработать все очереди, вызывая коллбэки для непустых очередей |
|
* Это нужно потому что в тестовой среде нет uasync_mainloop |
|
*/ |
|
static void process_queues(pkt_normalizer_pair* pair) { |
|
int iterations = 0; |
|
int processed; |
|
|
|
/* Сначала принудительно сбросим все флаги callback_suspended, потому что |
|
* uasync таймауты не работают в тестовой среде */ |
|
pair->packer->input->callback_suspended = 0; |
|
pair->packer->output->callback_suspended = 0; |
|
pair->unpacker->input->callback_suspended = 0; |
|
pair->unpacker->output->callback_suspended = 0; |
|
|
|
do { |
|
processed = 0; |
|
iterations++; |
|
if (iterations % 10 == 1) printf("process_queues iteration %d: counts: pi=%d po=%d ui=%d uo=%d\n", iterations, queue_entry_count(pair->packer->input), queue_entry_count(pair->packer->output), queue_entry_count(pair->unpacker->input), queue_entry_count(pair->unpacker->output)); |
|
|
|
/* Обработать входную очередь упаковщика */ |
|
while (pair->packer->input->callback && |
|
queue_entry_count(pair->packer->input) > 0) { |
|
pair->packer->input->callback(pair->packer->input, |
|
pair->packer->input->head, |
|
pair->packer->input->callback_arg); |
|
processed = 1; |
|
} |
|
|
|
/* Обработать выходную очередь упаковщика (forward_cb) */ |
|
if (pair->packer->output->callback && |
|
queue_entry_count(pair->packer->output) > 0) { |
|
pair->packer->output->callback(pair->packer->output, |
|
pair->packer->output->head, |
|
pair->packer->output->callback_arg); |
|
processed = 1; |
|
} |
|
|
|
/* Обработать входную очередь распаковщика */ |
|
if (pair->unpacker->input->callback && |
|
queue_entry_count(pair->unpacker->input) > 0) { |
|
pair->unpacker->input->callback(pair->unpacker->input, |
|
pair->unpacker->input->head, |
|
pair->unpacker->input->callback_arg); |
|
processed = 1; |
|
} |
|
|
|
/* Обработать выходную очередь распаковщика (receive_cb) */ |
|
if (pair->unpacker->output->callback && |
|
queue_entry_count(pair->unpacker->output) > 0) { |
|
pair->unpacker->output->callback(pair->unpacker->output, |
|
pair->unpacker->output->head, |
|
pair->unpacker->output->callback_arg); |
|
processed = 1; |
|
} |
|
|
|
if (iterations > 100000) { |
|
printf("ERROR: process_queues infinite loop detected\n"); |
|
break; |
|
} |
|
} while (processed); |
|
} |
|
|
|
/* Тест 1: мелкие пакеты */ |
|
static int test_small_packets(pkt_normalizer_pair* pair) { |
|
printf("\n--- Test 1: small packets ---\n"); |
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
|
|
const char* packets[] = {"hello", "world", "test"}; |
|
for (size_t i = 0; i < sizeof(packets)/sizeof(packets[0]); i++) { |
|
size_t len = strlen(packets[i]); |
|
ll_entry_t* entry = queue_entry_new(len); |
|
TEST_ASSERT(entry != NULL, "create packet"); |
|
memcpy(ll_entry_data(entry), packets[i], len); |
|
add_sent_packet((const uint8_t*)packets[i], len); |
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put into packer input"); |
|
} |
|
|
|
process_queues(pair); |
|
|
|
TEST_ASSERT(compare_packets() == 0, "small packets comparison"); |
|
return 0; |
|
} |
|
|
|
/* Тест 2: средний пакет (не фрагментируется) */ |
|
static int test_medium_packet(pkt_normalizer_pair* pair) { |
|
printf("\n--- Test 2: medium packet ---\n"); |
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
|
|
size_t data_size = 500; |
|
ll_entry_t* entry = create_test_packet(data_size); |
|
TEST_ASSERT(entry != NULL, "create medium packet"); |
|
|
|
uint8_t* data = ll_entry_data(entry); |
|
add_sent_packet(data, data_size); |
|
|
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put medium packet"); |
|
|
|
process_queues(pair); |
|
|
|
TEST_ASSERT(compare_packets() == 0, "medium packet comparison"); |
|
return 0; |
|
} |
|
|
|
/* Тест 3: большой пакет с фрагментацией */ |
|
static int test_fragmentation(pkt_normalizer_pair* pair) { |
|
printf("\n--- Test 3: fragmentation ---\n"); |
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
|
|
/* Сохранить оригинальный размер фрагмента */ |
|
int original_fragment_size = settings.max_fragment_size; |
|
|
|
/* Установить маленький размер фрагмента для тестирования */ |
|
settings.max_fragment_size = 300; |
|
|
|
size_t data_size = 1000; /* Должен быть фрагментирован */ |
|
ll_entry_t* entry = create_test_packet(data_size); |
|
TEST_ASSERT(entry != NULL, "create big packet"); |
|
|
|
uint8_t* data = ll_entry_data(entry); |
|
add_sent_packet(data, data_size); |
|
|
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put big packet"); |
|
|
|
process_queues(pair); |
|
|
|
/* Проверить, что не было ошибок сборки */ |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no fragmentation errors"); |
|
|
|
/* Восстановить оригинальный размер */ |
|
settings.max_fragment_size = original_fragment_size; |
|
|
|
TEST_ASSERT(compare_packets() == 0, "fragmented packet comparison"); |
|
return 0; |
|
} |
|
|
|
/* Тест 4: несколько пакетов разного размера */ |
|
static int test_mixed_packets(pkt_normalizer_pair* pair) { |
|
printf("\n--- Test 4: mixed size packets ---\n"); |
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
|
|
/* Отправить пакеты разных размеров */ |
|
size_t sizes[] = {10, 100, 50, 300, 5}; |
|
for (size_t i = 0; i < sizeof(sizes)/sizeof(sizes[0]); i++) { |
|
ll_entry_t* entry = create_test_packet(sizes[i]); |
|
TEST_ASSERT(entry != NULL, "create mixed packet"); |
|
|
|
uint8_t* data = ll_entry_data(entry); |
|
add_sent_packet(data, sizes[i]); |
|
|
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put mixed packet"); |
|
} |
|
|
|
process_queues(pair); |
|
|
|
TEST_ASSERT(compare_packets() == 0, "mixed packets comparison"); |
|
return 0; |
|
} |
|
|
|
/* Тест 6: стресс-тест со случайными размерами пакетов */ |
|
static int test_stress_random(pkt_normalizer_pair* pair) { |
|
printf("\n--- Test 6: stress test with 10000 random packets (0-4096 bytes) ---\n"); |
|
const int NUM_PACKETS = 10000; |
|
const int MAX_PACKET_SIZE = 4096; |
|
int original_fragment_size = settings.max_fragment_size; |
|
|
|
/* Часть 1: 10000 случайных пакетов без изменения размера фрагмента */ |
|
printf("Sending %d random packets...\n", NUM_PACKETS); |
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->packer); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
|
|
for (int i = 0; i < NUM_PACKETS; i++) { |
|
/* Случайный размер пакета от 0 до MAX_PACKET_SIZE */ |
|
size_t size = rand() % (MAX_PACKET_SIZE + 1); |
|
|
|
ll_entry_t* entry = create_test_packet(size); |
|
TEST_ASSERT(entry != NULL, "create random packet"); |
|
|
|
uint8_t* data = ll_entry_data(entry); |
|
add_sent_packet(data, size); |
|
|
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put random packet"); |
|
|
|
// Периодически обрабатывать очереди чтобы не переполнять |
|
if (i % 500 == 0) { |
|
process_queues(pair); |
|
} |
|
|
|
// Прогресс |
|
if (i % 1000 == 0) { |
|
printf(" Progress: %d/%d sent=%d received=%d pi=%d po=%d ui=%d uo=%d\n", |
|
i, NUM_PACKETS, sent_count, received_count, |
|
queue_entry_count(pair->packer->input), |
|
queue_entry_count(pair->packer->output), |
|
queue_entry_count(pair->unpacker->input), |
|
queue_entry_count(pair->unpacker->output)); |
|
} |
|
} |
|
|
|
// Обработать оставшиеся пакеты |
|
process_queues(pair); |
|
|
|
printf("Sent %d packets, received %d packets\n", sent_count, received_count); |
|
TEST_ASSERT(sent_count == NUM_PACKETS, "all packets sent"); |
|
TEST_ASSERT(received_count == NUM_PACKETS, "all packets received"); |
|
TEST_ASSERT(compare_packets() == 0, "all packets matched"); |
|
printf("Packer error count: %d\n", pkt_normalizer_get_error_count(pair->packer)); |
|
printf("Unpacker error count: %d\n", pkt_normalizer_get_error_count(pair->unpacker)); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->packer) <= 10, "too many packer errors"); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no unpacker errors"); |
|
|
|
/* Часть 2: проверка дефрагментации (большой пакет фрагментируется, а не отправляется мелкими пакетами) */ |
|
printf("\n--- Verifying fragmentation behavior ---\n"); |
|
settings.max_fragment_size = 500; |
|
|
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->packer); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
|
|
// Большой пакет, который должен быть фрагментирован |
|
size_t big_packet_size = 2000; |
|
ll_entry_t* entry = create_test_packet(big_packet_size); |
|
TEST_ASSERT(entry != NULL, "create big packet"); |
|
add_sent_packet(ll_entry_data(entry), big_packet_size); |
|
|
|
// Подсчитать количество элементов в output очереди packer'а до обработки |
|
int initial_output_count = queue_entry_count(pair->packer->output); |
|
|
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put big packet"); |
|
|
|
// Обработать только packer (чтобы фрагменты появились в его output) |
|
if (pair->packer->input->callback && |
|
queue_entry_count(pair->packer->input) > 0) { |
|
pair->packer->input->callback(pair->packer->input, |
|
pair->packer->input->head, |
|
pair->packer->input->callback_arg); |
|
} |
|
|
|
// Проверить количество фрагментов в output очереди packer'а |
|
int fragment_count = queue_entry_count(pair->packer->output); |
|
printf("Big packet %zu bytes, fragment size %d, produced %d fragments\n", |
|
big_packet_size, settings.max_fragment_size, fragment_count); |
|
|
|
// Должно быть больше 1 фрагмента |
|
TEST_ASSERT(fragment_count > 1, "packet was fragmented"); |
|
// Ожидаемое количество фрагментов: ceil(2000 / (500 - overhead)) |
|
TEST_ASSERT(fragment_count >= 3 && fragment_count <= 6, "reasonable fragment count"); |
|
|
|
// Теперь обработать все фрагменты через unpacker и проверить сборку |
|
process_queues(pair); |
|
|
|
TEST_ASSERT(compare_packets() == 0, "fragmented packet correctly reassembled"); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no unpacker errors"); |
|
|
|
/* Часть 3: проверка отправки буфера при пустой входной очереди */ |
|
printf("\n--- Verifying buffer flush on empty input queue ---\n"); |
|
settings.max_fragment_size = 500; |
|
|
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->packer); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
|
|
// Отправить маленький пакет, который поместится в буфер packer'а |
|
size_t small_size = 100; |
|
ll_entry_t* entry1 = create_test_packet(small_size); |
|
TEST_ASSERT(entry1 != NULL, "create first small packet"); |
|
add_sent_packet(ll_entry_data(entry1), small_size); |
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry1) == 0, "put first packet"); |
|
|
|
// Обработать packer input (пакет должен добавиться в буфер, но не отправиться) |
|
if (pair->packer->input->callback && |
|
queue_entry_count(pair->packer->input) > 0) { |
|
pair->packer->input->callback(pair->packer->input, |
|
pair->packer->input->head, |
|
pair->packer->input->callback_arg); |
|
} |
|
|
|
// Проверить, что в output очереди ничего нет (пакет ещё не отправлен) |
|
int output_before = queue_entry_count(pair->packer->output); |
|
printf("After first packet: packer output queue count = %d\n", output_before); |
|
|
|
// Отправим второй маленький пакет, который заставит packer отправить буфер |
|
ll_entry_t* entry2 = create_test_packet(small_size); |
|
TEST_ASSERT(entry2 != NULL, "create second small packet"); |
|
add_sent_packet(ll_entry_data(entry2), small_size); |
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry2) == 0, "put second packet"); |
|
|
|
// Обработать packer input |
|
if (pair->packer->input->callback && |
|
queue_entry_count(pair->packer->input) > 0) { |
|
pair->packer->input->callback(pair->packer->input, |
|
pair->packer->input->head, |
|
pair->packer->input->callback_arg); |
|
} |
|
|
|
// Теперь в output должна быть хотя бы одна запись (буфер отправлен) |
|
int output_after = queue_entry_count(pair->packer->output); |
|
printf("After second packet: packer output queue count = %d\n", output_after); |
|
TEST_ASSERT(output_after > output_before, "buffer flushed when new packet arrives"); |
|
|
|
// Обработать все оставшиеся очереди |
|
process_queues(pair); |
|
|
|
TEST_ASSERT(compare_packets() == 0, "both packets correctly processed"); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no unpacker errors"); |
|
|
|
settings.max_fragment_size = original_fragment_size; |
|
return 0; |
|
} |
|
|
|
/* Тест 7: граничные случаи и максимальные размеры */ |
|
static int test_edge_cases(pkt_normalizer_pair* pair) { |
|
printf("\n--- Test 7: edge cases and maximum sizes ---\n"); |
|
int original_fragment_size = settings.max_fragment_size; |
|
|
|
/* Сбросить данные теста */ |
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
pkt_normalizer_flush(pair->packer); |
|
|
|
/* Тест 1: минимальный размер пакета (1 байт) */ |
|
{ |
|
size_t size = 1; |
|
ll_entry_t* entry = create_test_packet(size); |
|
TEST_ASSERT(entry != NULL, "create 1-byte packet"); |
|
add_sent_packet(ll_entry_data(entry), size); |
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put 1-byte packet"); |
|
process_queues(pair); |
|
TEST_ASSERT(compare_packets() == 0, "1-byte packet comparison"); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no errors for 1-byte packet"); |
|
} |
|
|
|
/* Тест 2: граница одно- и двухбайтного заголовка (239/240) */ |
|
{ |
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
pkt_normalizer_flush(pair->packer); |
|
size_t size = 239; |
|
ll_entry_t* entry = create_test_packet(size); |
|
TEST_ASSERT(entry != NULL, "create 239-byte packet"); |
|
add_sent_packet(ll_entry_data(entry), size); |
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put 239-byte packet"); |
|
process_queues(pair); |
|
TEST_ASSERT(compare_packets() == 0, "239-byte packet comparison"); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no errors for 239-byte packet"); |
|
} |
|
{ |
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
size_t size = 240; |
|
ll_entry_t* entry = create_test_packet(size); |
|
TEST_ASSERT(entry != NULL, "create 240-byte packet"); |
|
add_sent_packet(ll_entry_data(entry), size); |
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put 240-byte packet"); |
|
process_queues(pair); |
|
TEST_ASSERT(compare_packets() == 0, "240-byte packet comparison"); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no errors for 240-byte packet"); |
|
} |
|
|
|
/* Тест 3: максимальный размер без фрагментации (3839) */ |
|
{ |
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
pkt_normalizer_flush(pair->packer); |
|
size_t size = 3839; |
|
ll_entry_t* entry = create_test_packet(size); |
|
TEST_ASSERT(entry != NULL, "create 3839-byte packet"); |
|
add_sent_packet(ll_entry_data(entry), size); |
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put 3839-byte packet"); |
|
process_queues(pair); |
|
TEST_ASSERT(compare_packets() == 0, "3839-byte packet comparison"); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no errors for 3839-byte packet"); |
|
} |
|
|
|
/* Тест 4: размер, требующий фрагментации (3840) с обычным max_fragment_size */ |
|
{ |
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
pkt_normalizer_flush(pair->packer); |
|
size_t size = 3840; |
|
ll_entry_t* entry = create_test_packet(size); |
|
TEST_ASSERT(entry != NULL, "create 3840-byte packet"); |
|
add_sent_packet(ll_entry_data(entry), size); |
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put 3840-byte packet"); |
|
process_queues(pair); |
|
TEST_ASSERT(compare_packets() == 0, "3840-byte packet comparison"); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no errors for 3840-byte packet"); |
|
} |
|
|
|
/* Тест 5: размер чуть меньше max_fragment_size, чтобы не фрагментироваться */ |
|
{ |
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
pkt_normalizer_flush(pair->packer); |
|
settings.max_fragment_size = 500; |
|
size_t size = 480; /* достаточно мало, чтобы поместиться с заголовком */ |
|
ll_entry_t* entry = create_test_packet(size); |
|
TEST_ASSERT(entry != NULL, "create 480-byte packet with fragment size 500"); |
|
add_sent_packet(ll_entry_data(entry), size); |
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put packet"); |
|
process_queues(pair); |
|
TEST_ASSERT(compare_packets() == 0, "packet comparison with fragment size 500"); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no errors"); |
|
} |
|
|
|
/* Тест 6: размер чуть больше max_fragment_size, чтобы вызвать фрагментацию */ |
|
{ |
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
pkt_normalizer_flush(pair->packer); |
|
settings.max_fragment_size = 500; |
|
size_t size = 520; /* потребует фрагментации */ |
|
ll_entry_t* entry = create_test_packet(size); |
|
TEST_ASSERT(entry != NULL, "create 520-byte packet with fragment size 500"); |
|
add_sent_packet(ll_entry_data(entry), size); |
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put packet"); |
|
process_queues(pair); |
|
TEST_ASSERT(compare_packets() == 0, "packet comparison with fragmentation"); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no errors"); |
|
} |
|
|
|
/* Тест 7: максимальный размер пакета с фрагментации (65535) - ограничим 10000 для скорости */ |
|
{ |
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
pkt_normalizer_flush(pair->packer); |
|
settings.max_fragment_size = 1000; |
|
size_t size = 10000; |
|
ll_entry_t* entry = create_test_packet(size); |
|
TEST_ASSERT(entry != NULL, "create 10000-byte fragmented packet"); |
|
add_sent_packet(ll_entry_data(entry), size); |
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put large fragmented packet"); |
|
process_queues(pair); |
|
TEST_ASSERT(compare_packets() == 0, "large fragmented packet comparison"); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no errors for large fragmented packet"); |
|
} |
|
|
|
settings.max_fragment_size = original_fragment_size; |
|
return 0; |
|
} |
|
|
|
/* Тест 8: 10000 пакетов случайного размера (1-1024 байт) */ |
|
|
|
/* Тест 9: проверка дефрагментации (большой пакет фрагментируется, не отправляется мелкими пакетами) */ |
|
static int test_fragmentation_verify(pkt_normalizer_pair* pair) { |
|
printf("\n--- Test 9: fragmentation verification ---\n"); |
|
int original_fragment_size = settings.max_fragment_size; |
|
|
|
// Установить маленький размер фрагмента для теста |
|
settings.max_fragment_size = 500; |
|
|
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->packer); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
|
|
// Большой пакет, который должен быть фрагментирован |
|
size_t big_packet_size = 2000; // Должен быть разбит на ~4 фрагмента (500 байт каждый с накладными расходами) |
|
ll_entry_t* entry = create_test_packet(big_packet_size); |
|
TEST_ASSERT(entry != NULL, "create big packet"); |
|
add_sent_packet(ll_entry_data(entry), big_packet_size); |
|
|
|
// Подсчитать количество элементов в output очереди packer'а до обработки |
|
int initial_output_count = queue_entry_count(pair->packer->output); |
|
|
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put big packet"); |
|
|
|
// Обработать только packer (чтобы фрагменты появились в его output) |
|
// Вместо process_queues вызовем обработку только packer input и output |
|
if (pair->packer->input->callback && |
|
queue_entry_count(pair->packer->input) > 0) { |
|
pair->packer->input->callback(pair->packer->input, |
|
pair->packer->input->head, |
|
pair->packer->input->callback_arg); |
|
} |
|
|
|
// Проверить количество фрагментов в output очереди packer'а |
|
int fragment_count = queue_entry_count(pair->packer->output); |
|
printf("Big packet %zu bytes, fragment size %d, produced %d fragments\n", |
|
big_packet_size, settings.max_fragment_size, fragment_count); |
|
|
|
// Должно быть больше 1 фрагмента |
|
TEST_ASSERT(fragment_count > 1, "packet was fragmented"); |
|
// Ожидаемое количество фрагментов: ceil(2000 / (500 - overhead)) |
|
// overhead зависит от заголовков. Минимум 1 байт заголовка на фрагмент + 2 байта длины |
|
// Приблизительно 4-5 фрагментов |
|
TEST_ASSERT(fragment_count >= 3 && fragment_count <= 6, "reasonable fragment count"); |
|
|
|
// Теперь обработать все фрагменты через unpacker и проверить сборку |
|
process_queues(pair); |
|
|
|
TEST_ASSERT(compare_packets() == 0, "fragmented packet correctly reassembled"); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no unpacker errors"); |
|
|
|
settings.max_fragment_size = original_fragment_size; |
|
return 0; |
|
} |
|
|
|
/* Тест 10: проверка отправки буфера при пустой входной очереди */ |
|
static int test_empty_queue_flush(pkt_normalizer_pair* pair) { |
|
printf("\n--- Test 10: empty queue buffer flush ---\n"); |
|
int original_fragment_size = settings.max_fragment_size; |
|
settings.max_fragment_size = 500; |
|
|
|
reset_test_data(); |
|
pkt_normalizer_reset_error_count(pair->packer); |
|
pkt_normalizer_reset_error_count(pair->unpacker); |
|
|
|
// Отправить маленький пакет, который поместится в буфер packer'а |
|
size_t small_size = 100; |
|
ll_entry_t* entry1 = create_test_packet(small_size); |
|
TEST_ASSERT(entry1 != NULL, "create first small packet"); |
|
add_sent_packet(ll_entry_data(entry1), small_size); |
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry1) == 0, "put first packet"); |
|
|
|
// Обработать packer input (пакет должен добавиться в буфер, но не отправиться) |
|
if (pair->packer->input->callback && |
|
queue_entry_count(pair->packer->input) > 0) { |
|
pair->packer->input->callback(pair->packer->input, |
|
pair->packer->input->head, |
|
pair->packer->input->callback_arg); |
|
} |
|
|
|
// Проверить, что буфер не пуст (packer накопил данные) |
|
// packer должен иметь len > 0, но мы не можем получить доступ к внутреннему состоянию. |
|
// Вместо этого проверим, что в output очереди ничего нет (пакет ещё не отправлен) |
|
int output_before = queue_entry_count(pair->packer->output); |
|
printf("After first packet: packer output queue count = %d\n", output_before); |
|
|
|
// Теперь симулируем ситуацию, когда входящая очередь пуста и вызывается callback |
|
// В реальности packer_handler вызывается при добавлении нового пакета. |
|
// Но мы можем проверить, что при следующем пакете буфер будет отправлен. |
|
// Отправим второй маленький пакет, который заставит packer отправить буфер |
|
ll_entry_t* entry2 = create_test_packet(small_size); |
|
TEST_ASSERT(entry2 != NULL, "create second small packet"); |
|
add_sent_packet(ll_entry_data(entry2), small_size); |
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry2) == 0, "put second packet"); |
|
|
|
// Обработать packer input |
|
if (pair->packer->input->callback && |
|
queue_entry_count(pair->packer->input) > 0) { |
|
pair->packer->input->callback(pair->packer->input, |
|
pair->packer->input->head, |
|
pair->packer->input->callback_arg); |
|
} |
|
|
|
// Теперь в output должна быть хотя бы одна запись (буфер отправлен) |
|
int output_after = queue_entry_count(pair->packer->output); |
|
printf("After second packet: packer output queue count = %d\n", output_after); |
|
TEST_ASSERT(output_after > output_before, "buffer flushed when new packet arrives"); |
|
|
|
// Обработать все оставшиеся очереди |
|
process_queues(pair); |
|
|
|
TEST_ASSERT(compare_packets() == 0, "both packets correctly processed"); |
|
TEST_ASSERT(pkt_normalizer_get_error_count(pair->unpacker) == 0, "no unpacker errors"); |
|
|
|
settings.max_fragment_size = original_fragment_size; |
|
return 0; |
|
} |
|
/* Тест async wait: проверка асинхронного ожидания порога в очереди */ |
|
static void test_callback(ll_queue_t* q_arg, void* arg) { |
|
(void)q_arg; |
|
int* flag = (int*)arg; |
|
*flag = 1; |
|
} |
|
|
|
static int test_async_wait(void) { |
|
printf("\n--- Test async wait: threshold waiter ---\n"); |
|
ll_queue_t* q = queue_new(test_ua); |
|
TEST_ASSERT(q != NULL, "create queue for async wait test"); |
|
|
|
int callback_called = 0; |
|
|
|
// Добавим элемент, чтобы очередь была непустой |
|
ll_entry_t* entry = queue_entry_new(10); |
|
TEST_ASSERT(entry != NULL, "create entry"); |
|
TEST_ASSERT(queue_entry_put(q, entry) == 0, "put entry"); |
|
|
|
// Регистрируем ожидание: когда очередь будет <= 0 пакетов и <= 0 байт |
|
// Условие не выполнено, поэтому коллбэк не должен вызваться сразу |
|
queue_waiter_t* waiter = queue_wait_threshold(q, 0, 0, test_callback, &callback_called); |
|
TEST_ASSERT(waiter != NULL, "waiter registered"); |
|
TEST_ASSERT(callback_called == 0, "callback not called immediately"); |
|
|
|
// Извлекаем элемент - условие должно выполниться и коллбэк вызваться |
|
ll_entry_t* retrieved = queue_entry_get(q); |
|
TEST_ASSERT(retrieved != NULL, "retrieved entry"); |
|
queue_entry_free(retrieved); |
|
|
|
// Коллбэк должен был быть вызван внутри queue_entry_get через check_waiters |
|
TEST_ASSERT(callback_called == 1, "callback called after threshold met"); |
|
|
|
// Отменяем waiter (хотя он уже должен быть удален) |
|
queue_cancel_wait(q, waiter); |
|
|
|
queue_free(q); |
|
return 0; |
|
} |
|
|
|
/* Тест 5: проверка жизненного цикла */ |
|
static int test_lifecycle(void) { |
|
printf("\n--- Test 5: lifecycle ---\n"); |
|
|
|
pkt_normalizer_pair* pair = pkt_normalizer_pair_init(test_ua); |
|
TEST_ASSERT(pair != NULL, "pair initialization"); |
|
TEST_ASSERT(pair->packer != NULL, "packer initialization"); |
|
TEST_ASSERT(pair->unpacker != NULL, "unpacker initialization"); |
|
|
|
/* Создать тестовые очереди */ |
|
ll_queue_t* test_queue = queue_new(test_ua); |
|
TEST_ASSERT(test_queue != NULL, "test queue creation"); |
|
|
|
/* Отправить один пакет */ |
|
const char* test_data = "test"; |
|
ll_entry_t* entry = queue_entry_new(strlen(test_data)); |
|
TEST_ASSERT(entry != NULL, "create test packet"); |
|
memcpy(ll_entry_data(entry), test_data, strlen(test_data)); |
|
|
|
TEST_ASSERT(queue_entry_put(pair->packer->input, entry) == 0, "put test packet"); |
|
|
|
/* Освободить всё */ |
|
pkt_normalizer_pair_deinit(pair); |
|
queue_free(test_queue); |
|
|
|
return 0; |
|
} |
|
|
|
/* Debug stress test to trace packet flow */ |
|
int main(void) { |
|
int result = 0; |
|
|
|
printf("=== Packet Normalizer Unit Test ===\n"); |
|
|
|
srand((unsigned int)time(NULL)); |
|
test_ua = uasync_create(); |
|
if (!test_ua) { |
|
fprintf(stderr, "Failed to create uasync instance\n"); |
|
return 1; |
|
} |
|
uasync_init_instance(test_ua); |
|
|
|
pkt_normalizer_pair* pair = pkt_normalizer_pair_init(test_ua); |
|
if (!pair) { |
|
fprintf(stderr, "Failed to initialize packet normalizer pair\n"); |
|
return 1; |
|
} |
|
|
|
/* Настроить цепочку обработки: packer->output -> unpacker->input -> unpacker->output */ |
|
queue_set_callback(pair->packer->output, forward_cb, pair->unpacker->input); |
|
queue_set_callback(pair->unpacker->output, receive_cb, NULL); |
|
|
|
result |= test_lifecycle(); |
|
result |= test_small_packets(pair); |
|
result |= test_medium_packet(pair); |
|
result |= test_fragmentation(pair); |
|
result |= test_mixed_packets(pair); |
|
result |= test_stress_random(pair); |
|
result |= test_edge_cases(pair); |
|
result |= test_fragmentation_verify(pair); |
|
result |= test_empty_queue_flush(pair); |
|
result |= test_async_wait(); |
|
|
|
pkt_normalizer_pair_deinit(pair); |
|
reset_test_data(); |
|
|
|
if (result == 0) { |
|
printf("\n=== All tests PASSED ===\n"); |
|
} else { |
|
printf("\n=== Some tests FAILED ===\n"); |
|
} |
|
|
|
return result; |
|
} |