#include #include #include #include #include "kv.h" #include "kvmgr.h" #include "utils.h" typedef struct { char* key; int data; } kv_data_t; static void test_kvmgr_create(void) { kvmgr_params_t params = { .key_type = KVMGR_KEY_STRING, .nslots = 4, .delta = 4, .datakiller = NULL }; var kvmgr = makeInstance(&xkvmgr_type, ¶ms); TEST_ASSERT_TRUE(kvmgr != NULL); destroy(kvmgr); } typedef struct { char* name; uint16_t age; } user_t; static void my_user_killer(void* data) { user_t* u = (user_t*)data; if (u == NULL) { return; } FREEUP(u->name); FREEUP(u); } static void test_kvmgr_add(void) { kvmgr_params_t params = { .key_type = KVMGR_KEY_STRING, .nslots = 4, .delta = 4, .datakiller = my_user_killer }; var kvmgr = makeInstance(&xkvmgr_type, ¶ms); user_t* u = NULL; int rc = 0; void* data = NULL; TEST_ASSERT_TRUE(kvmgr != NULL); u = calloc(1, sizeof(*u)); TEST_ASSERT_TRUE(u != NULL); u->name = strdup("tester1"); TEST_ASSERT_TRUE(u->name != NULL); u->age = 32; rc = key_add(kvmgr, (void*)u->name, (void*)u); TEST_ASSERT_TRUE(rc >= 0); rc = key_find(kvmgr, (void*)u->name, &data); TEST_ASSERT_TRUE(rc >= 0); TEST_ASSERT_TRUE(data == u); destroy(kvmgr); } static void test_kvmgr_iter(void) { kvmgr_params_t params = { .key_type = KVMGR_KEY_INT, .nslots = 4, .delta = 4, .datakiller = NULL }; var kvmgr = makeInstance(&xkvmgr_type, ¶ms); int rc = 0; int i; iterator_t it; TEST_ASSERT_TRUE(kvmgr != NULL); for (i = 0; i < 10; i++) { rc = key_add(kvmgr, (void*)(long)i, (void*)(long)(i * i)); TEST_ASSERT_TRUE(rc >= 0); } rc = iter_make(&it, kvmgr); TEST_ASSERT_TRUE(rc >= 0); i = 0; for (;;) { void* data; rc = iter_next(&it, &data); TEST_ASSERT_TRUE(rc >= 0); if (rc == RTTI_ITER_NO_DATA) { break; } TEST_ASSERT_TRUE((int)(long)data == i * i); i++; } TEST_ASSERT_TRUE(i == 10); iter_destroy(&it); destroy(kvmgr); } /* admin-plane-gaps 断点③: xkvmgr del_key / vclear wiring. key_del unlinks * the kvtab entry AND clears the backing varray slot; a subsequent find * misses, sibling entries stay intact, and a double delete propagates the * miss. With a datakiller wired, clearing the slot frees the value (the * same path the controller's jobmgr purge relies on, except there the * killer is a no-op and the caller dereferences the job itself). */ static void test_kvmgr_del_key(void) { kvmgr_params_t params = { .key_type = KVMGR_KEY_STRING, .nslots = 4, .delta = 4, .datakiller = my_user_killer }; var kvmgr = makeInstance(&xkvmgr_type, ¶ms); char key1[] = "user1"; char key2[] = "user2"; user_t* u1 = NULL; user_t* u2 = NULL; int rc = 0; void* data = NULL; TEST_ASSERT_TRUE(kvmgr != NULL); u1 = calloc(1, sizeof(*u1)); u2 = calloc(1, sizeof(*u2)); TEST_ASSERT_TRUE(u1 != NULL && u2 != NULL); u1->name = strdup(key1); u2->name = strdup(key2); TEST_ASSERT_TRUE(u1->name != NULL && u2->name != NULL); u1->age = 30; u2->age = 40; rc = key_add(kvmgr, (void*)key1, (void*)u1); TEST_ASSERT_TRUE(rc >= 0); rc = key_add(kvmgr, (void*)key2, (void*)u2); TEST_ASSERT_TRUE(rc >= 0); /* Delete the first key. With a datakiller wired, the cleared slot * frees u1 — so only the stack key buffers are used from here on. */ rc = key_del(kvmgr, (void*)key1); TEST_ASSERT_EQUAL_INT(1, rc); /* The deleted key no longer resolves. */ rc = key_find(kvmgr, (void*)key1, &data); TEST_ASSERT_TRUE(rc < 0); /* The sibling entry is untouched. */ rc = key_find(kvmgr, (void*)key2, &data); TEST_ASSERT_TRUE(rc >= 0); TEST_ASSERT_TRUE(data == u2); /* Double delete misses — xkvmgr_del_key propagates key_find's * -ENOENT for an absent key (same contract as xkvmgr_find). */ rc = key_del(kvmgr, (void*)key1); TEST_ASSERT_EQUAL_INT(-ENOENT, rc); destroy(kvmgr); } static void test_kvmgr_del_key_int(void) { kvmgr_params_t params = { .key_type = KVMGR_KEY_INT, .nslots = 4, .delta = 4, .datakiller = NULL }; var kvmgr = makeInstance(&xkvmgr_type, ¶ms); int rc = 0; void* data = NULL; TEST_ASSERT_TRUE(kvmgr != NULL); rc = key_add(kvmgr, (void*)(long)101, (void*)(long)10100); TEST_ASSERT_TRUE(rc >= 0); rc = key_add(kvmgr, (void*)(long)102, (void*)(long)10200); TEST_ASSERT_TRUE(rc >= 0); rc = key_del(kvmgr, (void*)(long)101); TEST_ASSERT_EQUAL_INT(1, rc); rc = key_find(kvmgr, (void*)(long)101, &data); TEST_ASSERT_TRUE(rc < 0); /* The int-keyed table's idx scalar was cleared with the entry — the * slot the deleted key occupied now stores nothing. */ rc = key_find(kvmgr, (void*)(long)102, &data); TEST_ASSERT_TRUE(rc >= 0); TEST_ASSERT_EQUAL_INT(10200, (int)(long)data); /* Delete the sibling too, then re-add: the freed slot must be * reusable (vclear reset it in place). */ rc = key_del(kvmgr, (void*)(long)102); TEST_ASSERT_EQUAL_INT(1, rc); rc = key_add(kvmgr, (void*)(long)103, (void*)(long)10300); TEST_ASSERT_TRUE(rc >= 0); rc = key_find(kvmgr, (void*)(long)103, &data); TEST_ASSERT_TRUE(rc >= 0); TEST_ASSERT_EQUAL_INT(10300, (int)(long)data); destroy(kvmgr); } void test_kvmgr(void) { printf("\n>> kvmgr test\n"); RUN_TEST(test_kvmgr_create); RUN_TEST(test_kvmgr_add); RUN_TEST(test_kvmgr_iter); RUN_TEST(test_kvmgr_del_key); RUN_TEST(test_kvmgr_del_key_int); }