1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#![cfg_attr(feature = "nightly", feature(external_doc) )]
#![cfg_attr(feature = "nightly", doc(include = "../Readme.md"))]
use jni_sys::*;
use std::convert::*;
use std::fmt::{self, Debug, Display, Formatter};
use std::path::{Path, PathBuf};
use std::ptr::null_mut;
pub type Result<T> = std::result::Result<T, JavaTestError>;
#[derive(Clone)]
pub enum JavaTestError {
Unknown(String),
#[doc(hidden)] _NonExhaustive,
}
impl Display for JavaTestError {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
match self {
JavaTestError::Unknown(message) => write!(fmt, "{}", message),
JavaTestError::_NonExhaustive => write!(fmt, "NonExhaustive"),
}
}
}
impl Debug for JavaTestError {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
Display::fmt(self, fmt)
}
}
impl<'a> From<&'a str> for JavaTestError {
fn from(value: &'a str) -> Self {
JavaTestError::Unknown(value.to_string())
}
}
impl From<String> for JavaTestError {
fn from(value: String) -> Self {
JavaTestError::Unknown(value)
}
}
pub fn run_test(package: &str, class: &str, method: &str) -> Result<()> {
let env = test_thread_env();
if env == null_mut() { return Err("Couldn't initialize Java VM".into()); }
let class_id = format!("{}/{}\0", package.replace(".", "/"), class);
let method_id = format!("{}\0", method);
unsafe {
let class_id = (**env).FindClass.unwrap()(env, class_id.as_ptr() as *const _);
assert_ne!(class_id, null_mut(), "Failed to FindClass {}.{} - the corresponding .jar may not be loaded", package, class);
let method_id = (**env).GetStaticMethodID.unwrap()(env, class_id, method_id.as_ptr() as *const _, "()V\0".as_ptr() as *const _);
assert_ne!(method_id, null_mut(), "Failed to GetStaticMethodID {}.{}", class, method);
(**env).CallStaticVoidMethodA.unwrap()(env, class_id, method_id, [].as_ptr());
if (**env).ExceptionCheck.unwrap()(env) == JNI_TRUE {
(**env).ExceptionDescribe.unwrap()(env);
(**env).ExceptionClear.unwrap()(env);
Err(format!("{}.{}() threw a Java Exception", class, method).into())
} else {
Ok(())
}
}
}
lazy_static::lazy_static! { static ref JVM : jerk::jvm::Library = jerk::jvm::Library::get().unwrap(); }
pub fn test_vm() -> *mut JavaVM { **VM }
lazy_static::lazy_static! { static ref VM : ThreadSafe<*mut JavaVM> = ThreadSafe(create_java_vm()); }
pub fn test_thread_env() -> *mut JNIEnv { ENV.with(|e| *e) }
thread_local! { static ENV : *mut JNIEnv = attach_current_thread(); }
fn attach_current_thread() -> *mut JNIEnv {
let vm = test_vm();
let mut env = null_mut();
assert_eq!(JNI_OK, unsafe { (**vm).AttachCurrentThread.unwrap()(vm, &mut env, null_mut()) });
env as *mut _
}
fn create_java_vm() -> *mut JavaVM {
JVM.create_java_vm(vec![
"-ea".to_string(),
"-esa".to_string(),
format!("-Djava.class.path={profile_dir}/java/jars/{pkg_name}.jar", profile_dir=taget_profile_dir().display(), pkg_name=std::env::var("CARGO_PKG_NAME").unwrap()),
]).unwrap()
}
fn taget_profile_dir() -> PathBuf {
let mut dir = jerk::paths::env("OUT_DIR").unwrap();
while !is_profile_dir(&dir) && dir.pop() {}
eprintln!("{}", dir.display());
dir
}
fn is_profile_dir(path: &Path) -> bool {
if path.parent().and_then(|p| p.file_name()).and_then(|n| n.to_str()) != Some("target") { return false; }
if !path.join("java").exists() { return false; }
true
}
struct ThreadSafe<T>(pub T);
impl<T> std::ops::Deref for ThreadSafe<T> { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } }
unsafe impl<T> Send for ThreadSafe<T> {}
unsafe impl<T> Sync for ThreadSafe<T> {}