diff --git a/tests/Makefile.am b/tests/Makefile.am index be2a36bb..4392021a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1320,6 +1320,8 @@ test_tcp_io_flush_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) test_chat_poll_SOURCES = test_chat_poll.c test_chat_poll_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/chat -I$(top_srcdir)/lib test_chat_poll_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) +test_chat_poll_LDFLAGS = -Wl,--wrap=sqlite3_prepare_v2 -Wl,--wrap=sqlite3_bind_blob -Wl,--wrap=sqlite3_bind_text \ + -Wl,--wrap=sqlite3_step -Wl,--wrap=sqlite3_finalize test_chat_poll_network_SOURCES = test_chat_poll_network.c test_chat_poll_network_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/chat -I$(top_srcdir)/lib diff --git a/tests/test_chat_poll.c b/tests/test_chat_poll.c index d1fb838f..40e16efa 100644 --- a/tests/test_chat_poll.c +++ b/tests/test_chat_poll.c @@ -5,6 +5,66 @@ #include "../src/config_parser.h" #include +enum poll_fault { P_NONE, P_PREPARE, P_BIND, P_STEP, P_FINALIZE }; +enum poll_query { P_VALID, P_FIELD, P_TARGET, P_SNAPSHOT, P_CHOICES, P_OPTIONS }; +static enum poll_fault fault; +static enum poll_query query; +static unsigned query_number, matching_queries, fault_bind, changed_events; +static sqlite3_stmt* fault_statement; + +static int query_matches(const char* sql) { + const char* prefix[] = {"SELECT json_valid(?1)", "SELECT json_type(?1,?2),json_extract(?1,?2)", + "SELECT CASE WHEN json_valid", "SELECT node_id,kind,ver,data FROM feedback_", + "SELECT type,value FROM json_each(?1,?2)", "SELECT type,value FROM json_each(?1,'$.options')"}; + return !strncmp(sql, prefix[query], strlen(prefix[query])); +} + +static int take_fault(enum poll_fault point, sqlite3_stmt* st, unsigned index) { + if (fault != point || st != fault_statement || (point == P_BIND && index != fault_bind)) return 0; + fault = P_NONE; + DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "poll test: injected query=%d occurrence=%u stage=%d bind=%u", query, query_number, point, index); + 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 length, sqlite3_stmt** st, const char** tail) { + int target = fault != P_NONE && query_matches(sql) && ++matching_queries == query_number; + if (target && fault == P_PREPARE) { + fault = P_NONE; *st = NULL; DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "poll test: prepare query=%d occurrence=%u", query, query_number); + return SQLITE_NOMEM; + } + int rc = __real_sqlite3_prepare_v2(db, sql, length, st, tail); + if (target && rc == SQLITE_OK) fault_statement = *st; + return rc; +} +extern int __real_sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int, void (*)(void*)); +int __wrap_sqlite3_bind_blob(sqlite3_stmt* st, int index, const void* bytes, int length, void (*destroy)(void*)) { + if (take_fault(P_BIND, st, index)) return SQLITE_NOMEM; + return __real_sqlite3_bind_blob(st, index, bytes, length, destroy); +} +extern int __real_sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void (*)(void*)); +int __wrap_sqlite3_bind_text(sqlite3_stmt* st, int index, const char* bytes, int length, void (*destroy)(void*)) { + if (take_fault(P_BIND, st, index)) return SQLITE_NOMEM; + return __real_sqlite3_bind_text(st, index, bytes, length, destroy); +} +extern int __real_sqlite3_step(sqlite3_stmt*); +int __wrap_sqlite3_step(sqlite3_stmt* st) { + if (take_fault(P_STEP, st, 0)) return SQLITE_IOERR; + return __real_sqlite3_step(st); +} +extern int __real_sqlite3_finalize(sqlite3_stmt*); +int __wrap_sqlite3_finalize(sqlite3_stmt* st) { + int fail = st && take_fault(P_FINALIZE, st, 0); + int rc = __real_sqlite3_finalize(st); if (st == fault_statement) fault_statement = NULL; + return fail ? SQLITE_IOERR : rc; +} + +static void poll_event(struct UTUN_INSTANCE* inst, int type, const uint8_t* data, int len) { + (void)data; (void)len; + assert(sqlite3_get_autocommit(inst->topo_sqlite_db)); + if (type == CHAT_EVT_FEEDBACK_CHANGED || type == CHAT_EVT_MSG_RECEIVED) changed_events++; +} + static sqlite3_int64 json_number(sqlite3* db, const char* json, const char* path) { sqlite3_stmt* st = NULL; assert(sqlite3_prepare_v2(db, "SELECT json_extract(?1,?2)", -1, &st, NULL) == SQLITE_OK);