• 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, JObject::from(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                     env.$jni_get(java_array, ReleaseMode::CopyBack).unwrap();
338 
339                 // Check array size
340                 assert_eq!(auto_ptr.size().unwrap(), 2);
341 
342                 // Check pointer access
343                 let ptr = auto_ptr.as_ptr();
344                 assert_eq!(unsafe { *ptr.offset(0) } as i32, 0);
345                 assert_eq!(unsafe { *ptr.offset(1) } as i32, 1);
346 
347                 // Check pointer From access
348                 let ptr: *mut $jni_type = std::convert::From::from(&auto_ptr);
349                 assert_eq!(unsafe { *ptr.offset(0) } as i32, 0);
350                 assert_eq!(unsafe { *ptr.offset(1) } as i32, 1);
351 
352                 // Check pointer into() access
353                 let ptr: *mut $jni_type = (&auto_ptr).into();
354                 assert_eq!(unsafe { *ptr.offset(0) } as i32, 0);
355                 assert_eq!(unsafe { *ptr.offset(1) } as i32, 1);
356 
357                 // Modify
358                 unsafe {
359                     *ptr.offset(0) += 1 as $jni_type;
360                     *ptr.offset(1) -= 1 as $jni_type;
361                 }
362 
363                 // Commit would be necessary here, if there were no closure
364                 //auto_ptr.commit().unwrap();
365             }
366 
367             // Confirm modification of original Java array
368             let mut res: [$jni_type; 2] = [0 as $jni_type; 2];
369             env.$get_array(java_array, 0, &mut res).unwrap();
370             assert_eq!(res[0] as i32, 1);
371             assert_eq!(res[1] as i32, 0);
372         }
373     };
374 }
375 
376 // Test generic get_array_elements
377 test_get_array_elements!(
378     get_array_elements,
379     jint,
380     new_int_array,
381     get_int_array_region,
382     set_int_array_region
383 );
384 
385 // Test type-specific array accessors
386 test_get_array_elements!(
387     get_int_array_elements,
388     jint,
389     new_int_array,
390     get_int_array_region,
391     set_int_array_region
392 );
393 
394 test_get_array_elements!(
395     get_long_array_elements,
396     jlong,
397     new_long_array,
398     get_long_array_region,
399     set_long_array_region
400 );
401 
402 test_get_array_elements!(
403     get_byte_array_elements,
404     jbyte,
405     new_byte_array,
406     get_byte_array_region,
407     set_byte_array_region
408 );
409 
410 test_get_array_elements!(
411     get_boolean_array_elements,
412     jboolean,
413     new_boolean_array,
414     get_boolean_array_region,
415     set_boolean_array_region
416 );
417 
418 test_get_array_elements!(
419     get_char_array_elements,
420     jchar,
421     new_char_array,
422     get_char_array_region,
423     set_char_array_region
424 );
425 
426 test_get_array_elements!(
427     get_short_array_elements,
428     jshort,
429     new_short_array,
430     get_short_array_region,
431     set_short_array_region
432 );
433 
434 test_get_array_elements!(
435     get_float_array_elements,
436     jfloat,
437     new_float_array,
438     get_float_array_region,
439     set_float_array_region
440 );
441 
442 test_get_array_elements!(
443     get_double_array_elements,
444     jdouble,
445     new_double_array,
446     get_double_array_region,
447     set_double_array_region
448 );
449 
450 #[test]
451 #[ignore] // Disabled until issue #283 is resolved
get_long_array_elements_commit()452 pub fn get_long_array_elements_commit() {
453     let env = attach_current_thread();
454 
455     // Create original Java array
456     let buf: &[i64] = &[1, 2, 3];
457     let java_array = env
458         .new_long_array(3)
459         .expect("JNIEnv#new_long_array must create a java array with given size");
460 
461     // Insert array elements
462     let _ = env.set_long_array_region(java_array, 0, buf);
463 
464     // Get long array elements auto wrapper
465     let auto_ptr = env
466         .get_long_array_elements(java_array, ReleaseMode::CopyBack)
467         .unwrap();
468 
469     // Copying the array depends on the VM vendor/version/GC combinations.
470     // If the wrapped array is not being copied, we can skip the test.
471     if !auto_ptr.is_copy() {
472         return;
473     }
474 
475     // Check pointer access
476     let ptr = auto_ptr.as_ptr();
477 
478     // Modify
479     unsafe {
480         *ptr.offset(0) += 1;
481         *ptr.offset(1) += 1;
482         *ptr.offset(2) += 1;
483     }
484 
485     // Check that original Java array is unmodified
486     let mut res: [i64; 3] = [0; 3];
487     env.get_long_array_region(java_array, 0, &mut res).unwrap();
488     assert_eq!(res[0], 1);
489     assert_eq!(res[1], 2);
490     assert_eq!(res[2], 3);
491 
492     auto_ptr.commit().unwrap();
493 
494     // Confirm modification of original Java array
495     env.get_long_array_region(java_array, 0, &mut res).unwrap();
496     assert_eq!(res[0], 2);
497     assert_eq!(res[1], 3);
498     assert_eq!(res[2], 4);
499 }
500 
501 #[test]
get_primitive_array_critical()502 pub fn get_primitive_array_critical() {
503     let env = attach_current_thread();
504 
505     // Create original Java array
506     let buf: &[u8] = &[1, 2, 3];
507     let java_array = env
508         .byte_array_from_slice(buf)
509         .expect("JNIEnv#byte_array_from_slice must create a java array from slice");
510 
511     // Use a scope to test Drop
512     {
513         // Get primitive array elements auto wrapper
514         let auto_ptr = env
515             .get_primitive_array_critical(java_array, ReleaseMode::CopyBack)
516             .unwrap();
517 
518         // Check array size
519         assert_eq!(auto_ptr.size().unwrap(), 3);
520 
521         // Get pointer
522         let ptr = auto_ptr.as_ptr();
523 
524         // Convert void pointer to an unsigned byte array, without copy
525         let mut vec;
526         unsafe { vec = Vec::from_raw_parts(ptr as *mut u8, 3, 3) }
527 
528         // Check
529         assert_eq!(vec[0], 1);
530         assert_eq!(vec[1], 2);
531         assert_eq!(vec[2], 3);
532 
533         // Modify
534         vec[0] += 1;
535         vec[1] += 1;
536         vec[2] += 1;
537 
538         // Release
539         // Make sure vec's destructor doesn't free the data it thinks it owns when it goes out
540         // of scope (avoid double free)
541         std::mem::forget(vec);
542     }
543 
544     // Confirm modification of original Java array
545     let mut res: [i8; 3] = [0; 3];
546     env.get_byte_array_region(java_array, 0, &mut res).unwrap();
547     assert_eq!(res[0], 2);
548     assert_eq!(res[1], 3);
549     assert_eq!(res[2], 4);
550 }
551 
552 #[test]
get_object_class()553 pub fn get_object_class() {
554     let env = attach_current_thread();
555     let string = env.new_string("test").unwrap();
556     let result = env.get_object_class(string);
557     assert!(result.is_ok());
558     assert!(!result.unwrap().is_null());
559 }
560 
561 #[test]
get_object_class_null_arg()562 pub fn get_object_class_null_arg() {
563     let env = attach_current_thread();
564     let null_obj = JObject::null();
565     let result = env
566         .get_object_class(null_obj)
567         .map_err(|error| matches!(error, Error::NullPtr(_)))
568         .expect_err("JNIEnv#get_object_class should return error for null argument");
569     assert!(result, "ErrorKind::NullPtr expected as error");
570 }
571 
572 #[test]
new_direct_byte_buffer()573 pub fn new_direct_byte_buffer() {
574     let env = attach_current_thread();
575     let mut vec: Vec<u8> = vec![0, 1, 2, 3];
576     let buf = vec.as_mut_slice();
577     let result = env.new_direct_byte_buffer(buf);
578     assert!(result.is_ok());
579     assert!(!result.unwrap().is_null());
580 }
581 
582 #[test]
get_direct_buffer_capacity_ok()583 pub fn get_direct_buffer_capacity_ok() {
584     let env = attach_current_thread();
585     let mut vec: Vec<u8> = vec![0, 1, 2, 3];
586     let buf = vec.as_mut_slice();
587     let result = env.new_direct_byte_buffer(buf).unwrap();
588     assert!(!result.is_null());
589 
590     let capacity = env.get_direct_buffer_capacity(result).unwrap();
591     assert_eq!(capacity, 4);
592 }
593 
594 #[test]
get_direct_buffer_capacity_wrong_arg()595 pub fn get_direct_buffer_capacity_wrong_arg() {
596     let env = attach_current_thread();
597     let wrong_obj = JByteBuffer::from(env.new_string("wrong").unwrap().into_inner());
598     let capacity = env.get_direct_buffer_capacity(wrong_obj);
599     assert!(capacity.is_err());
600 }
601 
602 #[test]
get_direct_buffer_address_ok()603 pub fn get_direct_buffer_address_ok() {
604     let env = attach_current_thread();
605     let mut vec: Vec<u8> = vec![0, 1, 2, 3];
606     let buf = vec.as_mut_slice();
607     let result = env.new_direct_byte_buffer(buf).unwrap();
608     assert!(!result.is_null());
609 
610     let dest_buffer = env.get_direct_buffer_address(result).unwrap();
611     assert_eq!(buf, dest_buffer);
612 }
613 
614 #[test]
get_direct_buffer_address_wrong_arg()615 pub fn get_direct_buffer_address_wrong_arg() {
616     let env = attach_current_thread();
617     let wrong_obj: JObject = env.new_string("wrong").unwrap().into();
618     let result = env.get_direct_buffer_address(wrong_obj.into());
619     assert!(result.is_err());
620 }
621 
622 #[test]
get_direct_buffer_address_null_arg()623 pub fn get_direct_buffer_address_null_arg() {
624     let env = attach_current_thread();
625     let result = env.get_direct_buffer_address(JObject::null().into());
626     assert!(result.is_err());
627 }
628 
629 // Group test for testing the family of new_PRIMITIVE_array functions with correct arguments
630 #[test]
new_primitive_array_ok()631 pub fn new_primitive_array_ok() {
632     let env = attach_current_thread();
633     const SIZE: jsize = 16;
634 
635     let result = env.new_boolean_array(SIZE);
636     assert!(result.is_ok());
637     assert!(!result.unwrap().is_null());
638 
639     let result = env.new_byte_array(SIZE);
640     assert!(result.is_ok());
641     assert!(!result.unwrap().is_null());
642 
643     let result = env.new_char_array(SIZE);
644     assert!(result.is_ok());
645     assert!(!result.unwrap().is_null());
646 
647     let result = env.new_short_array(SIZE);
648     assert!(result.is_ok());
649     assert!(!result.unwrap().is_null());
650 
651     let result = env.new_int_array(SIZE);
652     assert!(result.is_ok());
653     assert!(!result.unwrap().is_null());
654 
655     let result = env.new_long_array(SIZE);
656     assert!(result.is_ok());
657     assert!(!result.unwrap().is_null());
658 
659     let result = env.new_float_array(SIZE);
660     assert!(result.is_ok());
661     assert!(!result.unwrap().is_null());
662 
663     let result = env.new_double_array(SIZE);
664     assert!(result.is_ok());
665     assert!(!result.unwrap().is_null());
666 }
667 
668 // Group test for testing the family of new_PRIMITIVE_array functions with wrong arguments
669 #[test]
new_primitive_array_wrong()670 pub fn new_primitive_array_wrong() {
671     let env = attach_current_thread();
672     const WRONG_SIZE: jsize = -1;
673 
674     let result = env.new_boolean_array(WRONG_SIZE);
675     assert_exception(&result, "JNIEnv#new_boolean_array should throw exception");
676     assert_pending_java_exception(&env);
677 
678     let result = env.new_byte_array(WRONG_SIZE);
679     assert_exception(&result, "JNIEnv#new_byte_array should throw exception");
680     assert_pending_java_exception(&env);
681 
682     let result = env.new_char_array(WRONG_SIZE);
683     assert_exception(&result, "JNIEnv#new_char_array should throw exception");
684     assert_pending_java_exception(&env);
685 
686     let result = env.new_short_array(WRONG_SIZE);
687     assert_exception(&result, "JNIEnv#new_short_array should throw exception");
688     assert_pending_java_exception(&env);
689 
690     let result = env.new_int_array(WRONG_SIZE);
691     assert_exception(&result, "JNIEnv#new_int_array should throw exception");
692     assert_pending_java_exception(&env);
693 
694     let result = env.new_long_array(WRONG_SIZE);
695     assert_exception(&result, "JNIEnv#new_long_array should throw exception");
696     assert_pending_java_exception(&env);
697 
698     let result = env.new_float_array(WRONG_SIZE);
699     assert_exception(&result, "JNIEnv#new_float_array should throw exception");
700     assert_pending_java_exception(&env);
701 
702     let result = env.new_double_array(WRONG_SIZE);
703     assert_exception(&result, "JNIEnv#new_double_array should throw exception");
704     assert_pending_java_exception(&env);
705 }
706 
707 #[test]
get_super_class_ok()708 fn get_super_class_ok() {
709     let env = attach_current_thread();
710     let result = env.get_superclass(ARRAYLIST_CLASS);
711     assert!(result.is_ok());
712     assert!(!result.unwrap().is_null());
713 }
714 
715 #[test]
get_super_class_null()716 fn get_super_class_null() {
717     let env = attach_current_thread();
718     let result = env.get_superclass("java/lang/Object");
719     assert!(result.is_ok());
720     assert!(result.unwrap().is_null());
721 }
722 
723 #[test]
convert_byte_array()724 fn convert_byte_array() {
725     let env = attach_current_thread();
726     let src: Vec<u8> = vec![1, 2, 3, 4];
727     let java_byte_array = env.byte_array_from_slice(&src).unwrap();
728 
729     let dest = env.convert_byte_array(java_byte_array);
730     assert!(dest.is_ok());
731     assert_eq!(dest.unwrap(), src);
732 }
733 
734 #[test]
local_ref_null()735 fn local_ref_null() {
736     let env = attach_current_thread();
737     let null_obj = JObject::null();
738 
739     let result = env.new_local_ref::<JObject>(null_obj);
740     assert!(result.is_ok());
741     assert!(result.unwrap().is_null());
742 
743     // try to delete null reference
744     let result = env.delete_local_ref(null_obj);
745     assert!(result.is_ok());
746 }
747 
748 #[test]
new_global_ref_null()749 fn new_global_ref_null() {
750     let env = attach_current_thread();
751     let null_obj = JObject::null();
752     let result = env.new_global_ref(null_obj);
753     assert!(result.is_ok());
754     assert!(result.unwrap().as_obj().is_null());
755 }
756 
757 #[test]
auto_local_null()758 fn auto_local_null() {
759     let env = attach_current_thread();
760     let null_obj = JObject::null();
761     {
762         let auto_ref = AutoLocal::new(&env, null_obj);
763         assert!(auto_ref.as_obj().is_null());
764     }
765     assert!(null_obj.is_null());
766 }
767 
768 #[test]
short_lifetime_with_local_frame()769 fn short_lifetime_with_local_frame() {
770     let env = attach_current_thread();
771     let object = short_lifetime_with_local_frame_sub_fn(&env);
772     assert!(object.is_ok());
773 }
774 
short_lifetime_with_local_frame_sub_fn<'a>(env: &'_ JNIEnv<'a>) -> Result<JObject<'a>, Error>775 fn short_lifetime_with_local_frame_sub_fn<'a>(env: &'_ JNIEnv<'a>) -> Result<JObject<'a>, Error> {
776     env.with_local_frame(16, || {
777         env.new_object(INTEGER_CLASS, "(I)V", &[JValue::from(5)])
778     })
779 }
780 
781 #[test]
short_lifetime_list()782 fn short_lifetime_list() {
783     let env = attach_current_thread();
784     let first_list_object = short_lifetime_list_sub_fn(&env).unwrap();
785     let value = env.call_method(first_list_object, "intValue", "()I", &[]);
786     assert_eq!(value.unwrap().i().unwrap(), 1);
787 }
788 
short_lifetime_list_sub_fn<'a>(env: &'_ JNIEnv<'a>) -> Result<JObject<'a>, Error>789 fn short_lifetime_list_sub_fn<'a>(env: &'_ JNIEnv<'a>) -> Result<JObject<'a>, Error> {
790     let list_object = env.new_object(ARRAYLIST_CLASS, "()V", &[])?;
791     let list = JList::from_env(env, list_object)?;
792     let element = env.new_object(INTEGER_CLASS, "(I)V", &[JValue::from(1)])?;
793     list.add(element)?;
794     short_lifetime_list_sub_fn_get_first_element(&list)
795 }
796 
short_lifetime_list_sub_fn_get_first_element<'a>( list: &'_ JList<'a, '_>, ) -> Result<JObject<'a>, Error>797 fn short_lifetime_list_sub_fn_get_first_element<'a>(
798     list: &'_ JList<'a, '_>,
799 ) -> Result<JObject<'a>, Error> {
800     let mut iterator = list.iter()?;
801     Ok(iterator.next().unwrap())
802 }
803 
804 #[test]
get_object_array_element()805 fn get_object_array_element() {
806     let env = attach_current_thread();
807     let array = env
808         .new_object_array(1, STRING_CLASS, JObject::null())
809         .unwrap();
810     assert!(!array.is_null());
811     assert!(env.get_object_array_element(array, 0).unwrap().is_null());
812     let test_str = env.new_string("test").unwrap();
813     env.set_object_array_element(array, 0, test_str).unwrap();
814     assert!(!env.get_object_array_element(array, 0).unwrap().is_null());
815 }
816 
817 #[test]
throw_new()818 pub fn throw_new() {
819     let env = attach_current_thread();
820 
821     let result = env.throw_new(RUNTIME_EXCEPTION_CLASS, "Test Exception");
822     assert!(result.is_ok());
823     assert_pending_java_exception_detailed(
824         &env,
825         Some(RUNTIME_EXCEPTION_CLASS),
826         Some("Test Exception"),
827     );
828 }
829 
830 #[test]
throw_new_fail()831 pub fn throw_new_fail() {
832     let env = attach_current_thread();
833 
834     let result = env.throw_new("java/lang/NonexistentException", "Test Exception");
835     assert!(result.is_err());
836     // Just to clear the java.lang.NoClassDefFoundError
837     assert_pending_java_exception(&env);
838 }
839 
840 #[test]
throw_defaults()841 pub fn throw_defaults() {
842     let env = attach_current_thread();
843 
844     test_throwable_descriptor_with_default_type(&env, TEST_EXCEPTION_MESSAGE);
845     test_throwable_descriptor_with_default_type(&env, TEST_EXCEPTION_MESSAGE.to_owned());
846     test_throwable_descriptor_with_default_type(&env, JNIString::from(TEST_EXCEPTION_MESSAGE));
847 }
848 
849 #[test]
test_conversion()850 pub fn test_conversion() {
851     let env = attach_current_thread();
852     let orig_obj: JObject = env.new_string("Hello, world!").unwrap().into();
853 
854     let string = JString::from(orig_obj);
855     let actual = JObject::from(string);
856     assert!(unwrap(&env, env.is_same_object(orig_obj, actual)));
857 
858     let global_ref = env.new_global_ref(orig_obj).unwrap();
859     let actual = JObject::from(&global_ref);
860     assert!(unwrap(&env, env.is_same_object(orig_obj, actual)));
861 
862     let auto_local = env.auto_local(orig_obj);
863     let actual = JObject::from(&auto_local);
864     assert!(unwrap(&env, env.is_same_object(orig_obj, actual)));
865 }
866 
test_throwable_descriptor_with_default_type<'a, D>(env: &JNIEnv<'a>, descriptor: D) where D: Desc<'a, JThrowable<'a>>,867 fn test_throwable_descriptor_with_default_type<'a, D>(env: &JNIEnv<'a>, descriptor: D)
868 where
869     D: Desc<'a, JThrowable<'a>>,
870 {
871     let result = descriptor.lookup(env);
872     assert!(result.is_ok());
873     let exception = result.unwrap();
874 
875     assert_exception_type(env, exception, RUNTIME_EXCEPTION_CLASS);
876     assert_exception_message(env, exception, TEST_EXCEPTION_MESSAGE);
877 }
878 
879 // Helper method that asserts that result is Error and the cause is JavaException.
assert_exception(res: &Result<jobject, Error>, expect_message: &str)880 fn assert_exception(res: &Result<jobject, Error>, expect_message: &str) {
881     assert!(res.is_err());
882     assert!(res
883         .as_ref()
884         .map_err(|error| matches!(error, Error::JavaException))
885         .expect_err(expect_message));
886 }
887 
888 // Shortcut to `assert_pending_java_exception_detailed()` without checking for expected  type and
889 // message of exception.
assert_pending_java_exception(env: &JNIEnv)890 fn assert_pending_java_exception(env: &JNIEnv) {
891     assert_pending_java_exception_detailed(env, None, None)
892 }
893 
894 // Helper method that asserts there is a pending Java exception of `expected_type` with
895 // `expected_message` and clears it if any.
assert_pending_java_exception_detailed( env: &JNIEnv, expected_type: Option<&str>, expected_message: Option<&str>, )896 fn assert_pending_java_exception_detailed(
897     env: &JNIEnv,
898     expected_type: Option<&str>,
899     expected_message: Option<&str>,
900 ) {
901     assert!(env.exception_check().unwrap());
902     let exception = env.exception_occurred().expect("Unable to get exception");
903     env.exception_clear().unwrap();
904 
905     if let Some(expected_type) = expected_type {
906         assert_exception_type(env, exception, expected_type);
907     }
908 
909     if let Some(expected_message) = expected_message {
910         assert_exception_message(env, exception, expected_message);
911     }
912 }
913 
914 // Asserts that exception is of `expected_type` type.
assert_exception_type(env: &JNIEnv, exception: JThrowable, expected_type: &str)915 fn assert_exception_type(env: &JNIEnv, exception: JThrowable, expected_type: &str) {
916     assert!(env.is_instance_of(exception, expected_type).unwrap());
917 }
918 
919 // Asserts that exception's message is `expected_message`.
assert_exception_message(env: &JNIEnv, exception: JThrowable, expected_message: &str)920 fn assert_exception_message(env: &JNIEnv, exception: JThrowable, expected_message: &str) {
921     let message = env
922         .call_method(exception, "getMessage", "()Ljava/lang/String;", &[])
923         .unwrap()
924         .l()
925         .unwrap();
926     let msg_rust: String = env.get_string(message.into()).unwrap().into();
927     assert_eq!(msg_rust, expected_message);
928 }
929