Skip to content

Commit 843be94

Browse files
committed
test: cover attach breakpoint scan filtering
1 parent 5969484 commit 843be94

1 file changed

Lines changed: 124 additions & 0 deletions

File tree

src/test/java/org/javacs/JavaDebugServerTest.java

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package org.javacs;
22

3+
import com.sun.jdi.ReferenceType;
4+
import com.sun.jdi.VirtualMachine;
35
import java.io.IOException;
6+
import java.lang.reflect.Field;
7+
import java.lang.reflect.InvocationTargetException;
8+
import java.lang.reflect.Method;
9+
import java.lang.reflect.Proxy;
410
import java.nio.file.Path;
511
import java.nio.file.Paths;
612
import java.util.List;
@@ -81,6 +87,98 @@ private void setBreakpoint(String className, int line) {
8187
server.setBreakpoints(set);
8288
}
8389

90+
private static void setVm(JavaDebugServer server, VirtualMachine vm) throws ReflectiveOperationException {
91+
Field field = JavaDebugServer.class.getDeclaredField("vm");
92+
field.setAccessible(true);
93+
field.set(server, vm);
94+
}
95+
96+
@SuppressWarnings("unchecked")
97+
private static List<Breakpoint> pendingBreakpoints(JavaDebugServer server) throws ReflectiveOperationException {
98+
Field field = JavaDebugServer.class.getDeclaredField("pendingBreakpoints");
99+
field.setAccessible(true);
100+
return (List<Breakpoint>) field.get(server);
101+
}
102+
103+
private static Method privateMethod(String name, Class<?>... parameterTypes) throws ReflectiveOperationException {
104+
Method method = JavaDebugServer.class.getDeclaredMethod(name, parameterTypes);
105+
method.setAccessible(true);
106+
return method;
107+
}
108+
109+
private static VirtualMachine fakeVm(String defaultStratum, List<ReferenceType> allClasses) {
110+
return (VirtualMachine)
111+
Proxy.newProxyInstance(
112+
VirtualMachine.class.getClassLoader(),
113+
new Class<?>[] {VirtualMachine.class},
114+
(proxy, method, args) -> {
115+
switch (method.getName()) {
116+
case "allClasses":
117+
return allClasses;
118+
case "getDefaultStratum":
119+
return defaultStratum;
120+
case "toString":
121+
return "fakeVm";
122+
default:
123+
throw new UnsupportedOperationException(method.getName());
124+
}
125+
});
126+
}
127+
128+
private static ReferenceType fakeType(String name, String sourcePath) {
129+
return (ReferenceType)
130+
Proxy.newProxyInstance(
131+
ReferenceType.class.getClassLoader(),
132+
new Class<?>[] {ReferenceType.class},
133+
(proxy, method, args) -> {
134+
switch (method.getName()) {
135+
case "name":
136+
return name;
137+
case "sourcePaths":
138+
return List.of(sourcePath);
139+
case "toString":
140+
return name;
141+
default:
142+
throw new UnsupportedOperationException(method.getName());
143+
}
144+
});
145+
}
146+
147+
private static ReferenceType fakeIrrelevantType(String name) {
148+
return (ReferenceType)
149+
Proxy.newProxyInstance(
150+
ReferenceType.class.getClassLoader(),
151+
new Class<?>[] {ReferenceType.class},
152+
(proxy, method, args) -> {
153+
switch (method.getName()) {
154+
case "name":
155+
return name;
156+
case "sourcePaths":
157+
throw new AssertionError("Irrelevant classes should not need source paths");
158+
case "toString":
159+
return name;
160+
default:
161+
throw new UnsupportedOperationException(method.getName());
162+
}
163+
});
164+
}
165+
166+
private static <T> T invoke(Method method, Object target, Object... args) throws ReflectiveOperationException {
167+
try {
168+
@SuppressWarnings("unchecked")
169+
T result = (T) method.invoke(target, args);
170+
return result;
171+
} catch (InvocationTargetException e) {
172+
if (e.getCause() instanceof RuntimeException) {
173+
throw (RuntimeException) e.getCause();
174+
}
175+
if (e.getCause() instanceof Error) {
176+
throw (Error) e.getCause();
177+
}
178+
throw e;
179+
}
180+
}
181+
84182
@Test
85183
public void attachToProcess() throws IOException, InterruptedException {
86184
launchProcess("Hello");
@@ -89,6 +187,32 @@ public void attachToProcess() throws IOException, InterruptedException {
89187
process.waitFor();
90188
}
91189

190+
@Test
191+
public void loadedTypesMatchingSkipsIrrelevantClasses() throws ReflectiveOperationException {
192+
var relevant = fakeType("com.example.Hello", "com/example/Hello.java");
193+
var irrelevant = fakeIrrelevantType("org.other.Bar");
194+
setVm(server, fakeVm("Java", List.of(relevant, irrelevant)));
195+
196+
var loadedTypesMatching = privateMethod("loadedTypesMatching", String.class);
197+
List<ReferenceType> matches = invoke(loadedTypesMatching, server, "/tmp/src/com/example/Hello.java");
198+
org.junit.Assert.assertEquals(1, matches.size());
199+
org.junit.Assert.assertSame(relevant, matches.get(0));
200+
}
201+
202+
@Test
203+
public void enablePendingBreakpointsInLoadedClassesSkipsIrrelevantClasses() throws ReflectiveOperationException {
204+
var pending = new Breakpoint();
205+
pending.source = new Source();
206+
pending.source.path = "/tmp/src/com/example/Hello.java";
207+
pending.line = 4;
208+
pendingBreakpoints(server).add(pending);
209+
setVm(server, fakeVm("Java", List.of(fakeIrrelevantType("org.other.Bar"))));
210+
211+
invoke(privateMethod("enablePendingBreakpointsInLoadedClasses"), server);
212+
213+
org.junit.Assert.assertEquals(1, pendingBreakpoints(server).size());
214+
}
215+
92216
@Test
93217
public void setBreakpoint() throws IOException, InterruptedException {
94218
launchProcess("Hello");

0 commit comments

Comments
 (0)