From 8b776a01bbc7fe024a9c27e4da63caa6c0d68058 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Sun, 11 Jan 2026 11:45:06 +0100 Subject: [PATCH] add tests for note_store_create_default, user_settings_is_valid and note_store_reload --- application/application.c | 5 +- application/store.c | 17 ++++--- application/store.h | 5 +- application/tests/test-store.c | 93 ++++++++++++++++++++++++++++++++++ application/tests/test-store.h | 5 ++ application/tests/testmain.c | 6 +++ dbutils/db.c | 2 +- ui/cocoa/toolkit.m | 5 ++ ui/common/properties.c | 4 -- ui/common/threadpool.c | 51 +++++++++++++++++++ ui/common/threadpool.h | 4 ++ ui/gtk/toolkit.c | 6 +++ ui/motif/toolkit.c | 5 ++ ui/ui/toolkit.h | 4 ++ 14 files changed, 199 insertions(+), 13 deletions(-) diff --git a/application/application.c b/application/application.c index 6af3724..d41693a 100644 --- a/application/application.c +++ b/application/application.c @@ -98,7 +98,10 @@ void application_startup(UiEvent *event, void *data) { cxMempoolFree(mp); - note_store_reload(); + if(note_store_reload()) { + fprintf(stderr, "Error: note_store_reload failed\n"); + exit(1); + } ui_window_default_size(1600, 1200); window_create(); diff --git a/application/store.c b/application/store.c index efdbf8a..45a7f25 100644 --- a/application/store.c +++ b/application/store.c @@ -43,9 +43,8 @@ #include "types.h" #include "store_sqlite.h" -#include "../dbutils/dbutils/db.h" -#include "../dbutils/dbutils/sqlite.h" -#include "cx/mempool.h" +#include +#include #define SQL_REPOSITORY_GET_ALL "select * from repositories;" @@ -225,6 +224,10 @@ CxList* note_store_get_user_settings(const CxAllocator *a, const char *host, con return usersettings; } +DBUConnection* note_store_get_connection() { + return connection; +} + void close_note_store() { dbuConnectionFree(connection); connection = NULL; @@ -250,7 +253,7 @@ int user_settings_is_valid( if(settings->host && host && strcmp(settings->host, host)) { return 0; } - if(settings->user && host && strcmp(settings->user, user)) { + if(settings->user && user && strcmp(settings->user, user)) { return 0; } if(settings->profile_name && profile && strcmp(settings->profile_name, profile)) { @@ -369,7 +372,7 @@ int note_store_create_default(const char *host, const char *user) { * Reloads the NoteStore structure. The previous NoteStore* pointer will be * invalid after this call */ -void note_store_reload() { +int note_store_reload() { // load notebook/collection structure CxMempool *mp = cxMempoolCreateSimple(64); const CxAllocator *a = mp->allocator; @@ -383,7 +386,7 @@ void note_store_reload() { if(!collections) { fprintf(stderr, "Error: cannot query note store\n"); cxMempoolFree(mp); - return; + return 1; } // NoteStore root object @@ -439,6 +442,8 @@ void note_store_reload() { } current_store = store; + + return 0; } NoteStore* note_store_get() { diff --git a/application/store.h b/application/store.h index 4a19018..d60dacf 100644 --- a/application/store.h +++ b/application/store.h @@ -33,6 +33,7 @@ #include "types.h" #include #include +#include #ifdef __cplusplus extern "C" { @@ -64,6 +65,8 @@ int init_note_store(); CxList* note_store_get_user_settings(const CxAllocator *a, const char *host, const char *user, const char *profile); +DBUConnection* note_store_get_connection(); + void close_note_store(); int user_settings_is_valid( @@ -81,7 +84,7 @@ void note_store_set_settings( int note_store_create_default(const char *host, const char *user); -void note_store_reload(); +int note_store_reload(); NoteStore* note_store_get(); diff --git a/application/tests/test-store.c b/application/tests/test-store.c index 0beb76d..33e9b2c 100644 --- a/application/tests/test-store.c +++ b/application/tests/test-store.c @@ -30,6 +30,13 @@ #include "../store.h" +#include +#include + +static int64_t test_root_resource_id; +static int64_t test_repository_id; +static bool test_store_initialized = FALSE; + CX_TEST(test_init_note_store) { CX_TEST_DO { int ret = init_note_store(); @@ -42,5 +49,91 @@ CX_TEST(test_init_note_store) { // the database int ret2 = init_note_store(); CX_TEST_ASSERT(ret2 == 0); + + DBUConnection *conn = note_store_get_connection(); + CX_TEST_ASSERT(conn); + } +} + +CX_TEST(test_note_store_create_default) { + CX_TEST_DO { + DBUConnection *conn = note_store_get_connection(); + CX_TEST_ASSERT(conn); + + int ret = note_store_create_default("testhost", "testuser"); + CX_TEST_ASSERT(ret == 0); + + DBUResult *result = dbuSqlExec(conn, NULL, + "select r.repository_id, c.resource_id from user_settings u " + "inner join repositories r on u.default_repository_id = r.repository_id " + "inner join resources c on u.default_collection_id = c.resource_id " + "where host = 'testhost' and user = 'testuser';"); + CX_TEST_ASSERT(result); + + CX_TEST_ASSERT(dbuResultHasData(result)); + test_repository_id = dbuResultGetInt(result, 0); + test_root_resource_id = dbuResultGetInt(result, 1); + + dbuResultNextRow(result); + CX_TEST_ASSERT(!dbuResultHasData(result)); // result should only have 1 row + + dbuResultFree(result); + + note_store_set_settings("testhost", "testuser", "default", test_repository_id, test_root_resource_id); + + test_store_initialized = TRUE; + } +} + +CX_TEST(test_user_settings_is_valid) { + CX_TEST_DO { + UserSettings settings1; + settings1.host = "testhost"; + settings1.user = "testuser"; + settings1.profile_name = "default"; + + CX_TEST_ASSERT(user_settings_is_valid(&settings1, "testhost", "testuser", "default")); + CX_TEST_ASSERT(user_settings_is_valid(&settings1, "testhost", "testuser", NULL)); + CX_TEST_ASSERT(user_settings_is_valid(&settings1, "testhost", NULL, "default")); + CX_TEST_ASSERT(user_settings_is_valid(&settings1, NULL, "testuser", "default")); + CX_TEST_ASSERT(user_settings_is_valid(&settings1, NULL, NULL, NULL)); + + UserSettings settings2; + settings2.host = NULL; + settings2.user = NULL; + settings2.profile_name = NULL; + + CX_TEST_ASSERT(user_settings_is_valid(&settings2, "testhost", "testuser", "default")); + CX_TEST_ASSERT(user_settings_is_valid(&settings2, "testhost", "testuser", NULL)); + CX_TEST_ASSERT(user_settings_is_valid(&settings2, "testhost", NULL, "default")); + CX_TEST_ASSERT(user_settings_is_valid(&settings2, NULL, "testuser", "default")); + CX_TEST_ASSERT(user_settings_is_valid(&settings2, NULL, NULL, NULL)); + + // invalid settings + + UserSettings invalid1; + invalid1.host = "testhost2"; + invalid1.user = "root"; + invalid1.profile_name = "test"; + + CX_TEST_ASSERT(!user_settings_is_valid(&invalid1, "testhost", "root", "test")); + CX_TEST_ASSERT(!user_settings_is_valid(&invalid1, "testhost2", "testuser", "test")); + CX_TEST_ASSERT(!user_settings_is_valid(&invalid1, "testhost2", "root", "default")); + } +} + + +CX_TEST(test_note_store_reload) { + CX_TEST_DO { + CX_TEST_ASSERT(!note_store_reload()); + NoteStore *store = note_store_get(); + CX_TEST_ASSERT(store); + CX_TEST_ASSERT(store->mp); + CX_TEST_ASSERT(store->root); + CX_TEST_ASSERT(store->repositories); + + CX_TEST_ASSERT(store->root->children); + CX_TEST_ASSERT(cxListSize(store->root->children) > 0); // there should be some default groups/notebooks } } + diff --git a/application/tests/test-store.h b/application/tests/test-store.h index b918cf3..92dabd5 100644 --- a/application/tests/test-store.h +++ b/application/tests/test-store.h @@ -36,6 +36,11 @@ extern "C" { #endif CX_TEST(test_init_note_store); +CX_TEST(test_note_store_create_default); +CX_TEST(test_user_settings_is_valid); + +CX_TEST(test_note_store_reload); + #ifdef __cplusplus diff --git a/application/tests/testmain.c b/application/tests/testmain.c index 4da0c05..bc272e1 100644 --- a/application/tests/testmain.c +++ b/application/tests/testmain.c @@ -36,6 +36,8 @@ #include "test-editor.h" #include "test-store.h" +#include "../types.h" + int main(int argc, char **argv) { struct stat s; if(!stat("notetestdata", &s)) { @@ -44,10 +46,14 @@ int main(int argc, char **argv) { ui_setappdir("notetestdata"); ui_init(NULL, 0, NULL); + register_types(); CxTestSuite *suite = cx_test_suite_new("note"); cx_test_register(suite, test_init_note_store); + cx_test_register(suite, test_note_store_create_default); + cx_test_register(suite, test_user_settings_is_valid); + cx_test_register(suite, test_note_store_reload); cx_test_register(suite, test_parse_markdown_para); cx_test_register(suite, test_parse_markdown_formatting_simple); diff --git a/dbutils/db.c b/dbutils/db.c index a2a0d89..0dbea99 100644 --- a/dbutils/db.c +++ b/dbutils/db.c @@ -123,7 +123,7 @@ int64_t dbuResultGetInt(DBUResult *result, int field) { if(result->optional_getInt) { return result->optional_getInt(result, field); } else { - cxstring s = result->getText(result, 0); + cxstring s = result->getText(result, field); int64_t value = 0; cx_strtoi64(s, &value, 10); return value; diff --git a/ui/cocoa/toolkit.m b/ui/cocoa/toolkit.m index c0e9edf..ae6f4c7 100644 --- a/ui/cocoa/toolkit.m +++ b/ui/cocoa/toolkit.m @@ -174,6 +174,11 @@ void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd) @end void ui_call_mainthread(ui_threadfunc tf, void* td) { + if(uic_mainthread_calls_is_buffered()) { + uic_add_buffered_mainthread_call(tf, td); + return; + } + UiAppCallback *cb = [[UiAppCallback alloc]initWithCallback:tf userdata:td]; [cb callMainThread]; } diff --git a/ui/common/properties.c b/ui/common/properties.c index 10c1181..bedc918 100644 --- a/ui/common/properties.c +++ b/ui/common/properties.c @@ -234,10 +234,6 @@ void uic_load_app_properties() { return; } - //if(!ui_appname()) { - // // applications without name cannot load app properties - // return; - //} char *dir = ui_configfile(NULL); if(!dir) { diff --git a/ui/common/threadpool.c b/ui/common/threadpool.c index 3b4c096..5d3b72a 100644 --- a/ui/common/threadpool.c +++ b/ui/common/threadpool.c @@ -30,6 +30,7 @@ #include "threadpool.h" #include "context.h" +#include #include #include @@ -38,6 +39,56 @@ static threadpool_job kill_job; + + +static pthread_mutex_t mc_buffer_mutex; +static CxList *mainthread_call_buffer; +static volatile int mainthread_call_buffered = 0; + +typedef struct UiMainCall { + ui_threadfunc func; + void *data; +} UiMainCall; + +void uic_init_threads(void) { + pthread_mutex_init(&mc_buffer_mutex, NULL); + mainthread_call_buffer = cxLinkedListCreate(NULL, sizeof(UiMainCall)); +} + +int uic_mainthread_calls_is_buffered(void) { + return mainthread_call_buffered; +} + +void uic_add_buffered_mainthread_call(ui_threadfunc func, void *data) { + pthread_mutex_lock(&mc_buffer_mutex); + UiMainCall call; + call.func = func; + call.data = data; + cxListAdd(mainthread_call_buffer, &call); + pthread_mutex_unlock(&mc_buffer_mutex); +} + + +void ui_buffer_mainthread_calls(UiBool enable_buffering) { + mainthread_call_buffered = enable_buffering; + if(!enable_buffering) { + ui_exec_buffered_mainthread_calls(); + } +} +void ui_exec_buffered_mainthread_calls(void) { + pthread_mutex_lock(&mc_buffer_mutex); + CxIterator i = cxListIterator(mainthread_call_buffer); + cx_foreach(UiMainCall *, call, i) { + if(call->func) { + call->func(call->data); + } + } + cxListClear(mainthread_call_buffer); + pthread_mutex_unlock(&mc_buffer_mutex); +} + + + UiThreadpool* threadpool_new(int min, int max) { UiThreadpool *pool = malloc(sizeof(UiThreadpool)); pool->queue = ui_queue_create(); diff --git a/ui/common/threadpool.h b/ui/common/threadpool.h index 4a997f6..8413b24 100644 --- a/ui/common/threadpool.h +++ b/ui/common/threadpool.h @@ -39,6 +39,10 @@ extern "C" { #endif +void uic_init_threads(void); +int uic_mainthread_calls_is_buffered(void); +void uic_add_buffered_mainthread_call(ui_threadfunc func, void *data); + typedef struct UiQueueElm UiQueueElm; typedef struct UiQueue UiQueue; diff --git a/ui/gtk/toolkit.c b/ui/gtk/toolkit.c index 10cba07..d775f3b 100644 --- a/ui/gtk/toolkit.c +++ b/ui/gtk/toolkit.c @@ -76,6 +76,7 @@ UIEXPORT void ui_init(const char *appname, int argc, char **argv) { uic_toolbar_init(); ui_image_init(); uic_load_app_properties(); + uic_init_threads(); #if GTK_MAJOR_VERSION >= 4 scale_factor = 1; // TODO @@ -203,6 +204,11 @@ static gboolean ui_idle_func(void *data) { } void ui_call_mainthread(ui_threadfunc tf, void* td) { + if(uic_mainthread_calls_is_buffered()) { + uic_add_buffered_mainthread_call(tf, td); + return; + } + UiJob *job = malloc(sizeof(UiJob)); job->job_func = tf; job->job_data = td; diff --git a/ui/motif/toolkit.c b/ui/motif/toolkit.c index fe49df1..da7ab6e 100644 --- a/ui/motif/toolkit.c +++ b/ui/motif/toolkit.c @@ -221,6 +221,11 @@ static void* ui_jobthread(void *data) { } void ui_call_mainthread(ui_threadfunc tf, void* td) { + if(uic_mainthread_calls_is_buffered()) { + uic_add_buffered_mainthread_call(tf, td); + return; + } + UiJob *job = malloc(sizeof(UiJob)); memset(job, 0, sizeof(UiJob)); job->job_func = tf; diff --git a/ui/ui/toolkit.h b/ui/ui/toolkit.h index 606420e..6c0152b 100644 --- a/ui/ui/toolkit.h +++ b/ui/ui/toolkit.h @@ -556,6 +556,10 @@ UIEXPORT void ui_close(UiObject *obj); UIEXPORT void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd); UIEXPORT void ui_call_mainthread(ui_threadfunc tf, void* td); + +UIEXPORT void ui_buffer_mainthread_calls(UiBool enable_buffering); +UIEXPORT void ui_exec_buffered_mainthread_calls(void); + UIEXPORT UiThreadpool* ui_threadpool_create(int nthreads); UIEXPORT void ui_threadpool_destroy(UiThreadpool* pool); UIEXPORT void ui_threadpool_job(UiThreadpool* pool, UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd); -- 2.47.3