test(chat): add member SQL and committed radio state observers
This commit is contained in:
1 parent
1bb824fb8a
commit
2504dd0b40
2 files changed
+103
-3
No files matched your search
+3
-1
@@ -328,7 +328,9 @@ test_group_avatar_sql_LDFLAGS = -Wl,--wrap=sqlite3_bind_blob -Wl,--wrap=sqlite3_
|
||||
-Wl,--wrap=sc_ed25519_sign
|
||||
test_chat_member_SOURCES = test_chat_member.c
|
||||
test_chat_member_LDADD = $(LIBUTUN) $(COMMON_LIBS)
|
||||
test_chat_member_LDFLAGS = -Wl,--wrap=sqlite3_finalize
|
||||
test_chat_member_LDFLAGS = -Wl,--wrap=sqlite3_finalize -Wl,--wrap=sqlite3_prepare_v2 -Wl,--wrap=sqlite3_bind_int64 \
|
||||
-Wl,--wrap=sqlite3_bind_int -Wl,--wrap=sqlite3_step -Wl,--wrap=sqlite3_changes -Wl,--wrap=sqlite3_exec \
|
||||
-Wl,--wrap=u_malloc_impl -Wl,--wrap=u_realloc_impl
|
||||
else
|
||||
test_socket_monitor_LDFLAGS += -Wl,--wrap=read
|
||||
endif
|
||||
|
||||
+100
-2
@@ -5,14 +5,112 @@
|
||||
#include <assert.h>
|
||||
|
||||
static int fail_query;
|
||||
enum fault { F_NONE, F_PREPARE, F_BIND, F_STEP, F_CHANGES, F_FINALIZE, F_EXEC, F_ALLOCATE, F_REALLOCATE };
|
||||
static enum fault fault;
|
||||
static const char* target;
|
||||
static int bind_index, last_target, fail_rollback, arm_after_commit;
|
||||
static sqlite3* reader;
|
||||
static uint64_t group_id;
|
||||
static char channel[32];
|
||||
static unsigned member_events, mute_events, list_events;
|
||||
static uint8_t detail[CHAT_MEMBER_DETAIL_MAX_SIZE];
|
||||
static size_t detail_size;
|
||||
|
||||
static int matches(const char* sql) { return target && !strncmp(sql, target, strlen(target)); }
|
||||
static int take(enum fault point, const char* sql, int index) {
|
||||
if (fault != point || (sql && !matches(sql)) || (point == F_BIND && index != bind_index)) return 0;
|
||||
fault = F_NONE; DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "member test: fault stage=%d index=%d target=%s", point, index, target); return 1;
|
||||
}
|
||||
extern int __real_sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**);
|
||||
int __wrap_sqlite3_prepare_v2(sqlite3* db, const char* sql, int size, sqlite3_stmt** st, const char** tail) {
|
||||
if (take(F_PREPARE, sql, 0)) { *st = NULL; return SQLITE_NOMEM; }
|
||||
return __real_sqlite3_prepare_v2(db, sql, size, st, tail);
|
||||
}
|
||||
extern int __real_sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
|
||||
int __wrap_sqlite3_bind_int64(sqlite3_stmt* st, int index, sqlite3_int64 value) {
|
||||
if (take(F_BIND, sqlite3_sql(st), index)) return SQLITE_NOMEM;
|
||||
return __real_sqlite3_bind_int64(st, index, value);
|
||||
}
|
||||
extern int __real_sqlite3_bind_int(sqlite3_stmt*, int, int);
|
||||
int __wrap_sqlite3_bind_int(sqlite3_stmt* st, int index, int value) {
|
||||
if (take(F_BIND, sqlite3_sql(st), index)) return SQLITE_NOMEM;
|
||||
return __real_sqlite3_bind_int(st, index, value);
|
||||
}
|
||||
extern int __real_sqlite3_step(sqlite3_stmt*);
|
||||
int __wrap_sqlite3_step(sqlite3_stmt* st) {
|
||||
last_target = matches(sqlite3_sql(st)); if (take(F_STEP, sqlite3_sql(st), 0)) return SQLITE_IOERR;
|
||||
return __real_sqlite3_step(st);
|
||||
}
|
||||
extern int __real_sqlite3_changes(sqlite3*);
|
||||
int __wrap_sqlite3_changes(sqlite3* db) {
|
||||
if (last_target && take(F_CHANGES, NULL, 0)) return 0;
|
||||
return __real_sqlite3_changes(db);
|
||||
}
|
||||
extern int __real_sqlite3_exec(sqlite3*, const char*, int (*)(void*, int, char**, char**), void*, char**);
|
||||
int __wrap_sqlite3_exec(sqlite3* db, const char* sql, int (*cb)(void*, int, char**, char**), void* arg, char** error) {
|
||||
if (fail_rollback && !strcmp(sql, "ROLLBACK")) { fail_rollback = 0; return SQLITE_IOERR; }
|
||||
if (take(F_EXEC, sql, 0)) return SQLITE_IOERR;
|
||||
return __real_sqlite3_exec(db, sql, cb, arg, error);
|
||||
}
|
||||
extern void* __real_u_malloc_impl(uint32_t, const char*);
|
||||
void* __wrap_u_malloc_impl(uint32_t size, const char* location) {
|
||||
if (fault == F_ALLOCATE && strstr(location, target)) { fault = F_NONE; return NULL; }
|
||||
return __real_u_malloc_impl(size, location);
|
||||
}
|
||||
extern void* __real_u_realloc_impl(void*, uint32_t, const char*);
|
||||
void* __wrap_u_realloc_impl(void* ptr, uint32_t size, const char* location) {
|
||||
if (fault == F_REALLOCATE && strstr(location, target)) { fault = F_NONE; return NULL; }
|
||||
return __real_u_realloc_impl(ptr, size, location);
|
||||
}
|
||||
extern int __real_sqlite3_finalize(sqlite3_stmt*);
|
||||
int __wrap_sqlite3_finalize(sqlite3_stmt* st) {
|
||||
const char* target = "SELECT p.node_id";
|
||||
int fail = fail_query && st && !strncmp(sqlite3_sql(st), target, strlen(target));
|
||||
const char* snapshot = "SELECT p.node_id";
|
||||
int fail = fail_query && st && !strncmp(sqlite3_sql(st), snapshot, strlen(snapshot));
|
||||
if (fail) { fail_query = 0; DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "member test: injected snapshot finalize failure"); }
|
||||
if (st && take(F_FINALIZE, sqlite3_sql(st), 0)) fail = 1;
|
||||
int rc = __real_sqlite3_finalize(st); return fail ? SQLITE_IOERR : rc;
|
||||
}
|
||||
|
||||
static int stored_mute(void) {
|
||||
char sql[160]; snprintf(sql, sizeof(sql), "SELECT radio_muted FROM peers_%s WHERE node_id=7", channel); sqlite3_stmt* st = NULL;
|
||||
assert(sqlite3_prepare_v2(reader, sql, -1, &st, NULL) == SQLITE_OK && sqlite3_step(st) == SQLITE_ROW);
|
||||
int value = sqlite3_column_int(st, 0); assert(sqlite3_finalize(st) == SQLITE_OK); return value;
|
||||
}
|
||||
|
||||
static void event(struct UTUN_INSTANCE* inst, int type, const uint8_t* data, int size) {
|
||||
if (type == CHAT_EVT_MEMBER_DETAIL) {
|
||||
assert(size >= 0 && (size_t)size <= sizeof(detail)); memcpy(detail, data, (size_t)size); detail_size = (size_t)size; return;
|
||||
}
|
||||
if (type == CHAT_EVT_MEMBER_LIST) {
|
||||
assert(size >= data[0] + 3); uint16_t count; memcpy(&count, data + data[0] + 1, 2);
|
||||
assert((size_t)size == data[0] + 3 + (size_t)count * CHAT_MEMBER_DISPLAY_SIZE); list_events++; return;
|
||||
}
|
||||
if (type != CHAT_EVT_MEMBER_UPDATED) return;
|
||||
assert(sqlite3_get_autocommit(inst->topo_sqlite_db) && size == data[0] + 2 + CHAT_MEMBER_DISPLAY_SIZE);
|
||||
const uint8_t* wire = data + data[0] + 2;
|
||||
if (mf_get64(wire) == 7) assert(!!(wire[10] & CHAT_MEMBER_FLAG_RADIO_MUTED) == stored_mute());
|
||||
member_events++;
|
||||
}
|
||||
|
||||
static void mute(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t node, int muted, void* arg) {
|
||||
(void)arg; assert(group == group_id && node == 7 && sqlite3_get_autocommit(inst->topo_sqlite_db));
|
||||
assert(stored_mute() == muted && radio_member_muted(inst, group, node) == muted); mute_events++;
|
||||
if (arm_after_commit) { arm_after_commit = 0; target = "SELECT p.node_id"; fault = F_FINALIZE; }
|
||||
}
|
||||
|
||||
static void refused_mute(struct UTUN_INSTANCE* inst) {
|
||||
int before = stored_mute(); unsigned display = member_events, audio = mute_events;
|
||||
assert(chat_core_toggle_member_radio_mute(inst, channel, 7) < 0 && fault == F_NONE);
|
||||
assert(sqlite3_get_autocommit(inst->topo_sqlite_db) && stored_mute() == before && radio_member_muted(inst, group_id, 7) == before);
|
||||
assert(display == member_events && audio == mute_events);
|
||||
}
|
||||
|
||||
static void retry_mute(struct UTUN_INSTANCE* inst) {
|
||||
int before = stored_mute(); unsigned display = member_events, audio = mute_events;
|
||||
assert(!chat_core_toggle_member_radio_mute(inst, channel, 7));
|
||||
assert(stored_mute() != before && member_events == display + 1 && mute_events == audio + 1);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_category_level(DEBUG_CATEGORY_CHAT_SYNC, DEBUG_LEVEL_INFO);
|
||||
size_t baseline = u_get_allocated_count(); utun_instance_set_tun_init_enabled(0);
|
||||
|
||||
Reference in new issue
Block a user