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.
407 lines
14 KiB
407 lines
14 KiB
#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; |
|
} |