From 1c705e64ca03e5a019ee72566c4819e5c0cdd6fa Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 28 Oct 2026 12:02:25 +0100 Subject: [PATCH] Verify repeated JNI delivery and log subscription changes --- .../jni_bridge/android_jni_bridge.c | 3 ++ .../tests/jni_log/test_jni_log.c | 47 +++++++++++++++++-- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/tools/chatgui-android/jni_bridge/android_jni_bridge.c b/tools/chatgui-android/jni_bridge/android_jni_bridge.c index 4c7bf206..cbb483d6 100644 --- a/tools/chatgui-android/jni_bridge/android_jni_bridge.c +++ b/tools/chatgui-android/jni_bridge/android_jni_bridge.c @@ -1998,6 +1998,9 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeInit( __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "JNI log callback configured=%d; UTF-8 byte arrays", logCb != NULL); const char* db = dbPath ? (*env)->GetStringUTFChars(env, dbPath, NULL) : NULL; + if (dbPath && !db) { + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "JNI log: nativeInit db path access failed"); return JNI_FALSE; + } if (db) { strncpy(g_db_path, db, sizeof(g_db_path) - 1); g_db_path[sizeof(g_db_path) - 1] = '\0'; } utun_log_fn log_cb = logCb ? jni_on_log : NULL; diff --git a/tools/chatgui-android/tests/jni_log/test_jni_log.c b/tools/chatgui-android/tests/jni_log/test_jni_log.c index 8507d38c..1389b690 100644 --- a/tools/chatgui-android/tests/jni_log/test_jni_log.c +++ b/tools/chatgui-android/tests/jni_log/test_jni_log.c @@ -9,14 +9,18 @@ static struct ref refs[16], global_ref; static enum fault fault; static int pending, throw_on_failure, illegal_calls, calls, attaches, detaches, descriptions, diagnostics, nested; static int already_attached, arrays, copies, legacy_strings, payload_ok; +static int normal_logs; static char last_diagnostic[512], method_signature[128]; static JNIEnv test_env; static const char category[] = "чат", message[] = "Ошибка 😀"; +static const char* expected_category; +static const char* expected_message; #define CHECK(test) do { if (!(test)) { fprintf(stderr, "[FAIL] %s:%d: %s\n", __func__, __LINE__, #test); return 1; } } while (0) int __android_log_print(int priority, const char* tag, const char* format, ...) { (void)tag; + if (priority == ANDROID_LOG_INFO || priority == ANDROID_LOG_DEBUG) { normal_logs++; return 0; } if (priority != ANDROID_LOG_ERROR && priority != ANDROID_LOG_WARN) illegal_calls++; va_list ap; va_start(ap, format); vsnprintf(last_diagnostic, sizeof(last_diagnostic), format, ap); @@ -90,6 +94,7 @@ static void JNICALL copy_bytes(JNIEnv* env, jbyteArray array, jsize start, jsize static jstring JNICALL legacy_string(JNIEnv* env, const char* text) { (void)env; check_pending(); legacy_strings++; struct ref* ref = (struct ref*)new_ref(4); + if (!ref) return NULL; ref->length = (jsize)strlen(text); memcpy(ref->bytes, text, (size_t)ref->length); return (jstring)ref; } @@ -103,8 +108,9 @@ static void JNICALL call(JNIEnv* env, jobject object, jmethodID mid, ...) { struct ref* msg = (struct ref*)va_arg(ap, jobject); va_end(ap); payload_ok = level == BLEV_ERROR && cat && msg && cat->kind == 2 && msg->kind == 2 && - cat->length == (jsize)strlen(category) && msg->length == (jsize)strlen(message) && - !memcmp(cat->bytes, category, strlen(category)) && !memcmp(msg->bytes, message, strlen(message)); + cat->length == (jsize)strlen(expected_category) && msg->length == (jsize)strlen(expected_message) && + !memcmp(cat->bytes, expected_category, strlen(expected_category)) && + !memcmp(msg->bytes, expected_message, strlen(expected_message)); if (fault == CALLBACK) pending = 1; if (fault == RECURSION && !nested++) jni_on_log(BLEV_ERROR, category, message); } @@ -140,10 +146,11 @@ static JavaVM test_vm = &vm_functions; static void reset(enum fault next) { memset(refs, 0, sizeof(refs)); memset(&global_ref, 0, sizeof(global_ref)); global_ref.live = 1; pending = illegal_calls = calls = attaches = detaches = descriptions = diagnostics = nested = 0; - arrays = copies = legacy_strings = payload_ok = 0; + arrays = copies = legacy_strings = payload_ok = normal_logs = 0; last_diagnostic[0] = method_signature[0] = 0; throw_on_failure = already_attached = 1; fault = next; test_env = &functions; g_jvm = &test_vm; g_log_callback = (jobject)&global_ref; + expected_category = category; expected_message = message; } static int success(int detached) { @@ -192,6 +199,37 @@ static int replaced_callback(void) { return 0; } +static int empty_fields(void) { + reset(OK); expected_category = expected_message = ""; + jni_on_log(BLEV_ERROR, "", ""); + CHECK(calls == 1 && payload_ok && arrays == 2 && !copies && !illegal_calls && !live_refs() && !diagnostics); + return 0; +} + +static int repeated_callbacks(void) { + reset(OK); + for (unsigned i = 0; i < 1024; i++) { + jni_on_log(BLEV_ERROR, category, message); + CHECK(payload_ok && !live_refs() && !illegal_calls && !pending && !diagnostics); + } + CHECK(calls == 1024 && !attaches && !detaches); + return 0; +} + +static int subscription(void) { + reset(OK); + Java_com_utun_chat_data_NativeLib_nativeSubscribeLogs(&test_env, NULL, JNI_FALSE); + utun_bridge_log(BLEV_ERROR, category, message); + CHECK(!calls); + Java_com_utun_chat_data_NativeLib_nativeSubscribeLogs(&test_env, NULL, JNI_TRUE); + utun_bridge_log(BLEV_ERROR, category, message); + CHECK(calls == 1 && payload_ok && !illegal_calls && !live_refs()); + Java_com_utun_chat_data_NativeLib_nativeSubscribeLogs(&test_env, NULL, JNI_FALSE); + utun_bridge_log(BLEV_ERROR, category, message); + CHECK(calls == 1 && normal_logs == 3 && !diagnostics); + return 0; +} + static int disabled_and_invalid(void) { reset(OK); g_log_callback = NULL; jni_on_log(BLEV_ERROR, category, message); @@ -215,7 +253,8 @@ int main(void) { {COPY_CATEGORY, "category"}, {COPY_MESSAGE, "message"}, {CALLBACK, "onNativeLog"}, {DETACH, "DetachCurrentThread"} }; int failed = success(0) + success(1) + preexisting_exception() + recursive_callback() + replaced_callback() + disabled_and_invalid(); - int count = 6; + failed += empty_fields() + repeated_callbacks() + subscription(); + int count = 9; for (unsigned i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { failed += failure(cases[i].fault, cases[i].phase, 1); count++; if (cases[i].fault >= LOCAL_REF && cases[i].fault <= MESSAGE) {