/* Чистое чтение настроек, независимость копий и различие validation/apply-отказов. */ #include #include #include "../src/chat/chat_setting.h" #include "../src/utun_instance.h" #include "../lib/debug_config.h" #include "../lib/mem.h" static int fail_apply; static unsigned applied; extern int __real_chat_local_apply_settings(struct UTUN_INSTANCE*); int __wrap_chat_local_apply_settings(struct UTUN_INSTANCE* inst) { applied++; if (fail_apply) { fail_apply = 0; return -1; } return __real_chat_local_apply_settings(inst); } static void test_reads(void) { struct chat_setting_state state = {0}; assert(!chat_setting_get_state(&state, "stream_subblocks")); assert(chat_setting_get_int_state(&state, "stream_subblocks", 77) == 77); assert(!strcmp(chat_setting_get_state(&state, "call_video_bitrate_range_kbps"), "300:4000")); assert(!chat_setting_set_state(&state, "stream_subblocks", "8")); struct chat_setting_state before = state; const char* blocks = chat_setting_get_state(&state, "stream_subblocks"); const char* bitrate = chat_setting_get_state(&state, "radio_opus_bitrate"); assert(!strcmp(blocks, "8") && !strcmp(bitrate, "32") && blocks != bitrate); assert(!memcmp(&before, &state, sizeof(state))); assert(!chat_setting_get_state(&state, NULL) && !chat_setting_get_state(&state, "unknown")); assert(chat_setting_get_int_state(&state, NULL, 77) == 77); assert(chat_setting_get_int_state(&state, "member_files_path", 77) == 77); assert(!chat_setting_get_state(NULL, "stream_subblocks") && chat_setting_get_int_state(NULL, NULL, 77) == 77); assert(!chat_setting_set_state(&state, "radio_opus_bitrate", "48") && !strcmp(blocks, "8") && !strcmp(bitrate, "48")); struct chat_setting_state copy = state; assert(!chat_setting_set_state(©, "stream_subblocks", "12")); assert(!strcmp(blocks, "8") && !strcmp(chat_setting_get_state(©, "stream_subblocks"), "12")); DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "PASS settings reads: const state unchanged, distinct stable values, NULL/type defaults, independent copied state"); } static void test_validation(void) { const char* names[] = {"unknown", "stream_subblocks", "stream_subblocks", "stream_subblocks", "stream_subblocks", "stream_subblocks", "stream_subblocks", "local_invites_enabled", "call_video_bitrate_range_kbps", "", NULL}; const char* values[] = {"1", "0", "256", "", " ", "8x", "99999999999999999999999999999", "maybe", "4000:300", "1", "1"}; for (unsigned initialized = 0; initialized < 2; initialized++) { struct chat_setting_state state = {0}; if (initialized) assert(!chat_setting_set_state(&state, "stream_subblocks", "9")); const struct chat_setting_state before = state; for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); i++) { assert(chat_setting_set_state(&state, names[i], values[i]) == (i == 0 ? -1 : -2)); assert(!memcmp(&before, &state, sizeof(state))); } assert(chat_setting_set_state(&state, "stream_subblocks", NULL) == -2); assert(!memcmp(&before, &state, sizeof(state))); char oversized[257]; memset(oversized, 'a', sizeof(oversized)); oversized[256] = 0; assert(chat_setting_set_state(&state, "member_files_path", oversized) == -2); assert(!memcmp(&before, &state, sizeof(state))); } struct chat_setting_state state = {0}; const char* booleans[] = {"0", "no", "false", "1", "yes", "true"}; for (size_t i = 0; i < sizeof(booleans) / sizeof(booleans[0]); i++) { assert(!chat_setting_set_state(&state, "local_invites_enabled", booleans[i])); assert(chat_setting_get_int_state(&state, "local_invites_enabled", 77) == (i >= 3)); } char maximum[256]; memset(maximum, 'b', sizeof(maximum)); maximum[255] = 0; assert(!chat_setting_set_state(&state, "member_files_path", maximum)); assert(!strcmp(chat_setting_get_state(&state, "member_files_path"), maximum)); assert(!chat_setting_set_state(&state, "member_files_path", "")); assert(!strcmp(chat_setting_get_state(&state, "member_files_path"), "")); assert(!chat_setting_set_state(&state, "radio_level_on_dbfs", "-90")); assert(!strcmp(chat_setting_get_state(&state, "radio_level_on_dbfs"), "-90")); DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "PASS settings validation: invalid values preserve zero/initialized state, bool aliases, signed range,255-byte path and clearing"); } static void test_runtime_apply(void) { struct UTUN_INSTANCE inst; memset(&inst, 0, sizeof(inst)); fail_apply = 1; assert(chat_setting_set(&inst, "local_broadcasts_enabled", "0") == -3 && applied == 1 && !fail_apply); assert(chat_setting_get_int(&inst, "local_broadcasts_enabled", 77) == 0); assert(!chat_setting_set(&inst, "local_broadcasts_enabled", "0") && applied == 2); assert(chat_setting_set(&inst, "local_broadcasts_enabled", "invalid") == -2 && applied == 2); assert(!chat_setting_set(&inst, "local_invites_enabled", "1") && applied == 3); assert(chat_setting_set(NULL, "local_invites_enabled", "1") == -2); assert(!chat_setting_get(NULL, "stream_subblocks") && chat_setting_get_int(NULL, NULL, 77) == 77); DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "PASS settings apply: desired value survives LAN failure, distinct error, identical retry reapplies, validation does not apply"); } int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO); size_t baseline = u_get_allocated_count(); test_reads(); test_validation(); test_runtime_apply(); assert(u_get_allocated_count() == baseline); return 0; }