#include #include #include #include #include #include "delay.h" #include "unity.h" static int delay_work(void* arg) { time_t now; int* pcount = (int*)arg; now = time(NULL); (*pcount)++; printf("delay task run at %ld\n", now); return 0; } static void test_delay1() { delaymgr_t mgr; int count = 0; int runcount = 15; int delay_period = 2; time_t t_start = time(NULL); delaymgr_init(&mgr, 1000 /* precision_ms */, NULL); delaymgr_start(&mgr); delay_task(&mgr, delay_period, runcount, (delay_task_entry_fn_t)delay_work, &count); while (1) { time_t now = time(NULL); if (now - t_start > (runcount + 2) * delay_period) { break; } sleep(5); } TEST_ASSERT_TRUE(count == runcount); delaymgr_fini(&mgr); return; } static void test_delaymgr_precision_ms_field(void) { delaymgr_t mgr; memset(&mgr, 0, sizeof(mgr)); /* precision_ms field must exist and be stored correctly */ int rc = delaymgr_init(&mgr, 200 /* precision_ms */, NULL); TEST_ASSERT_EQUAL_INT(0, rc); TEST_ASSERT_EQUAL_INT(200, mgr.precision_ms); delaymgr_fini(&mgr); printf("test_delaymgr_precision_ms_field: PASS\n"); } /* ---------- RED: test for delay_task_ms (millisecond-granularity) ---------- */ static int ms_work_count = 0; static int ms_work(void* arg) { (void)arg; ms_work_count++; return 0; } static void test_delay_task_ms_fires_within_tolerance(void) { delaymgr_t mgr; struct timespec t_start, t_end; long elapsed_ms; const int delay_ms = 500; const int tolerance_ms = 200; const int precision_ms = 50; ms_work_count = 0; delaymgr_init(&mgr, precision_ms, NULL); delaymgr_start(&mgr); clock_gettime(CLOCK_MONOTONIC, &t_start); delay_task_ms(&mgr, delay_ms, 1, (delay_task_entry_fn_t)ms_work, NULL); /* wait long enough: delay + tolerance + one extra precision tick */ { struct timespec wait = { .tv_sec = 0, .tv_nsec = (long)(delay_ms + tolerance_ms + precision_ms) * 1000000L, }; nanosleep(&wait, NULL); } clock_gettime(CLOCK_MONOTONIC, &t_end); elapsed_ms = (t_end.tv_sec - t_start.tv_sec) * 1000L + (t_end.tv_nsec - t_start.tv_nsec) / 1000000L; TEST_ASSERT_EQUAL_INT_MESSAGE(1, ms_work_count, "ms task must have fired exactly once"); TEST_ASSERT_TRUE_MESSAGE(elapsed_ms >= (delay_ms - tolerance_ms), "task fired too early"); TEST_ASSERT_TRUE_MESSAGE(elapsed_ms <= (delay_ms + tolerance_ms + precision_ms + 50), "task fired too late"); delaymgr_fini(&mgr); printf("test_delay_task_ms_fires_within_tolerance: PASS\n"); } static void test_delay_task_ms_invalid_args(void) { delaymgr_t mgr; int rc; delaymgr_init(&mgr, 50, NULL); /* NULL manager */ rc = delay_task_ms(NULL, 500, 1, (delay_task_entry_fn_t)ms_work, NULL); TEST_ASSERT_TRUE(rc < 0); /* zero delay */ rc = delay_task_ms(&mgr, 0, 1, (delay_task_entry_fn_t)ms_work, NULL); TEST_ASSERT_TRUE(rc < 0); /* NULL entry */ rc = delay_task_ms(&mgr, 500, 1, NULL, NULL); TEST_ASSERT_TRUE(rc < 0); delaymgr_fini(&mgr); printf("test_delay_task_ms_invalid_args: PASS\n"); } /* ---------- existing tests still exercise backward compat ---------- */ void test_delay(void) { printf("\n>> delay test\n\n"); RUN_TEST(test_delay1); RUN_TEST(test_delaymgr_precision_ms_field); RUN_TEST(test_delay_task_ms_fires_within_tolerance); RUN_TEST(test_delay_task_ms_invalid_args); }