Only 21 of 86 test_*.c group functions called their own nested UNITY_BEGIN()/UNITY_END(). Each nested call reset Unity's global NumberOfTests/TestFailures counters, so controller_test.c's final `return UNITY_END();` -- the value meson test actually gates on -- only ever reflected whichever small window of tests ran since the LAST nested reset, silently discarding pass/fail evidence for everything before it (measured: 16 of 972 total tests in one run). The same nested-BEGIN calls also caused PASS/FAIL banners to show the wrong source file for tests in files that don't reset it themselves (Unity.TestFile just carries over from whichever group last called UNITY_BEGIN()). Fix: replace every nested UNITY_BEGIN() with UnitySetTestFile(__FILE__) (sets file attribution only, no counter reset) and drop the matching UNITY_END(); add the same UnitySetTestFile(__FILE__) as the first statement to the other group functions that had neither. The 2 genuinely standalone binaries (test_job_array_sched_push.c, test_job_array_apiserver_push.c -- each has its own int main(), not registered via controller_test.c's TEST() macro) are untouched. Verified: single consolidated summary now reads "972 Tests 0 Failures 0 Ignored OK", exactly matching the ground-truth RUN_TEST call-site count across all 83 registered group files (976 total minus 4 belonging to the 2 standalone binaries). Every PASS/FAIL banner now names its own true source file. Full xce-build.sh all regression pass clean (Ok: 9, Fail: 0). No production code touched -- test-harness plumbing only.
149 lines
4.7 KiB
C
149 lines
4.7 KiB
C
/*
|
|
* test_ha_cluster.c — P1-5: structural tests for ha_state_t embedded in cluster_t
|
|
*
|
|
* These tests validate that:
|
|
* 1. cluster_t has an ha_state_t ha field (compile-time structural check).
|
|
* 2. ha_promote_to_primary() and ha_demote_to_standby() use c->ha (not no-ops).
|
|
*
|
|
* Tests do NOT call cluster_init() — that requires a live config/SSL/network stack.
|
|
* Instead they manually zero-init cluster_t and call ha_init() on c.ha directly.
|
|
*/
|
|
|
|
#include <stdatomic.h>
|
|
#include <string.h>
|
|
|
|
#include <unity/unity.h>
|
|
|
|
#include "ha.h"
|
|
#include "xce/cluster_def.h"
|
|
|
|
/* Valid HA config shared across tests */
|
|
static ha_config_t g_cfg = {
|
|
.heartbeat_interval_ms = 3000,
|
|
.heartbeat_timeout_ms = 10000,
|
|
.election_timeout_ms = 2000,
|
|
.probe_connect_timeout_ms = 500,
|
|
.max_promotion_retries = 5,
|
|
};
|
|
|
|
/*
|
|
* test_cluster_t_has_ha_field:
|
|
* Compile-time structural check — cluster_t must embed ha_state_t as field `ha`.
|
|
* If `c.ha` does not exist, this file will not compile → RED.
|
|
*/
|
|
static void test_cluster_t_has_ha_field(void)
|
|
{
|
|
cluster_t c;
|
|
memset(&c, 0, sizeof(c));
|
|
|
|
/* Access the field — compile error if absent */
|
|
(void)atomic_load_explicit(&c.ha.role, memory_order_acquire);
|
|
|
|
printf("test_cluster_t_has_ha_field: PASS\n");
|
|
}
|
|
|
|
/*
|
|
* test_ha_promote_to_primary_uses_c_ha:
|
|
* After ha_init(&c.ha, ...) and calling ha_promote_to_primary(&c), the role
|
|
* stored in c.ha must be CTRL_PRIMARY (not still CTRL_STANDBY).
|
|
* This would fail if ha_promote_to_primary() is still the (void)c stub.
|
|
*/
|
|
static void test_ha_promote_to_primary_uses_c_ha(void)
|
|
{
|
|
cluster_t c;
|
|
memset(&c, 0, sizeof(c));
|
|
|
|
/* Manually initialise only the HA field — no cluster_init() required */
|
|
TEST_ASSERT_EQUAL_INT(0, ha_init(&c.ha, &g_cfg, 0 /* my_priority = 0 → primary */));
|
|
TEST_ASSERT_EQUAL_INT(CTRL_STANDBY,
|
|
(int)atomic_load_explicit(&c.ha.role, memory_order_acquire));
|
|
|
|
/* c.hooksys is zeroed — hooks_registered guard prevents xce_add_hook() call */
|
|
ha_promote_to_primary(&c);
|
|
|
|
TEST_ASSERT_EQUAL_INT(CTRL_PRIMARY,
|
|
(int)atomic_load_explicit(&c.ha.role, memory_order_acquire));
|
|
|
|
ha_fini(&c.ha);
|
|
printf("test_ha_promote_to_primary_uses_c_ha: PASS\n");
|
|
}
|
|
|
|
/*
|
|
* test_ha_demote_to_standby_uses_c_ha:
|
|
* Promote then demote. Role must end as CTRL_STANDBY.
|
|
*/
|
|
static void test_ha_demote_to_standby_uses_c_ha(void)
|
|
{
|
|
cluster_t c;
|
|
memset(&c, 0, sizeof(c));
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, ha_init(&c.ha, &g_cfg, 0));
|
|
|
|
ha_promote_to_primary(&c);
|
|
TEST_ASSERT_EQUAL_INT(CTRL_PRIMARY,
|
|
(int)atomic_load_explicit(&c.ha.role, memory_order_acquire));
|
|
|
|
ha_demote_to_standby(&c);
|
|
TEST_ASSERT_EQUAL_INT(CTRL_STANDBY,
|
|
(int)atomic_load_explicit(&c.ha.role, memory_order_acquire));
|
|
|
|
ha_fini(&c.ha);
|
|
printf("test_ha_demote_to_standby_uses_c_ha: PASS\n");
|
|
}
|
|
|
|
/*
|
|
* test_ha_promote_increments_term_via_cluster:
|
|
* Each ha_promote_to_primary() call must increment the election term stored
|
|
* in c.ha.term, because ha_do_promote() is called inside.
|
|
*/
|
|
static void test_ha_promote_increments_term_via_cluster(void)
|
|
{
|
|
cluster_t c;
|
|
memset(&c, 0, sizeof(c));
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, ha_init(&c.ha, &g_cfg, 0));
|
|
uint64_t term0 = atomic_load_explicit(&c.ha.term, memory_order_acquire);
|
|
|
|
ha_promote_to_primary(&c);
|
|
uint64_t term1 = atomic_load_explicit(&c.ha.term, memory_order_acquire);
|
|
TEST_ASSERT_TRUE(term1 > term0);
|
|
|
|
ha_fini(&c.ha);
|
|
printf("test_ha_promote_increments_term_via_cluster: PASS\n");
|
|
}
|
|
|
|
/*
|
|
* test_hooks_registered_guard_prevents_double_register:
|
|
* When hooksys is zeroed (no real hook system) and hooks_registered is 0,
|
|
* ha_promote_to_primary() must set hooks_registered = 1 so a second
|
|
* call skips the registration path.
|
|
*/
|
|
static void test_hooks_registered_guard_prevents_double_register(void)
|
|
{
|
|
cluster_t c;
|
|
memset(&c, 0, sizeof(c));
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, ha_init(&c.ha, &g_cfg, 0));
|
|
TEST_ASSERT_EQUAL_INT(0, c.ha.hooks_registered);
|
|
|
|
ha_promote_to_primary(&c);
|
|
/* hooks_registered must now be 1 (idempotency guard set) */
|
|
TEST_ASSERT_EQUAL_INT(1, c.ha.hooks_registered);
|
|
|
|
/* Second promote: must not crash even with zeroed hooksys */
|
|
ha_promote_to_primary(&c);
|
|
TEST_ASSERT_EQUAL_INT(1, c.ha.hooks_registered);
|
|
|
|
ha_fini(&c.ha);
|
|
printf("test_hooks_registered_guard_prevents_double_register: PASS\n");
|
|
}
|
|
|
|
void test_ha_cluster(void)
|
|
{
|
|
UnitySetTestFile(__FILE__);
|
|
RUN_TEST(test_cluster_t_has_ha_field);
|
|
RUN_TEST(test_ha_promote_to_primary_uses_c_ha);
|
|
RUN_TEST(test_ha_demote_to_standby_uses_c_ha);
|
|
RUN_TEST(test_ha_promote_increments_term_via_cluster);
|
|
RUN_TEST(test_hooks_registered_guard_prevents_double_register);
|
|
}
|