Skip to content

Commit 8836e41

Browse files
committed
Move class initialization tests to Java fixture
1 parent d361c9e commit 8836e41

10 files changed

Lines changed: 131 additions & 239 deletions

AGENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,8 @@
2222
- `classfile/` - Class file parser
2323
- `java_class_proto/` - Java class prototypes
2424
- `test_utils/` - Shared test utilities
25+
26+
## Testing Boundaries
27+
- Keep `java_runtime/tests/classes` limited to Java standard library class and API behavior.
28+
- Test JVM and interpreter semantics, including class initialization, bytecode execution, and monitor behavior, with compiled Java fixtures under `test_data/src` and expected output under `test_data`, executed by `tests/test_class.rs`.
29+
- Do not place JVM core behavior tests in the `java_runtime` standard library test tree.

java_runtime/tests/classes/java/lang/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ mod test_boolean;
22
mod test_byte;
33
mod test_character;
44
mod test_class;
5-
mod test_class_initialization;
65
mod test_cldc11_exceptions;
76
mod test_double;
87
mod test_float;

java_runtime/tests/classes/java/lang/test_class_initialization.rs

Lines changed: 0 additions & 238 deletions
This file was deleted.
783 Bytes
Binary file not shown.
809 Bytes
Binary file not shown.
814 Bytes
Binary file not shown.
616 Bytes
Binary file not shown.

test_data/ConcurrentClinit.class

1.7 KB
Binary file not shown.

test_data/ConcurrentClinit.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
42
2+
42
3+
1
4+
java.lang.ExceptionInInitializerError
5+
java.lang.NoClassDefFoundError
6+
java.lang.NoClassDefFoundError
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
public class ConcurrentClinit {
2+
private static volatile boolean successfulOwnerStarted;
3+
private static volatile boolean successfulWaiterStarted;
4+
private static volatile boolean failingOwnerStarted;
5+
private static volatile boolean failingWaiterStarted;
6+
7+
private static class Successful {
8+
static int initializationCount;
9+
static int value;
10+
11+
static {
12+
initializationCount++;
13+
successfulOwnerStarted = true;
14+
while (!successfulWaiterStarted) {
15+
Thread.yield();
16+
}
17+
try {
18+
Thread.sleep(10);
19+
} catch (InterruptedException exception) {
20+
throw new RuntimeException("interrupted");
21+
}
22+
value = 42;
23+
}
24+
}
25+
26+
private static class Failing {
27+
static int value;
28+
29+
static {
30+
failingOwnerStarted = true;
31+
while (!failingWaiterStarted) {
32+
Thread.yield();
33+
}
34+
try {
35+
Thread.sleep(10);
36+
} catch (InterruptedException exception) {
37+
throw new RuntimeException("interrupted");
38+
}
39+
if (failingOwnerStarted) {
40+
throw new RuntimeException("initialization failed");
41+
}
42+
}
43+
}
44+
45+
private static class SuccessfulReader implements Runnable {
46+
private final boolean waiter;
47+
int value;
48+
49+
SuccessfulReader(boolean waiter) {
50+
this.waiter = waiter;
51+
}
52+
53+
public void run() {
54+
if (waiter) {
55+
successfulWaiterStarted = true;
56+
}
57+
value = Successful.value;
58+
}
59+
}
60+
61+
private static class FailingReader implements Runnable {
62+
private final boolean waiter;
63+
String errorClass;
64+
65+
FailingReader(boolean waiter) {
66+
this.waiter = waiter;
67+
}
68+
69+
public void run() {
70+
if (waiter) {
71+
failingWaiterStarted = true;
72+
}
73+
try {
74+
int ignored = Failing.value;
75+
} catch (Throwable throwable) {
76+
errorClass = throwable.getClass().getName();
77+
}
78+
}
79+
}
80+
81+
public static void main(String[] args) throws Exception {
82+
SuccessfulReader successfulOwner = new SuccessfulReader(false);
83+
Thread successfulOwnerThread = new Thread(successfulOwner);
84+
successfulOwnerThread.start();
85+
while (!successfulOwnerStarted) {
86+
Thread.yield();
87+
}
88+
89+
SuccessfulReader successfulWaiter = new SuccessfulReader(true);
90+
Thread successfulWaiterThread = new Thread(successfulWaiter);
91+
successfulWaiterThread.start();
92+
successfulOwnerThread.join();
93+
successfulWaiterThread.join();
94+
95+
System.out.println(successfulOwner.value);
96+
System.out.println(successfulWaiter.value);
97+
System.out.println(Successful.initializationCount);
98+
99+
FailingReader failingOwner = new FailingReader(false);
100+
Thread failingOwnerThread = new Thread(failingOwner);
101+
failingOwnerThread.start();
102+
while (!failingOwnerStarted) {
103+
Thread.yield();
104+
}
105+
106+
FailingReader failingWaiter = new FailingReader(true);
107+
Thread failingWaiterThread = new Thread(failingWaiter);
108+
failingWaiterThread.start();
109+
failingOwnerThread.join();
110+
failingWaiterThread.join();
111+
112+
System.out.println(failingOwner.errorClass);
113+
System.out.println(failingWaiter.errorClass);
114+
try {
115+
int ignored = Failing.value;
116+
} catch (Throwable throwable) {
117+
System.out.println(throwable.getClass().getName());
118+
}
119+
}
120+
}

0 commit comments

Comments
 (0)