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.
 
 
 
 
 
 

299 lines
9.5 KiB

/**
* Компактные, логичные unit-тесты для ll_queue (embedded ll_entry + xxx=0)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#include "../lib/ll_queue.h"
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
#include "../lib/memory_pool.h"
#ifndef DEBUG_CATEGORY_LL_QUEUE
#define DEBUG_CATEGORY_LL_QUEUE 1
#endif
static struct {
int run, passed, failed;
int cb_queue, cb_waiter;
int mem_err, hash_err;
long ops;
double time_ms;
} stats = {0};
#define TEST(name) do { \
printf("TEST: %-35s ", name); fflush(stdout); \
stats.run++; \
} while(0)
#define PASS() do { puts("PASS"); stats.passed++; } while(0)
#define FAIL(msg) do { printf("FAIL: %s\n", msg); stats.failed++; } while(0)
#define ASSERT(c, m) do { if (!(c)) { FAIL(m); return; } } while(0)
#define ASSERT_EQ(a,b,m) ASSERT((a)==(b),m)
/* ------------------------------------------------------------------ */
static double now_ms(void) {
struct timeval tv; gettimeofday(&tv, NULL);
return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
}
typedef struct {
struct ll_entry ll; // ← embedded, first field
int id;
char name[32];
int value;
uint64_t checksum;
} test_data_t;
static uint64_t checksum(const test_data_t *d) {
return (uint64_t)d->id * 31 + (uint64_t)d->value * 17 + strlen(d->name);
}
/* Callback consumer: забирает и освобождает элемент */
static void queue_cb(struct ll_queue *q, void *arg) {
int *cnt = arg;
(*cnt)++; stats.cb_queue++;
test_data_t *taken = (test_data_t*)queue_data_get(q);
ASSERT(checksum(taken) == taken->checksum, "corruption in cb");
queue_entry_free((struct ll_entry*)taken);
queue_resume_callback(q); // next item when ready
}
static void waiter_cb(struct ll_queue *q, void *arg) {
(*(int*)arg)++; stats.cb_waiter++;
}
/* ------------------------------------------------------------------ */
static void test_basic(void) {
TEST("basic creation / free");
struct UASYNC *ua = uasync_create();
struct ll_queue *q1 = queue_new(ua, 0,"q1");
struct ll_queue *q2 = queue_new(ua, 16,"q2");
ASSERT(q1 && q2, "queue_new failed");
ASSERT_EQ(queue_entry_count(q1), 0, "");
queue_free(q1); queue_free(q2);
uasync_destroy(ua, 0);
PASS();
}
static void test_fifo(void) {
TEST("FIFO ordering");
struct UASYNC *ua = uasync_create();
struct ll_queue *q = queue_new(ua, 0,"q3");
for (int i = 0; i < 10; i++) {
test_data_t *d = (test_data_t*)queue_entry_new(sizeof(test_data_t));
d->id = i; snprintf(d->name, sizeof(d->name), "item%d", i);
d->value = i*10; d->checksum = checksum(d);
queue_data_put_with_index(q, (struct ll_entry*)d, 0, 4);
}
ASSERT_EQ(queue_entry_count(q), 10, "");
for (int i = 0; i < 10; i++) {
test_data_t *d = (test_data_t*)queue_data_get(q);
ASSERT(d && d->id == i, "FIFO violation");
queue_entry_free((struct ll_entry*)d);
}
ASSERT_EQ(queue_entry_count(q), 0, "");
queue_free(q); uasync_destroy(ua, 0);
PASS();
}
static void test_lifo_priority(void) {
TEST("put_first (LIFO priority)");
struct UASYNC *ua = uasync_create();
struct ll_queue *q = queue_new(ua, 0,"q4");
for (int i = 0; i < 3; i++) {
test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d));
d->id = i;
d->value = i;
d->checksum = checksum(d);
queue_data_put_with_index(q, (struct ll_entry*)d, 0, 4);
}
test_data_t *pri = (test_data_t*)queue_entry_new(sizeof(*pri));
pri->id = 999; pri->value = 999; pri->checksum = checksum(pri);
queue_data_put_first_with_index(q, (struct ll_entry*)pri, 0, 4);
test_data_t *first = (test_data_t*)queue_data_get(q);
ASSERT(first && first->id == 999, "priority first");
queue_entry_free((struct ll_entry*)first);
for (int i = 0; i < 3; i++) {
test_data_t *d = (test_data_t*)queue_data_get(q);
ASSERT(d && d->id == i, "remaining FIFO");
queue_entry_free((struct ll_entry*)d);
}
queue_free(q); uasync_destroy(ua, 0);
PASS();
}
static void test_callback(void) {
TEST("callback serial processing");
struct UASYNC *ua = uasync_create();
struct ll_queue *q = queue_new(ua, 0,"q5");
int cnt = 0;
queue_set_callback(q, queue_cb, &cnt);
for (int i = 0; i < 5; i++) {
test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d));
d->id = i; d->value = i*10; d->checksum = checksum(d);
queue_data_put_with_index(q, (struct ll_entry*)d, 0, 4);
}
for (int i = 0; i < 30 && cnt < 5; i++) uasync_poll(ua, 5);
ASSERT_EQ(cnt, 5, "all items processed via callback");
ASSERT_EQ(queue_entry_count(q), 0, "");
queue_free(q); uasync_destroy(ua, 0);
PASS();
}
static void test_waiter(void) {
TEST("wait_threshold + cancel");
struct UASYNC *ua = uasync_create();
struct ll_queue *q = queue_new(ua, 0,"q6");
int called = 0;
struct queue_waiter *w = queue_wait_threshold(q, 2, 0, waiter_cb, &called);
ASSERT(w == NULL && called == 1, "immediate when condition met"); // empty queue
for (int i = 0; i < 5; i++) {
test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i;
queue_data_put_with_index(q, (struct ll_entry*)d, 0, 4);
}
called = 0;
w = queue_wait_threshold(q, 2, 0, waiter_cb, &called);
ASSERT(w != NULL && called == 0, "");
for (int i = 0; i < 3; i++) queue_entry_free((struct ll_entry*)queue_data_get(q));
for (int i = 0; i < 15; i++) uasync_poll(ua, 1);
ASSERT_EQ(called, 1, "");
queue_cancel_wait(q, w);
queue_free(q); uasync_destroy(ua, 0);
PASS();
}
static void test_limits_hash(void) {
TEST("size limit + hash find/remove");
struct UASYNC *ua = uasync_create();
struct ll_queue *q = queue_new(ua, 16,"q7");
queue_set_size_limit(q, 3);
for (int i = 0; i < 3; i++) {
test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i*10+1;
queue_data_put_with_index(q, (struct ll_entry*)d, 0, 4);
}
test_data_t *ex = (test_data_t*)queue_entry_new(sizeof(*ex));
ASSERT_EQ(queue_data_put_with_index(q, (struct ll_entry*)ex, 0, 4), -1, "limit reject");
uint32_t hash=21;
test_data_t *found = (test_data_t*)queue_find_data_by_index(q, &hash, 4);
ASSERT(found && found->id == 21, "hash find");
queue_remove_data(q, (struct ll_entry*)found);
ASSERT(queue_find_data_by_index(q, &hash, 4) == NULL, "removed");
while (queue_entry_count(q)) queue_entry_free((struct ll_entry*)queue_data_get(q));
queue_free(q); uasync_destroy(ua, 0);
PASS();
}
static void test_pool(void) {
TEST("memory_pool integration + reuse");
struct UASYNC *ua = uasync_create();
struct memory_pool *pool = memory_pool_init(sizeof(test_data_t));
struct ll_queue *q = queue_new(ua, 0,"q8");
size_t alloc1 = 0, reuse1 = 0;
memory_pool_get_stats(pool, &alloc1, &reuse1);
test_data_t *d1 = (test_data_t*)queue_entry_new_from_pool(pool);
d1->id = 1; d1->checksum = checksum(d1);
queue_data_put_with_index(q, (struct ll_entry*)d1, 0, 4);
test_data_t *d2 = (test_data_t*)queue_entry_new_from_pool(pool);
d2->id = 2; d2->checksum = checksum(d2);
queue_data_put_with_index(q, (struct ll_entry*)d2, 0, 4);
queue_entry_free((struct ll_entry*)queue_data_get(q)); // free d1 back to pool
queue_entry_free((struct ll_entry*)queue_data_get(q)); // free d2 back to pool
// Now allocate again to trigger reuse
test_data_t *d3 = (test_data_t*)queue_entry_new_from_pool(pool);
ASSERT(d3 != NULL, "alloc after free failed");
d3->id = 3; d3->checksum = checksum(d3);
queue_data_put_with_index(q, (struct ll_entry*)d3, 0, 4);
queue_entry_free((struct ll_entry*)queue_data_get(q)); // free d3 back
size_t alloc2 = 0, reuse2 = 0;
memory_pool_get_stats(pool, &alloc2, &reuse2);
ASSERT(reuse2 > reuse1, "pool reuse works");
queue_free(q); memory_pool_destroy(pool); uasync_destroy(ua, 0);
PASS();
}
static void test_stress(void) {
TEST("stress 10k ops");
double start = now_ms();
struct UASYNC *ua = uasync_create();
struct ll_queue *q = queue_new(ua, 64,"q9");
for (int i = 0; i < 10000; i++) {
if (queue_entry_count(q) > 80) {
queue_entry_free((struct ll_entry*)queue_data_get(q));
}
test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d));
d->id = rand() % 10000;
d->checksum = checksum(d);
queue_data_put_with_index(q, (struct ll_entry*)d, 0, 4);
stats.ops++;
}
while (queue_entry_count(q)) queue_entry_free((struct ll_entry*)queue_data_get(q));
stats.time_ms += now_ms() - start;
queue_free(q); uasync_destroy(ua, 0);
PASS();
}
/* ------------------------------------------------------------------ */
int main(void) {
debug_config_init();
debug_set_level(DEBUG_LEVEL_DEBUG);
debug_set_categories(DEBUG_CATEGORY_LL_QUEUE);
double t0 = now_ms();
test_basic();
test_fifo();
test_lifo_priority();
test_callback();
test_waiter();
test_limits_hash();
test_pool();
test_stress();
printf("\n=== SUMMARY ===\n"
"run=%d passed=%d failed=%d\n"
"callbacks: queue=%d waiter=%d\n"
"ops=%ld time=%.2f ms\n",
stats.run, stats.passed, stats.failed,
stats.cb_queue, stats.cb_waiter, stats.ops, now_ms()-t0);
return stats.failed ? 1 : 0;
}