Exercise event callback replacement and exceptions on a real JVM
This commit is contained in:
1 parent
2f4cded218
commit
57227b331d
3 files changed
+43
-2
No files matched your search
@@ -0,0 +1,13 @@
|
||||
/** Настоящий Java callback для проверки регистрации, замены и исключений доставки JNI. */
|
||||
public final class EventSink {
|
||||
public int calls;
|
||||
public int type;
|
||||
public int bytes;
|
||||
public boolean fail;
|
||||
public void onEvent(int eventType, byte[] data) {
|
||||
calls++;
|
||||
type = eventType;
|
||||
bytes = data == null ? 0 : data.length;
|
||||
if (fail) throw new IllegalStateException("injected event callback failure");
|
||||
}
|
||||
}
|
||||
@@ -25,12 +25,13 @@ def main():
|
||||
root / "src/chat", root / "src/media_async", root / "lib"]
|
||||
with tempfile.TemporaryDirectory(prefix="utun-jni-utf8-") as output:
|
||||
binary = Path(output) / "test_jni_utf8"
|
||||
subprocess.run([str(java_home / "bin/javac"), "-d", output, str(directory / "EventSink.java")], check=True, timeout=30)
|
||||
command = shlex.split(os.environ.get("CC", "cc")) + ["-std=c99", "-D_DEFAULT_SOURCE", "-D__ANDROID__", "-DUSE_OPENSSL",
|
||||
"-ffunction-sections", "-fdata-sections", "-Wl,--gc-sections", "-pthread", "-Wno-incompatible-pointer-types"]
|
||||
command += [f"-I{path}" for path in includes]
|
||||
command += [str(directory / "test_jni_utf8.c"), f"-L{server}", f"-Wl,-rpath,{server}", "-ljvm", "-o", str(binary)]
|
||||
subprocess.run(command, check=True, timeout=90)
|
||||
subprocess.run([str(binary)], check=True, timeout=30)
|
||||
subprocess.run([str(binary)], cwd=output, check=True, timeout=30)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -34,10 +34,37 @@ int main(void) {
|
||||
CHECK(!(*env)->ThrowNew(env, exception, "preserved"));
|
||||
CHECK(!jni_utf8(env, original) && !jni_text(env, expected) && (*env)->ExceptionCheck(env));
|
||||
(*env)->ExceptionClear(env);
|
||||
g_jvm = vm;
|
||||
jclass sink_class = (*env)->FindClass(env, "EventSink"); CHECK(sink_class);
|
||||
jmethodID constructor = (*env)->GetMethodID(env, sink_class, "<init>", "()V"); CHECK(constructor);
|
||||
jobject sink = (*env)->NewObject(env, sink_class, constructor); CHECK(!(*env)->ExceptionCheck(env) && sink);
|
||||
jfieldID calls = (*env)->GetFieldID(env, sink_class, "calls", "I"); CHECK(calls);
|
||||
jfieldID type = (*env)->GetFieldID(env, sink_class, "type", "I"); CHECK(type);
|
||||
jfieldID size = (*env)->GetFieldID(env, sink_class, "bytes", "I"); CHECK(size);
|
||||
jfieldID fail = (*env)->GetFieldID(env, sink_class, "fail", "Z"); CHECK(fail);
|
||||
Java_com_utun_chat_data_NativeLib_nativeSetEventCallback(env, NULL, sink); CHECK(!(*env)->ExceptionCheck(env));
|
||||
const uint8_t payload[] = {1, 2, 3};
|
||||
jni_on_event(7, payload, sizeof(payload));
|
||||
CHECK((*env)->GetIntField(env, sink, calls) == 1 && (*env)->GetIntField(env, sink, type) == 7);
|
||||
CHECK((*env)->GetIntField(env, sink, size) == sizeof(payload));
|
||||
(*env)->SetBooleanField(env, sink, fail, JNI_TRUE);
|
||||
jni_on_event(8, payload, sizeof(payload)); CHECK(!(*env)->ExceptionCheck(env));
|
||||
(*env)->SetBooleanField(env, sink, fail, JNI_FALSE);
|
||||
jni_on_event(9, NULL, 0); CHECK((*env)->GetIntField(env, sink, calls) == 3 && !(*env)->GetIntField(env, sink, size));
|
||||
jobject replacement = (*env)->NewObject(env, sink_class, constructor); CHECK(!(*env)->ExceptionCheck(env) && replacement);
|
||||
Java_com_utun_chat_data_NativeLib_nativeSetEventCallback(env, NULL, replacement); CHECK(!(*env)->ExceptionCheck(env));
|
||||
jni_on_event(10, payload, sizeof(payload));
|
||||
CHECK((*env)->GetIntField(env, replacement, calls) == 1 && (*env)->GetIntField(env, sink, calls) == 3);
|
||||
CHECK(!(*env)->ThrowNew(env, exception, "preserved event exception"));
|
||||
jni_on_event(11, payload, sizeof(payload)); CHECK((*env)->ExceptionCheck(env)); (*env)->ExceptionClear(env);
|
||||
jni_on_event(12, NULL, 3); CHECK((*env)->GetIntField(env, replacement, calls) == 1);
|
||||
Java_com_utun_chat_data_NativeLib_nativeSetEventCallback(env, NULL, NULL); CHECK(!(*env)->ExceptionCheck(env));
|
||||
jni_on_event(13, NULL, 0); CHECK(!(*env)->ExceptionCheck(env));
|
||||
(*env)->DeleteLocalRef(env, replacement); (*env)->DeleteLocalRef(env, sink); (*env)->DeleteLocalRef(env, sink_class);
|
||||
jstring empty = jni_text(env, ""); CHECK(empty && !(*env)->GetStringLength(env, empty));
|
||||
(*env)->DeleteLocalRef(env, empty); (*env)->DeleteLocalRef(env, exception);
|
||||
(*env)->DeleteLocalRef(env, embedded); (*env)->DeleteLocalRef(env, original);
|
||||
CHECK(!(*env)->ExceptionCheck(env) && (*vm)->DestroyJavaVM(vm) == JNI_OK);
|
||||
puts("JNI UTF-8: 1024 real JVM roundtrips, allocation failure/retry, embedded NUL and pending exception PASS");
|
||||
puts("JNI UTF-8/events: real JVM roundtrips, allocation failure/retry, NUL, callback replacement and exceptions PASS");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user