diff --git a/crates/molotov-core/tests/jni_lifecycle_test.rs b/crates/molotov-core/tests/jni_lifecycle_test.rs new file mode 100644 index 0000000..3c8140f --- /dev/null +++ b/crates/molotov-core/tests/jni_lifecycle_test.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +use molotov_core::jni::activity::{ANativeActivity, ActivityRunner}; +use molotov_core::jni::types::{JavaVMPtr, JNI_VERSION_1_6}; + +// Simulated JNI_OnLoad exported by libroblox.so +unsafe extern "C" fn mock_jni_onload(vm: JavaVMPtr, _reserved: *mut ()) -> i32 { + let mut env = std::ptr::null_mut(); + let res = (**vm).GetEnv.unwrap()(vm, &mut env, JNI_VERSION_1_6); + if res == 0 && !env.is_null() { + JNI_VERSION_1_6 + } else { + -1 + } +} + +// Simulated ANativeActivity_onCreate exported by libroblox.so +unsafe extern "C" fn mock_native_activity_on_create( + activity: *mut ANativeActivity, + _saved_state: *mut (), + _saved_state_size: usize, +) { + if let Some(act) = activity.as_mut() { + if let Some(cbs) = act.callbacks.as_mut() { + cbs.onResume = Some(mock_on_resume); + } + } +} + +static mut RESUME_CALLED: bool = false; +unsafe extern "C" fn mock_on_resume(_activity: *mut ANativeActivity) { + RESUME_CALLED = true; +} + +#[test] +fn test_jni_onload_and_native_activity_lifecycle() { + let mut runner = ActivityRunner::new(); + + // 1. Simulate JNI_OnLoad execution + unsafe { + let ver = mock_jni_onload(runner.activity.vm, std::ptr::null_mut()); + assert_eq!(ver, JNI_VERSION_1_6, "JNI_OnLoad must return JNI_VERSION_1_6"); + + // 2. Simulate ANativeActivity_onCreate + mock_native_activity_on_create(&mut *runner.activity, std::ptr::null_mut(), 0); + + // 3. Trigger Resume callback registered by the engine + runner.trigger_resume(); + assert!(RESUME_CALLED, "Registered onResume callback must be invoked"); + } +}