• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![cfg(feature = "invocation")]
2 
3 use std::str::FromStr;
4 
5 use jni::{
6     descriptors::Desc,
7     errors::Error,
8     objects::{
9         AutoArray, AutoLocal, JByteBuffer, JList, JObject, JString, JThrowable, JValue, ReleaseMode,
10     },
11     signature::JavaType,
12     strings::JNIString,
13     sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jobject, jshort, jsize},
14     JNIEnv,
15 };
16 
17 mod util;
18 use util::{attach_current_thread, unwrap};
19 
20 static ARRAYLIST_CLASS: &str = "java/util/ArrayList";
21 static EXCEPTION_CLASS: &str = "java/lang/Exception";
22 static ARITHMETIC_EXCEPTION_CLASS: &str = "java/lang/ArithmeticException";
23 static RUNTIME_EXCEPTION_CLASS: &str = "java/lang/RuntimeException";
24 static INTEGER_CLASS: &str = "java/lang/Integer";
25 static MATH_CLASS: &str = "java/lang/Math";
26 static STRING_CLASS: &str = "java/lang/String";
27 static MATH_ABS_METHOD_NAME: &str = "abs";
28 static MATH_TO_INT_METHOD_NAME: &str = "toIntExact";
29 static MATH_ABS_SIGNATURE: &str = "(I)I";
30 static MATH_TO_INT_SIGNATURE: &str = "(J)I";
31 static TEST_EXCEPTION_MESSAGE: &str = "Default exception thrown";
32 static TESTING_OBJECT_STR: &str = "TESTING OBJECT";
33 
34 #[test]
call_method_returning_null()35 pub fn call_method_returning_null() {
36     let env = attach_current_thread();
37     // Create an Exception with no message
38     let obj = AutoLocal::new(
39         &env,
40         unwrap(&env, env.new_object(EXCEPTION_CLASS, "()V", &[])),
41     );
42     // Call Throwable#getMessage must return null
43     let message = unwrap(
44         &env,
45         env.call_method(&obj, "getMessage", "()Ljava/lang/String;", &[]),
46     );
47     let message_ref = env.auto_local(unwrap(&env, message.l()));
48 
49     assert!(message_ref.as_obj().is_null());
50 }
51 
52 #[test]
is_instance_of_same_class()53 pub fn is_instance_of_same_class() {
54     let env = attach_current_thread();
55     let obj = AutoLocal::new(
56         &env,
57         unwrap(&env, env.new_object(EXCEPTION_CLASS, "()V", &[])),
58     );
59     assert!(unwrap(&env, env.is_instance_of(&obj, EXCEPTION_CLASS)));
60 }
61 
62 #[test]
is_instance_of_superclass()63 pub fn is_instance_of_superclass() {
64     let env = attach_current_thread();
65     let obj = AutoLocal::new(
66         &env,
67         unwrap(&env, env.new_object(ARITHMETIC_EXCEPTION_CLASS, "()V", &[])),
68     );
69     assert!(unwrap(&env, env.is_instance_of(&obj, EXCEPTION_CLASS)));
70 }
71 
72 #[test]
is_instance_of_subclass()73 pub fn is_instance_of_subclass() {
74     let env = attach_current_thread();
75     let obj = AutoLocal::new(
76         &env,
77         unwrap(&env, env.new_object(EXCEPTION_CLASS, "()V", &[])),
78     );
79     assert!(!unwrap(
80         &env,
81         env.is_instance_of(&obj, ARITHMETIC_EXCEPTION_CLASS)
82     ));
83 }
84 
85 #[test]
is_instance_of_not_superclass()86 pub fn is_instance_of_not_superclass() {
87     let env = attach_current_thread();
88     let obj = AutoLocal::new(
89         &env,
90         unwrap(&env, env.new_object(ARITHMETIC_EXCEPTION_CLASS, "()V", &[])),
91     );
92     assert!(!unwrap(&env, env.is_instance_of(&obj, ARRAYLIST_CLASS)));
93 }
94 
95 #[test]
is_instance_of_null()96 pub fn is_instance_of_null() {
97     let env = attach_current_thread();
98     let obj = JObject::null();
99     assert!(unwrap(&env, env.is_instance_of(obj, ARRAYLIST_CLASS)));
100     assert!(unwrap(&env, env.is_instance_of(obj, EXCEPTION_CLASS)));
101     assert!(unwrap(
102         &env,
103         env.is_instance_of(obj, ARITHMETIC_EXCEPTION_CLASS)
104     ));
105 }
106 
107 #[test]
is_same_object_diff_references()108 pub fn is_same_object_diff_references() {
109     let env = attach_current_thread();
110     let string = env.new_string(TESTING_OBJECT_STR).unwrap();
111     let ref_from_string = unwrap(&env, env.new_local_ref::<JObject>(string.into()));
112     assert!(unwrap(&env, env.is_same_object(string, ref_from_string)));
113     unwrap(&env, env.delete_local_ref(ref_from_string));
114 }
115 
116 #[test]
is_same_object_same_reference()117 pub fn is_same_object_same_reference() {
118     let env = attach_current_thread();
119     let string = env.new_string(TESTING_OBJECT_STR).unwrap();
120     assert!(unwrap(&env, env.is_same_object(string, string)));
121 }
122 
123 #[test]
is_not_same_object()124 pub fn is_not_same_object() {
125     let env = attach_current_thread();
126     let string = env.new_string(TESTING_OBJECT_STR).unwrap();
127     let same_src_str = env.new_string(TESTING_OBJECT_STR).unwrap();
128     assert!(!unwrap(&env, env.is_same_object(string, same_src_str)));
129 }
130 
131 #[test]
is_not_same_object_null()132 pub fn is_not_same_object_null() {
133     let env = attach_current_thread();
134     assert!(unwrap(
135         &env,
136         env.is_same_object(JObject::null(), JObject::null())
137     ));
138 }
139 
140 #[test]
get_static_public_field()141 pub fn get_static_public_field() {
142     let env = attach_current_thread();
143 
144     let min_int_value = env
145         .get_static_field(INTEGER_CLASS, "MIN_VALUE", "I")
146         .unwrap()
147         .i()
148         .unwrap();
149 
150     assert_eq!(min_int_value, i32::min_value());
151 }
152 
153 #[test]
get_static_public_field_by_id()154 pub fn get_static_public_field_by_id() {
155     let env = attach_current_thread();
156 
157     // One can't pass a JavaType::Primitive(Primitive::Int) to
158     //   `get_static_field_id` unfortunately: #137
159     let field_type = "I";
160     let field_id = env
161         .get_static_field_id(INTEGER_CLASS, "MIN_VALUE", field_type)
162         .unwrap();
163 
164     let field_type = JavaType::from_str(field_type).unwrap();
165     let min_int_value = env
166         .get_static_field_unchecked(INTEGER_CLASS, field_id, field_type)
167         .unwrap()
168         .i()
169         .unwrap();
170 
171     assert_eq!(min_int_value, i32::min_value());
172 }
173 
174 #[test]
pop_local_frame_pending_exception()175 pub fn pop_local_frame_pending_exception() {
176     let env = attach_current_thread();
177 
178     env.push_local_frame(16).unwrap();
179 
180     env.throw_new(RUNTIME_EXCEPTION_CLASS, "Test Exception")
181         .unwrap();
182 
183     // Pop the local frame with a pending exception
184     env.pop_local_frame(JObject::null())
185         .expect("JNIEnv#pop_local_frame must work in case of pending exception");
186 
187     env.exception_clear().unwrap();
188 }
189 
190 #[test]
push_local_frame_pending_exception()191 pub fn push_local_frame_pending_exception() {
192     let env = attach_current_thread();
193 
194     env.throw_new(RUNTIME_EXCEPTION_CLASS, "Test Exception")
195         .unwrap();
196 
197     // Push a new local frame with a pending exception
198     env.push_local_frame(16)
199         .expect("JNIEnv#push_local_frame must work in case of pending exception");
200 
201     env.exception_clear().unwrap();
202 
203     env.pop_local_frame(JObject::null()).unwrap();
204 }
205 
206 #[test]
push_local_frame_too_many_refs()207 pub fn push_local_frame_too_many_refs() {
208     let env = attach_current_thread();
209 
210     // Try to push a new local frame with a ridiculous size
211     let frame_size = i32::max_value();
212     env.push_local_frame(frame_size)
213         .expect_err("push_local_frame(2B) must Err");
214 
215     env.pop_local_frame(JObject::null()).unwrap();
216 }
217 
218 #[test]
with_local_frame()219 pub fn with_local_frame() {
220     let env = attach_current_thread();
221 
222     let s = env
223         .with_local_frame(16, || {
224             let res = env.new_string("Test").unwrap();
225             Ok(res.into())
226         })
227         .unwrap();
228 
229     let s = env
230         .get_string(s.into())
231         .expect("The object returned from the local frame must remain valid");
232     assert_eq!(s.to_str().unwrap(), "Test");
233 }
234 
235 #[test]
with_local_frame_pending_exception()236 pub fn with_local_frame_pending_exception() {
237     let env = attach_current_thread();
238 
239     env.throw_new(RUNTIME_EXCEPTION_CLASS, "Test Exception")
240         .unwrap();
241 
242     // Try to allocate a frame of locals
243     env.with_local_frame(16, || Ok(JObject::null()))
244         .expect("JNIEnv#with_local_frame must work in case of pending exception");
245 
246     env.exception_clear().unwrap();
247 }
248 
249 #[test]
call_static_method_ok()250 pub fn call_static_method_ok() {
251     let env = attach_current_thread();
252 
253     let x = JValue::from(-10);
254     let val: jint = env
255         .call_static_method(MATH_CLASS, MATH_ABS_METHOD_NAME, MATH_ABS_SIGNATURE, &[x])
256         .expect("JNIEnv#call_static_method_unsafe should return JValue")
257         .i()
258         .unwrap();
259 
260     assert_eq!(val, 10);
261 }
262 
263 #[test]
call_static_method_throws()264 pub fn call_static_method_throws() {
265     let env = attach_current_thread();
266 
267     let x = JValue::Long(4_000_000_000);
268     let is_java_exception = env
269         .call_static_method(
270             MATH_CLASS,
271             MATH_TO_INT_METHOD_NAME,
272             MATH_TO_INT_SIGNATURE,
273             &[x],
274         )
275         .map_err(|error| matches!(error, Error::JavaException))
276         .expect_err("JNIEnv#call_static_method_unsafe should return error");
277 
278     assert!(
279         is_java_exception,
280         "ErrorKind::JavaException expected as error"
281     );
282     assert_pending_java_exception(&env);
283 }
284 
285 #[test]
call_static_method_wrong_arg()286 pub fn call_static_method_wrong_arg() {
287     let env = attach_current_thread();
288 
289     let x = JValue::Double(4.567_891_23);
290     env.call_static_method(
291         MATH_CLASS,
292         MATH_TO_INT_METHOD_NAME,
293         MATH_TO_INT_SIGNATURE,
294         &[x],
295     )
296     .expect_err("JNIEnv#call_static_method_unsafe should return error");
297 
298     assert_pending_java_exception(&env);
299 }
300 
301 #[test]
java_byte_array_from_slice()302 pub fn java_byte_array_from_slice() {
303     let env = attach_current_thread();
304     let buf: &[u8] = &[1, 2, 3];
305     let java_array = env
306         .byte_array_from_slice(buf)
307         .expect("JNIEnv#byte_array_from_slice must create a java array from slice");
308     let obj = AutoLocal::new(&env, unsafe { JObject::from_raw(java_array) });
309 
310     assert!(!obj.as_obj().is_null());
311     let mut res: [i8; 3] = [0; 3];
312     env.get_byte_array_region(java_array, 0, &mut res).unwrap();
313     assert_eq!(res[0], 1);
314     assert_eq!(res[1], 2);
315     assert_eq!(res[2], 3);
316 }
317 
318 macro_rules! test_get_array_elements {
319     ( $jni_get:tt, $jni_type:ty, $new_array:tt, $get_array:tt, $set_array:tt ) => {
320         #[test]
321         pub fn $jni_get() {
322             let env = attach_current_thread();
323 
324             // Create original Java array
325             let buf: &[$jni_type] = &[0 as $jni_type, 1 as $jni_type];
326             let java_array = env
327                 .$new_array(2)
328                 .expect(stringify!(JNIEnv#$new_array must create a Java $jni_type array with given size));
329 
330             // Insert array elements
331             let _ = env.$set_array(java_array, 0, buf);
332 
333             // Use a scope to test Drop
334             {
335                 // Get byte array elements auto wrapper
336                 let auto_ptr: AutoArray<$jni_type> = {
337                     // Make sure the lifetime is tied to the environment,
338                     // not the particular JNIEnv reference
339                     let temporary_env: JNIEnv = *env;
340                     temporary_env.$jni_get(java_array, ReleaseMode::CopyBack).unwrap()
341                 };
342 
343                 // Check array size
344                 assert_eq!(auto_ptr.size().unwrap(), 2);
345 
346                 // Check pointer access
347                 let ptr = auto_ptr.as_ptr();
348                 assert_eq!(unsafe { *ptr.offset(0) } as i32, 0);
349                 assert_eq!(unsafe { *ptr.offset(1) } as i32, 1);
350 
351                 // Check pointer From access
352                 let ptr: *mut $jni_type = std::convert::From::from(&auto_ptr);
353                 assert_eq!(unsafe { *ptr.offset(0) } as i32, 0);
354                 assert_eq!(unsafe { *ptr.offset(1) } as i32, 1);
355 
356                 // Check pointer into() access
357                 let ptr: *mut $jni_type = (&auto_ptr).into();
358                 assert_eq!(unsafe { *ptr.offset(0) } as i32, 0);
359                 assert_eq!(unsafe { *ptr.offset(1) } as i32, 1);
360 
361                 // Modify
362                 unsafe {
363                     *ptr.offset(0) += 1 as $jni_type;
364                     *ptr.offset(1) -= 1 as $jni_type;
365                 }
366 
367                 // Commit would be necessary here, if there were no closure
368                 //auto_ptr.commit().unwrap();
369             }
370 
371             // Confirm modification of original Java array
372             let mut res: [$jni_type; 2] = [0 as $jni_type; 2];
373             env.$get_array(java_array, 0, &mut res).unwrap();
374             assert_eq!(res[0] as i32, 1);
375             assert_eq!(res[1] as i32, 0);
376         }
377     };
378 }
379 
380 // Test generic get_array_elements
381 test_get_array_elements!(
382     get_array_elements,
383     jint,
384     new_int_array,
385     get_int_array_region,
386     set_int_array_region
387 );
388 
389 // Test type-specific array accessors
390 test_get_array_elements!(
391     get_int_array_elements,
392     jint,
393     new_int_array,
394     get_int_array_region,
395     set_int_array_region
396 );
397 
398 test_get_array_elements!(
399     get_long_array_elements,
400     jlong,
401     new_long_array,
402     get_long_array_region,
403     set_long_array_region
404 );
405 
406 test_get_array_elements!(
407     get_byte_array_elements,
408     jbyte,
409     new_byte_array,
410     get_byte_array_region,
411     set_byte_array_region
412 );
413 
414 test_get_array_elements!(
415     get_boolean_array_elements,
416     jboolean,
417     new_boolean_array,
418     get_boolean_array_region,
419     set_boolean_array_region
420 );
421 
422 test_get_array_elements!(
423     get_char_array_elements,
424     jchar,
425     new_char_array,
426     get_char_array_region,
427     set_char_array_region
428 );
429 
430 test_get_array_elements!(
431     get_short_array_elements,
432     jshort,
433     new_short_array,
434     get_short_array_region,
435     set_short_array_region
436 );
437 
438 test_get_array_elements!(
439     get_float_array_elements,
440     jfloat,
441     new_float_array,
442     get_float_array_region,
443     set_float_array_region
444 );
445 
446 test_get_array_elements!(
447     get_double_array_elements,
448     jdouble,
449     new_double_array,
450     get_double_array_region,
451     set_double_array_region
452 );
453 
454 #[test]
455 #[ignore] // Disabled until issue #283 is resolved
get_long_array_elements_commit()456 pub fn get_long_array_elements_commit() {
457     let env = attach_current_thread();
458 
459     // Create original Java array
460     let buf: &[i64] = &[1, 2, 3];
461     let java_array = env
462         .new_long_array(3)
463         .expect("JNIEnv#new_long_array must create a java array with given size");
464 
465     // Insert array elements
466     let _ = env.set_long_array_region(java_array, 0, buf);
467 
468     // Get long array elements auto wrapper
469     let auto_ptr = env
470         .get_long_array_elements(java_array, ReleaseMode::CopyBack)
471         .unwrap();
472 
473     // Copying the array depends on the VM vendor/version/GC combinations.
474     // If the wrapped array is not being copied, we can skip the test.
475     if !auto_ptr.is_copy() {
476         return;
477     }
478 
479     // Check pointer access
480     let ptr = auto_ptr.as_ptr();
481 
482     // Modify
483     unsafe {
484         *ptr.offset(0) += 1;
485         *ptr.offset(1) += 1;
486         *ptr.offset(2) += 1;
487     }
488 
489     // Check that original Java array is unmodified
490     let mut res: [i64; 3] = [0; 3];
491     env.get_long_array_region(java_array, 0, &mut res).unwrap();
492     assert_eq!(res[0], 1);
493     assert_eq!(res[1], 2);
494     assert_eq!(res[2], 3);
495 
496     auto_ptr.commit().unwrap();
497 
498     // Confirm modification of original Java array
499     env.get_long_array_region(java_array, 0, &mut res).unwrap();
500     assert_eq!(res[0], 2);
501     assert_eq!(res[1], 3);
502     assert_eq!(res[2], 4);
503 }
504 
505 #[test]
get_primitive_array_critical()506 pub fn get_primitive_array_critical() {
507     let env = attach_current_thread();
508 
509     // Create original Java array
510     let buf: &[u8] = &[1, 2, 3];
511     let java_array = env
512         .byte_array_from_slice(buf)
513         .expect("JNIEnv#byte_array_from_slice must create a java array from slice");
514 
515     // Use a scope to test Drop
516     {
517         // Get primitive array elements auto wrapper
518         let auto_ptr = env
519             .get_primitive_array_critical(java_array, ReleaseMode::CopyBack)
520             .unwrap();
521 
522         // Check array size
523         assert_eq!(auto_ptr.size().unwrap(), 3);
524 
525         // Get pointer
526         let ptr = auto_ptr.as_ptr();
527 
528         // Convert void pointer to an unsigned byte array, without copy
529         let mut vec;
530         unsafe { vec = Vec::from_raw_parts(ptr as *mut u8, 3, 3) }
531 
532         // Check
533         assert_eq!(vec[0], 1);
534         assert_eq!(vec[1], 2);
535         assert_eq!(vec[2], 3);
536 
537         // Modify
538         vec[0] += 1;
539         vec[1] += 1;
540         vec[2] += 1;
541 
542         // Release
543         // Make sure vec's destructor doesn't free the data it thinks it owns when it goes out
544         // of scope (avoid double free)
545         std::mem::forget(vec);
546     }
547 
548     // Confirm modification of original Java array
549     let mut res: [i8; 3] = [0; 3];
550     env.get_byte_array_region(java_array, 0, &mut res).unwrap();
551     assert_eq!(res[0], 2);
552     assert_eq!(res[1], 3);
553     assert_eq!(res[2], 4);
554 }
555 
556 #[test]
get_object_class()557 pub fn get_object_class() {
558     let env = attach_current_thread();
559     let string = env.new_string("test").unwrap();
560     let result = env.get_object_class(string);
561     assert!(result.is_ok());
562     assert!(!result.unwrap().is_null());
563 }
564 
565 #[test]
get_object_class_null_arg()566 pub fn get_object_class_null_arg() {
567     let env = attach_current_thread();
568     let null_obj = JObject::null();
569     let result = env
570         .get_object_class(null_obj)
571         .map_err(|error| matches!(error, Error::NullPtr(_)))
572         .expect_err("JNIEnv#get_object_class should return error for null argument");
573     assert!(result, "ErrorKind::NullPtr expected as error");
574 }
575 
576 #[test]
new_direct_byte_buffer()577 pub fn new_direct_byte_buffer() {
578     let env = attach_current_thread();
579     let vec: Vec<u8> = vec![0, 1, 2, 3];
580     let (addr, len) = {
581         // (would use buf.into_raw_parts() on nightly)
582         let buf = vec.leak();
583         (buf.as_mut_ptr(), buf.len())
584     };
585     let result = unsafe { env.new_direct_byte_buffer(addr, len) };
586     assert!(result.is_ok());
587     assert!(!result.unwrap().is_null());
588 }
589 
590 #[test]
new_direct_byte_buffer_invalid_addr()591 pub fn new_direct_byte_buffer_invalid_addr() {
592     let env = attach_current_thread();
593     let result = unsafe { env.new_direct_byte_buffer(std::ptr::null_mut(), 5) };
594     assert!(result.is_err());
595 }
596 
597 #[test]
get_direct_buffer_capacity_ok()598 pub fn get_direct_buffer_capacity_ok() {
599     let env = attach_current_thread();
600     let vec: Vec<u8> = vec![0, 1, 2, 3];
601     let (addr, len) = {
602         // (would use buf.into_raw_parts() on nightly)
603         let buf = vec.leak();
604         (buf.as_mut_ptr(), buf.len())
605     };
606     let result = unsafe { env.new_direct_byte_buffer(addr, len) }.unwrap();
607     assert!(!result.is_null());
608 
609     let capacity = env.get_direct_buffer_capacity(result).unwrap();
610     assert_eq!(capacity, 4);
611 }
612 
613 #[test]
get_direct_buffer_capacity_wrong_arg()614 pub fn get_direct_buffer_capacity_wrong_arg() {
615     let env = attach_current_thread();
616     let wrong_obj = unsafe { JByteBuffer::from_raw(env.new_string("wrong").unwrap().into_raw()) };
617     let capacity = env.get_direct_buffer_capacity(wrong_obj);
618     assert!(capacity.is_err());
619 }
620 
621 #[test]
get_direct_buffer_capacity_null_arg()622 pub fn get_direct_buffer_capacity_null_arg() {
623     let env = attach_current_thread();
624     let result = env.get_direct_buffer_capacity(JObject::null().into());
625     assert!(result.is_err());
626 }
627 
628 #[test]
get_direct_buffer_address_ok()629 pub fn get_direct_buffer_address_ok() {
630     let env = attach_current_thread();
631     let vec: Vec<u8> = vec![0, 1, 2, 3];
632     let (addr, len) = {
633         // (would use buf.into_raw_parts() on nightly)
634         let buf = vec.leak();
635         (buf.as_mut_ptr(), buf.len())
636     };
637     let result = unsafe { env.new_direct_byte_buffer(addr, len) }.unwrap();
638     assert!(!result.is_null());
639 
640     let dest_buffer = env.get_direct_buffer_address(result).unwrap();
641     assert_eq!(addr, dest_buffer);
642 }
643 
644 #[test]
get_direct_buffer_address_wrong_arg()645 pub fn get_direct_buffer_address_wrong_arg() {
646     let env = attach_current_thread();
647     let wrong_obj: JObject = env.new_string("wrong").unwrap().into();
648     let result = env.get_direct_buffer_address(wrong_obj.into());
649     assert!(result.is_err());
650 }
651 
652 #[test]
get_direct_buffer_address_null_arg()653 pub fn get_direct_buffer_address_null_arg() {
654     let env = attach_current_thread();
655     let result = env.get_direct_buffer_address(JObject::null().into());
656     assert!(result.is_err());
657 }
658 
659 // Group test for testing the family of new_PRIMITIVE_array functions with correct arguments
660 #[test]
new_primitive_array_ok()661 pub fn new_primitive_array_ok() {
662     let env = attach_current_thread();
663     const SIZE: jsize = 16;
664 
665     let result = env.new_boolean_array(SIZE);
666     assert!(result.is_ok());
667     assert!(!result.unwrap().is_null());
668 
669     let result = env.new_byte_array(SIZE);
670     assert!(result.is_ok());
671     assert!(!result.unwrap().is_null());
672 
673     let result = env.new_char_array(SIZE);
674     assert!(result.is_ok());
675     assert!(!result.unwrap().is_null());
676 
677     let result = env.new_short_array(SIZE);
678     assert!(result.is_ok());
679     assert!(!result.unwrap().is_null());
680 
681     let result = env.new_int_array(SIZE);
682     assert!(result.is_ok());
683     assert!(!result.unwrap().is_null());
684 
685     let result = env.new_long_array(SIZE);
686     assert!(result.is_ok());
687     assert!(!result.unwrap().is_null());
688 
689     let result = env.new_float_array(SIZE);
690     assert!(result.is_ok());
691     assert!(!result.unwrap().is_null());
692 
693     let result = env.new_double_array(SIZE);
694     assert!(result.is_ok());
695     assert!(!result.unwrap().is_null());
696 }
697 
698 // Group test for testing the family of new_PRIMITIVE_array functions with wrong arguments
699 #[test]
new_primitive_array_wrong()700 pub fn new_primitive_array_wrong() {
701     let env = attach_current_thread();
702     const WRONG_SIZE: jsize = -1;
703 
704     let result = env.new_boolean_array(WRONG_SIZE);
705     assert_exception(&result, "JNIEnv#new_boolean_array should throw exception");
706     assert_pending_java_exception(&env);
707 
708     let result = env.new_byte_array(WRONG_SIZE);
709     assert_exception(&result, "JNIEnv#new_byte_array should throw exception");
710     assert_pending_java_exception(&env);
711 
712     let result = env.new_char_array(WRONG_SIZE);
713     assert_exception(&result, "JNIEnv#new_char_array should throw exception");
714     assert_pending_java_exception(&env);
715 
716     let result = env.new_short_array(WRONG_SIZE);
717     assert_exception(&result, "JNIEnv#new_short_array should throw exception");
718     assert_pending_java_exception(&env);
719 
720     let result = env.new_int_array(WRONG_SIZE);
721     assert_exception(&result, "JNIEnv#new_int_array should throw exception");
722     assert_pending_java_exception(&env);
723 
724     let result = env.new_long_array(WRONG_SIZE);
725     assert_exception(&result, "JNIEnv#new_long_array should throw exception");
726     assert_pending_java_exception(&env);
727 
728     let result = env.new_float_array(WRONG_SIZE);
729     assert_exception(&result, "JNIEnv#new_float_array should throw exception");
730     assert_pending_java_exception(&env);
731 
732     let result = env.new_double_array(WRONG_SIZE);
733     assert_exception(&result, "JNIEnv#new_double_array should throw exception");
734     assert_pending_java_exception(&env);
735 }
736 
737 #[test]
get_super_class_ok()738 fn get_super_class_ok() {
739     let env = attach_current_thread();
740     let result = env.get_superclass(ARRAYLIST_CLASS);
741     assert!(result.is_ok());
742     assert!(!result.unwrap().is_null());
743 }
744 
745 #[test]
get_super_class_null()746 fn get_super_class_null() {
747     let env = attach_current_thread();
748     let result = env.get_superclass("java/lang/Object");
749     assert!(result.is_ok());
750     assert!(result.unwrap().is_null());
751 }
752 
753 #[test]
convert_byte_array()754 fn convert_byte_array() {
755     let env = attach_current_thread();
756     let src: Vec<u8> = vec![1, 2, 3, 4];
757     let java_byte_array = env.byte_array_from_slice(&src).unwrap();
758 
759     let dest = env.convert_byte_array(java_byte_array);
760     assert!(dest.is_ok());
761     assert_eq!(dest.unwrap(), src);
762 }
763 
764 #[test]
local_ref_null()765 fn local_ref_null() {
766     let env = attach_current_thread();
767     let null_obj = JObject::null();
768 
769     let result = env.new_local_ref::<JObject>(null_obj);
770     assert!(result.is_ok());
771     assert!(result.unwrap().is_null());
772 
773     // try to delete null reference
774     let result = env.delete_local_ref(null_obj);
775     assert!(result.is_ok());
776 }
777 
778 #[test]
new_global_ref_null()779 fn new_global_ref_null() {
780     let env = attach_current_thread();
781     let null_obj = JObject::null();
782     let result = env.new_global_ref(null_obj);
783     assert!(result.is_ok());
784     assert!(result.unwrap().as_obj().is_null());
785 }
786 
787 #[test]
auto_local_null()788 fn auto_local_null() {
789     let env = attach_current_thread();
790     let null_obj = JObject::null();
791     {
792         let auto_ref = AutoLocal::new(&env, null_obj);
793         assert!(auto_ref.as_obj().is_null());
794     }
795     assert!(null_obj.is_null());
796 }
797 
798 #[test]
short_lifetime_with_local_frame()799 fn short_lifetime_with_local_frame() {
800     let env = attach_current_thread();
801     let object = short_lifetime_with_local_frame_sub_fn(&env);
802     assert!(object.is_ok());
803 }
804 
short_lifetime_with_local_frame_sub_fn<'a>(env: &'_ JNIEnv<'a>) -> Result<JObject<'a>, Error>805 fn short_lifetime_with_local_frame_sub_fn<'a>(env: &'_ JNIEnv<'a>) -> Result<JObject<'a>, Error> {
806     env.with_local_frame(16, || {
807         env.new_object(INTEGER_CLASS, "(I)V", &[JValue::from(5)])
808     })
809 }
810 
811 #[test]
short_lifetime_list()812 fn short_lifetime_list() {
813     let env = attach_current_thread();
814     let first_list_object = short_lifetime_list_sub_fn(&env).unwrap();
815     let value = env.call_method(first_list_object, "intValue", "()I", &[]);
816     assert_eq!(value.unwrap().i().unwrap(), 1);
817 }
818 
short_lifetime_list_sub_fn<'a>(env: &'_ JNIEnv<'a>) -> Result<JObject<'a>, Error>819 fn short_lifetime_list_sub_fn<'a>(env: &'_ JNIEnv<'a>) -> Result<JObject<'a>, Error> {
820     let list_object = env.new_object(ARRAYLIST_CLASS, "()V", &[])?;
821     let list = JList::from_env(env, list_object)?;
822     let element = env.new_object(INTEGER_CLASS, "(I)V", &[JValue::from(1)])?;
823     list.add(element)?;
824     short_lifetime_list_sub_fn_get_first_element(&list)
825 }
826 
short_lifetime_list_sub_fn_get_first_element<'a>( list: &'_ JList<'a, '_>, ) -> Result<JObject<'a>, Error>827 fn short_lifetime_list_sub_fn_get_first_element<'a>(
828     list: &'_ JList<'a, '_>,
829 ) -> Result<JObject<'a>, Error> {
830     let mut iterator = list.iter()?;
831     Ok(iterator.next().unwrap())
832 }
833 
834 #[test]
get_object_array_element()835 fn get_object_array_element() {
836     let env = attach_current_thread();
837     let array = env
838         .new_object_array(1, STRING_CLASS, JObject::null())
839         .unwrap();
840     assert!(!array.is_null());
841     assert!(env.get_object_array_element(array, 0).unwrap().is_null());
842     let test_str = env.new_string("test").unwrap();
843     env.set_object_array_element(array, 0, test_str).unwrap();
844     assert!(!env.get_object_array_element(array, 0).unwrap().is_null());
845 }
846 
847 #[test]
throw_new()848 pub fn throw_new() {
849     let env = attach_current_thread();
850 
851     let result = env.throw_new(RUNTIME_EXCEPTION_CLASS, "Test Exception");
852     assert!(result.is_ok());
853     assert_pending_java_exception_detailed(
854         &env,
855         Some(RUNTIME_EXCEPTION_CLASS),
856         Some("Test Exception"),
857     );
858 }
859 
860 #[test]
throw_new_fail()861 pub fn throw_new_fail() {
862     let env = attach_current_thread();
863 
864     let result = env.throw_new("java/lang/NonexistentException", "Test Exception");
865     assert!(result.is_err());
866     // Just to clear the java.lang.NoClassDefFoundError
867     assert_pending_java_exception(&env);
868 }
869 
870 #[test]
throw_defaults()871 pub fn throw_defaults() {
872     let env = attach_current_thread();
873 
874     test_throwable_descriptor_with_default_type(&env, TEST_EXCEPTION_MESSAGE);
875     test_throwable_descriptor_with_default_type(&env, TEST_EXCEPTION_MESSAGE.to_owned());
876     test_throwable_descriptor_with_default_type(&env, JNIString::from(TEST_EXCEPTION_MESSAGE));
877 }
878 
879 #[test]
test_conversion()880 pub fn test_conversion() {
881     let env = attach_current_thread();
882     let orig_obj: JObject = env.new_string("Hello, world!").unwrap().into();
883 
884     let string = JString::from(orig_obj);
885     let actual = JObject::from(string);
886     assert!(unwrap(&env, env.is_same_object(orig_obj, actual)));
887 
888     let global_ref = env.new_global_ref(orig_obj).unwrap();
889     let actual = JObject::from(&global_ref);
890     assert!(unwrap(&env, env.is_same_object(orig_obj, actual)));
891 
892     let auto_local = env.auto_local(orig_obj);
893     let actual = JObject::from(&auto_local);
894     assert!(unwrap(&env, env.is_same_object(orig_obj, actual)));
895 }
896 
test_throwable_descriptor_with_default_type<'a, D>(env: &JNIEnv<'a>, descriptor: D) where D: Desc<'a, JThrowable<'a>>,897 fn test_throwable_descriptor_with_default_type<'a, D>(env: &JNIEnv<'a>, descriptor: D)
898 where
899     D: Desc<'a, JThrowable<'a>>,
900 {
901     let result = descriptor.lookup(env);
902     assert!(result.is_ok());
903     let exception = result.unwrap();
904 
905     assert_exception_type(env, exception, RUNTIME_EXCEPTION_CLASS);
906     assert_exception_message(env, exception, TEST_EXCEPTION_MESSAGE);
907 }
908 
909 // Helper method that asserts that result is Error and the cause is JavaException.
assert_exception(res: &Result<jobject, Error>, expect_message: &str)910 fn assert_exception(res: &Result<jobject, Error>, expect_message: &str) {
911     assert!(res.is_err());
912     assert!(res
913         .as_ref()
914         .map_err(|error| matches!(error, Error::JavaException))
915         .expect_err(expect_message));
916 }
917 
918 // Shortcut to `assert_pending_java_exception_detailed()` without checking for expected  type and
919 // message of exception.
assert_pending_java_exception(env: &JNIEnv)920 fn assert_pending_java_exception(env: &JNIEnv) {
921     assert_pending_java_exception_detailed(env, None, None)
922 }
923 
924 // Helper method that asserts there is a pending Java exception of `expected_type` with
925 // `expected_message` and clears it if any.
assert_pending_java_exception_detailed( env: &JNIEnv, expected_type: Option<&str>, expected_message: Option<&str>, )926 fn assert_pending_java_exception_detailed(
927     env: &JNIEnv,
928     expected_type: Option<&str>,
929     expected_message: Option<&str>,
930 ) {
931     assert!(env.exception_check().unwrap());
932     let exception = env.exception_occurred().expect("Unable to get exception");
933     env.exception_clear().unwrap();
934 
935     if let Some(expected_type) = expected_type {
936         assert_exception_type(env, exception, expected_type);
937     }
938 
939     if let Some(expected_message) = expected_message {
940         assert_exception_message(env, exception, expected_message);
941     }
942 }
943 
944 // Asserts that exception is of `expected_type` type.
assert_exception_type(env: &JNIEnv, exception: JThrowable, expected_type: &str)945 fn assert_exception_type(env: &JNIEnv, exception: JThrowable, expected_type: &str) {
946     assert!(env.is_instance_of(exception, expected_type).unwrap());
947 }
948 
949 // Asserts that exception's message is `expected_message`.
assert_exception_message(env: &JNIEnv, exception: JThrowable, expected_message: &str)950 fn assert_exception_message(env: &JNIEnv, exception: JThrowable, expected_message: &str) {
951     let message = env
952         .call_method(exception, "getMessage", "()Ljava/lang/String;", &[])
953         .unwrap()
954         .l()
955         .unwrap();
956     let msg_rust: String = env.get_string(message.into()).unwrap().into();
957     assert_eq!(msg_rust, expected_message);
958 }
959