use jvm::{ClassInstance, JavaChar, JavaError, Result, runtime::JavaLangString};
use test_utils::test_jvm;
#[tokio::test]
async fn test_to_rust_string_unpaired_surrogate() -> Result<()> {
let jvm = test_jvm().await?;
let mut chars = jvm.instantiate_array("C", 3).await?;
jvm.store_array(&mut chars, 0, [0x61 as JavaChar, 0xd800, 0x62]).await?;
let string = jvm.new_class("java/lang/String", "([C)V", (chars,)).await?;
assert_eq!(JavaLangString::to_rust_string(&jvm, &string).await?, "a\u{fffd}b");
Ok(())
}
#[tokio::test]
async fn test_to_utf16_preserves_unpaired_surrogate() -> Result<()> {
let jvm = test_jvm().await?;
let mut chars = jvm.instantiate_array("C", 3).await?;
jvm.store_array(&mut chars, 0, [0x61 as JavaChar, 0xd800, 0x62]).await?;
let string = jvm.new_class("java/lang/String", "([C)V", (chars,)).await?;
assert_eq!(JavaLangString::to_utf16(&jvm, &string).await?, [0x61, 0xd800, 0x62]);
Ok(())
}
#[tokio::test]
async fn test_to_utf16_on_substring_preserves_unpaired_surrogate() -> Result<()> {
let jvm = test_jvm().await?;
let mut chars = jvm.instantiate_array("C", 3).await?;
jvm.store_array(&mut chars, 0, [0x61 as JavaChar, 0xd800, 0x62]).await?;
let string = jvm.new_class("java/lang/String", "([C)V", (chars,)).await?;
let sub = jvm
.invoke_virtual(&string, &string.class_definition().name(), "substring", "(II)Ljava/lang/String;", (1, 3))
.await?;
assert_eq!(JavaLangString::to_utf16(&jvm, &sub).await?, [0xd800, 0x62]);
Ok(())
}
#[tokio::test]
async fn test_intern_on_substring_uses_logical_slice() -> Result<()> {
let jvm = test_jvm().await?;
let parent = JavaLangString::from_rust_string(&jvm, "xxHelloyy").await?;
let sub = jvm
.invoke_virtual(&parent, &parent.class_definition().name(), "substring", "(II)Ljava/lang/String;", (2, 7))
.await?;
let interned: Box<dyn ClassInstance> = jvm
.invoke_virtual(&sub, &sub.class_definition().name(), "intern", "()Ljava/lang/String;", ())
.await?;
let pooled = jvm.intern_string("Hello").await?;
assert!(interned == pooled);
let independent = JavaLangString::from_rust_string(&jvm, "Hello").await?;
let independent_interned: Box<dyn ClassInstance> = jvm
.invoke_virtual(&independent, &independent.class_definition().name(), "intern", "()Ljava/lang/String;", ())
.await?;
assert!(interned == independent_interned);
Ok(())
}
#[tokio::test]
async fn test_intern_identity_survives_gc() -> Result<()> {
let jvm = test_jvm().await?;
jvm.collect_garbage()?;
let a = jvm.intern_string("interned").await?;
let b = jvm.intern_string("interned").await?;
assert!(a == b);
let garbage_count = jvm.collect_garbage()?;
assert_eq!(garbage_count, 0);
let c = jvm.intern_string("interned").await?;
assert!(a == c);
Ok(())
}
#[tokio::test]
async fn test_array_bounds_check_survives_offset_overflow() -> Result<()> {
let jvm = test_jvm().await?;
let mut chars = jvm.instantiate_array("C", 3).await?;
jvm.store_array(&mut chars, 0, [0x61 as JavaChar, 0x62, 0x63]).await?;
for offset in [usize::MAX, usize::MAX - 2, 4] {
let result: Result<Vec<JavaChar>> = jvm.load_array(&chars, offset, 3).await;
let Err(JavaError::JavaException(exception)) = result else {
panic!("load_array at {offset} must report a java exception");
};
assert!(jvm.is_instance(&*exception, "java/lang/ArrayIndexOutOfBoundsException"));
let result = jvm.store_array(&mut chars, offset, [0x64 as JavaChar]).await;
assert!(matches!(result, Err(JavaError::JavaException(_))));
}
let loaded: Vec<JavaChar> = jvm.load_array(&chars, 0, 3).await?;
assert_eq!(loaded, [0x61, 0x62, 0x63]);
Ok(())
}