167 lines
5.2 KiB
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);
|
|
}
|