Fix: ETCP module compilation errors and build system issues

- Added missing structure members to ETCP_CONN (ack_packets_count, last_rx_ack_id, rtt_history, rtt_history_idx, total_packets_sent)
- Added missing bandwidth member to ETCP_LINK structure
- Fixed include path for ll_queue.h header
- Added math library linking (-lm) to build system
- Added etcp_loadbalancer.c to build sources
- Fixed forward declarations and function calls in ETCP modules
- Updated test dependencies to include loadbalancer object files

All simple compilation errors resolved, project builds successfully and tests pass (except TUN permission test which is expected in container).
This commit is contained in:
Evgeny committed 2026-01-23 00:43:48 +03:00
1 parent 3bf178475d
commit e048b10899
35 files changed
+7011 -669

No files matched your search

+14 -2
View File
@@ -3,7 +3,9 @@
# Essential working tests
check_PROGRAMS = test_etcp_crypto$(EXEEXT) \
test_crypto$(EXEEXT) \
test_etcp_two_instances$(EXEEXT)
test_etcp_two_instances$(EXEEXT) \
test_ll_queue_pos$(EXEEXT) \
test_ll_queue_unified$(EXEEXT)
# Basic includes
AM_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
@@ -21,7 +23,17 @@ test_crypto_LDADD = $(top_builddir)/tinycrypt/lib/source/utun-aes_encrypt.o $(to
# ETCP two instances test - comprehensive connection test
test_etcp_two_instances_SOURCES = test_etcp_two_instances.c
test_etcp_two_instances_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
test_etcp_two_instances_LDADD = $(top_builddir)/src/utun-config_parser.o $(top_builddir)/src/utun-config_updater.o $(top_builddir)/src/utun-crc32.o $(top_builddir)/src/utun-etcp.o $(top_builddir)/src/utun-etcp_connections.o $(top_builddir)/src/utun-secure_channel.o $(top_builddir)/src/utun-routing.o $(top_builddir)/src/utun-tun_if.o $(top_builddir)/src/utun-utun_instance.o $(top_builddir)/tinycrypt/lib/source/utun-aes_encrypt.o $(top_builddir)/tinycrypt/lib/source/utun-aes_decrypt.o $(top_builddir)/tinycrypt/lib/source/utun-ccm_mode.o $(top_builddir)/tinycrypt/lib/source/utun-cmac_mode.o $(top_builddir)/tinycrypt/lib/source/utun-ctr_mode.o $(top_builddir)/tinycrypt/lib/source/utun-ecc.o $(top_builddir)/tinycrypt/lib/source/utun-ecc_dh.o $(top_builddir)/tinycrypt/lib/source/utun-ecc_dsa.o $(top_builddir)/tinycrypt/lib/source/utun-sha256.o $(top_builddir)/tinycrypt/lib/source/utun-ecc_platform_specific.o $(top_builddir)/tinycrypt/lib/source/utun-utils.o $(top_builddir)/lib/libuasync.a -lpthread -lcrypto
test_etcp_two_instances_LDADD = $(top_builddir)/src/utun-config_parser.o $(top_builddir)/src/utun-config_updater.o $(top_builddir)/src/utun-crc32.o $(top_builddir)/src/utun-etcp.o $(top_builddir)/src/utun-etcp_connections.o $(top_builddir)/src/utun-etcp_loadbalancer.o $(top_builddir)/src/utun-secure_channel.o $(top_builddir)/src/utun-routing.o $(top_builddir)/src/utun-tun_if.o $(top_builddir)/src/utun-utun_instance.o $(top_builddir)/tinycrypt/lib/source/utun-aes_encrypt.o $(top_builddir)/tinycrypt/lib/source/utun-aes_decrypt.o $(top_builddir)/tinycrypt/lib/source/utun-ccm_mode.o $(top_builddir)/tinycrypt/lib/source/utun-cmac_mode.o $(top_builddir)/tinycrypt/lib/source/utun-ctr_mode.o $(top_builddir)/tinycrypt/lib/source/utun-ecc.o $(top_builddir)/tinycrypt/lib/source/utun-ecc_dh.o $(top_builddir)/tinycrypt/lib/source/utun-ecc_dsa.o $(top_builddir)/tinycrypt/lib/source/utun-sha256.o $(top_builddir)/tinycrypt/lib/source/utun-ecc_platform_specific.o $(top_builddir)/tinycrypt/lib/source/utun-utils.o $(top_builddir)/lib/libuasync.a -lpthread -lcrypto
# LL queue position test
test_ll_queue_pos_SOURCES = test_ll_queue_pos.c
test_ll_queue_pos_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_ll_queue_pos_LDADD = $(top_builddir)/lib/libuasync.a -lpthread -lcrypto
# Unified comprehensive ll_queue test
test_ll_queue_unified_SOURCES = test_ll_queue_unified.c
test_ll_queue_unified_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_ll_queue_unified_LDADD = $(top_builddir)/lib/libuasync.a -lpthread -lcrypto
# Register tests
TESTS = $(check_PROGRAMS)
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
+407
View File
@@ -0,0 +1,407 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include "../lib/ll_queue.h"
#include "../lib/u_async.h"
// Тестовые данные
struct test_data {
int value;
char name[32];
};
// Простой контекст uasync для тестов
struct test_context {
struct UASYNC* ua;
int callback_count;
};
// Коллбэк для тестов
void test_callback(struct ll_queue* q, struct ll_entry* entry, void* arg) {
struct test_context* ctx = (struct test_context*)arg;
ctx->callback_count++;
printf("Callback called for entry %p\n", entry);
}
// Базовый тест двусвязного списка
void test_doubly_linked_basics() {
printf("=== Тест базовых операций двусвязного списка ===\n");
struct UASYNC* ua = uasync_create();
struct ll_queue* q = queue_new(ua, NULL);
assert(q != NULL);
assert(queue_entry_count(q) == 0);
// Создать и добавить несколько элементов
for (int i = 0; i < 5; i++) {
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
assert(entry != NULL);
struct test_data* data = (struct test_data*)ll_entry_data(entry);
data->value = i;
snprintf(data->name, sizeof(data->name), "item_%d", i);
assert(queue_entry_put(q, entry) == 0);
}
assert(queue_entry_count(q) == 5);
// Проверить, что prev/next указатели корректны
struct ll_entry* curr = q->head;
int count = 0;
while (curr) {
struct test_data* data = (struct test_data*)ll_entry_data(curr);
printf(" Forward: %s (value=%d)\n", data->name, data->value);
// Проверить двусвязность
if (curr->prev) {
struct test_data* prev_data = (struct test_data*)ll_entry_data(curr->prev);
printf(" prev: %s\n", prev_data->name);
}
if (curr->next) {
struct test_data* next_data = (struct test_data*)ll_entry_data(curr->next);
printf(" next: %s\n", next_data->name);
}
curr = curr->next;
count++;
}
assert(count == 5);
// Проверить обратный проход
curr = q->tail;
count = 0;
while (curr) {
struct test_data* data = (struct test_data*)ll_entry_data(curr);
printf(" Backward: %s (value=%d)\n", data->name, data->value);
curr = curr->prev;
count++;
}
assert(count == 5);
queue_free(q);
uasync_destroy(ua);
printf("✓ Базовые операции двусвязного списка работают\n\n");
}
// Тест навигации по позициям
void test_position_navigation() {
printf("=== Тест навигации по позициям ===\n");
struct UASYNC* ua = uasync_create();
struct ll_queue* q = queue_new(ua, NULL);
// Добавить тестовые данные
for (int i = 0; i < 5; i++) {
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
struct test_data* data = (struct test_data*)ll_entry_data(entry);
data->value = i;
snprintf(data->name, sizeof(data->name), "item_%d", i);
queue_entry_put(q, entry);
}
struct ll_queue_pos* pos = queue_pos_new(q);
assert(pos != NULL);
// Тест reset
queue_pos_reset(pos);
assert(queue_pos_index(pos) == -1);
assert(queue_pos_current(pos) == NULL);
// Тест next
int count = 0;
while (queue_pos_next(pos)) {
struct ll_entry* entry = queue_pos_current(pos);
struct test_data* data = (struct test_data*)ll_entry_data(entry);
printf(" Position %d: %s (value=%d)\n", queue_pos_index(pos), data->name, data->value);
assert(queue_pos_index(pos) == count);
assert(data->value == count);
count++;
}
assert(count == 5);
// Тест prev
int prev_count = 0;
while (queue_pos_prev(pos)) {
struct ll_entry* entry = queue_pos_current(pos);
if (entry) { // Проверить, что entry не NULL
struct test_data* data = (struct test_data*)ll_entry_data(entry);
printf(" Back Position %d: %s (value=%d)\n", queue_pos_index(pos), data->name, data->value);
} else {
printf(" Back Position %d: NULL (before head)\n", queue_pos_index(pos));
}
prev_count++;
}
assert(prev_count == 5);
// Тест seek
assert(queue_pos_seek(pos, 2) == 1);
assert(queue_pos_index(pos) == 2);
struct ll_entry* entry = queue_pos_current(pos);
struct test_data* data = (struct test_data*)ll_entry_data(entry);
assert(data->value == 2);
printf(" Seek to 2: %s (value=%d)\n", data->name, data->value);
queue_pos_free(pos);
queue_free(q);
uasync_destroy(ua);
printf("✓ Навигация по позициям работает\n\n");
}
// Тест вставки и удаления
void test_insert_remove_operations() {
printf("=== Тест вставки и удаления ===\n");
struct UASYNC* ua = uasync_create();
struct ll_queue* q = queue_new(ua, NULL);
// Создать начальную очередь: [0, 1, 2, 3, 4]
for (int i = 0; i < 5; i++) {
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
struct test_data* data = (struct test_data*)ll_entry_data(entry);
data->value = i;
snprintf(data->name, sizeof(data->name), "item_%d", i);
queue_entry_put(q, entry);
}
struct ll_queue_pos* pos = queue_pos_new(q);
// Тест вставки перед элементом 2
queue_pos_seek(pos, 2);
struct ll_entry* new_entry = queue_entry_new(sizeof(struct test_data));
struct test_data* new_data = (struct test_data*)ll_entry_data(new_entry);
new_data->value = 99;
snprintf(new_data->name, sizeof(new_data->name), "inserted_before_2");
assert(queue_pos_insert(pos, new_entry, 0) == 0); // Вставить перед
assert(queue_entry_count(q) == 6);
// Проверить вставку
queue_pos_seek(pos, 2);
struct ll_entry* check_entry = queue_pos_current(pos);
struct test_data* check_data = (struct test_data*)ll_entry_data(check_entry);
assert(check_data->value == 99);
printf(" Inserted before position 2: %s\n", check_data->name);
// Тест вставки после элемента 2 (теперь это вставленный элемент)
new_entry = queue_entry_new(sizeof(struct test_data));
new_data = (struct test_data*)ll_entry_data(new_entry);
new_data->value = 88;
snprintf(new_data->name, sizeof(new_data->name), "inserted_after_2");
assert(queue_pos_insert(pos, new_entry, 1) == 0); // Вставить после
assert(queue_entry_count(q) == 7);
// Проверить вставку
queue_pos_next(pos);
check_entry = queue_pos_current(pos);
check_data = (struct test_data*)ll_entry_data(check_entry);
assert(check_data->value == 88);
printf(" Inserted after position 2: %s\n", check_data->name);
// Тест удаления
queue_pos_seek(pos, 3); // Вернемся к вставленному элементу
struct ll_entry* removed_entry = queue_pos_remove(pos);
assert(removed_entry != NULL);
struct test_data* removed_data = (struct test_data*)ll_entry_data(removed_entry);
assert(removed_data->value == 88);
printf(" Removed element: %s\n", removed_data->name);
assert(queue_entry_count(q) == 6);
queue_entry_free(removed_entry);
// Тест удаления по указателю
// Найдем элемент с value = 99 и удалим его
struct ll_entry* curr = q->head;
while (curr) {
struct test_data* data = (struct test_data*)ll_entry_data(curr);
if (data->value == 99) {
assert(queue_entry_remove(q, curr) == 0);
queue_entry_free(curr);
break;
}
curr = curr->next;
}
assert(queue_entry_count(q) == 5);
printf(" Removed element by pointer: item_99\n");
queue_pos_free(pos);
queue_free(q);
uasync_destroy(ua);
printf("✓ Вставка и удаление работают\n\n");
}
// Стресс-тест
void test_stress() {
printf("=== Стресс-тест ===\n");
struct UASYNC* ua = uasync_create();
struct ll_queue* q = queue_new(ua, NULL);
const int iterations = 1000;
const int max_queue_size = 50;
srand(time(NULL));
for (int i = 0; i < iterations; i++) {
int operation = rand() % 4;
switch (operation) {
case 0: { // Добавить в конец
struct ll_entry* entry = queue_entry_new(sizeof(int));
int* value = (int*)ll_entry_data(entry);
*value = rand();
queue_entry_put(q, entry);
break;
}
case 1: { // Добавить в начало
struct ll_entry* entry = queue_entry_new(sizeof(int));
int* value = (int*)ll_entry_data(entry);
*value = rand();
queue_entry_put_first(q, entry);
break;
}
case 2: { // Удалить из начала
struct ll_entry* entry = queue_entry_get(q);
if (entry) {
queue_entry_free(entry);
}
break;
}
case 3: { // Произвольная операция с позицией
if (queue_entry_count(q) > 0) {
struct ll_queue_pos* pos = queue_pos_new(q);
int index = rand() % queue_entry_count(q);
if (queue_pos_seek(pos, index)) {
// Случайная операция: вставка или удаление
if (rand() % 2) {
struct ll_entry* entry = queue_entry_new(sizeof(int));
int* value = (int*)ll_entry_data(entry);
*value = rand();
queue_pos_insert(pos, entry, rand() % 2);
} else {
struct ll_entry* removed = queue_pos_remove(pos);
if (removed) {
queue_entry_free(removed);
}
}
}
queue_pos_free(pos);
}
break;
}
}
// Проверить размер очереди
if (queue_entry_count(q) > max_queue_size) {
// Удалить лишние элементы
while (queue_entry_count(q) > max_queue_size / 2) {
struct ll_entry* entry = queue_entry_get(q);
if (entry) {
queue_entry_free(entry);
}
}
}
// Периодическая проверка целостности
if (i % 100 == 0) {
int count = 0;
struct ll_entry* curr = q->head;
while (curr) {
count++;
// Проверить двусвязность
if (curr->prev) {
assert(curr->prev->next == curr);
}
if (curr->next) {
assert(curr->next->prev == curr);
}
curr = curr->next;
}
assert(count == queue_entry_count(q));
}
}
printf(" Прошло %d случайных операций\n", iterations);
printf(" Финальный размер очереди: %d\n", queue_entry_count(q));
queue_free(q);
uasync_destroy(ua);
printf("✓ Стресс-тест пройден\n\n");
}
// Тест совместимости существующего API
void test_api_compatibility() {
printf("=== Тест совместимости API ===\n");
struct UASYNC* ua = uasync_create();
struct ll_queue* q = queue_new(ua, NULL);
// Тест старого API
struct ll_entry* entries[5];
for (int i = 0; i < 5; i++) {
entries[i] = queue_entry_new(sizeof(struct test_data));
struct test_data* data = (struct test_data*)ll_entry_data(entries[i]);
data->value = i;
snprintf(data->name, sizeof(data->name), "compat_%d", i);
}
// Добавить в конец
for (int i = 0; i < 5; i++) {
assert(queue_entry_put(q, entries[i]) == 0);
}
assert(queue_entry_count(q) == 5);
// Извлечь из начала
for (int i = 0; i < 5; i++) {
struct ll_entry* entry = queue_entry_get(q);
assert(entry == entries[i]); // Проверить порядок FIFO
struct test_data* data = (struct test_data*)ll_entry_data(entry);
printf(" Extracted: %s (value=%d)\n", data->name, data->value);
assert(data->value == i);
queue_entry_free(entry);
}
assert(queue_entry_count(q) == 0);
// Тест put_first
for (int i = 0; i < 3; i++) {
struct ll_entry* entry = queue_entry_new(sizeof(int));
int* value = (int*)ll_entry_data(entry);
*value = i;
queue_entry_put_first(q, entry);
}
// Извлечь в обратном порядке (LIFO)
for (int i = 0; i < 3; i++) {
struct ll_entry* entry = queue_entry_get(q);
int* value = (int*)ll_entry_data(entry);
printf(" LIFO extracted: %d (expected %d)\n", *value, 2-i);
assert(*value == 2-i);
queue_entry_free(entry);
}
queue_free(q);
uasync_destroy(ua);
printf("✓ Совместимость API сохранена\n\n");
}
int main() {
printf("=== Запуск тестов doubly-linked ll_queue ===\n\n");
test_doubly_linked_basics();
test_position_navigation();
test_insert_remove_operations();
test_stress();
test_api_compatibility();
printf("=== Все тесты пройдены успешно! ===\n");
return 0;
}
+752
View File
@@ -0,0 +1,752 @@
/**
* Unified comprehensive test suite for ll_queue module
* Combines: basic operations, position navigation, doubly-linked list, stress testing
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <unistd.h>
#include <sys/time.h>
#include "../lib/ll_queue.h"
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
/* Test statistics */
static struct {
int tests_run;
int tests_passed;
int tests_failed;
/* Performance metrics */
double total_time_ms;
int operations_count;
/* Error tracking */
int memory_errors;
int invalid_parameter_errors;
} test_stats = {0};
/* Test result tracking */
#define TEST_START(name) do { \
printf("TEST: %s... ", name); \
test_stats.tests_run++; \
} while(0)
#define TEST_PASS() do { \
printf("PASS\n"); \
test_stats.tests_passed++; \
} while(0)
#define TEST_FAIL(msg) do { \
printf("FAIL: %s\n", msg); \
test_stats.tests_failed++; \
} while(0)
#define ASSERT_TRUE(cond, msg) do { \
if (!(cond)) { \
TEST_FAIL(msg); \
return; \
} \
} while(0)
#define ASSERT_FALSE(cond, msg) ASSERT_TRUE(!(cond), msg)
#define ASSERT_NULL(ptr, msg) ASSERT_TRUE((ptr) == NULL, msg)
#define ASSERT_NOT_NULL(ptr, msg) ASSERT_TRUE((ptr) != NULL, msg)
#define ASSERT_EQ(a, b, msg) ASSERT_TRUE((a) == (b), msg)
#define ASSERT_NEQ(a, b, msg) ASSERT_TRUE((a) != (b), msg)
/* Test data structure */
struct test_data {
int value;
char name[32];
int checksum;
};
/* Utility functions */
static double get_time_ms() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
}
static struct test_data* create_test_data(int value) {
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
if (!entry) return NULL;
struct test_data* data = (struct test_data*)ll_entry_data(entry);
data->value = value;
snprintf(data->name, sizeof(data->name), "item_%d", value);
data->checksum = value * 7 + 3; // Simple checksum
return data;
}
static int verify_test_data(struct test_data* data) {
if (!data) return 0;
return data->checksum == (data->value * 7 + 3);
}
/* Test: Basic doubly-linked list operations */
static void test_doubly_linked_basics() {
TEST_START("doubly_linked_basics");
struct UASYNC* ua = uasync_create();
ASSERT_NOT_NULL(ua, "Failed to create uasync");
struct ll_queue* q = queue_new(ua, NULL);
ASSERT_NOT_NULL(q, "Failed to create queue");
ASSERT_EQ(queue_entry_count(q), 0, "Queue should be empty");
// Create and add test elements
for (int i = 0; i < 5; i++) {
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
ASSERT_NOT_NULL(entry, "Failed to create entry");
struct test_data* data = (struct test_data*)ll_entry_data(entry);
data->value = i;
snprintf(data->name, sizeof(data->name), "item_%d", i);
data->checksum = i * 7 + 3;
ASSERT_EQ(queue_entry_put(q, entry), 0, "Failed to add entry to queue");
}
ASSERT_EQ(queue_entry_count(q), 5, "Wrong queue count");
// Verify forward traversal with doubly-linked integrity
struct ll_entry* curr = q->head;
int forward_count = 0;
while (curr) {
struct test_data* data = (struct test_data*)ll_entry_data(curr);
ASSERT_TRUE(verify_test_data(data), "Data corruption in forward traversal");
ASSERT_EQ(data->value, forward_count, "Wrong value in forward traversal");
// Verify doubly-linked integrity
if (curr->prev) {
ASSERT_NOT_NULL(curr->prev->next, "Broken prev link");
ASSERT_EQ(curr->prev->next, curr, "Invalid prev->next link");
}
if (curr->next) {
ASSERT_NOT_NULL(curr->next->prev, "Broken next link");
ASSERT_EQ(curr->next->prev, curr, "Invalid next->prev link");
}
curr = curr->next;
forward_count++;
}
ASSERT_EQ(forward_count, 5, "Wrong forward count");
// Verify backward traversal
curr = q->tail;
int backward_count = 4;
while (curr) {
struct test_data* data = (struct test_data*)ll_entry_data(curr);
ASSERT_TRUE(verify_test_data(data), "Data corruption in backward traversal");
ASSERT_EQ(data->value, backward_count, "Wrong value in backward traversal");
curr = curr->prev;
backward_count--;
}
ASSERT_EQ(backward_count, -1, "Wrong backward count");
queue_free(q);
uasync_destroy(ua);
TEST_PASS();
}
/* Test: Position navigation system */
static void test_position_navigation() {
TEST_START("position_navigation");
struct UASYNC* ua = uasync_create();
ASSERT_NOT_NULL(ua, "Failed to create uasync");
struct ll_queue* q = queue_new(ua, NULL);
ASSERT_NOT_NULL(q, "Failed to create queue");
// Add test elements
for (int i = 0; i < 5; i++) {
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
ASSERT_NOT_NULL(entry, "Failed to create entry");
struct test_data* data = (struct test_data*)ll_entry_data(entry);
data->value = i;
snprintf(data->name, sizeof(data->name), "item_%d", i);
data->checksum = i * 7 + 3;
ASSERT_EQ(queue_entry_put(q, entry), 0, "Failed to add entry");
}
struct ll_queue_pos* pos = queue_pos_new(q);
ASSERT_NOT_NULL(pos, "Failed to create position");
// Test initial state
ASSERT_EQ(queue_pos_index(pos), -1, "Wrong initial index");
ASSERT_NULL(queue_pos_current(pos), "Current should be NULL initially");
// Test forward navigation
int count = 0;
while (queue_pos_next(pos)) {
struct ll_entry* entry = queue_pos_current(pos);
ASSERT_NOT_NULL(entry, "Current entry should not be NULL");
ASSERT_EQ(queue_pos_index(pos), count, "Wrong index in forward navigation");
struct test_data* data = (struct test_data*)ll_entry_data(entry);
ASSERT_TRUE(verify_test_data(data), "Data corruption in position navigation");
ASSERT_EQ(data->value, count, "Wrong value in forward navigation");
count++;
}
ASSERT_EQ(count, 5, "Wrong forward navigation count");
// Test backward navigation
int prev_count = 0;
while (queue_pos_prev(pos)) {
struct ll_entry* entry = queue_pos_current(pos);
// Note: entry can be NULL when at position -1 (before head)
if (entry) {
struct test_data* data = (struct test_data*)ll_entry_data(entry);
ASSERT_TRUE(verify_test_data(data), "Data corruption in backward navigation");
}
prev_count++;
}
ASSERT_EQ(prev_count, 5, "Wrong backward navigation count");
// Test seek functionality
ASSERT_EQ(queue_pos_seek(pos, 2), 1, "Seek to index 2 failed");
ASSERT_EQ(queue_pos_index(pos), 2, "Wrong index after seek");
struct ll_entry* entry = queue_pos_current(pos);
ASSERT_NOT_NULL(entry, "Current entry should not be NULL after seek");
struct test_data* data = (struct test_data*)ll_entry_data(entry);
ASSERT_TRUE(verify_test_data(data), "Data corruption after seek");
ASSERT_EQ(data->value, 2, "Wrong value after seek");
// Test invalid seek
ASSERT_EQ(queue_pos_seek(pos, -2), 0, "Invalid seek should fail");
ASSERT_EQ(queue_pos_seek(pos, 10), 0, "Out of bounds seek should fail");
queue_pos_free(pos);
queue_free(q);
uasync_destroy(ua);
TEST_PASS();
}
/* Test: Position-based insert and remove operations */
static void test_position_insert_remove() {
TEST_START("position_insert_remove");
struct UASYNC* ua = uasync_create();
ASSERT_NOT_NULL(ua, "Failed to create uasync");
struct ll_queue* q = queue_new(ua, NULL);
ASSERT_NOT_NULL(q, "Failed to create queue");
// Create initial queue: [0, 1, 2, 3, 4]
for (int i = 0; i < 5; i++) {
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
ASSERT_NOT_NULL(entry, "Failed to create entry");
struct test_data* data = (struct test_data*)ll_entry_data(entry);
data->value = i;
snprintf(data->name, sizeof(data->name), "item_%d", i);
data->checksum = i * 7 + 3;
ASSERT_EQ(queue_entry_put(q, entry), 0, "Failed to add entry");
}
struct ll_queue_pos* pos = queue_pos_new(q);
ASSERT_NOT_NULL(pos, "Failed to create position");
// Test insertion before position 2
ASSERT_EQ(queue_pos_seek(pos, 2), 1, "Failed to seek to position 2");
struct ll_entry* new_entry = queue_entry_new(sizeof(struct test_data));
ASSERT_NOT_NULL(new_entry, "Failed to create new entry");
struct test_data* new_data = (struct test_data*)ll_entry_data(new_entry);
new_data->value = 99;
snprintf(new_data->name, sizeof(new_data->name), "inserted_before_2");
new_data->checksum = 99 * 7 + 3;
ASSERT_EQ(queue_pos_insert(pos, new_entry, 0), 0, "Failed to insert before position");
ASSERT_EQ(queue_entry_count(q), 6, "Wrong count after insertion");
// Verify insertion (refresh position first due to version change)
queue_pos_refresh(pos); // Обновить позицию после изменения очереди
ASSERT_EQ(queue_pos_seek(pos, 2), 1, "Failed to seek after insertion");
struct ll_entry* check_entry = queue_pos_current(pos);
ASSERT_NOT_NULL(check_entry, "Current entry should not be NULL after insertion");
struct test_data* check_data = (struct test_data*)ll_entry_data(check_entry);
ASSERT_TRUE(verify_test_data(check_data), "Data corruption after insertion");
ASSERT_EQ(check_data->value, 99, "Wrong value after insertion");
// Test insertion after position (refresh position first, then create new entry)
queue_pos_refresh(pos); // Обновить позицию после предыдущей вставки
struct ll_entry* new_entry2 = queue_entry_new(sizeof(struct test_data));
ASSERT_NOT_NULL(new_entry2, "Failed to create second new entry");
struct test_data* new_data2 = (struct test_data*)ll_entry_data(new_entry2);
new_data2->value = 88;
snprintf(new_data2->name, sizeof(new_data2->name), "inserted_after_2");
new_data2->checksum = 88 * 7 + 3;
ASSERT_EQ(queue_pos_insert(pos, new_entry2, 1), 0, "Failed to insert after position");
ASSERT_EQ(queue_entry_count(q), 7, "Wrong count after second insertion");
// Test removal (refresh position first)
queue_pos_refresh(pos); // Обновить позицию после вставки
struct ll_entry* removed_entry = queue_pos_remove(pos);
ASSERT_NOT_NULL(removed_entry, "Failed to remove entry");
ASSERT_EQ(queue_entry_count(q), 6, "Wrong count after removal");
struct test_data* removed_data = (struct test_data*)ll_entry_data(removed_entry);
ASSERT_TRUE(verify_test_data(removed_data), "Data corruption in removed entry");
ASSERT_EQ(removed_data->value, 99, "Wrong value in removed entry");
queue_entry_free(removed_entry);
// Test removal by pointer (find element with value 88 - the other inserted element)
struct ll_entry* curr = q->head;
int found = 0;
while (curr) {
struct test_data* data = (struct test_data*)ll_entry_data(curr);
if (data->value == 88) { // Target the element that was inserted after position 2
ASSERT_EQ(queue_entry_remove(q, curr), 0, "Failed to remove by pointer");
queue_entry_free(curr);
found = 1;
break;
}
curr = curr->next;
}
ASSERT_TRUE(found, "Element with value 88 not found in queue after operations");
ASSERT_EQ(queue_entry_count(q), 5, "Wrong count after pointer removal");
queue_pos_free(pos);
queue_free(q);
uasync_destroy(ua);
TEST_PASS();
}
/* Test: Stress testing with random operations */
static void test_stress_random_operations() {
TEST_START("stress_random_operations");
double start_time = get_time_ms();
struct UASYNC* ua = uasync_create();
ASSERT_NOT_NULL(ua, "Failed to create uasync");
struct ll_queue* q = queue_new(ua, NULL);
ASSERT_NOT_NULL(q, "Failed to create queue");
const int iterations = 1000;
const int max_queue_size = 50;
srand(time(NULL));
for (int i = 0; i < iterations; i++) {
int operation = rand() % 5; // 5 types of operations
switch (operation) {
case 0: { // Add to tail
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
if (entry) {
struct test_data* data = (struct test_data*)ll_entry_data(entry);
data->value = rand() % 1000;
data->checksum = data->value * 7 + 3;
queue_entry_put(q, entry);
test_stats.operations_count++;
}
break;
}
case 1: { // Add to head
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
if (entry) {
struct test_data* data = (struct test_data*)ll_entry_data(entry);
data->value = rand() % 1000;
data->checksum = data->value * 7 + 3;
queue_entry_put_first(q, entry);
test_stats.operations_count++;
}
break;
}
case 2: { // Remove from head
struct ll_entry* entry = queue_entry_get(q);
if (entry) {
struct test_data* data = (struct test_data*)ll_entry_data(entry);
if (!verify_test_data(data)) {
test_stats.memory_errors++;
}
queue_entry_free(entry);
test_stats.operations_count++;
}
break;
}
case 3: { // Position-based operations
if (queue_entry_count(q) > 0) {
struct ll_queue_pos* pos = queue_pos_new(q);
if (pos) {
int index = rand() % queue_entry_count(q);
if (queue_pos_seek(pos, index)) {
if (rand() % 2) {
// Insert operation
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
if (entry) {
struct test_data* data = (struct test_data*)ll_entry_data(entry);
data->value = rand() % 1000;
data->checksum = data->value * 7 + 3;
queue_pos_insert(pos, entry, rand() % 2);
test_stats.operations_count++;
}
} else {
// Remove operation
struct ll_entry* removed = queue_pos_remove(pos);
if (removed) {
struct test_data* data = (struct test_data*)ll_entry_data(removed);
if (!verify_test_data(data)) {
test_stats.memory_errors++;
}
queue_entry_free(removed);
test_stats.operations_count++;
}
}
}
queue_pos_free(pos);
}
}
break;
}
case 4: { // Random remove by pointer
if (queue_entry_count(q) > 0) {
// Find random element to remove
int target_index = rand() % queue_entry_count(q);
struct ll_entry* curr = q->head;
for (int j = 0; j < target_index && curr; j++) {
curr = curr->next;
}
if (curr) {
struct test_data* data = (struct test_data*)ll_entry_data(curr);
if (verify_test_data(data)) {
queue_entry_remove(q, curr);
queue_entry_free(curr);
test_stats.operations_count++;
}
}
}
break;
}
}
// Keep queue size reasonable
while (queue_entry_count(q) > max_queue_size) {
struct ll_entry* entry = queue_entry_get(q);
if (entry) {
struct test_data* data = (struct test_data*)ll_entry_data(entry);
if (!verify_test_data(data)) {
test_stats.memory_errors++;
}
queue_entry_free(entry);
}
}
// Periodic integrity check
if (i % 100 == 0) {
int count = 0;
struct ll_entry* curr = q->head;
while (curr) {
count++;
// Verify doubly-linked integrity
if (curr->prev) {
if (curr->prev->next != curr) {
test_stats.memory_errors++;
}
}
if (curr->next) {
if (curr->next->prev != curr) {
test_stats.memory_errors++;
}
}
curr = curr->next;
}
if (count != queue_entry_count(q)) {
test_stats.memory_errors++;
}
}
}
double end_time = get_time_ms();
test_stats.total_time_ms += (end_time - start_time);
ASSERT_EQ(test_stats.memory_errors, 0, "Memory corruption detected during stress test");
ASSERT_TRUE(test_stats.operations_count > iterations / 2, "Too few operations performed");
queue_free(q);
uasync_destroy(ua);
TEST_PASS();
}
/* Test: API compatibility with existing code */
static void test_api_compatibility() {
TEST_START("api_compatibility");
struct UASYNC* ua = uasync_create();
ASSERT_NOT_NULL(ua, "Failed to create uasync");
struct ll_queue* q = queue_new(ua, NULL);
ASSERT_NOT_NULL(q, "Failed to create queue");
// Test FIFO operations (existing API)
struct ll_entry* entries[5];
for (int i = 0; i < 5; i++) {
entries[i] = queue_entry_new(sizeof(struct test_data));
ASSERT_NOT_NULL(entries[i], "Failed to create entry");
struct test_data* data = (struct test_data*)ll_entry_data(entries[i]);
data->value = i;
snprintf(data->name, sizeof(data->name), "fifo_%d", i);
data->checksum = i * 7 + 3;
}
// Add to tail (FIFO)
for (int i = 0; i < 5; i++) {
ASSERT_EQ(queue_entry_put(q, entries[i]), 0, "Failed to add entry (FIFO)");
}
ASSERT_EQ(queue_entry_count(q), 5, "Wrong count after FIFO adds");
// Extract from head (FIFO)
for (int i = 0; i < 5; i++) {
struct ll_entry* entry = queue_entry_get(q);
ASSERT_NOT_NULL(entry, "Failed to get entry (FIFO)");
ASSERT_EQ(entry, entries[i], "Wrong order in FIFO extraction");
struct test_data* data = (struct test_data*)ll_entry_data(entry);
ASSERT_TRUE(verify_test_data(data), "Data corruption in FIFO");
ASSERT_EQ(data->value, i, "Wrong value in FIFO extraction");
queue_entry_free(entry);
}
ASSERT_EQ(queue_entry_count(q), 0, "Queue should be empty after FIFO extraction");
// Test LIFO operations (existing API)
for (int i = 0; i < 3; i++) {
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
ASSERT_NOT_NULL(entry, "Failed to create entry");
struct test_data* data = (struct test_data*)ll_entry_data(entry);
data->value = i;
data->checksum = i * 7 + 3;
ASSERT_EQ(queue_entry_put_first(q, entry), 0, "Failed to add entry (LIFO)");
}
ASSERT_EQ(queue_entry_count(q), 3, "Wrong count after LIFO adds");
// Extract from head (LIFO - should be reverse order)
for (int i = 0; i < 3; i++) {
struct ll_entry* entry = queue_entry_get(q);
ASSERT_NOT_NULL(entry, "Failed to get entry (LIFO)");
struct test_data* data = (struct test_data*)ll_entry_data(entry);
ASSERT_TRUE(verify_test_data(data), "Data corruption in LIFO");
ASSERT_EQ(data->value, 2 - i, "Wrong order in LIFO extraction");
queue_entry_free(entry);
}
ASSERT_EQ(queue_entry_count(q), 0, "Queue should be empty after LIFO extraction");
queue_free(q);
uasync_destroy(ua);
TEST_PASS();
}
/* Test: Performance benchmarks */
static void test_performance_benchmarks() {
TEST_START("performance_benchmarks");
struct UASYNC* ua = uasync_create();
ASSERT_NOT_NULL(ua, "Failed to create uasync");
struct ll_queue* q = queue_new(ua, NULL);
ASSERT_NOT_NULL(q, "Failed to create queue");
const int benchmark_size = 10000;
double start_time, end_time;
// Benchmark: Sequential add operations
start_time = get_time_ms();
for (int i = 0; i < benchmark_size; i++) {
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
if (entry) {
struct test_data* data = (struct test_data*)ll_entry_data(entry);
data->value = i;
data->checksum = i * 7 + 3;
queue_entry_put(q, entry);
}
}
end_time = get_time_ms();
printf("\n Sequential add (%d ops): %.2f ms (%.2f ops/sec)",
benchmark_size, end_time - start_time,
benchmark_size * 1000.0 / (end_time - start_time));
// Benchmark: Sequential remove operations
start_time = get_time_ms();
for (int i = 0; i < benchmark_size; i++) {
struct ll_entry* entry = queue_entry_get(q);
if (entry) {
struct test_data* data = (struct test_data*)ll_entry_data(entry);
if (!verify_test_data(data)) {
test_stats.memory_errors++;
}
queue_entry_free(entry);
}
}
end_time = get_time_ms();
printf("\n Sequential remove (%d ops): %.2f ms (%.2f ops/sec)",
benchmark_size, end_time - start_time,
benchmark_size * 1000.0 / (end_time - start_time));
// Benchmark: Position navigation
// Rebuild queue for navigation benchmark
for (int i = 0; i < benchmark_size / 10; i++) {
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
if (entry) {
queue_entry_put(q, entry);
}
}
struct ll_queue_pos* pos = queue_pos_new(q);
ASSERT_NOT_NULL(pos, "Failed to create position for benchmark");
start_time = get_time_ms();
int navigation_ops = 0;
for (int i = 0; i < benchmark_size / 10; i++) {
// Forward and backward navigation
while (queue_pos_next(pos)) navigation_ops++;
while (queue_pos_prev(pos)) navigation_ops++;
// Random seek operations
int target = rand() % queue_entry_count(q);
queue_pos_seek(pos, target);
navigation_ops++;
}
end_time = get_time_ms();
printf("\n Position navigation (%d ops): %.2f ms (%.2f ops/sec)",
navigation_ops, end_time - start_time,
navigation_ops * 1000.0 / (end_time - start_time));
queue_pos_free(pos);
queue_free(q);
uasync_destroy(ua);
TEST_PASS();
}
/* Test: Edge cases and error conditions */
static void test_edge_cases() {
TEST_START("edge_cases");
struct UASYNC* ua = uasync_create();
ASSERT_NOT_NULL(ua, "Failed to create uasync");
// Test NULL parameter handling
ASSERT_NULL(queue_pos_new(NULL), "Should return NULL for NULL queue");
ASSERT_NULL(queue_entry_get(NULL), "Should return NULL for NULL queue");
ASSERT_EQ(queue_entry_put(NULL, NULL), -1, "Should fail on NULL parameters");
struct ll_queue* q = queue_new(ua, NULL);
ASSERT_NOT_NULL(q, "Failed to create queue");
struct ll_queue_pos* pos = queue_pos_new(q);
ASSERT_NOT_NULL(pos, "Failed to create position");
// Test operations on empty queue
ASSERT_NULL(queue_entry_get(q), "Should return NULL from empty queue");
ASSERT_NULL(queue_pos_current(pos), "Should return NULL for empty queue");
ASSERT_EQ(queue_pos_seek(pos, 0), 0, "Seek should fail on empty queue");
// Test navigation on empty queue
ASSERT_EQ(queue_pos_next(pos), 0, "Next should fail on empty queue");
ASSERT_EQ(queue_pos_prev(pos), 0, "Prev should fail on empty queue");
// Test single element queue
struct ll_entry* entry = queue_entry_new(sizeof(struct test_data));
ASSERT_NOT_NULL(entry, "Failed to create entry");
ASSERT_EQ(queue_entry_put(q, entry), 0, "Failed to add single entry");
ASSERT_EQ(queue_entry_count(q), 1, "Wrong count for single entry");
// Test navigation with single element
ASSERT_EQ(queue_pos_seek(pos, 0), 1, "Should seek to single element");
ASSERT_NOT_NULL(queue_pos_current(pos), "Should have current element");
ASSERT_EQ(queue_pos_next(pos), 0, "Next should fail at end");
ASSERT_EQ(queue_pos_seek(pos, 1), 0, "Should fail to seek beyond single element");
// Test removal of single element
struct ll_entry* removed = queue_pos_remove(pos);
ASSERT_NOT_NULL(removed, "Should remove single element");
ASSERT_EQ(queue_entry_count(q), 0, "Queue should be empty after removal");
queue_entry_free(removed);
// Test size limit enforcement
queue_set_size_limit(q, 2);
struct ll_entry* e1 = queue_entry_new(sizeof(struct test_data));
struct ll_entry* e2 = queue_entry_new(sizeof(struct test_data));
struct ll_entry* e3 = queue_entry_new(sizeof(struct test_data));
ASSERT_NOT_NULL(e1, "Failed to create entry 1");
ASSERT_NOT_NULL(e2, "Failed to create entry 2");
ASSERT_NOT_NULL(e3, "Failed to create entry 3");
ASSERT_EQ(queue_entry_put(q, e1), 0, "Should add first entry");
ASSERT_EQ(queue_entry_put(q, e2), 0, "Should add second entry");
ASSERT_EQ(queue_entry_put(q, e3), -1, "Should reject third entry (size limit)");
ASSERT_EQ(queue_entry_count(q), 2, "Wrong count with size limit");
queue_entry_free(e3); // Free the rejected entry
queue_pos_free(pos);
queue_free(q);
uasync_destroy(ua);
TEST_PASS();
}
/* Main test runner */
int main() {
printf("=== Unified Comprehensive ll_queue Test Suite ===\n");
printf("Starting tests at %s\n", __TIME__);
double suite_start_time = get_time_ms();
// Run all tests
test_doubly_linked_basics();
test_position_navigation();
test_position_insert_remove();
test_stress_random_operations();
test_api_compatibility();
test_performance_benchmarks();
test_edge_cases();
double suite_end_time = get_time_ms();
double total_suite_time = suite_end_time - suite_start_time;
printf("\n=== Test Suite Summary ===\n");
printf("Tests run: %d\n", test_stats.tests_run);
printf("Tests passed: %d\n", test_stats.tests_passed);
printf("Tests failed: %d\n", test_stats.tests_failed);
printf("Memory errors: %d\n", test_stats.memory_errors);
printf("Total operations: %d\n", test_stats.operations_count);
printf("Total time: %.2f ms\n", total_suite_time);
printf("Average ops/sec: %.2f\n", test_stats.operations_count * 1000.0 / total_suite_time);
if (test_stats.tests_failed == 0 && test_stats.memory_errors == 0) {
printf("\n✅ ALL TESTS PASSED - ll_queue module is working correctly!\n");
return 0;
} else {
printf("\n❌ TEST FAILURES DETECTED - Check output above for details\n");
return 1;
}
}