Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Pazderski2020-02-22 20:23:28 +0000
committerPaul Pazderski2020-05-01 23:17:19 +0000
commit79c7833392ac1e85adf0f48e5808998c6f3a1caf (patch)
tree17817364c3e69c10f2424c6eab4237e024b016f4
parent4296b4127e2e3b8549f5733c4c7d8f2f24a4b891 (diff)
downloadeclipse.platform.debug-79c7833392ac1e85adf0f48e5808998c6f3a1caf.tar.gz
eclipse.platform.debug-79c7833392ac1e85adf0f48e5808998c6f3a1caf.tar.xz
eclipse.platform.debug-79c7833392ac1e85adf0f48e5808998c6f3a1caf.zip
Bug 333239 - [console] Console redirection treats file name as regularI20200505-1800I20200504-1800I20200503-1800I20200502-1800
expression Change-Id: I8b475d001a6e0a626540d9aa93285fa918d4b21e Signed-off-by: Paul Pazderski <paul-eclipse@ppazderski.de>
-rw-r--r--org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/MockProcess.java26
-rw-r--r--org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ProcessConsoleTests.java171
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java15
3 files changed, 193 insertions, 19 deletions
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 aa84545c0..135b54c7c 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
@@ -17,13 +17,18 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.Launch;
import org.eclipse.debug.core.model.RuntimeProcess;
+import org.eclipse.debug.tests.launching.LaunchConfigurationTests;
/**
* A mockup process which can either simulate generation of output or wait for
@@ -265,4 +270,25 @@ public class MockProcess extends Process {
public RuntimeProcess toRuntimeProcess(String name) {
return (RuntimeProcess) DebugPlugin.newProcess(new Launch(null, ILaunchManager.RUN_MODE, null), this, name);
}
+
+ /**
+ * Create a {@link RuntimeProcess} which wraps this {@link MockProcess}.
+ * <p>
+ * This method also attaches a
+ * {@link LaunchConfigurationTests#ID_TEST_LAUNCH_TYPE} launch configuration
+ * to the {@link RuntimeProcess}.
+ * </p>
+ *
+ * @param name name for the process and launch configuration
+ * @return the created {@link RuntimeProcess}
+ */
+ public RuntimeProcess toRuntimeProcess(String name, Map<String, Object> launchConfigAttributes) throws CoreException {
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType launchType = launchManager.getLaunchConfigurationType(LaunchConfigurationTests.ID_TEST_LAUNCH_TYPE);
+ ILaunchConfigurationWorkingCopy launchConfiguration = launchType.newInstance(null, name);
+ if (launchConfigAttributes != null) {
+ launchConfiguration.setAttributes(launchConfigAttributes);
+ }
+ return (RuntimeProcess) DebugPlugin.newProcess(new Launch(launchConfiguration, ILaunchManager.RUN_MODE, null), this, name);
+ }
}
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ProcessConsoleTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ProcessConsoleTests.java
index 37e3737a6..7b274bcc2 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ProcessConsoleTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ProcessConsoleTests.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
@@ -13,12 +13,21 @@
*******************************************************************************/
package org.eclipse.debug.tests.console;
+import static org.junit.Assert.assertArrayEquals;
+
+import java.io.ByteArrayInputStream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.File;
+import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.text.MessageFormat;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -41,10 +50,13 @@ import org.eclipse.debug.tests.TestUtil;
import org.eclipse.debug.tests.launching.LaunchConfigurationTests;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.console.ConsoleColorProvider;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleConstants;
import org.eclipse.ui.console.IConsoleManager;
+import org.eclipse.ui.console.IOConsole;
import org.eclipse.ui.console.IOConsoleInputStream;
import org.junit.After;
import org.junit.Before;
@@ -62,11 +74,14 @@ public class ProcessConsoleTests extends AbstractDebugTest {
/** Listener to count error messages in {@link ConsolePlugin} log. */
private final ILogListener errorLogListener = (status, plugin) -> {
- if (status.matches(IStatus.ERROR)) {
- loggedErrors.incrementAndGet();
- }
+ if (status.matches(IStatus.ERROR)) {
+ loggedErrors.incrementAndGet();
+ }
};
+ /** Temporary test files created by a test. Will be deleted on teardown. */
+ private final ArrayList<File> tmpFiles = new ArrayList<>();
+
@Override
@Before
public void setUp() throws Exception {
@@ -79,12 +94,34 @@ public class ProcessConsoleTests extends AbstractDebugTest {
@After
public void tearDown() throws Exception {
Platform.removeLogListener(errorLogListener);
+ for (File tmpFile : tmpFiles) {
+ tmpFile.delete();
+ }
+ tmpFiles.clear();
+
super.tearDown();
assertEquals("Test triggered errors.", 0, loggedErrors.get());
}
/**
+ * Create a new temporary file for testing. File will be deleted when test
+ * finishes.
+ *
+ * @param filename name of the temporary file
+ * @return the created temporary file
+ * @throws IOException if creating the file failed. Includes file already
+ * exists.
+ */
+ private File createTmpFile(String filename) throws IOException {
+ File file = DebugUIPlugin.getDefault().getStateLocation().addTrailingSeparator().append(filename).toFile();
+ boolean fileCreated = file.createNewFile();
+ assertTrue("Failed to prepare temporary test file.", fileCreated);
+ tmpFiles.add(file);
+ return file;
+ }
+
+ /**
* Test if two byte UTF-8 characters get disrupted on there way from process
* console to the runtime process.
* <p>
@@ -231,9 +268,9 @@ public class ProcessConsoleTests extends AbstractDebugTest {
@SuppressWarnings("restriction")
final org.eclipse.debug.internal.ui.views.console.ProcessConsole console = new org.eclipse.debug.internal.ui.views.console.ProcessConsole(process, new ConsoleColorProvider());
console.addPropertyChangeListener(event -> {
- if (event.getSource() == console && IConsoleConstants.P_CONSOLE_OUTPUT_COMPLETE.equals(event.getProperty())) {
- terminationSignaled.set(true);
- }
+ if (event.getSource() == console && IConsoleConstants.P_CONSOLE_OUTPUT_COMPLETE.equals(event.getProperty())) {
+ terminationSignaled.set(true);
+ }
});
final IConsoleManager consoleManager = ConsolePlugin.getDefault().getConsoleManager();
try {
@@ -248,4 +285,124 @@ public class ProcessConsoleTests extends AbstractDebugTest {
TestUtil.waitForJobs(name.getMethodName(), 0, 10000);
}
}
+
+ /**
+ * Test simple redirect of console output into file.
+ */
+ @Test
+ public void testRedirectOutputToFile() throws Exception {
+ final String testContent = "Hello World!";
+ final File outFile = createTmpFile("test.out");
+ Map<String, Object> launchConfigAttributes = new HashMap<>();
+ launchConfigAttributes.put(IDebugUIConstants.ATTR_CAPTURE_IN_FILE, outFile.getCanonicalPath());
+ launchConfigAttributes.put(IDebugUIConstants.ATTR_CAPTURE_IN_CONSOLE, true);
+ doConsoleOutputTest(testContent.getBytes(), launchConfigAttributes);
+ assertArrayEquals("Wrong content redirected to file.", testContent.getBytes(), Files.readAllBytes(outFile.toPath()));
+ }
+
+ /**
+ * Test appending of console output into existing file.
+ */
+ @Test
+ public void testAppendOutputToFile() throws Exception {
+ final String testContent = "Hello World!";
+ final File outFile = createTmpFile("test.out");
+ Map<String, Object> launchConfigAttributes = new HashMap<>();
+ launchConfigAttributes.put(IDebugUIConstants.ATTR_CAPTURE_IN_FILE, outFile.getCanonicalPath());
+ launchConfigAttributes.put(IDebugUIConstants.ATTR_APPEND_TO_FILE, true);
+ launchConfigAttributes.put(IDebugUIConstants.ATTR_CAPTURE_IN_CONSOLE, true);
+ doConsoleOutputTest(testContent.getBytes(), launchConfigAttributes);
+ assertArrayEquals("Wrong content redirected to file.", testContent.getBytes(), Files.readAllBytes(outFile.toPath()));
+
+ String appendedContent = "append";
+ doConsoleOutputTest(appendedContent.getBytes(), launchConfigAttributes);
+ assertArrayEquals("Wrong content redirected to file.", (testContent + appendedContent).getBytes(), Files.readAllBytes(outFile.toPath()));
+ }
+
+ /**
+ * Test output redirect with a filename containing regular expression
+ * specific special characters.
+ * <p>
+ * Test a filename with special characters which is still a valid regular
+ * expression and a filename whose name is an invalid regular expression.
+ */
+ @Test
+ public void testBug333239_regexSpecialCharactersInOutputFilename() throws Exception {
+ final String testContent = "1.\n2.\n3.\n";
+ File outFile = createTmpFile("test.[out]");
+ Map<String, Object> launchConfigAttributes = new HashMap<>();
+ launchConfigAttributes.put(IDebugUIConstants.ATTR_CAPTURE_IN_FILE, outFile.getCanonicalPath());
+ launchConfigAttributes.put(IDebugUIConstants.ATTR_CAPTURE_IN_CONSOLE, false);
+ IOConsole console = doConsoleOutputTest(testContent.getBytes(), launchConfigAttributes);
+ assertArrayEquals("Wrong content redirected to file.", testContent.getBytes(), Files.readAllBytes(outFile.toPath()));
+ assertEquals("Output in console.", 2, console.getDocument().getNumberOfLines());
+
+ outFile = createTmpFile("exhaustive[128-32].out");
+ launchConfigAttributes.put(IDebugUIConstants.ATTR_CAPTURE_IN_FILE, outFile.getCanonicalPath());
+ console = doConsoleOutputTest(testContent.getBytes(), launchConfigAttributes);
+ assertArrayEquals("Wrong content redirected to file.", testContent.getBytes(), Files.readAllBytes(outFile.toPath()));
+ assertEquals("Output in console.", 2, console.getDocument().getNumberOfLines());
+
+ outFile = createTmpFile("ug(ly.out");
+ launchConfigAttributes.put(IDebugUIConstants.ATTR_CAPTURE_IN_FILE, outFile.getCanonicalPath());
+ console = doConsoleOutputTest(testContent.getBytes(), launchConfigAttributes);
+ assertArrayEquals("Wrong content redirected to file.", testContent.getBytes(), Files.readAllBytes(outFile.toPath()));
+ assertEquals("Output in console.", 2, console.getDocument().getNumberOfLines());
+ }
+
+ /**
+ * Shared test code for tests who want to write and verify content to
+ * console. Method will open a console for a mockup process, output the
+ * given content, terminate the process and close the console. If content is
+ * expected to be found in console it will be verified. If output is
+ * redirected to file the file path which should be printed to console is
+ * checked.
+ *
+ * @param testContent content to output in console
+ * @param launchConfigAttributes optional launch configuration attributes to
+ * specify behavior
+ * @return the console object after it has finished
+ */
+ private IOConsole doConsoleOutputTest(byte[] testContent, Map<String, Object> launchConfigAttributes) throws Exception {
+ final MockProcess mockProcess = new MockProcess(new ByteArrayInputStream(testContent), null, 0);
+ final IProcess process = mockProcess.toRuntimeProcess("Output Redirect", launchConfigAttributes);
+ final String encoding = launchConfigAttributes != null ? (String) launchConfigAttributes.get(DebugPlugin.ATTR_CONSOLE_ENCODING) : null;
+ final AtomicBoolean consoleFinished = new AtomicBoolean(false);
+ @SuppressWarnings("restriction")
+ final org.eclipse.debug.internal.ui.views.console.ProcessConsole console = new org.eclipse.debug.internal.ui.views.console.ProcessConsole(process, new ConsoleColorProvider(), encoding);
+ console.addPropertyChangeListener((PropertyChangeEvent event) -> {
+ if (event.getSource() == console && IConsoleConstants.P_CONSOLE_OUTPUT_COMPLETE.equals(event.getProperty())) {
+ consoleFinished.set(true);
+ }
+ });
+ final IConsoleManager consoleManager = ConsolePlugin.getDefault().getConsoleManager();
+ try {
+ consoleManager.addConsoles(new IConsole[] { console });
+ waitWhile(c -> !consoleFinished.get(), testTimeout, c -> "Console did not finished.");
+
+ Object value = launchConfigAttributes != null ? launchConfigAttributes.get(IDebugUIConstants.ATTR_CAPTURE_IN_FILE) : null;
+ final File outFile = value != null ? new File((String) value) : null;
+ value = launchConfigAttributes != null ? launchConfigAttributes.get(IDebugUIConstants.ATTR_CAPTURE_IN_CONSOLE) : null;
+ final boolean checkOutput = value != null ? (boolean) value : true;
+ final IDocument doc = console.getDocument();
+
+ if (outFile != null) {
+ @SuppressWarnings("restriction")
+ String expectedPathMsg = MessageFormat.format(org.eclipse.debug.internal.ui.views.console.ConsoleMessages.ProcessConsole_1, new Object[] {
+ outFile.getAbsolutePath() });
+ assertEquals("No or wrong output of redirect file path in console.", expectedPathMsg, doc.get(doc.getLineOffset(0), doc.getLineLength(0)));
+ assertEquals("Expected redirect file path to be linked.", 1, console.getHyperlinks().length);
+ }
+ if (checkOutput) {
+ assertEquals("Output not found in console.", new String(testContent), doc.get(doc.getLineOffset(1), doc.getLineLength(1)));
+ }
+ return console;
+ } finally {
+ if (!process.isTerminated()) {
+ process.terminate();
+ }
+ consoleManager.removeConsoles(new IConsole[] { console });
+ TestUtil.waitForJobs(name.getMethodName(), 0, 1000);
+ }
+ }
}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java
index be44bdc16..a759e5dbc 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/console/ProcessConsole.java
@@ -34,6 +34,7 @@ import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import java.util.regex.Pattern;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IStorage;
@@ -884,22 +885,12 @@ public class ProcessConsole extends IOConsole implements IConsole, IDebugEventSe
String fFilePath;
public ConsoleLogFilePatternMatcher(String filePath) {
- fFilePath = escape(filePath);
- }
-
- private String escape(String path) {
- StringBuilder buffer = new StringBuilder(path);
- int index = buffer.indexOf("\\"); //$NON-NLS-1$
- while (index >= 0) {
- buffer.insert(index, '\\');
- index = buffer.indexOf("\\", index+2); //$NON-NLS-1$
- }
- return buffer.toString();
+ fFilePath = filePath;
}
@Override
public String getPattern() {
- return fFilePath;
+ return Pattern.quote(fFilePath);
}
@Override

Back to the top