Verify repeated JNI delivery and log subscription changes

This commit is contained in:
evgeny committed 2026-10-28 12:28:28 +01:00
1 parent 269d09815b
commit 1c705e64ca
2 files changed
+46 -4

No files matched your search

@@ -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;
@@ -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) {