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