#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "config.h" #include "persist.h" /* ------------------------------------------------------------------ */ /* Test-local helpers */ /* ------------------------------------------------------------------ */ static char g_tmpdir[256]; /* Count *.wal files in a directory */ static int count_wal_files(const char* dir_path) { DIR* dir = opendir(dir_path); if (!dir) return 0; int count = 0; struct dirent* ent; while ((ent = readdir(dir)) != NULL) { size_t len = strlen(ent->d_name); if (len > 4 && strcmp(ent->d_name + len - 4, ".wal") == 0) count++; } closedir(dir); return count; } /* Allocate and initialise a persist_t for testing */ static persist_t* make_test_persist(void) { strcpy(g_tmpdir, "/tmp/xce_test_XXXXXX"); if (!mkdtemp(g_tmpdir)) return NULL; persist_t* p = calloc(1, sizeof(persist_t)); if (!p) return NULL; persist_config_t cfg = persist_config_defaults(); cfg.wal_sync_ms = 5000; /* slow background fsync — don't want it firing during tests */ persist_set_workdir(p, g_tmpdir, "ctrl-test"); if (persist_init(p, &cfg, NULL) != 0) { free(p); return NULL; } return p; } static void cleanup_test_persist(persist_t* p) { persist_stop(p); persist_fini(p); free(p); /* tmpdir is left on disk — /tmp is ephemeral */ } /* ------------------------------------------------------------------ */ /* Tests */ /* ------------------------------------------------------------------ */ static void test_persist_init_creates_directories(void) { persist_t* p = make_test_persist(); TEST_ASSERT_NOT_NULL(p); char wal_path[512], snap_path[512]; snprintf(wal_path, sizeof(wal_path), "%s/ctrl-test/data/wal", g_tmpdir); snprintf(snap_path, sizeof(snap_path), "%s/ctrl-test/data/snap", g_tmpdir); struct stat st; TEST_ASSERT_EQUAL_INT(0, stat(wal_path, &st)); TEST_ASSERT_TRUE(S_ISDIR(st.st_mode)); TEST_ASSERT_EQUAL_INT(0, stat(snap_path, &st)); TEST_ASSERT_TRUE(S_ISDIR(st.st_mode)); cleanup_test_persist(p); printf("test_persist_init_creates_directories: PASS\n"); } static void test_persist_wal_async_writes_entry(void) { persist_t* p = make_test_persist(); TEST_ASSERT_NOT_NULL(p); TEST_ASSERT_EQUAL_INT(0, persist_start(p)); const char payload[] = "test_job_submit_payload"; int rc = persist_wal_async(p, WAL_JOB_UPDATE, payload, (uint32_t)strlen(payload)); TEST_ASSERT_EQUAL_INT(0, rc); /* Force sync so WAL is on disk */ TEST_ASSERT_EQUAL_INT(0, persist_flush(p)); /* WAL directory must contain at least one .wal segment file */ char wal_path[512]; snprintf(wal_path, sizeof(wal_path), "%s/ctrl-test/data/wal", g_tmpdir); int found = count_wal_files(wal_path); TEST_ASSERT_TRUE(found >= 1); cleanup_test_persist(p); printf("test_persist_wal_async_writes_entry: PASS\n"); } static void test_wal_checkpoint_type_rejected(void) { persist_t* p = make_test_persist(); TEST_ASSERT_NOT_NULL(p); TEST_ASSERT_EQUAL_INT(0, persist_start(p)); /* WAL_CHECKPOINT must never be passed to persist_wal_async — * it must return a non-zero error code (not abort/assert). */ int rc = persist_wal_async(p, WAL_CHECKPOINT, NULL, 0); TEST_ASSERT_TRUE(rc != 0); cleanup_test_persist(p); printf("test_wal_checkpoint_type_rejected: PASS\n"); } /* ------------------------------------------------------------------ */ /* Suite entry point (called from controller_test.c) */ /* ------------------------------------------------------------------ */ void test_persist_wal(void) { printf("\n>> persist WAL writer test\n\n"); RUN_TEST(test_persist_init_creates_directories); RUN_TEST(test_persist_wal_async_writes_entry); RUN_TEST(test_wal_checkpoint_type_rejected); }