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