93 lines
3.1 KiB
Rust
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();
|
|
}
|
|
}
|