From 9a06d19984ab6e7ac9545ba7e5d300abf860fa46 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 6 Oct 2026 11:33:57 +0200 Subject: [PATCH] Test incremental counts, empty replacements and startup-only scans --- tests/test_member_files.c | 75 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/tests/test_member_files.c b/tests/test_member_files.c index 4b066360..d10fbf14 100644 --- a/tests/test_member_files.c +++ b/tests/test_member_files.c @@ -177,6 +177,79 @@ static void quota_commit(struct mf_storage* s, uint64_t group) { q.data_len = 0; run(s, group, q.owner, MF_COMMIT, MF_OK); } +static void check_counts(struct mf_storage* s, uint64_t group, uint64_t owner, uint64_t files, uint64_t directories) { + struct mf_message request = { .command = MF_QUOTA, .kind = MF_REQUEST, .owner = owner, .request_id = ++sequence }, response; + mf_storage_execute(s, group, owner, &request, &response); + EXPECT(response.status == MF_OK && response.data_len == MF_QUOTA_BYTES, "count snapshot response"); + EXPECT(mf_get64(response.data + 24) == files && mf_get64(response.data + 32) == directories, "file/directory counters"); +} + +static void count_mutation(struct mf_storage* s, uint64_t group, uint64_t owner, const char* path, + const char* target, int command, int status) { + memset(&q, 0, sizeof(q)); q.owner = owner; snprintf(q.path, sizeof(q.path), "%s", path); + if (command != MF_MKDIR) { run(s, group, owner, MF_STAT, MF_OK); memcpy(q.version, r.version, 16); } + if (target) snprintf(q.target, sizeof(q.target), "%s", target); + run(s, group, owner, command, status); +} + +static void count_test(const char* directory) { + char root[1200]; snprintf(root, sizeof(root), "%s/counts", directory); + struct mf_storage* s = mf_storage_create(root, 40, 200000); + EXPECT(s && !mf_storage_initialize(s), "count storage startup"); if (!s) return; + check_counts(s, 10, 101, 0, 0); + count_mutation(s, 10, 101, "folder", NULL, MF_MKDIR, MF_OK); + check_counts(s, 20, 101, 0, 1); + mf_storage_execute(s, 10, 101, &q, &r); EXPECT(r.status == MF_OK, "mkdir replay"); + run(s, 10, 101, MF_MKDIR, MF_EXISTS); check_counts(s, 10, 101, 0, 1); + count_mutation(s, 10, 101, "folder/sub", NULL, MF_MKDIR, MF_OK); + check_counts(s, 10, 101, 0, 2); + quota_begin(s, 10, 101, "folder/zero", 0, 61, NULL, MF_OK); + check_counts(s, 10, 101, 0, 2); quota_commit(s, 10); check_counts(s, 10, 101, 1, 2); + uint8_t version[16]; memcpy(version, r.version, 16); + quota_begin(s, 10, 101, "folder/zero", 0, 62, version, MF_OK); quota_commit(s, 10); + memcpy(version, r.version, 16); check_counts(s, 10, 101, 1, 2); + quota_begin(s, 10, 101, "folder/zero", 2, 63, version, MF_OK); quota_commit(s, 10); + mf_storage_execute(s, 10, 101, &q, &r); EXPECT(r.status == MF_OK, "replacement replay"); + check_counts(s, 20, 101, 1, 2); + quota_begin(s, 20, 101, "other", 3, 64, NULL, MF_OK); quota_commit(s, 20); + check_counts(s, 10, 101, 2, 2); check_quota(s, 10, 101, 5, 0, 40); + quota_begin(s, 10, 202, "other-user", 3, 65, NULL, MF_OK); quota_commit(s, 10); + check_counts(s, 10, 101, 2, 2); check_counts(s, 10, 202, 1, 0); + count_mutation(s, 10, 101, "folder", "moved", MF_RENAME, MF_OK); + check_counts(s, 10, 101, 2, 2); + count_mutation(s, 10, 101, "moved", NULL, MF_REMOVE, MF_IO_ERROR); + check_counts(s, 10, 101, 2, 2); + count_mutation(s, 10, 101, "moved/zero", NULL, MF_REMOVE, MF_OK); + mf_storage_execute(s, 10, 101, &q, &r); EXPECT(r.status == MF_OK, "file remove replay"); + check_counts(s, 20, 101, 1, 2); check_quota(s, 20, 101, 3, 0, 40); + count_mutation(s, 10, 101, "moved/sub", NULL, MF_REMOVE, MF_OK); + mf_storage_execute(s, 10, 101, &q, &r); EXPECT(r.status == MF_OK, "directory remove replay"); + check_counts(s, 20, 101, 1, 1); + count_mutation(s, 10, 101, "moved", NULL, MF_REMOVE, MF_OK); check_counts(s, 10, 101, 1, 0); + quota_begin(s, 10, 101, "aborted", 4, 66, NULL, MF_OK); + check_counts(s, 10, 101, 1, 0); run(s, 10, 101, MF_ABORT, MF_OK); check_counts(s, 10, 101, 1, 0); + quota_begin(s, 10, 101, "expired", 4, 67, NULL, MF_OK); + mf_storage_sweep(s, get_time_tb() + 200001); check_counts(s, 20, 101, 1, 0); + // Внешние изменения появляются в статистике только после нового старта, не после мутации/QUOTA. + char external[1400], folder[1400]; + snprintf(external, sizeof(external), "%s/10/101/external.bin", root); + FILE* file = fopen(external, "wb"); EXPECT(file != NULL, "external file fixture"); + if (file) { fputs("extra", file); fclose(file); } + snprintf(folder, sizeof(folder), "10/101/external-dir"); + struct mf_disk_root disk; EXPECT(!mf_disk_root_open(&disk, root), "external directory fixture root"); + int created = 0; EXPECT(!mf_disk_mkdir(&disk, folder, &created) && created, "mkdir reports creation"); + created = 1; EXPECT(mf_disk_mkdir(&disk, folder, &created) == MF_EXISTS && !created, "existing mkdir reports no creation"); + mf_disk_root_close(&disk); + count_mutation(s, 10, 101, "api-dir", NULL, MF_MKDIR, MF_OK); + check_counts(s, 10, 101, 1, 1); check_quota(s, 20, 101, 3, 0, 40); + quota_begin(s, 10, 101, "api-file", 1, 68, NULL, MF_OK); quota_commit(s, 10); + check_counts(s, 20, 101, 2, 1); check_quota(s, 10, 101, 4, 0, 40); + mf_storage_destroy(s); s = mf_storage_create(root, 40, 200000); + EXPECT(s && !mf_storage_initialize(s), "count restart scan"); if (!s) return; + check_counts(s, 20, 101, 3, 2); check_quota(s, 10, 101, 9, 0, 40); + check_counts(s, 10, 202, 1, 0); mf_storage_destroy(s); +} + static void quota_test(const char* directory) { char root[1200]; snprintf(root, sizeof(root), "%s/quota", directory); struct mf_storage* s = mf_storage_create(root, 40, 200000); @@ -273,7 +346,7 @@ int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_MEDIA, DEBUG_LEVEL_DEBUG); char directory[1024] = "/tmp/utun_member_files_XXXXXX"; if (test_mkdtemp(directory)) return 1; - protocol_test(); storage_test(directory); quota_test(directory); quota_scan_failure_test(directory); worker_test(); + protocol_test(); storage_test(directory); quota_test(directory); count_test(directory); quota_scan_failure_test(directory); worker_test(); #ifndef _WIN32 large_offset_test(directory); #endif