Bug 32205 - [console] Support output merging for standard VM launches
Change-Id: I34ffe0fbaef8b7e31d723ed2cd32ac6a241c642e
Signed-off-by: Paul Pazderski <paul-eclipse@ppazderski.de>
diff --git a/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF b/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF
index 88ad986..d692f5b 100644
--- a/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.debug.tests/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.jdt.debug.tests; singleton:=true
-Bundle-Version: 3.11.500.qualifier
+Bundle-Version: 3.11.600.qualifier
Bundle-ClassPath: javadebugtests.jar
Bundle-Activator: org.eclipse.jdt.debug.testplugin.JavaTestPlugin
Bundle-Vendor: %providerName
diff --git a/org.eclipse.jdt.debug.tests/pom.xml b/org.eclipse.jdt.debug.tests/pom.xml
index b2547ab..3760844 100644
--- a/org.eclipse.jdt.debug.tests/pom.xml
+++ b/org.eclipse.jdt.debug.tests/pom.xml
@@ -18,7 +18,7 @@
</parent>
<groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.debug.tests</artifactId>
- <version>3.11.500-SNAPSHOT</version>
+ <version>3.11.600-SNAPSHOT</version>
<packaging>eclipse-test-plugin</packaging>
<properties>
<code.ignoredWarnings>${tests.ignoredWarnings}</code.ignoredWarnings>
diff --git a/org.eclipse.jdt.debug.tests/testprograms/OutSync.java b/org.eclipse.jdt.debug.tests/testprograms/OutSync.java
new file mode 100644
index 0000000..bf25330
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/testprograms/OutSync.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2019 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
+ *******************************************************************************/
+public class OutSync {
+ public static void main(String[] args) throws InterruptedException {
+ for (int i = 0; i < 50; i++) {
+ System.out.println("o");
+ System.err.println("e");
+ }
+ }
+}
diff --git a/org.eclipse.jdt.debug.tests/testprograms/OutSync2.java b/org.eclipse.jdt.debug.tests/testprograms/OutSync2.java
new file mode 100644
index 0000000..0e5914c
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/testprograms/OutSync2.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2019 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
+ *******************************************************************************/
+public class OutSync2 {
+ public static void main(String[] args) throws InterruptedException {
+ for (int i = 0; i < 1000; i++) {
+ System.out.print("o");
+ System.out.flush();
+ System.err.print("e");
+ System.err.flush();
+ }
+ }
+}
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
index 786dc41..f0db582 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
@@ -207,7 +207,7 @@
"org.eclipse.debug.tests.targets.HcrClass5", "org.eclipse.debug.tests.targets.HcrClass6", "org.eclipse.debug.tests.targets.HcrClass7", "org.eclipse.debug.tests.targets.HcrClass8",
"org.eclipse.debug.tests.targets.HcrClass9", "TestContributedStepFilterClass", "TerminateAll_01", "TerminateAll_02", "StepResult1",
"StepResult2", "StepResult3", "StepUncaught", "TriggerPoint_01", "BulkThreadCreationTest", "MethodExitAndException",
- "Bug534319earlyStart", "Bug534319lateStart", "Bug534319singleThread", "Bug534319startBetwen", "MethodCall", "Bug538303", "Bug540243" };
+ "Bug534319earlyStart", "Bug534319lateStart", "Bug534319singleThread", "Bug534319startBetwen", "MethodCall", "Bug538303", "Bug540243", "OutSync", "OutSync2" };
/**
* the default timeout
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ConsoleTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ConsoleTests.java
index be45210..b4565a4 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ConsoleTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ConsoleTests.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -13,16 +13,28 @@
*******************************************************************************/
package org.eclipse.jdt.debug.tests.core;
+import java.util.Arrays;
+import java.util.Collections;
+
import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.tests.AbstractDebugTest;
+import org.eclipse.jdt.debug.tests.TestUtil;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleManager;
import org.eclipse.ui.console.MessageConsole;
+import org.eclipse.ui.console.TextConsole;
+import org.eclipse.ui.internal.console.IOConsolePartitioner;
/**
* Tests console line tracker.
@@ -112,4 +124,111 @@
console.dispose();
}
+ /**
+ * Test synchronization of standard and error output stream of started process.
+ * <p>
+ * This variant tests output on multiple lines and is launched in DEBUG mode.
+ * </p>
+ *
+ * @throws Exception
+ * if test failed
+ */
+ public void testConsoleOutputSynchronization() throws Exception {
+ String typeName = "OutSync";
+ IJavaDebugTarget target = null;
+ try {
+ ILaunchConfiguration launchConfig = getLaunchConfiguration(typeName);
+ ILaunchConfigurationWorkingCopy launchCopy = launchConfig.getWorkingCopy();
+ launchCopy.setAttribute(IDebugUIConstants.ATTR_MERGE_OUTPUT, true);
+ target = launchAndTerminate(launchCopy, DEFAULT_TIMEOUT);
+ String content = getConsoleContent(target.getProcess());
+ // normalize new lines to unix style
+ content = content.replace("\r\n", "\n").replace('\r', '\n');
+ String expectedOutput = String.join("", Collections.nCopies(content.length() / 4, "o\ne\n"));
+ assertEquals("Received wrong output. Probably not synchronized.", expectedOutput, content);
+ } finally {
+ if (target != null) {
+ terminateAndRemove(target);
+ }
+ }
+ }
+
+ /**
+ * Test synchronization of standard and error output stream of started process.
+ * <p>
+ * This variant tests output on single line and is launched in RUN mode.
+ * </p>
+ *
+ * @throws Exception
+ * if test failed
+ */
+ public void testConsoleOutputSynchronization2() throws Exception {
+ String typeName = "OutSync2";
+ ILaunch launch = null;
+ try {
+ ILaunchConfiguration launchConfig = getLaunchConfiguration(typeName);
+ ILaunchConfigurationWorkingCopy launchCopy = launchConfig.getWorkingCopy();
+ launchCopy.setAttribute(IDebugUIConstants.ATTR_MERGE_OUTPUT, true);
+ launch = launchCopy.launch(ILaunchManager.RUN_MODE, null);
+ TestUtil.waitForJobs(getName(), 0, DEFAULT_TIMEOUT);
+ String content = getConsoleContent(launch.getProcesses()[0]);
+ String expectedOutput = String.join("", Collections.nCopies(content.length() / 2, "oe"));
+ assertEquals("Received wrong output. Probably not synchronized.", expectedOutput, content);
+ } finally {
+ if (launch != null) {
+ getLaunchManager().removeLaunch(launch);
+ }
+ }
+ }
+
+ /**
+ * Test if process error output has another color in console than standard output.
+ *
+ * @throws Exception
+ * if test failed
+ */
+ public void testConsoleErrorColoring() throws Exception {
+ String typeName = "OutSync";
+ IJavaDebugTarget target = null;
+ try {
+ target = launchAndTerminate(typeName);
+ final IProcess process = target.getProcess();
+ assertNotNull("Missing VM process.", process);
+ final IConsole console = DebugUITools.getConsole(process);
+ assertTrue("Console is not a TextConsole.", console instanceof TextConsole);
+ final TextConsole textConsole = (TextConsole) console;
+ final IDocumentPartitioner partitioner = textConsole.getDocument().getDocumentPartitioner();
+ assertTrue("Partitioner is not a IOConsolePartitioner.", partitioner instanceof IOConsolePartitioner);
+ final IOConsolePartitioner ioPartitioner = (IOConsolePartitioner) partitioner;
+ TestUtil.waitForJobs(getName(), 100, DEFAULT_TIMEOUT); // wait for output appending
+
+ final long numStyleTypes = Arrays.stream(ioPartitioner.getStyleRanges(0, textConsole.getDocument().getLength())).map((s) -> s.foreground).distinct().count();
+ assertTrue("Console partitioner did not distinct standard and error output.", numStyleTypes > 1);
+ } finally {
+ if (target != null) {
+ terminateAndRemove(target);
+ }
+ }
+ }
+
+ /**
+ * Try to get the console content associated with given process.
+ *
+ * @param process
+ * the process on whose output we are interested
+ * @return the raw content in console probably written by given process. Note: result may be affected by user input and console trimming.
+ * @throws Exception
+ * if content retrieval failed
+ */
+ private String getConsoleContent(IProcess process) throws Exception {
+ assertNotNull("Missing VM process.", process);
+ final IConsole console = DebugUITools.getConsole(process);
+ assertNotNull("Missing console", console);
+ assertTrue("Console is not a TextConsole.", console instanceof TextConsole);
+ final TextConsole textConsole = (TextConsole) console;
+ TestUtil.waitForJobs(getName(), 100, DEFAULT_TIMEOUT); // wait for output appending
+ assertEquals("Test program failed with error.", 0, process.getExitValue());
+ final IDocument consoleDocument = textConsole.getDocument();
+ return consoleDocument.get();
+ }
}
diff --git a/org.eclipse.jdt.launching/META-INF/MANIFEST.MF b/org.eclipse.jdt.launching/META-INF/MANIFEST.MF
index 1f976a9..dfb4ded 100644
--- a/org.eclipse.jdt.launching/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.launching/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.jdt.launching; singleton:=true
-Bundle-Version: 3.14.100.qualifier
+Bundle-Version: 3.15.0.qualifier
Bundle-Activator: org.eclipse.jdt.internal.launching.LaunchingPlugin
Bundle-Vendor: %providerName
Bundle-Localization: plugin
@@ -16,12 +16,13 @@
org.eclipse.jdt.launching.sourcelookup.containers
Require-Bundle: org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)",
org.eclipse.jdt.core;bundle-version="[3.18.0,4.0.0)",
- org.eclipse.debug.core;bundle-version="[3.13.200,4.0.0)",
+ org.eclipse.debug.core;bundle-version="[3.14.0,4.0.0)",
org.eclipse.jdt.debug;bundle-version="[3.11.0,4.0.0)",
org.eclipse.core.variables;bundle-version="[3.2.0,4.0.0)",
org.eclipse.core.runtime;bundle-version="[3.11.0,4.0.0)",
org.eclipse.osgi;bundle-version="[3.8.0,4.0.0)",
- org.eclipse.core.expressions;bundle-version="[3.4.0,4.0.0)"
+ org.eclipse.core.expressions;bundle-version="[3.4.0,4.0.0)",
+ org.eclipse.debug.ui;bundle-version="[3.14.0,4.0.0)"
Bundle-ActivationPolicy: lazy
Import-Package: com.ibm.icu.text
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/StandardVMDebugger.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/StandardVMDebugger.java
index d97bb68..2b50a66 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/StandardVMDebugger.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/StandardVMDebugger.java
@@ -331,7 +331,7 @@
connector.startListening(map);
- p = exec(cmdLine, cmdDetails.getWorkingDir(), cmdDetails.getEnvp());
+ p = exec(cmdLine, cmdDetails.getWorkingDir(), cmdDetails.getEnvp(), config.isMergeOutput());
if (p == null) {
return;
}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/StandardVMRunner.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/StandardVMRunner.java
index 010c2a6..3501323 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/StandardVMRunner.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/StandardVMRunner.java
@@ -504,8 +504,8 @@
IProgressMonitor subMonitor = SubMonitor.convert(monitor, 1);
subMonitor.beginTask(LaunchingMessages.StandardVMRunner_Launching_VM____1, 2);
subMonitor.subTask(LaunchingMessages.StandardVMRunner_Starting_virtual_machine____3);
- Process p= null;
- p = exec(cmdLine, cmdDetails.getWorkingDir(), cmdDetails.getEnvp());
+ Process p = null;
+ p = exec(cmdLine, cmdDetails.getWorkingDir(), cmdDetails.getEnvp(), config.isMergeOutput());
if (p == null) {
return;
}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/AbstractVMRunner.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/AbstractVMRunner.java
index 07084f3..e6ef2e6 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/AbstractVMRunner.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/AbstractVMRunner.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -96,6 +96,28 @@
}
/**
+ * Executes the given command line using the given working directory and environment
+ *
+ * @param cmdLine
+ * the command line
+ * @param workingDirectory
+ * the working directory
+ * @param envp
+ * the environment
+ * @param mergeOutput
+ * if <code>true</code> the error stream will be merged with standard output stream and both can be read through the same output stream
+ * @return the {@link Process}
+ * @throws CoreException
+ * is the execution fails
+ * @since 3.15
+ * @see DebugPlugin#exec(String[], File, String[])
+ */
+ protected Process exec(String[] cmdLine, File workingDirectory, String[] envp, boolean mergeOutput) throws CoreException {
+ cmdLine = quoteWindowsArgs(cmdLine);
+ return DebugPlugin.exec(cmdLine, workingDirectory, envp, mergeOutput);
+ }
+
+ /**
* @since 3.11
*/
protected static String[] quoteWindowsArgs(String[] cmdLine) {
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaLaunchDelegate.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaLaunchDelegate.java
index ccd4a4a..edb94cb 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaLaunchDelegate.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaLaunchDelegate.java
@@ -21,6 +21,7 @@
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IModuleDescription;
import org.eclipse.jdt.internal.launching.LaunchingMessages;
@@ -131,6 +132,8 @@
runConfig.setOverrideDependencies(getModuleCLIOptions(configuration));
}
}
+ runConfig.setMergeOutput(configuration.getAttribute(IDebugUIConstants.ATTR_MERGE_OUTPUT, false));
+
// check for cancellation
if (monitor.isCanceled()) {
return null;
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/VMRunnerConfiguration.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/VMRunnerConfiguration.java
index 4215ed4..c43a96e 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/VMRunnerConfiguration.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/VMRunnerConfiguration.java
@@ -41,6 +41,7 @@
private Boolean fPreviewEnabled = false;
private Map<String, Object> fVMSpecificAttributesMap;
private boolean fResume = true;
+ private boolean fMergeOutput = false;
private static final String[] fgEmpty= new String[0];
@@ -355,4 +356,24 @@
this.fPreviewEnabled = fPreviewEnabled;
}
+ /**
+ * Gets the fMergeOutput.
+ *
+ * @return the fMergeOutput
+ * @since 3.15
+ */
+ public boolean isMergeOutput() {
+ return fMergeOutput;
+ }
+
+ /**
+ * Sets the fMergeOutput. If <code>true</code> the VM will be run with redirectErrorStream(true) to merge error and standard output.
+ *
+ * @param fMergeOutput
+ * the fMergeOutput to set
+ * @since 3.15
+ */
+ public void setMergeOutput(boolean fMergeOutput) {
+ this.fMergeOutput = fMergeOutput;
+ }
}
diff --git a/org.eclipse.jdt.launching/plugin.xml b/org.eclipse.jdt.launching/plugin.xml
index 3bd9d83..2e36a20 100644
--- a/org.eclipse.jdt.launching/plugin.xml
+++ b/org.eclipse.jdt.launching/plugin.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<!--
- Copyright (c) 2005, 2018 IBM Corporation and others.
+ Copyright (c) 2005, 2019 IBM Corporation and others.
This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
@@ -50,6 +50,7 @@
<launchConfigurationType
allowPrototypes="true"
allowCommandLine="true"
+ allowOutputMerging="true"
delegate="org.eclipse.jdt.launching.sourcelookup.advanced.AdvancedJavaLaunchDelegate"
delegateDescription="%localJavaApplicationDelegate.description"
delegateName="%eclipseJDTLauncher.name"
diff --git a/org.eclipse.jdt.launching/pom.xml b/org.eclipse.jdt.launching/pom.xml
index 2c8af17..679c53b 100644
--- a/org.eclipse.jdt.launching/pom.xml
+++ b/org.eclipse.jdt.launching/pom.xml
@@ -18,7 +18,7 @@
</parent>
<groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.launching</artifactId>
- <version>3.14.100-SNAPSHOT</version>
+ <version>3.15.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
<build>