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.
258 lines
7.3 KiB
258 lines
7.3 KiB
// test_uasync_random.c - Test uasync instance with random timeouts |
|
#include "u_async.h" |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <time.h> |
|
|
|
#define MAX_TIMEOUTS 1000 |
|
#define MAX_TIMEOUT_TB 10000 // 1 second in timebase units (0.1 ms) |
|
#define CANCEL_PROBABILITY 20 // 20% chance to cancel a timeout |
|
#define NUM_ITERATIONS 50000 |
|
|
|
typedef struct { |
|
int id; |
|
int fired; |
|
int cancelled; |
|
uasync_t* ua; |
|
} test_context_t; |
|
|
|
static int g_fired_count = 0; |
|
static int g_cancelled_count = 0; |
|
static int g_expected_fired = 0; |
|
|
|
static void timeout_callback(void* arg) { |
|
test_context_t* ctx = (test_context_t*)arg; |
|
if (!ctx) return; |
|
|
|
if (ctx->cancelled) { |
|
printf("ERROR: Cancelled timeout %d fired!\n", ctx->id); |
|
exit(1); |
|
} |
|
|
|
ctx->fired = 1; |
|
g_fired_count++; |
|
|
|
// Verify the timeout was removed from heap |
|
// (we can't directly check, but we trust the implementation) |
|
} |
|
|
|
static void run_random_test(void) { |
|
printf("=== Random uasync timeout test ===\n"); |
|
|
|
// Create uasync instance |
|
uasync_t* ua = uasync_create(); |
|
if (!ua) { |
|
printf("FAIL: uasync_create returned NULL\n"); |
|
exit(1); |
|
} |
|
|
|
printf("Created uasync instance\n"); |
|
|
|
// Allocate test contexts |
|
test_context_t* contexts = malloc(MAX_TIMEOUTS * sizeof(test_context_t)); |
|
if (!contexts) { |
|
printf("FAIL: malloc failed\n"); |
|
uasync_destroy(ua); |
|
exit(1); |
|
} |
|
|
|
memset(contexts, 0, MAX_TIMEOUTS * sizeof(test_context_t)); |
|
|
|
srand(time(NULL)); |
|
|
|
// Set random timeouts |
|
int active_timeouts = 0; |
|
for (int i = 0; i < MAX_TIMEOUTS; i++) { |
|
contexts[i].id = i; |
|
contexts[i].ua = ua; |
|
|
|
// Random timeout between 1 and MAX_TIMEOUT_TB |
|
int timeout_tb = (rand() % MAX_TIMEOUT_TB) + 1; |
|
|
|
void* handle = uasync_set_timeout(ua, timeout_tb, &contexts[i], timeout_callback); |
|
if (!handle) { |
|
printf("WARN: uasync_set_timeout failed for timeout %d\n", i); |
|
continue; |
|
} |
|
|
|
// Store handle in context (we could store it, but we'll just track) |
|
contexts[i].fired = 0; |
|
contexts[i].cancelled = 0; |
|
active_timeouts++; |
|
|
|
// Randomly cancel some timeouts |
|
if (rand() % 100 < CANCEL_PROBABILITY) { |
|
if (uasync_cancel_timeout(ua, handle) == ERR_OK) { |
|
contexts[i].cancelled = 1; |
|
g_cancelled_count++; |
|
active_timeouts--; |
|
} else { |
|
printf("WARN: Failed to cancel timeout %d\n", i); |
|
} |
|
} |
|
} |
|
|
|
g_expected_fired = active_timeouts; |
|
printf("Set %d timeouts, cancelled %d, expecting %d to fire\n", |
|
MAX_TIMEOUTS, g_cancelled_count, g_expected_fired); |
|
|
|
// Run event loop for enough time to fire all timeouts |
|
// Calculate maximum timeout in milliseconds plus some margin |
|
int max_timeout_ms = (MAX_TIMEOUT_TB + 1000) / 10; // convert to ms with margin |
|
int iterations = 0; |
|
|
|
while (g_fired_count < g_expected_fired && iterations < NUM_ITERATIONS) { |
|
// Poll with short timeout (10ms = 100 timebase units) |
|
uasync_poll(ua, 100); |
|
iterations++; |
|
|
|
// Check if we've waited long enough |
|
if (iterations * 10 > max_timeout_ms * 5) { |
|
// We've waited five times the max timeout, something might be wrong |
|
if (g_fired_count < g_expected_fired) { |
|
printf("WARN: Only %d of %d timeouts fired after %d ms\n", |
|
g_fired_count, g_expected_fired, iterations * 10); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
// Verify all non-cancelled timeouts fired |
|
int failed_fires = 0; |
|
for (int i = 0; i < MAX_TIMEOUTS; i++) { |
|
if (!contexts[i].cancelled && !contexts[i].fired) { |
|
failed_fires++; |
|
if (failed_fires < 10) { // Limit output |
|
printf("ERROR: Timeout %d didn't fire (not cancelled)\n", i); |
|
} |
|
} |
|
|
|
if (contexts[i].cancelled && contexts[i].fired) { |
|
printf("ERROR: Cancelled timeout %d fired\n", i); |
|
failed_fires++; |
|
} |
|
} |
|
|
|
if (failed_fires > 0) { |
|
printf("FAIL: %d timeout failures\n", failed_fires); |
|
} else { |
|
printf("PASS: All %d expected timeouts fired correctly\n", g_fired_count); |
|
} |
|
|
|
// Verify that no cancelled timeouts fired (already checked above) |
|
printf("Cancelled timeouts: %d (none should fire)\n", g_cancelled_count); |
|
|
|
// Cleanup |
|
free(contexts); |
|
uasync_destroy(ua); |
|
|
|
if (failed_fires == 0) { |
|
printf("=== Test PASSED ===\n"); |
|
} else { |
|
printf("=== Test FAILED ===\n"); |
|
exit(1); |
|
} |
|
} |
|
|
|
static void simple_callback(void* arg) { |
|
int* flag = (int*)arg; |
|
*flag = 1; |
|
} |
|
|
|
static void free_callback(void* arg) { |
|
free(arg); |
|
} |
|
|
|
static void test_instance_isolation(void) { |
|
printf("\n=== Instance isolation test ===\n"); |
|
|
|
// Create two independent instances |
|
uasync_t* ua1 = uasync_create(); |
|
uasync_t* ua2 = uasync_create(); |
|
if (!ua1 || !ua2) { |
|
printf("FAIL: Failed to create instances\n"); |
|
exit(1); |
|
} |
|
|
|
int fired1 = 0; |
|
int fired2 = 0; |
|
|
|
void* timeout1 = uasync_set_timeout(ua1, 10, &fired1, simple_callback); |
|
void* timeout2 = uasync_set_timeout(ua2, 20, &fired2, simple_callback); |
|
|
|
if (!timeout1 || !timeout2) { |
|
printf("FAIL: Failed to set timeouts\n"); |
|
exit(1); |
|
} |
|
|
|
// Run first instance - should fire timeout1 only |
|
uasync_poll(ua1, 50); // 5ms wait |
|
if (fired1 != 1) { |
|
printf("FAIL: Instance 1 timeout didn't fire\n"); |
|
exit(1); |
|
} |
|
if (fired2 != 0) { |
|
printf("FAIL: Instance 2 timeout fired incorrectly\n"); |
|
exit(1); |
|
} |
|
|
|
// Cancel timeout2 |
|
if (uasync_cancel_timeout(ua2, timeout2) != ERR_OK) { |
|
printf("FAIL: Failed to cancel timeout2\n"); |
|
exit(1); |
|
} |
|
|
|
// Run second instance - nothing should fire |
|
fired1 = 0; |
|
uasync_poll(ua2, 50); |
|
if (fired1 != 0) { |
|
printf("FAIL: Instance 1 timeout fired from instance 2\n"); |
|
exit(1); |
|
} |
|
if (fired2 != 0) { |
|
printf("FAIL: Cancelled timeout fired\n"); |
|
exit(1); |
|
} |
|
|
|
uasync_destroy(ua1); |
|
uasync_destroy(ua2); |
|
|
|
printf("PASS: Instance isolation works correctly\n"); |
|
} |
|
|
|
static void test_memory_cleanup(void) { |
|
printf("\n=== Memory cleanup test ===\n"); |
|
|
|
uasync_t* ua = uasync_create(); |
|
if (!ua) { |
|
printf("FAIL: uasync_create failed\n"); |
|
exit(1); |
|
} |
|
|
|
// Set many timeouts |
|
for (int i = 0; i < 100; i++) { |
|
int* counter = malloc(sizeof(int)); |
|
*counter = i; |
|
uasync_set_timeout(ua, 1000 + i, counter, free_callback); |
|
} |
|
|
|
// Destroy instance without waiting for timeouts |
|
// This should free all pending timeout nodes |
|
uasync_destroy(ua); |
|
|
|
printf("PASS: Memory cleanup completed\n"); |
|
} |
|
|
|
int main(void) { |
|
printf("Starting uasync random timeout tests\n"); |
|
printf("====================================\n"); |
|
|
|
run_random_test(); |
|
test_instance_isolation(); |
|
test_memory_cleanup(); |
|
|
|
printf("\n====================================\n"); |
|
printf("All tests PASSED!\n"); |
|
return 0; |
|
} |