Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java7
-rw-r--r--org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java2
-rw-r--r--org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/MockProcess.java47
-rw-r--r--org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/RuntimeProcessTests.java96
4 files changed, 147 insertions, 5 deletions
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java b/org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java
index 8ee0a099d..1bfb08a1e 100644
--- a/org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java
+++ b/org.eclipse.debug.core/core/org/eclipse/debug/core/model/RuntimeProcess.java
@@ -20,6 +20,7 @@ import java.nio.charset.UnsupportedCharsetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.PlatformObject;
@@ -219,7 +220,11 @@ public class RuntimeProcess extends PlatformObject implements IProcess {
} catch (IllegalThreadStateException ie) {
}
try {
- Thread.sleep(TIME_TO_WAIT_FOR_THREAD_DEATH);
+ if (process != null) {
+ process.waitFor(TIME_TO_WAIT_FOR_THREAD_DEATH, TimeUnit.MILLISECONDS);
+ } else {
+ Thread.sleep(TIME_TO_WAIT_FOR_THREAD_DEATH);
+ }
} catch (InterruptedException e) {
}
attempts++;
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
index 9e3efa615..32e1ed3c0 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
@@ -23,6 +23,7 @@ import org.eclipse.debug.tests.console.IOConsoleFixedWidthTests;
import org.eclipse.debug.tests.console.IOConsoleTests;
import org.eclipse.debug.tests.console.ProcessConsoleManagerTests;
import org.eclipse.debug.tests.console.ProcessConsoleTests;
+import org.eclipse.debug.tests.console.RuntimeProcessTests;
import org.eclipse.debug.tests.console.StreamsProxyTests;
import org.eclipse.debug.tests.console.TextConsoleViewerTest;
import org.eclipse.debug.tests.launching.AcceleratorSubstitutionTests;
@@ -123,6 +124,7 @@ public class AutomatedSuite extends TestSuite {
addTest(new TestSuite(ProcessConsoleTests.class));
addTest(new TestSuite(StreamsProxyTests.class));
addTest(new TestSuite(TextConsoleViewerTest.class));
+ addTest(new TestSuite(RuntimeProcessTests.class));
// Launch Groups
addTest(new TestSuite(LaunchGroupTests.class));
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/MockProcess.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/MockProcess.java
index 7bd305e18..aa84545c0 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/MockProcess.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/MockProcess.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2019 Paul Pazderski and others.
+ * Copyright (c) 2019, 2020 Paul Pazderski and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -17,6 +17,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.debug.core.DebugPlugin;
@@ -57,6 +58,8 @@ public class MockProcess extends Process {
* </p>
*/
private long endTime;
+ /** The simulated exit code. */
+ private int exitCode = 0;
/**
* Create new silent mockup process which runs for a given amount of time.
@@ -171,7 +174,24 @@ public class MockProcess extends Process {
}
}
}
- return 0;
+ return exitCode;
+ }
+
+ @Override
+ public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException {
+ long remainingMs = unit.toMillis(timeout);
+ final long timeoutMs = System.currentTimeMillis() + remainingMs;
+ synchronized (waitForTerminationLock) {
+ while (!isTerminated() && remainingMs > 0) {
+ long waitTime = endTime == RUN_FOREVER ? Long.MAX_VALUE : endTime - System.currentTimeMillis();
+ waitTime = Math.min(waitTime, remainingMs);
+ if (waitTime > 0) {
+ waitForTerminationLock.wait(waitTime);
+ }
+ remainingMs = timeoutMs - System.currentTimeMillis();
+ }
+ }
+ return isTerminated();
}
@Override
@@ -180,13 +200,23 @@ public class MockProcess extends Process {
final String end = (endTime == RUN_FOREVER ? "never." : "in " + (endTime - System.currentTimeMillis()) + " ms.");
throw new IllegalThreadStateException("Mockup process terminates " + end);
}
- return 0;
+ return exitCode;
}
@Override
public void destroy() {
+ destroy(0);
+ }
+
+ /**
+ * Simulate a delay for the mockup process shutdown.
+ *
+ * @param delay amount of milliseconds must pass after destroy was called
+ * and before the mockup process goes in terminated state
+ */
+ public void destroy(int delay) {
synchronized (waitForTerminationLock) {
- endTime = System.currentTimeMillis();
+ endTime = System.currentTimeMillis() + delay;
waitForTerminationLock.notifyAll();
}
}
@@ -201,6 +231,15 @@ public class MockProcess extends Process {
}
/**
+ * Set the exit code returned once the process is finished.
+ *
+ * @param exitCode new exit code
+ */
+ public void setExitValue(int exitCode) {
+ this.exitCode = exitCode;
+ }
+
+ /**
* Create a {@link RuntimeProcess} which wraps this {@link MockProcess}.
* <p>
* Note: the process will only be connected to a minimal dummy launch
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/RuntimeProcessTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/RuntimeProcessTests.java
new file mode 100644
index 000000000..c3d631b10
--- /dev/null
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/RuntimeProcessTests.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Paul Pazderski and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Paul Pazderski - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.tests.console;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IDebugEventSetListener;
+import org.eclipse.debug.core.model.RuntimeProcess;
+import org.eclipse.debug.tests.AbstractDebugTest;
+import org.eclipse.debug.tests.TestUtil;
+
+public class RuntimeProcessTests extends AbstractDebugTest {
+
+ public RuntimeProcessTests() {
+ super(RuntimeProcessTests.class.getSimpleName());
+ }
+
+ public RuntimeProcessTests(String name) {
+ super(name);
+ }
+
+ /**
+ * Test behavior of {@link RuntimeProcess} if the wrapped process
+ * terminates.
+ */
+ public void testProcessTerminated() throws Exception {
+ AtomicInteger processTerminateEvents = new AtomicInteger();
+ DebugPlugin.getDefault().addDebugEventListener(new IDebugEventSetListener() {
+ @Override
+ public void handleDebugEvents(DebugEvent[] events) {
+ for (DebugEvent event : events) {
+ if (event.getKind() == DebugEvent.TERMINATE) {
+ processTerminateEvents.incrementAndGet();
+ }
+ }
+ }
+ });
+
+ MockProcess mockProcess = new MockProcess(MockProcess.RUN_FOREVER);
+ RuntimeProcess runtimeProcess = mockProcess.toRuntimeProcess();
+
+ assertFalse("RuntimeProcess already terminated.", runtimeProcess.isTerminated());
+ assertTrue(runtimeProcess.canTerminate());
+
+ mockProcess.setExitValue(1);
+ mockProcess.destroy();
+
+ TestUtil.waitWhile(p -> !p.isTerminated(), runtimeProcess, 1000, p -> "RuntimePocess not terminated.");
+ TestUtil.waitForJobs(getName(), 25, 500);
+ assertEquals("Wrong number of terminate events.", 1, processTerminateEvents.get());
+ assertEquals("RuntimeProcess reported wrong exit code.", 1, runtimeProcess.getExitValue());
+ }
+
+ /** Test {@link RuntimeProcess} terminating the wrapped process. */
+ public void testTerminateProcess() throws Exception {
+ AtomicInteger processTerminateEvents = new AtomicInteger();
+ DebugPlugin.getDefault().addDebugEventListener(new IDebugEventSetListener() {
+ @Override
+ public void handleDebugEvents(DebugEvent[] events) {
+ for (DebugEvent event : events) {
+ if (event.getKind() == DebugEvent.TERMINATE) {
+ processTerminateEvents.incrementAndGet();
+ }
+ }
+ }
+ });
+
+ MockProcess mockProcess = new MockProcess(MockProcess.RUN_FOREVER);
+ RuntimeProcess runtimeProcess = mockProcess.toRuntimeProcess();
+
+ assertFalse("RuntimeProcess already terminated.", runtimeProcess.isTerminated());
+ assertTrue(runtimeProcess.canTerminate());
+
+ mockProcess.setExitValue(1);
+ runtimeProcess.terminate();
+ assertFalse("RuntimeProcess failed to terminated wrapped process.", mockProcess.isAlive());
+
+ TestUtil.waitWhile(p -> !p.isTerminated(), runtimeProcess, 1000, p -> "RuntimePocess not terminated.");
+ TestUtil.waitForJobs(getName(), 25, 500);
+ assertEquals("Wrong number of terminate events.", 1, processTerminateEvents.get());
+ assertEquals("RuntimeProcess reported wrong exit code.", 1, runtimeProcess.getExitValue());
+ }
+}

Back to the top