Skip to content

Commit 3ec8633

Browse files
trancexpressiloveeclipse
authored andcommitted
Trigger GC in test snippet via JDI
During TestLogicalStructures, its possible that the test snippet triggers GC before the debug framework has called Interpreter.disableCollection() on objects created for the logical structure. In this case, the test fails with an ObjectCollectedException. This change adjusts how GC is performed in the test snippet of TestLogicalStructures. Instead of waiting a fixed amount of time in the snippet and then running GC, the tests now run GC with an evaluation, via JDI. This ensures that Interpreter.disableCollection() is called before GC is forced in the test snippet, avoiding potential sporadic failures. Fixes: #301
1 parent ffa9eaa commit 3ec8633

File tree

3 files changed

+87
-37
lines changed

3 files changed

+87
-37
lines changed

org.eclipse.jdt.debug.tests/java9/LogicalStructures.java

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,8 @@
1919
* Tests built in logical structures.
2020
*/
2121
public class LogicalStructures {
22-
22+
2323
public static void main(String[] args) {
24-
generateGarbage();
25-
2624
Map<String, Integer> map = new HashMap<>();
2725
map.put("one", 1);
2826
map.put("two", 2);
@@ -33,18 +31,15 @@ public static void main(String[] args) {
3331
Map.Entry<String, Integer> entry = set.iterator().next();
3432
entry.getKey();
3533
}
36-
37-
private static void generateGarbage() {
38-
// generate garbage repeatedly to verify that logical values don't get GCed
39-
Timer timer = new Timer(true);
40-
timer.scheduleAtFixedRate(new TimerTask() {
41-
private List<String> garbage;
42-
@Override
43-
public void run() {
44-
System.gc();
45-
garbage = Arrays.asList(new String("a"), new String("b"), new String("c"));
46-
}
47-
}, 200, 20);
34+
35+
// called from test to verify that logical values don't get GCed
36+
private static void generateGarbageAndGC() throws InterruptedException {
37+
List<Object> garbage;
38+
for (int i = 0; i < 10; ++i) {
39+
System.gc();
40+
garbage = Arrays.asList(new Object(), new Object(), new Object());
41+
Thread.sleep(20);
42+
}
4843
}
4944

5045
}

org.eclipse.jdt.debug.tests/testprograms/LogicalStructures.java

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,8 @@
1919
* Tests built in logical structures.
2020
*/
2121
public class LogicalStructures {
22-
22+
2323
public static void main(String[] args) {
24-
generateGarbage();
25-
2624
Map<String, Integer> map = new HashMap<>();
2725
map.put("one", 1);
2826
map.put("two", 2);
@@ -33,18 +31,15 @@ public static void main(String[] args) {
3331
Map.Entry<String, Integer> entry = set.iterator().next();
3432
entry.getKey();
3533
}
36-
37-
private static void generateGarbage() {
38-
// generate garbage repeatedly to verify that logical values don't get GCed
39-
Timer timer = new Timer(true);
40-
timer.scheduleAtFixedRate(new TimerTask() {
41-
private List<String> garbage;
42-
@Override
43-
public void run() {
44-
System.gc();
45-
garbage = Arrays.asList(new String("a"), new String("b"), new String("c"));
46-
}
47-
}, 200, 20);
34+
35+
// called from test to verify that logical values don't get GCed
36+
private static void generateGarbageAndGC() throws InterruptedException {
37+
List<Object> garbage;
38+
for (int i = 0; i < 10; ++i) {
39+
System.gc();
40+
garbage = Arrays.asList(new Object(), new Object(), new Object());
41+
Thread.sleep(20);
42+
}
4843
}
4944

5045
}

org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/variables/TestLogicalStructures.java

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,29 @@
1313
*******************************************************************************/
1414
package org.eclipse.jdt.debug.tests.variables;
1515

16+
import java.io.PrintWriter;
17+
import java.io.StringWriter;
18+
import java.util.concurrent.atomic.AtomicBoolean;
19+
20+
import org.eclipse.core.runtime.CoreException;
21+
import org.eclipse.debug.core.DebugEvent;
22+
import org.eclipse.debug.core.DebugException;
1623
import org.eclipse.debug.core.DebugPlugin;
1724
import org.eclipse.debug.core.ILogicalStructureType;
1825
import org.eclipse.debug.core.model.IBreakpoint;
1926
import org.eclipse.debug.core.model.IValue;
2027
import org.eclipse.debug.core.model.IVariable;
2128
import org.eclipse.jdt.debug.core.IJavaArray;
29+
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
2230
import org.eclipse.jdt.debug.core.IJavaObject;
2331
import org.eclipse.jdt.debug.core.IJavaStackFrame;
2432
import org.eclipse.jdt.debug.core.IJavaThread;
2533
import org.eclipse.jdt.debug.core.IJavaVariable;
34+
import org.eclipse.jdt.debug.eval.IAstEvaluationEngine;
35+
import org.eclipse.jdt.debug.eval.IEvaluationListener;
36+
import org.eclipse.jdt.debug.eval.IEvaluationResult;
2637
import org.eclipse.jdt.debug.tests.AbstractDebugTest;
38+
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
2739

2840
/**
2941
* Tests for logical structures
@@ -44,7 +56,7 @@ public TestLogicalStructures(String name) {
4456
*/
4557
public void testListLogicalStructure() throws Exception {
4658
String typeName = "LogicalStructures";
47-
createLineBreakpoint(34, typeName);
59+
createLineBreakpoint(32, typeName);
4860
IJavaThread thread= null;
4961
try {
5062
thread= launchToBreakpoint(typeName);
@@ -64,7 +76,7 @@ public void testListLogicalStructure() throws Exception {
6476
assertEquals("Should be one logical structure type", 1, types.length);
6577

6678
IJavaObject logicalValue = (IJavaObject) types[0].getLogicalStructure(value);
67-
Thread.sleep(500); // run a few GC cycles
79+
gcInSnippet(frame);
6880
assertEquals("Logical value should be an array", "java.lang.Object[]", logicalValue.getJavaType().getName());
6981

7082
IJavaArray array = (IJavaArray) logicalValue;
@@ -81,7 +93,7 @@ public void testListLogicalStructure() throws Exception {
8193
*/
8294
public void testMapLogicalStructure() throws Exception {
8395
String typeName = "LogicalStructures";
84-
createLineBreakpoint(34, typeName);
96+
createLineBreakpoint(32, typeName);
8597
IJavaThread thread= null;
8698
try {
8799
thread= launchToBreakpoint(typeName);
@@ -101,7 +113,7 @@ public void testMapLogicalStructure() throws Exception {
101113
assertEquals("Should be one logical structure type", 1, types.length);
102114

103115
IJavaObject logicalValue = (IJavaObject) types[0].getLogicalStructure(value);
104-
Thread.sleep(500); // run a few GC cycles
116+
gcInSnippet(frame);
105117
assertEquals("Logical value should be an array", "java.lang.Object[]", logicalValue.getJavaType().getName());
106118

107119
IJavaArray array = (IJavaArray) logicalValue;
@@ -118,7 +130,7 @@ public void testMapLogicalStructure() throws Exception {
118130
*/
119131
public void testEntryLogicalStructure() throws Exception {
120132
String typeName = "LogicalStructures";
121-
createLineBreakpoint(34, typeName);
133+
createLineBreakpoint(32, typeName);
122134
IJavaThread thread= null;
123135
try {
124136
thread= launchToBreakpoint(typeName);
@@ -138,15 +150,63 @@ public void testEntryLogicalStructure() throws Exception {
138150
assertEquals("Should be one logical structure type", 1, types.length);
139151

140152
IJavaObject logicalValue = (IJavaObject) types[0].getLogicalStructure(value);
141-
Thread.sleep(500); // run a few GC cycles
153+
gcInSnippet(frame);
142154
IVariable[] children = logicalValue.getVariables();
143155
assertEquals("Should be two elements in the structure", 2, children.length);
144156
assertEquals("First entry should be key", "key", children[0].getName());
145157
assertEquals("Second entry should be value", "value", children[1].getName());
146-
147158
} finally {
148159
terminateAndRemove(thread);
149160
removeAllBreakpoints();
150161
}
151162
}
163+
164+
private void gcInSnippet(IJavaStackFrame stackFrame) throws CoreException, InterruptedException {
165+
IAstEvaluationEngine engine = JDIDebugPlugin.getDefault().getEvaluationEngine(getProjectContext(), (IJavaDebugTarget) stackFrame.getDebugTarget());
166+
EvaluationListener listener = new EvaluationListener();
167+
engine.evaluate("generateGarbageAndGC()", stackFrame, listener, DebugEvent.EVALUATION_IMPLICIT, false);
168+
listener.await();
169+
170+
}
171+
172+
private static class EvaluationListener implements IEvaluationListener {
173+
174+
private final AtomicBoolean done = new AtomicBoolean(false);
175+
private final StringBuilder errors = new StringBuilder();
176+
177+
@Override
178+
public void evaluationComplete(IEvaluationResult result) {
179+
if (result.hasErrors()) {
180+
errors.append("Evaluation resulted in errors:");
181+
errors.append(System.lineSeparator());
182+
for (String error : result.getErrorMessages()) {
183+
errors.append(error);
184+
errors.append(System.lineSeparator());
185+
}
186+
DebugException exception = result.getException();
187+
if (exception != null) {
188+
StringWriter sw = new StringWriter();
189+
PrintWriter pw = new PrintWriter(sw);
190+
exception.printStackTrace(pw);
191+
errors.append(sw.getBuffer().toString());
192+
errors.append(System.lineSeparator());
193+
}
194+
}
195+
done.set(true);
196+
197+
}
198+
199+
private void await() throws InterruptedException {
200+
long start = System.currentTimeMillis();
201+
while (!done.get() && System.currentTimeMillis() - start <= DEFAULT_TIMEOUT) {
202+
Thread.sleep(10);
203+
}
204+
if (!done.get()) {
205+
fail("Timeout occurred while waiting on evaluation result");
206+
}
207+
if (!errors.isEmpty()) {
208+
fail(errors.toString());
209+
}
210+
}
211+
}
152212
}

0 commit comments

Comments
 (0)