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.
752 lines
28 KiB
752 lines
28 KiB
/** |
|
* 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; |
|
} |
|
} |