This repository has been archived on 2026-10-10. You can view files and clone it, but cannot push or open issues or pull requests.
molotov/crates/molotov-core/tests/jni_lifecycle_test.rs

93 lines
3.1 KiB
Rust

// 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};
use std::ffi::{c_void, CStr, CString};
static mut RESUME_CALLED: bool = false;
static mut WINDOW_CREATED: bool = false;
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() {
return -1;
}
let jni_env = env as molotov_core::jni::types::JNIEnvPtr;
// Verify JNIEnv can find classes
let class_name = CString::new("android/os/Build").unwrap();
let build_class = (**jni_env).FindClass.unwrap()(jni_env, class_name.as_ptr());
if build_class.is_null() {
return -2;
}
// Verify String creation
let test_str = CString::new("RobloxEngine_Init").unwrap();
let jstr = (**jni_env).NewStringUTF.unwrap()(jni_env, test_str.as_ptr());
if jstr.is_null() {
return -3;
}
let utf = (**jni_env).GetStringUTFChars.unwrap()(jni_env, jstr, std::ptr::null_mut());
if CStr::from_ptr(utf).to_str().unwrap() != "RobloxEngine_Init" {
return -4;
}
(**jni_env).ReleaseStringUTFChars.unwrap()(jni_env, jstr, utf);
JNI_VERSION_1_6
}
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() {
// Assert internalDataPath is non-null and valid
assert!(!act.internalDataPath.is_null());
let path = CStr::from_ptr(act.internalDataPath).to_str().unwrap();
assert!(path.contains("molotov"));
if let Some(cbs) = act.callbacks.as_mut() {
cbs.onResume = Some(mock_on_resume);
cbs.onNativeWindowCreated = Some(mock_on_window_created);
}
}
}
unsafe extern "C" fn mock_on_resume(_activity: *mut ANativeActivity) {
RESUME_CALLED = true;
}
unsafe extern "C" fn mock_on_window_created(_activity: *mut ANativeActivity, window: *mut c_void) {
if !window.is_null() {
WINDOW_CREATED = true;
}
}
#[test]
fn test_jni_onload_and_native_activity_lifecycle() {
let mut runner = ActivityRunner::new();
unsafe {
// 1. Simulate JNI_OnLoad execution
let ver = mock_jni_onload(runner.vm_ptr(), 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 Lifecycle
runner.trigger_start();
runner.trigger_resume();
assert!(RESUME_CALLED, "Registered onResume callback must be invoked");
let dummy_window = 0x1234_5678 as *mut c_void;
runner.trigger_window_created(dummy_window);
assert!(WINDOW_CREATED, "onNativeWindowCreated callback must be invoked");
runner.trigger_pause();
runner.trigger_stop();
runner.trigger_destroy();
}
}