myxce/src/lib/testsuite/test_hooks.c

167 lines
5.2 KiB
C

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "hook.h"
#include <unity/unity.h>
/* Shared call counter for hook tests */
static int s_call_count = 0;
static int counting_fn(int type, int sit, int det, void* data, va_list ap)
{
(void)type;
(void)sit;
(void)det;
(void)ap;
(*(int*)data)++;
return 0;
}
/* --- test cases --- */
static void test_disable_hook_stops_invocation(void)
{
hooksys_t hs;
int rc;
s_call_count = 0;
rc = xce_hook_init(&hs, 2 /* maxtypes */);
TEST_ASSERT_EQUAL_INT(0, rc);
rc = xce_add_hook(&hs, HOOK_AFTER_EVENT, counting_fn, &s_call_count);
TEST_ASSERT_EQUAL_INT(0, rc);
/* Before disable: hook fires once */
xce_run_hooks(&hs, HOOK_AFTER_EVENT, 0, 0);
TEST_ASSERT_EQUAL_INT(1, s_call_count);
/* Disable the hook */
rc = xce_disable_hook(&hs, HOOK_AFTER_EVENT, counting_fn);
TEST_ASSERT_EQUAL_INT(0, rc);
/* After disable: hook must not fire */
xce_run_hooks(&hs, HOOK_AFTER_EVENT, 0, 0);
TEST_ASSERT_EQUAL_INT(1, s_call_count); /* unchanged */
xce_hook_fini(&hs);
printf("test_disable_hook_stops_invocation: PASS\n");
}
static void test_disable_hook_returns_enoent_if_not_found(void)
{
hooksys_t hs;
int rc;
rc = xce_hook_init(&hs, 2);
TEST_ASSERT_EQUAL_INT(0, rc);
/* Disabling a hook never added must return non-zero (ENOENT) */
rc = xce_disable_hook(&hs, HOOK_AFTER_EVENT, counting_fn);
TEST_ASSERT_NOT_EQUAL(0, rc);
xce_hook_fini(&hs);
printf("test_disable_hook_returns_enoent_if_not_found: PASS\n");
}
static void test_add_hook_reuses_null_slot(void)
{
hooksys_t hs;
int rc;
int count_before_reuse;
s_call_count = 0;
rc = xce_hook_init(&hs, 2);
TEST_ASSERT_EQUAL_INT(0, rc);
rc = xce_add_hook(&hs, HOOK_AFTER_EVENT, counting_fn, &s_call_count);
TEST_ASSERT_EQUAL_INT(0, rc);
/* Record list length after first add */
count_before_reuse = hs.hooks[HOOK_AFTER_EVENT].count;
TEST_ASSERT_EQUAL_INT(1, count_before_reuse);
/* Disable the slot — fn becomes NULL atomically */
rc = xce_disable_hook(&hs, HOOK_AFTER_EVENT, counting_fn);
TEST_ASSERT_EQUAL_INT(0, rc);
/* Re-add the same fn — must reuse the NULL slot, not grow the list */
rc = xce_add_hook(&hs, HOOK_AFTER_EVENT, counting_fn, &s_call_count);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_INT(count_before_reuse, hs.hooks[HOOK_AFTER_EVENT].count);
/* Fire: must get exactly 1 call (not 2 if slot was duplicated) */
xce_run_hooks(&hs, HOOK_AFTER_EVENT, 0, 0);
TEST_ASSERT_EQUAL_INT(1, s_call_count);
xce_hook_fini(&hs);
printf("test_add_hook_reuses_null_slot: PASS\n");
}
/* bug-845: re-registering the SAME (fn,data) on a type must be idempotent.
* xce_disable_hook removes only ONE matching slot and xce_run_hooks fires ALL
* slots, so a duplicate (fn,data) registration would (a) fire twice and (b)
* survive a single disable. This is exactly how the controller double-appended
* every WAL_JOB_SUBMIT: persist_start() and ha_register_persist_hooks() both
* register persist_job_hook_fun with the same persist_t closure. */
static void test_add_hook_dedups_same_fn_and_data(void)
{
hooksys_t hs;
int a = 0;
TEST_ASSERT_EQUAL_INT(0, xce_hook_init(&hs, 2));
/* First add registers; the second add with the SAME fn AND data must be a
* no-op (still rc=0, still exactly one slot). */
TEST_ASSERT_EQUAL_INT(0, xce_add_hook(&hs, HOOK_AFTER_EVENT, counting_fn, &a));
TEST_ASSERT_EQUAL_INT(0, xce_add_hook(&hs, HOOK_AFTER_EVENT, counting_fn, &a));
TEST_ASSERT_EQUAL_INT(1, hs.hooks[HOOK_AFTER_EVENT].count);
/* Fires ONCE per event, never twice. */
xce_run_hooks(&hs, HOOK_AFTER_EVENT, 0, 0);
TEST_ASSERT_EQUAL_INT(1, a);
/* A single disable fully removes it — no duplicate slot lingers. */
TEST_ASSERT_EQUAL_INT(0, xce_disable_hook(&hs, HOOK_AFTER_EVENT, counting_fn));
xce_run_hooks(&hs, HOOK_AFTER_EVENT, 0, 0);
TEST_ASSERT_EQUAL_INT(1, a); /* unchanged */
xce_hook_fini(&hs);
printf("test_add_hook_dedups_same_fn_and_data: PASS\n");
}
/* The dedup is keyed on (fn,data): the SAME fn with DISTINCT closures is a
* legitimate pair of registrations and BOTH must fire. */
static void test_add_hook_allows_same_fn_distinct_data(void)
{
hooksys_t hs;
int a = 0, b = 0;
TEST_ASSERT_EQUAL_INT(0, xce_hook_init(&hs, 2));
TEST_ASSERT_EQUAL_INT(0, xce_add_hook(&hs, HOOK_AFTER_EVENT, counting_fn, &a));
TEST_ASSERT_EQUAL_INT(0, xce_add_hook(&hs, HOOK_AFTER_EVENT, counting_fn, &b));
TEST_ASSERT_EQUAL_INT(2, hs.hooks[HOOK_AFTER_EVENT].count);
xce_run_hooks(&hs, HOOK_AFTER_EVENT, 0, 0);
TEST_ASSERT_EQUAL_INT(1, a);
TEST_ASSERT_EQUAL_INT(1, b);
xce_hook_fini(&hs);
printf("test_add_hook_allows_same_fn_distinct_data: PASS\n");
}
/* --- suite entry point --- */
void test_hooks(void)
{
printf("\n>> hook system tests\n\n");
RUN_TEST(test_disable_hook_stops_invocation);
RUN_TEST(test_disable_hook_returns_enoent_if_not_found);
RUN_TEST(test_add_hook_reuses_null_slot);
RUN_TEST(test_add_hook_dedups_same_fn_and_data);
RUN_TEST(test_add_hook_allows_same_fn_distinct_data);
}