Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreutarass2011-07-06 04:21:43 +0000
committereutarass2011-07-06 04:21:43 +0000
commitcd529d385d79a2ecc6b97a2bd110273c90835e8f (patch)
treea3e1946f812809c127e97bacadccde008568bf18 /plugins/org.eclipse.tm.tcf.debug
parenta6c9682d4414a815f5f78df6b3f6539cfe3e0935 (diff)
downloadorg.eclipse.tcf-cd529d385d79a2ecc6b97a2bd110273c90835e8f.tar.gz
org.eclipse.tcf-cd529d385d79a2ecc6b97a2bd110273c90835e8f.tar.xz
org.eclipse.tcf-cd529d385d79a2ecc6b97a2bd110273c90835e8f.zip
TCF Tests: improved handling of run control groups.
Diffstat (limited to 'plugins/org.eclipse.tm.tcf.debug')
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/ITCFTest.java9
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TCFTestSuite.java8
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestAttachTerminate.java113
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEcho.java5
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEchoERR.java5
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEchoFP.java5
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestExpressions.java186
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestFileSystem.java5
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestPathMap.java5
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestRCBP1.java210
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestStreams.java5
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestSysMonitor.java5
-rw-r--r--plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestTerminals.java5
13 files changed, 409 insertions, 157 deletions
diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/ITCFTest.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/ITCFTest.java
index a10aecb57..961abadf9 100644
--- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/ITCFTest.java
+++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/ITCFTest.java
@@ -10,6 +10,8 @@
*******************************************************************************/
package org.eclipse.tm.internal.tcf.debug.tests;
+import org.eclipse.tm.tcf.services.IRunControl;
+
/**
* Each (sub)test in TCF Test Suite should implement this interface.
*/
@@ -19,4 +21,11 @@ interface ITCFTest {
* Start execution of the test.
*/
void start();
+
+ /**
+ * Check if the test don't need the context to stay suspended.
+ * @param ctx - run control context.
+ * @return true if it is OK to resume the context.
+ */
+ boolean canResume(IRunControl.RunControlContext ctx);
} \ No newline at end of file
diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TCFTestSuite.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TCFTestSuite.java
index bdbc981f4..3a55c09b3 100644
--- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TCFTestSuite.java
+++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TCFTestSuite.java
@@ -24,6 +24,7 @@ import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IPeer;
import org.eclipse.tm.tcf.protocol.Protocol;
import org.eclipse.tm.tcf.services.IMemoryMap;
+import org.eclipse.tm.tcf.services.IRunControl;
import org.eclipse.tm.tcf.services.IPathMap.PathMapRule;
/**
@@ -264,6 +265,13 @@ public class TCFTestSuite {
return active_tests.keySet();
}
+ boolean canResume(IRunControl.RunControlContext ctx) {
+ for (ITCFTest t : active_tests.keySet()) {
+ if (!t.canResume(ctx)) return false;
+ }
+ return true;
+ }
+
void done(ITCFTest test, Throwable error) {
assert active_tests.get(test) != null;
if (error != null && !canceled) errors.add(error);
diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestAttachTerminate.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestAttachTerminate.java
index c7f5b6427..415ddd1b7 100644
--- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestAttachTerminate.java
+++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestAttachTerminate.java
@@ -13,6 +13,7 @@ package org.eclipse.tm.internal.tcf.debug.tests;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Random;
import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IErrorReport;
@@ -27,12 +28,15 @@ class TestAttachTerminate implements ITCFTest, IRunControl.RunControlListener {
private final TCFTestSuite test_suite;
private final IDiagnostics diag;
private final IRunControl rc;
+ private final Random rnd = new Random();
private int cnt = 0;
private final HashMap<String,IRunControl.RunControlContext> ctx_map =
new HashMap<String,IRunControl.RunControlContext>();
- private final HashSet<String> process_ids = new HashSet<String>();
+ private final HashSet<String> test_ctx_ids = new HashSet<String>();
+ private final HashSet<String> suspended_ctx_ids = new HashSet<String>();
+ private final HashMap<String,IToken> resume_cmds = new HashMap<String,IToken>();
TestAttachTerminate(TCFTestSuite test_suite, IChannel channel) {
this.test_suite = test_suite;
@@ -52,32 +56,29 @@ class TestAttachTerminate implements ITCFTest, IRunControl.RunControlListener {
if (error != null) {
exit(error);
}
- else {
- for (int i = 0; i < list.length; i++) {
- if (list[i].equals("RCBP1")) {
- startProcess();
- Protocol.invokeLater(1000, new Runnable() {
- int cnt = 0;
- public void run() {
- if (!test_suite.isActive(TestAttachTerminate.this)) return;
- cnt++;
- if (cnt < 30) {
- Protocol.invokeLater(1000, this);
- }
- else if (test_suite.cancel) {
- exit(null);
- }
- else if (process_ids.isEmpty()) {
- exit(new Error("Missing 'contextAdded' event"));
- }
- else {
- exit(new Error("Missing 'contextRemoved' event for " + process_ids));
- }
- }
- });
- return;
+ else if (list.length > 0) {
+ startTestContext(list[rnd.nextInt(list.length)]);
+ Protocol.invokeLater(100, new Runnable() {
+ int cnt = 0;
+ public void run() {
+ if (!test_suite.isActive(TestAttachTerminate.this)) return;
+ cnt++;
+ if (test_suite.cancel) {
+ exit(null);
+ }
+ else if (cnt < 300) {
+ Protocol.invokeLater(100, this);
+ for (String id : suspended_ctx_ids) resume(id);
+ }
+ else if (test_ctx_ids.isEmpty()) {
+ exit(new Error("Missing 'contextAdded' event"));
+ }
+ else {
+ exit(new Error("Missing 'contextRemoved' event for " + test_ctx_ids));
+ }
}
- }
+ });
+ return;
}
exit(null);
}
@@ -85,9 +86,41 @@ class TestAttachTerminate implements ITCFTest, IRunControl.RunControlListener {
}
}
- private void startProcess() {
+ public boolean canResume(IRunControl.RunControlContext ctx) {
+ if (resume_cmds.get(ctx.getID()) != null) return false;
+ String grp = ctx.getRCGroup();
+ if (grp != null) {
+ for (String id : resume_cmds.keySet()) {
+ IRunControl.RunControlContext c = ctx_map.get(id);
+ if (c == null) return false;
+ if (grp.equals(c.getRCGroup())) return false;
+ }
+ }
+ return true;
+ }
+
+ private void resume(final String id) {
+ IRunControl.RunControlContext ctx = ctx_map.get(id);
+ if (ctx != null && test_suite.canResume(ctx)) {
+ assert resume_cmds.get(id) == null;
+ resume_cmds.put(id, ctx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ assert resume_cmds.get(id) == token;
+ resume_cmds.remove(id);
+ if (error instanceof IErrorReport) {
+ int code = ((IErrorReport)error).getErrorCode();
+ if (code == IErrorReport.TCF_ERROR_ALREADY_RUNNING) return;
+ if (code == IErrorReport.TCF_ERROR_INV_CONTEXT) return;
+ }
+ if (error != null) exit(error);
+ }
+ }));
+ }
+ }
+
+ private void startTestContext(String test_name) {
for (int i = 0; i < 4; i++) {
- diag.runTest("RCBP1", new IDiagnostics.DoneRunTest() {
+ diag.runTest(test_name, new IDiagnostics.DoneRunTest() {
public void doneRunTest(IToken token, Throwable error, String context_id) {
cnt--;
if (error != null) {
@@ -98,7 +131,7 @@ class TestAttachTerminate implements ITCFTest, IRunControl.RunControlListener {
if (ctx_map.get(context_id) == null) {
exit(new Error("Missing 'contextAdded' event for context " + context_id));
}
- process_ids.add(context_id);
+ test_ctx_ids.add(context_id);
diag.cancelTest(context_id, new IDiagnostics.DoneCancelTest() {
public void doneCancelTest(IToken token, Throwable error) {
if (error != null) exit(error);
@@ -118,6 +151,7 @@ class TestAttachTerminate implements ITCFTest, IRunControl.RunControlListener {
}
public void containerResumed(String[] context_ids) {
+ for (String id : context_ids) contextResumed(id);
}
public void containerSuspended(String main_context, String pc,
@@ -149,28 +183,19 @@ class TestAttachTerminate implements ITCFTest, IRunControl.RunControlListener {
public void contextRemoved(String[] context_ids) {
for (String id : context_ids) {
ctx_map.remove(id);
- process_ids.remove(id);
+ test_ctx_ids.remove(id);
+ suspended_ctx_ids.remove(id);
}
- if (cnt == 0 && process_ids.isEmpty()) exit(null);
+ if (cnt == 0 && test_ctx_ids.isEmpty()) exit(null);
}
public void contextResumed(String context) {
+ suspended_ctx_ids.remove(context);
}
public void contextSuspended(String context, String pc, String reason, Map<String,Object> params) {
assert context != null;
- IRunControl.RunControlContext ctx = ctx_map.get(context);
- if (ctx != null && process_ids.contains(ctx.getParentID())) {
- ctx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() {
- public void doneCommand(IToken token, Exception error) {
- if (error instanceof IErrorReport) {
- int code = ((IErrorReport)error).getErrorCode();
- if (code == IErrorReport.TCF_ERROR_ALREADY_RUNNING) return;
- if (code == IErrorReport.TCF_ERROR_INV_CONTEXT) return;
- }
- if (error != null) exit(error);
- }
- });
- }
+ suspended_ctx_ids.add(context);
+ resume(context);
}
} \ No newline at end of file
diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEcho.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEcho.java
index 9117dce09..b5f33175d 100644
--- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEcho.java
+++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEcho.java
@@ -16,6 +16,7 @@ import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IErrorReport;
import org.eclipse.tm.tcf.protocol.IToken;
import org.eclipse.tm.tcf.services.IDiagnostics;
+import org.eclipse.tm.tcf.services.IRunControl;
class TestEcho implements ITCFTest, IDiagnostics.DoneEcho {
@@ -87,4 +88,8 @@ class TestEcho implements ITCFTest, IDiagnostics.DoneEcho {
test_suite.done(this, null);
}
}
+
+ public boolean canResume(IRunControl.RunControlContext ctx) {
+ return true;
+ }
} \ No newline at end of file
diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEchoERR.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEchoERR.java
index bebf589ab..1b49c6c49 100644
--- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEchoERR.java
+++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEchoERR.java
@@ -22,6 +22,7 @@ import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IErrorReport;
import org.eclipse.tm.tcf.protocol.IToken;
import org.eclipse.tm.tcf.services.IDiagnostics;
+import org.eclipse.tm.tcf.services.IRunControl;
class TestEchoERR implements ITCFTest, IDiagnostics.DoneEchoERR {
@@ -110,4 +111,8 @@ class TestEchoERR implements ITCFTest, IDiagnostics.DoneEchoERR {
test_suite.done(this, null);
}
}
+
+ public boolean canResume(IRunControl.RunControlContext ctx) {
+ return true;
+ }
}
diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEchoFP.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEchoFP.java
index c77f8787f..998defdd2 100644
--- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEchoFP.java
+++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestEchoFP.java
@@ -17,6 +17,7 @@ import java.util.Random;
import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IToken;
import org.eclipse.tm.tcf.services.IDiagnostics;
+import org.eclipse.tm.tcf.services.IRunControl;
class TestEchoFP implements ITCFTest, IDiagnostics.DoneEchoFP {
@@ -74,4 +75,8 @@ class TestEchoFP implements ITCFTest, IDiagnostics.DoneEchoFP {
test_suite.done(this, null);
}
}
+
+ public boolean canResume(IRunControl.RunControlContext ctx) {
+ return true;
+ }
}
diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestExpressions.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestExpressions.java
index a861cb341..a54fd3d4c 100644
--- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestExpressions.java
+++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestExpressions.java
@@ -15,10 +15,10 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Random;
import java.util.Set;
import org.eclipse.tm.tcf.protocol.IChannel;
-import org.eclipse.tm.tcf.protocol.IErrorReport;
import org.eclipse.tm.tcf.protocol.IToken;
import org.eclipse.tm.tcf.protocol.Protocol;
import org.eclipse.tm.tcf.services.IBreakpoints;
@@ -38,13 +38,16 @@ class TestExpressions implements ITCFTest,
private final IStackTrace stk;
private final IRunControl rc;
private final IBreakpoints bp;
+ private final Random rnd = new Random();
+ private String test_id;
private String bp_id;
private boolean bp_ok;
private IDiagnostics.ISymbol sym_func3;
private String test_ctx_id;
private String process_id;
private String thread_id;
+ private boolean run_to_bp_done;
private boolean test_done;
private IRunControl.RunControlContext test_ctx;
private IRunControl.RunControlContext thread_ctx;
@@ -53,6 +56,8 @@ class TestExpressions implements ITCFTest,
private String[] stack_trace;
private IStackTrace.StackTraceContext[] stack_frames;
private String[] local_vars;
+ private final HashSet<String> suspended_ctx_ids = new HashSet<String>();
+ private final HashMap<String,IToken> resume_cmds = new HashMap<String,IToken>();
private final HashMap<String,IRunControl.RunControlContext> ctx_map = new HashMap<String,IRunControl.RunControlContext>();
private final Map<String,IExpressions.Expression> expr_ctx = new HashMap<String,IExpressions.Expression>();
private final Map<String,IExpressions.Value> expr_val = new HashMap<String,IExpressions.Value>();
@@ -129,27 +134,30 @@ class TestExpressions implements ITCFTest,
exit(error);
}
else {
- for (int i = 0; i < list.length; i++) {
- if (list[i].equals("RCBP1")) {
- runTest();
- Protocol.invokeLater(1000, new Runnable() {
- int cnt = 0;
- public void run() {
- if (!test_suite.isActive(TestExpressions.this)) return;
- cnt++;
- if (cnt < 60) {
- Protocol.invokeLater(1000, this);
- }
- else if (test_suite.cancel) {
- exit(null);
- }
- else {
- exit(new Error("Missing 'contextRemoved' event for " + test_ctx_id));
- }
+ if (list.length > 0) {
+ test_id = list[rnd.nextInt(list.length)];
+ runTest();
+ Protocol.invokeLater(100, new Runnable() {
+ int cnt = 0;
+ public void run() {
+ if (!test_suite.isActive(TestExpressions.this)) return;
+ cnt++;
+ if (test_suite.cancel) {
+ exit(null);
}
- });
- return;
- }
+ else if (cnt < 600) {
+ Protocol.invokeLater(100, this);
+ for (String id : suspended_ctx_ids) resume(id);
+ }
+ else if (test_ctx_id == null) {
+ exit(new Error("Timeout waiting for reply of Diagnostics.runTest command"));
+ }
+ else {
+ exit(new Error("Missing 'contextRemoved' event for " + test_ctx_id));
+ }
+ }
+ });
+ return;
}
exit(null);
}
@@ -158,6 +166,43 @@ class TestExpressions implements ITCFTest,
}
}
+ public boolean canResume(IRunControl.RunControlContext ctx) {
+ if (test_ctx_id != null && thread_ctx == null) return false;
+ if (resume_cmds.get(ctx.getID()) != null) return false;
+ String grp = ctx.getRCGroup();
+ if (thread_ctx != null && !test_done) {
+ if (ctx.getID().equals(thread_id) || grp != null && grp.equals(thread_ctx.getRCGroup())) {
+ if (sym_func3 == null) return false;
+ if (suspended_pc == null) return false;
+ BigInteger pc0 = new BigInteger(sym_func3.getValue().toString());
+ BigInteger pc1 = new BigInteger(suspended_pc);
+ if (pc0.equals(pc1)) return false;
+ }
+ }
+ if (grp != null) {
+ for (String id : resume_cmds.keySet()) {
+ IRunControl.RunControlContext c = ctx_map.get(id);
+ if (c == null) return false;
+ if (grp.equals(c.getRCGroup())) return false;
+ }
+ }
+ return true;
+ }
+
+ private void resume(final String id) {
+ IRunControl.RunControlContext ctx = ctx_map.get(id);
+ if (ctx != null && test_suite.canResume(ctx)) {
+ assert resume_cmds.get(id) == null;
+ resume_cmds.put(id, ctx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ assert resume_cmds.get(id) == token;
+ resume_cmds.remove(id);
+ if (error != null && suspended_ctx_ids.contains(id)) exit(error);
+ }
+ }));
+ }
+ }
+
@SuppressWarnings("unchecked")
private void runTest() {
if (bp_id == null) {
@@ -193,7 +238,7 @@ class TestExpressions implements ITCFTest,
return;
}
if (test_ctx_id == null) {
- diag.runTest("RCBP1", new IDiagnostics.DoneRunTest() {
+ diag.runTest(test_id, new IDiagnostics.DoneRunTest() {
public void doneRunTest(IToken token, Throwable error, String id) {
if (error != null) {
exit(error);
@@ -201,6 +246,9 @@ class TestExpressions implements ITCFTest,
else if (id == null) {
exit(new Exception("Test context ID must not be null"));
}
+ else if (ctx_map.get(id) == null) {
+ exit(new Exception("Missing context added event"));
+ }
else {
test_ctx_id = id;
runTest();
@@ -221,9 +269,7 @@ class TestExpressions implements ITCFTest,
else {
test_ctx = ctx;
process_id = test_ctx.getProcessID();
- if (!process_id.equals(test_ctx_id)) {
- thread_id = test_ctx_id;
- }
+ if (test_ctx.hasState()) thread_id = test_ctx_id;
runTest();
}
}
@@ -303,17 +349,15 @@ class TestExpressions implements ITCFTest,
});
return;
}
- BigInteger pc0 = new BigInteger(sym_func3.getValue().toString());
- BigInteger pc1 = new BigInteger(suspended_pc);
- if (!pc0.equals(pc1)) {
- suspended_pc = null;
- waiting_suspend = true;
- thread_ctx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() {
- public void doneCommand(IToken token, Exception error) {
- if (error != null) exit(error);
- }
- });
- return;
+ if (!run_to_bp_done) {
+ BigInteger pc0 = new BigInteger(sym_func3.getValue().toString());
+ BigInteger pc1 = new BigInteger(suspended_pc);
+ if (!pc0.equals(pc1)) {
+ waiting_suspend = true;
+ resume(thread_ctx.getID());
+ return;
+ }
+ run_to_bp_done = true;
}
if (stack_trace == null) {
stk.getChildren(thread_id, new IStackTrace.DoneGetChildren() {
@@ -503,11 +547,24 @@ class TestExpressions implements ITCFTest,
return;
}
test_done = true;
- diag.cancelTest(test_ctx_id, new IDiagnostics.DoneCancelTest() {
- public void doneCancelTest(IToken token, Throwable error) {
- if (error != null) exit(error);
- }
- });
+ if (test_suite.canResume(thread_ctx)) {
+ final String id = thread_ctx.getID();
+ assert resume_cmds.get(id) == null;
+ resume_cmds.put(id, diag.cancelTest(test_ctx_id, new IDiagnostics.DoneCancelTest() {
+ public void doneCancelTest(IToken token, Throwable error) {
+ assert resume_cmds.get(id) == token;
+ resume_cmds.remove(id);
+ if (error != null) exit(error);
+ }
+ }));
+ }
+ else {
+ Protocol.invokeLater(20, new Runnable() {
+ public void run() {
+ runTest();
+ }
+ });
+ }
}
private void exit(Throwable x) {
@@ -521,12 +578,12 @@ class TestExpressions implements ITCFTest,
//--------------------------- Run Control listener ---------------------------//
public void containerResumed(String[] context_ids) {
+ for (String id : context_ids) contextResumed(id);
}
public void containerSuspended(String context, String pc, String reason,
Map<String,Object> params, String[] suspended_ids) {
for (String id : suspended_ids) {
- assert id != null;
contextSuspended(id, null, null, null);
}
}
@@ -558,6 +615,7 @@ class TestExpressions implements ITCFTest,
public void contextRemoved(String[] context_ids) {
for (String id : context_ids) {
+ suspended_ctx_ids.remove(id);
ctx_map.remove(id);
if (id.equals(test_ctx_id)) {
if (test_done) {
@@ -576,31 +634,43 @@ class TestExpressions implements ITCFTest,
}
public void contextResumed(String context) {
+ suspended_ctx_ids.remove(context);
+ if (context.equals(thread_id)) {
+ suspended_pc = null;
+ }
}
public void contextSuspended(String context, String pc, String reason, Map<String,Object> params) {
+ assert context != null;
+ suspended_ctx_ids.add(context);
if (context.equals(thread_id)) {
- suspended_pc = pc;
- if (waiting_suspend) {
- waiting_suspend = false;
- runTest();
- }
- }
- if (test_done) {
- IRunControl.RunControlContext ctx = ctx_map.get(context);
- if (ctx != null && process_id != null && process_id.equals(ctx.getParentID())) {
- ctx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() {
- public void doneCommand(IToken token, Exception error) {
- if (error instanceof IErrorReport) {
- int code = ((IErrorReport)error).getErrorCode();
- if (code == IErrorReport.TCF_ERROR_ALREADY_RUNNING) return;
- if (code == IErrorReport.TCF_ERROR_INV_CONTEXT) return;
+ if (pc == null && thread_ctx != null) {
+ thread_ctx.getState(new IRunControl.DoneGetState() {
+ public void doneGetState(IToken token, Exception error,
+ boolean suspended, String pc, String reason,
+ Map<String,Object> params) {
+ if (error != null) {
+ exit(error);
+ }
+ else if (suspended) {
+ suspended_pc = pc;
+ if (waiting_suspend) {
+ waiting_suspend = false;
+ runTest();
+ }
}
- if (error != null) exit(error);
}
});
}
+ else {
+ suspended_pc = pc;
+ if (waiting_suspend) {
+ waiting_suspend = false;
+ runTest();
+ }
+ }
}
+ resume(context);
}
//--------------------------- Expressions listener ---------------------------//
diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestFileSystem.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestFileSystem.java
index 8ebb53a67..5457f5508 100644
--- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestFileSystem.java
+++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestFileSystem.java
@@ -19,6 +19,7 @@ import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IToken;
import org.eclipse.tm.tcf.protocol.Protocol;
import org.eclipse.tm.tcf.services.IFileSystem;
+import org.eclipse.tm.tcf.services.IRunControl;
import org.eclipse.tm.tcf.services.IFileSystem.DirEntry;
import org.eclipse.tm.tcf.services.IFileSystem.FileAttrs;
import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
@@ -424,4 +425,8 @@ class TestFileSystem implements ITCFTest, IFileSystem.DoneStat,
if (!test_suite.isActive(this)) return;
test_suite.done(this, x);
}
+
+ public boolean canResume(IRunControl.RunControlContext ctx) {
+ return true;
+ }
} \ No newline at end of file
diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestPathMap.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestPathMap.java
index b21cc84c7..079a26df3 100644
--- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestPathMap.java
+++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestPathMap.java
@@ -18,6 +18,7 @@ import java.util.Random;
import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IToken;
import org.eclipse.tm.tcf.services.IPathMap;
+import org.eclipse.tm.tcf.services.IRunControl;
import org.eclipse.tm.tcf.services.IPathMap.PathMapRule;
class TestPathMap implements ITCFTest {
@@ -175,4 +176,8 @@ class TestPathMap implements ITCFTest {
if (!test_suite.isActive(this)) return;
test_suite.done(this, x);
}
+
+ public boolean canResume(IRunControl.RunControlContext ctx) {
+ return false;
+ }
}
diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestRCBP1.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestRCBP1.java
index 32ef648ba..8af7995ea 100644
--- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestRCBP1.java
+++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestRCBP1.java
@@ -93,6 +93,8 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
private boolean bp_set_done;
private boolean bp_change_done;
private boolean bp_sync_done;
+ private boolean data_bp_area_done;
+ private ILineNumbers.CodeArea data_bp_area;
private String data_bp_id;
private int data_bp_cnt;
private boolean mem_map_test_running;
@@ -161,7 +163,19 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
@SuppressWarnings("unchecked")
public void breakpointStatusChanged(String id, Map<String,Object> status) {
- if (bp_list.get(id) != null && test_context != null && bp_cnt < 40 && !mem_map_test_running) {
+ if (bp_list.get(id) != null && test_context != null && bp_cnt < 40) {
+ if (test_context != null) {
+ String prs = test_context.getProcessID();
+ if (prs != null) {
+ for (ITCFTest test : test_suite.getActiveTests()) {
+ if (test instanceof TestRCBP1) {
+ TestRCBP1 rcbp = (TestRCBP1)test;
+ if (!rcbp.mem_map_test_running) continue;
+ if (prs.equals(rcbp.test_context.getProcessID())) return;
+ }
+ }
+ }
+ }
String s = (String)status.get(IBreakpoints.STATUS_ERROR);
if (s != null) exit(new Exception("Invalid BP status: " + s));
Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)status.get(IBreakpoints.STATUS_INSTANCES);
@@ -267,6 +281,28 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
if (test_id != null) {
if (test_ctx_id == null) {
startTestContext();
+ Protocol.invokeLater(100, new Runnable() {
+ int cnt = 0;
+ public void run() {
+ if (!test_suite.isActive(TestRCBP1.this)) return;
+ cnt++;
+ if (test_suite.cancel) {
+ exit(null);
+ }
+ else if (cnt < 600) {
+ Protocol.invokeLater(100, this);
+ for (SuspendedContext sc : suspended.values()) {
+ if (sc.ok_to_resume) resume(sc);
+ }
+ }
+ else if (test_ctx_id == null) {
+ exit(new Error("Timeout waiting for reply of Diagnostics.runTest command"));
+ }
+ else {
+ exit(new Error("Missing 'contextRemoved' event for " + test_ctx_id));
+ }
+ }
+ });
return;
}
if (test_context == null) {
@@ -277,6 +313,10 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
getSymbols();
return;
}
+ if (!data_bp_area_done) {
+ getDataBPFile();
+ return;
+ }
if (bp_capabilities == null) {
getBreakpointCapabilities();
return;
@@ -528,6 +568,27 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
for (String name : syms) cmds.put(srv_diag.getSymbol(prs, name, done), name);
}
+ private void getDataBPFile() {
+ ISymbol sym = sym_list.get("tcf_test_func3");
+ if (sym == null || srv_line_numbers == null) {
+ data_bp_area_done = true;
+ runTest();
+ return;
+ }
+ srv_line_numbers.mapToSource(test_ctx_id, sym.getValue(), sym.getValue().longValue() + 1, new ILineNumbers.DoneMapToSource() {
+ public void doneMapToSource(IToken token, Exception error, CodeArea[] areas) {
+ if (error != null) {
+ exit(error);
+ }
+ else {
+ if (areas != null && areas.length > 0) data_bp_area = areas[0];
+ data_bp_area_done = true;
+ runTest();
+ }
+ }
+ });
+ }
+
@SuppressWarnings("unchecked")
private void iniBreakpoints() {
assert !bp_set_done;
@@ -576,7 +637,9 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
m[i].put(IBreakpoints.PROP_LOCATION, "&tcf_test_char");
m[i].put(IBreakpoints.PROP_ACCESSMODE, IBreakpoints.ACCESSMODE_WRITE);
Number ca = (Number)bp_capabilities.get(IBreakpoints.CAPABILITY_ACCESSMODE);
- if (ca != null && (ca.intValue() & IBreakpoints.ACCESSMODE_WRITE) != 0) {
+ if (data_bp_area != null && ca != null && (ca.intValue() & IBreakpoints.ACCESSMODE_WRITE) != 0) {
+ m[i].put(IBreakpoints.PROP_FILE, data_bp_area.file);
+ m[i].put(IBreakpoints.PROP_LINE, data_bp_area.start_line);
data_bp_id = (String)m[i].get(IBreakpoints.PROP_ID);
}
else {
@@ -614,16 +677,22 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
}));
if (id == null) return;
get_state_cmds.add(srv_run_ctrl.getContext(id, new IRunControl.DoneGetContext() {
- public void doneGetContext(IToken token, Exception error, RunControlContext context) {
+ public void doneGetContext(IToken token, Exception error, RunControlContext ctx) {
get_state_cmds.remove(token);
if (test_suite.cancel) return;
if (error != null) {
exit(error);
return;
}
- if (context.hasState()) {
- threads.put(id, context);
- get_state_cmds.add(context.getState(new IRunControl.DoneGetState() {
+ for (ITCFTest t : test_suite.getActiveTests()) {
+ if (t instanceof TestRCBP1 && ((TestRCBP1)t).threads.get(id) != null) {
+ exit(new Exception("Invalid or missing 'CreatorID' context attribute.\nContext: " + ctx));
+ return;
+ }
+ }
+ if (ctx.hasState()) {
+ threads.put(id, ctx);
+ get_state_cmds.add(ctx.getState(new IRunControl.DoneGetState() {
public void doneGetState(IToken token, Exception error,
boolean susp, String pc, String reason,
Map<String, Object> params) {
@@ -840,9 +909,13 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
exit(new Exception("Invalid contextAdded event:\nContext: " + ctx));
return;
}
- String p = ctx.getParentID();
- String c = ctx.getCreatorID();
- if (test_ctx_id.equals(c) || test_ctx_id.equals(p)) {
+ if (isMyContext(ctx)) {
+ for (ITCFTest t : test_suite.getActiveTests()) {
+ if (t instanceof TestRCBP1 && ((TestRCBP1)t).threads.get(id) != null) {
+ exit(new Exception("Invalid or missing 'CreatorID' context attribute.\nContext: " + ctx));
+ return;
+ }
+ }
if (!bp_change_done) {
exit(new Exception("Unexpected contextAdded event\nContext: " + ctx));
return;
@@ -864,12 +937,19 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
for (RunControlContext ctx : contexts) {
String id = ctx.getID();
if (id.equals(test_ctx_id)) test_context = ctx;
- if (threads.get(id) != null) threads.put(id, ctx);
+ if (threads.get(id) != null) {
+ assert isMyContext(ctx);
+ threads.put(id, ctx);
+ }
}
}
public void contextException(String id, String msg) {
- if (threads.get(id) != null) exit(new Exception("Context exception: " + msg));
+ RunControlContext ctx = threads.get(id);
+ if (ctx != null) {
+ assert isMyContext(ctx);
+ exit(new Exception("Context exception: " + msg));
+ }
}
public void contextRemoved(String[] contexts) {
@@ -902,6 +982,7 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
resume_cmds.remove(id);
IRunControl.RunControlContext ctx = threads.get(id);
if (ctx == null) return;
+ assert isMyContext(ctx);
if (!ctx.hasState()) {
exit(new Exception("Resumed event for context that HasState = false"));
return;
@@ -941,7 +1022,12 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
if (ids != null) {
@SuppressWarnings("unchecked")
Collection<String> c = (Collection<String>)ids;
+ HashSet<String> set = new HashSet<String>();
for (String id : c) {
+ if (!set.add(id)) {
+ exit(new Exception("Invalid value of 'BPs' attribute: duplicate items"));
+ return;
+ }
if (bp_list.get(id) != null) {
bp_id = id;
break;
@@ -1028,6 +1114,13 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
}
}
+ private boolean isMyContext(IRunControl.RunControlContext ctx) {
+ // Check if the context was created by this test
+ return test_ctx_id.equals(ctx.getID()) ||
+ test_ctx_id.equals(ctx.getParentID()) ||
+ test_ctx_id.equals(ctx.getCreatorID());
+ }
+
private boolean isMyBreakpoint(SuspendedContext sc) {
// Check if the context is suspended by one of our breakpoints
if (!"Breakpoint".equals(sc.reason)) return false;
@@ -1055,6 +1148,7 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
public void contextSuspended(final String id, String pc, String reason, Map<String, Object> params) {
IRunControl.RunControlContext ctx = threads.get(id);
if (ctx == null) return;
+ assert isMyContext(ctx);
if (!ctx.hasState()) {
exit(new Exception("Suspended event for context that HasState = false"));
return;
@@ -1113,67 +1207,73 @@ class TestRCBP1 implements ITCFTest, IRunControl.RunControlListener {
});
}
- private boolean canResume(IRunControl.RunControlContext ctx) {
+ public boolean canResume(IRunControl.RunControlContext ctx) {
+ if (test_ctx_id != null && !all_setup_done) return false;
+ if (resume_cmds.get(ctx.getID()) != null) return false;
String grp = ctx.getRCGroup();
+ for (IRunControl.RunControlContext x : threads.values()) {
+ if (x.getID().equals(ctx.getID()) || grp != null && grp.equals(x.getRCGroup())) {
+ SuspendedContext sc = suspended.get(x.getID());
+ if (sc == null) return false;
+ if (!sc.ok_to_resume) return false;
+ }
+ }
if (grp != null) {
- for (ITCFTest test : test_suite.getActiveTests()) {
- if (test instanceof TestRCBP1) {
- TestRCBP1 rcbp = (TestRCBP1)test;
- for (IRunControl.RunControlContext x : rcbp.threads.values()) {
- if (!grp.equals(x.getRCGroup())) continue;
- SuspendedContext sc = rcbp.suspended.get(x.getID());
- if (sc == null) {
- if (rcbp != TestRCBP1.this) return false;
- exit(new Exception("Invalid value of run control group ID"));
- return false;
- }
- if (!sc.ok_to_resume) return false;
- }
- }
+ for (String id : resume_cmds.keySet()) {
+ IRunControl.RunControlContext c = threads.get(id);
+ if (c == null) return false;
+ if (grp.equals(c.getRCGroup())) return false;
}
}
return true;
}
- private void resume(final String id) {
+ private void resume(String id) {
assert done_get_state || resume_cnt == 0;
assert resume_cmds.get(id) == null;
assert bp_sync_done;
assert mem_map_test_done;
resume_cnt++;
- final SuspendedContext sc = suspended.get(id);
- final IRunControl.RunControlContext ctx = threads.get(id);
+ SuspendedContext sc = suspended.get(id);
+ IRunControl.RunControlContext ctx = threads.get(id);
if (ctx != null && sc != null) {
assert !sc.get_state_pending;
assert !sc.ok_to_resume;
sc.ok_to_resume = true;
- if (!canResume(ctx)) return;
- int rm = IRunControl.RM_RESUME;
- if (isMyBreakpoint(sc)) {
- rm = rnd.nextInt(6);
- if (!ctx.canResume(rm)) rm = IRunControl.RM_RESUME;
- }
- resume_cmds.put(id, ctx.resume(rm, 1, new HashMap<String,Object>(), new IRunControl.DoneCommand() {
- public void doneCommand(IToken token, Exception error) {
- if (test_suite.cancel) return;
- if (!test_suite.isActive(TestRCBP1.this)) return;
- if (threads.get(id) == null) return;
- if (error instanceof IErrorReport &&
- ((IErrorReport)error).getErrorCode() == IErrorReport.TCF_ERROR_ALREADY_RUNNING &&
- ctx.getRCGroup() != null) {
- error = null;
- }
- if (error != null) {
- exit(error);
- return;
- }
- if (suspended.get(id) == sc || resume_cmds.get(id) == token) {
- exit(new Exception("Missing contextResumed event after resume command"));
- return;
- }
- }
- }));
+ resume(sc);
+ }
+ }
+
+ private void resume(final SuspendedContext sc) {
+ assert sc.ok_to_resume;
+ final String id = sc.id;
+ final IRunControl.RunControlContext ctx = threads.get(id);
+ if (!test_suite.canResume(ctx)) return;
+ int rm = IRunControl.RM_RESUME;
+ if (isMyBreakpoint(sc)) {
+ rm = rnd.nextInt(6);
+ if (!ctx.canResume(rm)) rm = IRunControl.RM_RESUME;
}
+ resume_cmds.put(id, ctx.resume(rm, 1, new HashMap<String,Object>(), new IRunControl.DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (test_suite.cancel) return;
+ if (!test_suite.isActive(TestRCBP1.this)) return;
+ if (threads.get(id) == null) return;
+ if (error instanceof IErrorReport &&
+ ((IErrorReport)error).getErrorCode() == IErrorReport.TCF_ERROR_ALREADY_RUNNING &&
+ ctx.getRCGroup() != null) {
+ error = null;
+ }
+ if (error != null) {
+ exit(error);
+ return;
+ }
+ if (suspended.get(id) == sc || resume_cmds.get(id) == token) {
+ exit(new Exception("Missing contextResumed event after resume command"));
+ return;
+ }
+ }
+ }));
}
private void runMemoryTest(final SuspendedContext sc, final Runnable done) {
diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestStreams.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestStreams.java
index 2384e3c88..e818c583d 100644
--- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestStreams.java
+++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestStreams.java
@@ -16,6 +16,7 @@ import java.util.Random;
import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IToken;
import org.eclipse.tm.tcf.services.IDiagnostics;
+import org.eclipse.tm.tcf.services.IRunControl;
import org.eclipse.tm.tcf.services.IStreams;
class TestStreams implements ITCFTest, IStreams.StreamsListener {
@@ -298,4 +299,8 @@ class TestStreams implements ITCFTest, IStreams.StreamsListener {
if (!IDiagnostics.NAME.equals(stream_type)) exit(new Exception("Invalid stream type in Streams.disposed event"));
if (!stream_ids.remove(stream_id)) exit(new Exception("Invalid stream ID in Streams.disposed event"));
}
+
+ public boolean canResume(IRunControl.RunControlContext ctx) {
+ return true;
+ }
}
diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestSysMonitor.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestSysMonitor.java
index 568e22b35..c15545d5a 100644
--- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestSysMonitor.java
+++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestSysMonitor.java
@@ -16,6 +16,7 @@ import java.util.HashSet;
import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IErrorReport;
import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.IRunControl;
import org.eclipse.tm.tcf.services.ISysMonitor;
import org.eclipse.tm.tcf.services.ISysMonitor.SysMonitorContext;
@@ -112,4 +113,8 @@ class TestSysMonitor implements ITCFTest {
if (!test_suite.isActive(this)) return;
test_suite.done(this, x);
}
+
+ public boolean canResume(IRunControl.RunControlContext ctx) {
+ return true;
+ }
}
diff --git a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestTerminals.java b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestTerminals.java
index df6c4edd9..055990468 100644
--- a/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestTerminals.java
+++ b/plugins/org.eclipse.tm.tcf.debug/src/org/eclipse/tm/internal/tcf/debug/tests/TestTerminals.java
@@ -7,6 +7,7 @@ import java.util.Random;
import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IToken;
import org.eclipse.tm.tcf.services.IProcesses;
+import org.eclipse.tm.tcf.services.IRunControl;
import org.eclipse.tm.tcf.services.ITerminals;
import org.eclipse.tm.tcf.services.ITerminals.TerminalContext;
@@ -184,4 +185,8 @@ class TestTerminals implements ITCFTest {
if (terminals != null) terminals.removeListener(listener);
test_suite.done(this, x);
}
+
+ public boolean canResume(IRunControl.RunControlContext ctx) {
+ return true;
+ }
}

Back to the top