Skip to content

Commit 1292ea8

Browse files
committed
Fix CI lint and improve CLDC coverage
1 parent 08d84ae commit 1292ea8

6 files changed

Lines changed: 259 additions & 9 deletions

File tree

java_runtime/src/classes/java/util/random.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ impl Random {
7575
return Err(jvm.exception("java/lang/IllegalArgumentException", "n must be positive").await);
7676
}
7777

78-
if bound & -bound == bound {
78+
if (bound as u32).is_power_of_two() {
7979
let bits: i32 = jvm.invoke_virtual(&this, "next", "(I)I", (31,)).await?;
8080
return Ok(((bound as i64 * bits as i64) >> 31) as i32);
8181
}

java_runtime/tests/classes/java/io/test_byte_array_input_stream.rs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use jvm::Result;
1+
use jvm::{JavaError, Result};
22

33
use test_utils::test_jvm;
44

@@ -26,3 +26,76 @@ async fn test_mark_reset() -> Result<()> {
2626

2727
Ok(())
2828
}
29+
30+
#[tokio::test]
31+
async fn test_input_stream_default_contract() -> Result<()> {
32+
let jvm = test_jvm().await?;
33+
34+
let mut data = jvm.instantiate_array("B", 3).await?;
35+
jvm.store_array(&mut data, 0, [10i8, 20, 30]).await?;
36+
let stream = jvm.new_class("java/io/ByteArrayInputStream", "([B)V", (data,)).await?;
37+
let target = jvm.instantiate_array("B", 5).await?;
38+
39+
assert_eq!(
40+
jvm.invoke_special::<_, i32>(&stream, "java/io/InputStream", "read", "([BII)I", (target.clone(), 1, 3))
41+
.await?,
42+
3
43+
);
44+
assert_eq!(jvm.load_array::<i8>(&target, 0, 5).await?, [0, 10, 20, 30, 0]);
45+
assert_eq!(
46+
jvm.invoke_special::<_, i32>(&stream, "java/io/InputStream", "read", "([BII)I", (target.clone(), 0, 1))
47+
.await?,
48+
-1
49+
);
50+
assert_eq!(
51+
jvm.invoke_special::<_, i32>(&stream, "java/io/InputStream", "read", "([BII)I", (target.clone(), 0, 0))
52+
.await?,
53+
0
54+
);
55+
56+
let invalid: Result<i32> = jvm
57+
.invoke_special(&stream, "java/io/InputStream", "read", "([BII)I", (target, -1, 1))
58+
.await;
59+
let Err(JavaError::JavaException(exception)) = invalid else {
60+
panic!("invalid range must throw IndexOutOfBoundsException");
61+
};
62+
assert!(jvm.is_instance(&*exception, "java/lang/IndexOutOfBoundsException"));
63+
64+
let mut data = jvm.instantiate_array("B", 3).await?;
65+
jvm.store_array(&mut data, 0, [1i8, 2, 3]).await?;
66+
let stream = jvm.new_class("java/io/ByteArrayInputStream", "([B)V", (data,)).await?;
67+
assert_eq!(
68+
jvm.invoke_special::<_, i64>(&stream, "java/io/InputStream", "skip", "(J)J", (2i64,))
69+
.await?,
70+
2
71+
);
72+
assert_eq!(
73+
jvm.invoke_special::<_, i64>(&stream, "java/io/InputStream", "skip", "(J)J", (5i64,))
74+
.await?,
75+
1
76+
);
77+
assert_eq!(
78+
jvm.invoke_special::<_, i64>(&stream, "java/io/InputStream", "skip", "(J)J", (-1i64,))
79+
.await?,
80+
0
81+
);
82+
assert_eq!(
83+
jvm.invoke_special::<_, i32>(&stream, "java/io/InputStream", "available", "()I", ())
84+
.await?,
85+
0
86+
);
87+
assert!(
88+
!jvm.invoke_special::<_, bool>(&stream, "java/io/InputStream", "markSupported", "()Z", ())
89+
.await?
90+
);
91+
let _: () = jvm.invoke_special(&stream, "java/io/InputStream", "mark", "(I)V", (10,)).await?;
92+
93+
let reset: Result<()> = jvm.invoke_special(&stream, "java/io/InputStream", "reset", "()V", ()).await;
94+
let Err(JavaError::JavaException(exception)) = reset else {
95+
panic!("default reset must throw IOException");
96+
};
97+
assert!(jvm.is_instance(&*exception, "java/io/IOException"));
98+
let _: () = jvm.invoke_special(&stream, "java/io/InputStream", "close", "()V", ()).await?;
99+
100+
Ok(())
101+
}

java_runtime/tests/classes/java/io/test_input_stream_reader.rs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use alloc::{vec, vec::Vec};
22

3-
use jvm::{JavaChar, JavaError, Result, runtime::JavaLangString};
3+
use java_runtime::classes::java::lang::Object;
4+
use jvm::{ClassInstanceRef, JavaChar, JavaError, Result, runtime::JavaLangString};
45

56
use test_utils::test_jvm;
67

@@ -76,3 +77,64 @@ async fn test_input_stream_reader_rejects_unknown_encoding() -> Result<()> {
7677

7778
Ok(())
7879
}
80+
81+
#[tokio::test]
82+
async fn test_reader_default_contract_and_lifecycle() -> Result<()> {
83+
let jvm = test_jvm().await?;
84+
85+
let mut bytes = jvm.instantiate_array("B", 3).await?;
86+
jvm.store_array(&mut bytes, 0, [b'a' as i8, b'b' as i8, b'c' as i8]).await?;
87+
let input = jvm.new_class("java/io/ByteArrayInputStream", "([B)V", (bytes,)).await?;
88+
let reader = jvm.new_class("java/io/InputStreamReader", "(Ljava/io/InputStream;)V", (input,)).await?;
89+
90+
assert!(jvm.invoke_virtual::<_, bool>(&reader, "ready", "()Z", ()).await?);
91+
assert!(!jvm.invoke_special::<_, bool>(&reader, "java/io/Reader", "ready", "()Z", ()).await?);
92+
assert_eq!(jvm.invoke_virtual::<_, i32>(&reader, "read", "()I", ()).await?, 'a' as i32);
93+
94+
let chars = jvm.instantiate_array("C", 2).await?;
95+
assert_eq!(jvm.invoke_virtual::<_, i32>(&reader, "read", "([C)I", (chars.clone(),)).await?, 2);
96+
assert_eq!(jvm.load_array::<JavaChar>(&chars, 0, 2).await?, ['b' as JavaChar, 'c' as JavaChar]);
97+
assert_eq!(jvm.invoke_virtual::<_, i32>(&reader, "read", "()I", ()).await?, -1);
98+
99+
let empty = jvm.instantiate_array("C", 0).await?;
100+
assert_eq!(jvm.invoke_virtual::<_, i32>(&reader, "read", "([CII)I", (empty, 0, 0)).await?, 0);
101+
assert!(!jvm.invoke_virtual::<_, bool>(&reader, "markSupported", "()Z", ()).await?);
102+
103+
let mark: Result<()> = jvm.invoke_virtual(&reader, "mark", "(I)V", (1,)).await;
104+
let Err(JavaError::JavaException(exception)) = mark else {
105+
panic!("default mark must throw IOException");
106+
};
107+
assert!(jvm.is_instance(&*exception, "java/io/IOException"));
108+
109+
let reset: Result<()> = jvm.invoke_virtual(&reader, "reset", "()V", ()).await;
110+
let Err(JavaError::JavaException(exception)) = reset else {
111+
panic!("default reset must throw IOException");
112+
};
113+
assert!(jvm.is_instance(&*exception, "java/io/IOException"));
114+
115+
let negative_skip: Result<i64> = jvm.invoke_virtual(&reader, "skip", "(J)J", (-1i64,)).await;
116+
let Err(JavaError::JavaException(exception)) = negative_skip else {
117+
panic!("negative skip must throw IllegalArgumentException");
118+
};
119+
assert!(jvm.is_instance(&*exception, "java/lang/IllegalArgumentException"));
120+
121+
let mut bytes = jvm.instantiate_array("B", 3).await?;
122+
jvm.store_array(&mut bytes, 0, [b'x' as i8, b'y' as i8, b'z' as i8]).await?;
123+
let input = jvm.new_class("java/io/ByteArrayInputStream", "([B)V", (bytes,)).await?;
124+
let reader = jvm.new_class("java/io/InputStreamReader", "(Ljava/io/InputStream;)V", (input,)).await?;
125+
assert_eq!(jvm.invoke_virtual::<_, i64>(&reader, "skip", "(J)J", (2i64,)).await?, 2);
126+
assert_eq!(jvm.invoke_virtual::<_, i32>(&reader, "read", "()I", ()).await?, 'z' as i32);
127+
assert_eq!(jvm.invoke_virtual::<_, i64>(&reader, "skip", "(J)J", (2i64,)).await?, 0);
128+
let _: () = jvm.invoke_virtual(&reader, "close", "()V", ()).await?;
129+
130+
let null_lock: ClassInstanceRef<Object> = None.into();
131+
let result: Result<()> = jvm
132+
.invoke_special(&reader, "java/io/Reader", "<init>", "(Ljava/lang/Object;)V", (null_lock,))
133+
.await;
134+
let Err(JavaError::JavaException(exception)) = result else {
135+
panic!("null lock must throw NullPointerException");
136+
};
137+
assert!(jvm.is_instance(&*exception, "java/lang/NullPointerException"));
138+
139+
Ok(())
140+
}

java_runtime/tests/classes/java/io/test_print_stream.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
use alloc::vec::Vec;
22

3-
use jvm::{Array, ClassInstanceRef, JavaChar, Result, runtime::JavaLangString};
3+
use java_runtime::classes::java::{
4+
io::OutputStream,
5+
lang::{Object, String},
6+
};
7+
use jvm::{Array, ClassInstanceRef, JavaChar, JavaError, Result, runtime::JavaLangString};
48

59
use test_utils::test_jvm;
610

@@ -36,3 +40,57 @@ async fn test_print_stream_cldc_api() -> Result<()> {
3640

3741
Ok(())
3842
}
43+
44+
#[tokio::test]
45+
async fn test_print_stream_remaining_overloads_and_close() -> Result<()> {
46+
let jvm = test_jvm().await?;
47+
48+
let output = jvm.new_class("java/io/ByteArrayOutputStream", "()V", ()).await?;
49+
let stream = jvm
50+
.new_class("java/io/PrintStream", "(Ljava/io/OutputStream;)V", (output.clone(),))
51+
.await?;
52+
let text = JavaLangString::from_rust_string(&jvm, "obj").await?;
53+
let object: ClassInstanceRef<Object> = text.clone().into();
54+
let null_object: ClassInstanceRef<Object> = None.into();
55+
let null_string: ClassInstanceRef<String> = None.into();
56+
57+
let _: () = jvm.invoke_virtual(&stream, "print", "(Ljava/lang/Object;)V", (object.clone(),)).await?;
58+
let _: () = jvm.invoke_virtual(&stream, "print", "(Ljava/lang/Object;)V", (null_object,)).await?;
59+
let _: () = jvm
60+
.invoke_virtual(&stream, "print", "(Ljava/lang/String;)V", (null_string.clone(),))
61+
.await?;
62+
let _: () = jvm.invoke_virtual(&stream, "print", "(J)V", (9i64,)).await?;
63+
let _: () = jvm.invoke_virtual(&stream, "write", "(I)V", ('|' as i32,)).await?;
64+
65+
let _: () = jvm.invoke_virtual(&stream, "println", "(Ljava/lang/Object;)V", (object,)).await?;
66+
let _: () = jvm.invoke_virtual(&stream, "println", "(Ljava/lang/String;)V", (null_string,)).await?;
67+
let _: () = jvm.invoke_virtual(&stream, "println", "(I)V", (-1,)).await?;
68+
let _: () = jvm.invoke_virtual(&stream, "println", "(J)V", (2i64,)).await?;
69+
let _: () = jvm.invoke_virtual(&stream, "println", "(C)V", ('A' as JavaChar,)).await?;
70+
71+
let mut chars = jvm.instantiate_array("C", 2).await?;
72+
jvm.store_array(&mut chars, 0, ['B' as JavaChar, 'C' as JavaChar]).await?;
73+
let _: () = jvm.invoke_virtual(&stream, "println", "([C)V", (chars,)).await?;
74+
let _: () = jvm.invoke_virtual(&stream, "println", "(B)V", (-3i8,)).await?;
75+
let _: () = jvm.invoke_virtual(&stream, "println", "(S)V", (4i16,)).await?;
76+
let _: () = jvm.invoke_virtual(&stream, "println", "(Z)V", (false,)).await?;
77+
let _: () = jvm.invoke_virtual(&stream, "println", "(F)V", (2.5f32,)).await?;
78+
let _: () = jvm.invoke_virtual(&stream, "close", "()V", ()).await?;
79+
assert!(!jvm.invoke_virtual::<_, bool>(&stream, "checkError", "()Z", ()).await?);
80+
81+
let bytes: ClassInstanceRef<Array<i8>> = jvm.invoke_virtual(&output, "toByteArray", "()[B", ()).await?;
82+
let values: Vec<i8> = jvm.load_array(&bytes, 0, jvm.array_length(&bytes).await?).await?;
83+
assert_eq!(
84+
values.into_iter().map(|value| value as u8).collect::<Vec<_>>(),
85+
b"objnullnull9|obj\nnull\n-1\n2\nA\nBC\n-3\n4\nfalse\n2.5\n"
86+
);
87+
88+
let null_output: ClassInstanceRef<OutputStream> = None.into();
89+
let result = jvm.new_class("java/io/PrintStream", "(Ljava/io/OutputStream;)V", (null_output,)).await;
90+
let Err(JavaError::JavaException(exception)) = result else {
91+
panic!("null output must throw NullPointerException");
92+
};
93+
assert!(jvm.is_instance(&*exception, "java/lang/NullPointerException"));
94+
95+
Ok(())
96+
}

java_runtime/tests/classes/java/io/test_string_writer.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use alloc::vec;
22

3-
use jvm::{JavaChar, Result, runtime::JavaLangString};
3+
use java_runtime::classes::java::lang::Object;
4+
use jvm::{ClassInstanceRef, JavaChar, JavaError, Result, runtime::JavaLangString};
45

56
use test_utils::test_jvm;
67

@@ -17,14 +18,34 @@ async fn test_string_writer() -> Result<()> {
1718

1819
let _: () = jvm.invoke_virtual(&string_writer, "write", "([CII)V", (buf.clone(), 0, 3)).await?;
1920

20-
let _: () = jvm.invoke_virtual(&string_writer, "write", "([CII)V", (buf, 1, 2)).await?;
21+
let _: () = jvm.invoke_virtual(&string_writer, "write", "([CII)V", (buf.clone(), 1, 2)).await?;
2122
let _: () = jvm.invoke_virtual(&string_writer, "write", "(I)V", ('d' as i32,)).await?;
23+
let _: () = jvm.invoke_virtual(&string_writer, "write", "([C)V", (buf,)).await?;
24+
25+
let value = JavaLangString::from_rust_string(&jvm, "XYZ").await?;
26+
let _: () = jvm
27+
.invoke_virtual(&string_writer, "write", "(Ljava/lang/String;)V", (value.clone(),))
28+
.await?;
29+
let _: () = jvm
30+
.invoke_virtual(&string_writer, "write", "(Ljava/lang/String;II)V", (value, 1, 1))
31+
.await?;
32+
let _: () = jvm.invoke_virtual(&string_writer, "flush", "()V", ()).await?;
33+
let _: () = jvm.invoke_virtual(&string_writer, "close", "()V", ()).await?;
2234

2335
let string = jvm.invoke_virtual(&string_writer, "toString", "()Ljava/lang/String;", ()).await.unwrap();
2436

2537
let string = JavaLangString::to_rust_string(&jvm, &string).await?;
2638

27-
assert_eq!(string, "abcbcd"); // cspell: disable-line
39+
assert_eq!(string, "abcbcdabcXYZY"); // cspell: disable-line
40+
41+
let null_lock: ClassInstanceRef<Object> = None.into();
42+
let result: Result<()> = jvm
43+
.invoke_special(&string_writer, "java/io/Writer", "<init>", "(Ljava/lang/Object;)V", (null_lock,))
44+
.await;
45+
let Err(JavaError::JavaException(exception)) = result else {
46+
panic!("null lock must throw NullPointerException");
47+
};
48+
assert!(jvm.is_instance(&*exception, "java/lang/NullPointerException"));
2849

2950
Ok(())
3051
}

java_runtime/tests/classes/java/util/test_timezone.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use java_runtime::classes::java::util::TimeZone;
2-
use jvm::{Array, ClassInstanceRef, Result, runtime::JavaLangString};
1+
use java_runtime::classes::java::{lang::String, util::TimeZone};
2+
use jvm::{Array, ClassInstanceRef, JavaError, Result, runtime::JavaLangString};
33

44
use test_utils::test_jvm;
55

@@ -51,3 +51,39 @@ async fn test_timezone() -> Result<()> {
5151

5252
Ok(())
5353
}
54+
55+
#[tokio::test]
56+
async fn test_simple_timezone_constructors_and_offset_validation() -> Result<()> {
57+
let jvm = test_jvm().await?;
58+
59+
let id = JavaLangString::from_rust_string(&jvm, "Fixed").await?;
60+
let timezone = jvm
61+
.new_class("java/util/SimpleTimeZone", "(ILjava/lang/String;)V", (3_600_000, id))
62+
.await?;
63+
assert_eq!(jvm.invoke_virtual::<_, i32>(&timezone, "getRawOffset", "()I", ()).await?, 3_600_000);
64+
assert_eq!(
65+
jvm.invoke_virtual::<_, i32>(&timezone, "getOffset", "(IIIIII)I", (1, 2026, 0, 1, 1, 0))
66+
.await?,
67+
3_600_000
68+
);
69+
70+
let invalid: Result<i32> = jvm.invoke_virtual(&timezone, "getOffset", "(IIIIII)I", (2, 2026, 0, 1, 1, 0)).await;
71+
let Err(JavaError::JavaException(exception)) = invalid else {
72+
panic!("invalid era must throw IllegalArgumentException");
73+
};
74+
assert!(jvm.is_instance(&*exception, "java/lang/IllegalArgumentException"));
75+
76+
let id = JavaLangString::from_rust_string(&jvm, "Legacy").await?;
77+
let legacy = jvm.new_class("java/util/SimpleTimeZone", "(Ljava/lang/String;)V", (id,)).await?;
78+
assert_eq!(jvm.invoke_virtual::<_, i32>(&legacy, "getRawOffset", "()I", ()).await?, 0);
79+
assert!(!jvm.invoke_virtual::<_, bool>(&legacy, "useDaylightTime", "()Z", ()).await?);
80+
81+
let null_id: ClassInstanceRef<String> = None.into();
82+
let result = jvm.new_class("java/util/SimpleTimeZone", "(ILjava/lang/String;)V", (0, null_id)).await;
83+
let Err(JavaError::JavaException(exception)) = result else {
84+
panic!("null ID must throw NullPointerException");
85+
};
86+
assert!(jvm.is_instance(&*exception, "java/lang/NullPointerException"));
87+
88+
Ok(())
89+
}

0 commit comments

Comments
 (0)