1 /*******************************************************************************
2  * Copyright (c) 2000, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.jdt.debug.tests.core;
15 
16 import java.util.ArrayList;
17 import java.util.Iterator;
18 import java.util.List;
19 
20 import org.eclipse.debug.core.DebugPlugin;
21 import org.eclipse.debug.core.ILaunch;
22 import org.eclipse.debug.core.ILaunchConfiguration;
23 import org.eclipse.debug.core.ILaunchManager;
24 import org.eclipse.debug.core.model.IBreakpoint;
25 import org.eclipse.debug.internal.core.IInternalDebugCoreConstants;
26 import org.eclipse.debug.ui.console.IConsole;
27 import org.eclipse.debug.ui.console.IConsoleLineTrackerExtension;
28 import org.eclipse.jdt.debug.core.IJavaDebugTarget;
29 import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint;
30 import org.eclipse.jdt.debug.core.JDIDebugModel;
31 import org.eclipse.jdt.debug.testplugin.ConsoleLineTracker;
32 import org.eclipse.jdt.debug.tests.AbstractDebugTest;
33 import org.eclipse.jdt.internal.debug.ui.IJDIPreferencesConstants;
34 import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
35 import org.eclipse.jdt.internal.debug.ui.console.JavaExceptionHyperLink;
36 import org.eclipse.jdt.internal.debug.ui.console.JavaStackTraceHyperlink;
37 import org.eclipse.jdt.internal.debug.ui.propertypages.JavaBreakpointPage;
38 import org.eclipse.jface.preference.IPreferenceStore;
39 import org.eclipse.jface.text.BadLocationException;
40 import org.eclipse.jface.text.IRegion;
41 import org.eclipse.ui.console.IHyperlink;
42 import org.eclipse.ui.console.IOConsole;
43 
44 /**
45  * Tests console line tracker.
46  */
47 public class LineTrackerTests extends AbstractDebugTest implements IConsoleLineTrackerExtension {
48 
49 	protected String[] fLines = new String[] {
50 		"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"
51 	};
52 
53 	protected IJavaDebugTarget fTarget;
54 
55 	protected List<String> fLinesRead = new ArrayList<>();
56 
57 	protected boolean fStarted = false;
58 
59 	protected boolean fStopped = false;
60 
61 	protected IConsole fConsole = null;
62 
63 	protected Object fLock = new Object();
64 
LineTrackerTests(String name)65 	public LineTrackerTests(String name) {
66 		super(name);
67 	}
68 
69     @Override
setUp()70 	protected void setUp() throws Exception {
71         super.setUp();
72         fStarted = false;
73         fStopped = false;
74     }
75 
76     @Override
tearDown()77 	protected void tearDown() throws Exception {
78         // delete references and gc to free memory.
79         fConsole = null;
80         fLines = null;
81         fLinesRead.clear();
82         fLinesRead = null;
83 
84         System.gc();
85 		super.tearDown();
86     }
87 
88 
testSimpleLineCounter()89 	public void testSimpleLineCounter() throws Exception {
90 		ConsoleLineTracker.setDelegate(this);
91 		fTarget = null;
92 		try {
93 			fTarget = launchAndTerminate("OneToTen");
94 			synchronized (fLock) {
95 			    if (!fStopped) {
96 			        fLock.wait(30000);
97 			    }
98 			}
99 			dumpOnError(11);
100 			assertTrue("Never received 'start' notification", fStarted);
101 			assertTrue("Never received 'stopped' notification", fStopped);
102 			// there are 10 lines and one "empty line" (i.e. the last "new line")
103 			String firstLine = fLinesRead.get(0);
104 			if (firstLine.contains("advanced source lookup disabled")) {
105 				fLinesRead.remove(0);
106 			}
107 			assertEquals("Wrong number of lines output", 11, fLinesRead.size());
108 			for (int i = 0; i < 10; i++) {
109 				assertEquals("Line " + i + " not equal", fLines[i], fLinesRead.get(i));
110 			}
111 			assertEquals("Should be an empty last line", IInternalDebugCoreConstants.EMPTY_STRING, fLinesRead.get(10));
112 		} finally {
113 			ConsoleLineTracker.setDelegate(null);
114 			terminateAndRemove(fTarget);
115 		}
116 	}
117 
118 	/**
119 	 * This program prints the final line without a new line
120 	 * @throws Exception
121 	 */
testNoPrintln()122 	public void testNoPrintln() throws Exception {
123 		ConsoleLineTracker.setDelegate(this);
124 		fTarget = null;
125 		try {
126 			fTarget = launchAndTerminate("OneToTenPrint");
127 			synchronized (fLock) {
128 			    if (!fStopped) {
129 			        fLock.wait(30000);
130 			    }
131             }
132 			dumpOnError(10);
133 			assertTrue("Never received 'start' notification", fStarted);
134 			assertTrue("Did not receive 'stopped' notification", fStopped);
135 			String firstLine = fLinesRead.get(0);
136 			if (firstLine.contains("advanced source lookup disabled")) {
137 				fLinesRead.remove(0);
138 			}
139 			assertEquals("Wrong number of lines", 10, fLinesRead.size());
140 			for (int i = 0; i < 10; i++) {
141 				assertEquals("Line " + i + " not equal", fLines[i], fLinesRead.get(i));
142 			}
143 		} finally {
144 			ConsoleLineTracker.setDelegate(null);
145 			terminateAndRemove(fTarget);
146 		}
147 	}
148 
149 	/**
150 	 * Test 10,000 lines of output.
151 	 *
152 	 * @throws Exception
153 	 */
testFlood()154 	public void testFlood() throws Exception {
155 		ConsoleLineTracker.setDelegate(this);
156 		ILaunch launch = null;
157 		try {
158 			ILaunchConfiguration config = getLaunchConfiguration("FloodConsole");
159 			assertNotNull("Could not locate launch configuration", config);
160 			launch = config.launch(ILaunchManager.RUN_MODE, null);
161 
162 			synchronized (fLock) {
163 			    if (!fStopped) {
164 			        fLock.wait(480000);
165 			    }
166 			}
167 			assertTrue("Never received 'start' notification", fStarted);
168 			assertTrue("Never received 'stopped' notification", fStopped);
169 			// Should be 10,000 lines
170 			assertEquals("Wrong number of lines", 10000, fLinesRead.size());
171 		} finally {
172 			ConsoleLineTracker.setDelegate(null);
173 			launch.getProcesses()[0].terminate();
174 			DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
175 		}
176 	}
177 
testHyperLink()178 	public void testHyperLink() throws Exception {
179 	    try {
180 			ConsoleLineTracker.setDelegate(this);
181 			fTarget = launchAndTerminate("ThrowsNPE");
182 			getHyperlink(0, ConsoleLineTracker.getDocument());
183 	    } finally {
184 	        ConsoleLineTracker.setDelegate(null);
185 	        terminateAndRemove(fTarget);
186 	    }
187 	}
188 
testJavaExceptionHyperLink()189 	public void testJavaExceptionHyperLink() throws Exception {
190 		ConsoleLineTracker.setDelegate(this);
191 		fTarget = null;
192 		IPreferenceStore jdiUIPreferences = JDIDebugUIPlugin.getDefault().getPreferenceStore();
193 		boolean suspendOnException = jdiUIPreferences.getBoolean(IJDIPreferencesConstants.PREF_SUSPEND_ON_UNCAUGHT_EXCEPTIONS);
194 		jdiUIPreferences.setValue(IJDIPreferencesConstants.PREF_SUSPEND_ON_UNCAUGHT_EXCEPTIONS, false);
195 		try {
196 			fTarget = launchAndTerminate("ThrowsNPE");
197 
198 			synchronized (fLock) {
199 				if (!fStopped) {
200 					fLock.wait(30000);
201 				}
202 			}
203 			assertTrue("Never received 'start' notification", fStarted);
204 			assertTrue("Never received 'stopped' notification", fStopped);
205 			assertTrue("Console should be an IOCosnole", fConsole instanceof IOConsole);
206 			IOConsole console = (IOConsole) fConsole;
207 			IHyperlink[] hyperlinks = console.getHyperlinks();
208 
209 			// should be 1 exception hyperlink
210 			int total = 0;
211 			for (int i = 0; i < hyperlinks.length; i++) {
212 				IHyperlink hyperlink = hyperlinks[i];
213 				if (hyperlink instanceof JavaExceptionHyperLink) {
214 					total++;
215 					// should be followed by a stack trace hyperlink
216 					assertTrue("Stack trace hyperlink missing", hyperlinks[i + 1] instanceof JavaStackTraceHyperlink);
217 				}
218 			}
219 			assertEquals("Wrong number of exception hyperlinks", 1, total);
220 			IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(JDIDebugModel.getPluginIdentifier());
221 			IJavaExceptionBreakpoint foundBreakpoint = null;
222 			for (int i = 0; i < breakpoints.length; i++) {
223 				IBreakpoint breakpoint = breakpoints[i];
224 				if (breakpoint instanceof IJavaExceptionBreakpoint) {
225 					IJavaExceptionBreakpoint exceptionBreakpoint = (IJavaExceptionBreakpoint) breakpoint;
226 					if ("java.lang.NullPointerException".equals(exceptionBreakpoint.getTypeName())) {
227 						foundBreakpoint = exceptionBreakpoint;
228 						break;
229 					}
230 				}
231 			}
232 			assertTrue("NPE breakpoint should not exist yet", foundBreakpoint == null);
233 			IJavaExceptionBreakpoint ex = createExceptionBreakpoint("java.lang.NullPointerException", true, false);
234 			ex.setEnabled(false);
235 			JavaExceptionHyperLink exLink = (JavaExceptionHyperLink) hyperlinks[0];
236 			exLink.linkActivated();
237 			breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(JDIDebugModel.getPluginIdentifier());
238 			foundBreakpoint = null;
239 			for (int i = 0; i < breakpoints.length; i++) {
240 				IBreakpoint breakpoint = breakpoints[i];
241 				if (breakpoint instanceof IJavaExceptionBreakpoint) {
242 					IJavaExceptionBreakpoint exceptionBreakpoint = (IJavaExceptionBreakpoint) breakpoint;
243 					if ("java.lang.NullPointerException".equals(exceptionBreakpoint.getTypeName())) {
244 						foundBreakpoint = exceptionBreakpoint;
245 						break;
246 					}
247 				}
248 			}
249 			assertTrue("NPE breakpoint not found", foundBreakpoint != null);
250 			assertTrue("NPE breakpoint not enabled", foundBreakpoint.isEnabled());
251 			assertTrue("NPE breakpoint cancel enablement value not false", foundBreakpoint.getMarker().getAttribute(JavaBreakpointPage.ATTR_ENABLED_SETTING_ON_CANCEL, "").equals("false"));
252 		} finally {
253 			ConsoleLineTracker.setDelegate(null);
254 			jdiUIPreferences.setValue(IJDIPreferencesConstants.PREF_SUSPEND_ON_UNCAUGHT_EXCEPTIONS, suspendOnException);
255 			terminateAndRemove(fTarget);
256 		}
257 
258 	}
259 
260 	/**
261 	 * @see org.eclipse.debug.ui.console.IConsoleLineTracker#dispose()
262 	 */
263 	@Override
dispose()264 	public void dispose() {
265 	}
266 
267 	/**
268 	 * @see org.eclipse.debug.ui.console.IConsoleLineTracker#init(org.eclipse.debug.ui.console.IConsole)
269 	 */
270 	@Override
init(IConsole console)271 	public void init(IConsole console) {
272 		fConsole = console;
273 		fStarted = true;
274 	}
275 
276 	/**
277 	 * @see org.eclipse.debug.ui.console.IConsoleLineTracker#lineAppended(org.eclipse.jface.text.IRegion)
278 	 */
279 	@Override
lineAppended(IRegion line)280 	public void lineAppended(IRegion line) {
281 		if (fStarted) {
282 			try {
283 				String text = fConsole.getDocument().get(line.getOffset(), line.getLength());
284 				if (!JavaOutputHelpers.isKnownExtraneousOutput(text)) {
285 					fLinesRead.add(text);
286 				}
287 			} catch (BadLocationException e) {
288 			    e.printStackTrace();
289 			}
290 		}
291 	}
292 
293 	/**
294 	 * @see org.eclipse.debug.ui.console.IConsoleLineTracker#streamClosed()
295 	 */
296 	@Override
consoleClosed()297 	public void consoleClosed() {
298 	    synchronized (fLock) {
299 			fStopped = true;
300 			fLock.notifyAll();
301         }
302 	}
303 
304 	/**
305 	 * Tests that stack traces appear with hyperlinks
306 	 */
testStackTraces()307 	public void testStackTraces() throws Exception {
308 		ConsoleLineTracker.setDelegate(this);
309 		fTarget = null;
310 		IPreferenceStore jdiUIPreferences = JDIDebugUIPlugin.getDefault().getPreferenceStore();
311 		boolean suspendOnException = jdiUIPreferences.getBoolean(IJDIPreferencesConstants.PREF_SUSPEND_ON_UNCAUGHT_EXCEPTIONS);
312 		jdiUIPreferences.setValue(IJDIPreferencesConstants.PREF_SUSPEND_ON_UNCAUGHT_EXCEPTIONS, false);
313 		try {
314 			fTarget = launchAndTerminate("StackTraces");
315 			synchronized (fLock) {
316 			    if (!fStopped) {
317 			        fLock.wait(30000);
318 			    }
319 			}
320 			assertTrue("Never received 'start' notification", fStarted);
321 			assertTrue("Never received 'stopped' notification", fStopped);
322 			assertTrue("Console should be an IOCosnole", fConsole instanceof IOConsole);
323 			IOConsole console = (IOConsole)fConsole;
324 			IHyperlink[] hyperlinks = console.getHyperlinks();
325 			// should be 100 exception hyperlinks
326 			int total = 0;
327 			for (int i = 0; i < hyperlinks.length; i++) {
328                 IHyperlink hyperlink = hyperlinks[i];
329                 if (hyperlink instanceof JavaExceptionHyperLink) {
330                     total++;
331                     // should be followed by a stack trace hyperlink
332                     assertTrue("Stack trace hyperlink missing", hyperlinks[i + 1] instanceof JavaStackTraceHyperlink);
333                 }
334             }
335 			assertEquals("Wrong number of exception hyperlinks", 100, total);
336 		} finally {
337 			ConsoleLineTracker.setDelegate(null);
338 			jdiUIPreferences.setValue(IJDIPreferencesConstants.PREF_SUSPEND_ON_UNCAUGHT_EXCEPTIONS, suspendOnException);
339 			terminateAndRemove(fTarget);
340 		}
341 	}
342 
testStringConcatenation()343 	public void testStringConcatenation() throws Exception {
344 	    ConsoleLineTracker.setDelegate(this);
345 	    String typeName = "PrintConcatenation";
346 		createConditionalLineBreakpoint(23, typeName, "System.out.println(\"var = \" + foo); return false;", true);
347 	    fTarget = null;
348 	    try {
349 	        fTarget = launchAndTerminate(typeName);
350 	        synchronized (fLock) {
351 			    if (!fStopped) {
352 			        fLock.wait(30000);
353 			    }
354 			}
355 	        dumpOnError(2);
356 	        assertTrue("Never received 'start' notification", fStarted);
357 			assertTrue("Never received 'stopped' notification", fStopped);
358 			String firstLine = fLinesRead.get(0);
359 			if (firstLine.contains("advanced source lookup disabled")) {
360 				fLinesRead.remove(0);
361 			}
362 			assertEquals("Wrong number of lines output", 2, fLinesRead.size());
363 			assertEquals("Wrong output", "var = foo", fLinesRead.get(0));
364 	    } finally {
365 	        ConsoleLineTracker.setDelegate(null);
366 	        removeAllBreakpoints();
367 	        terminateAndRemove(fTarget);
368 	    }
369 	}
370 
testIntConcatenation()371 	public void testIntConcatenation() throws Exception {
372 	    ConsoleLineTracker.setDelegate(this);
373 	    String typeName = "PrintConcatenation";
374 		createConditionalLineBreakpoint(23, typeName, "System.out.println(\"var = \" + x); return false;", true);
375 	    fTarget = null;
376 	    try {
377 	        fTarget = launchAndTerminate(typeName);
378 	        synchronized (fLock) {
379 			    if (!fStopped) {
380 			        fLock.wait(30000);
381 			    }
382 			}
383 	        dumpOnError(2);
384 	        assertTrue("Never received 'start' notification", fStarted);
385 			assertTrue("Never received 'stopped' notification", fStopped);
386 			String firstLine = fLinesRead.get(0);
387 			if (firstLine.contains("advanced source lookup disabled")) {
388 				fLinesRead.remove(0);
389 			}
390 			assertEquals("Wrong number of lines output", 2, fLinesRead.size());
391 			assertEquals("Wrong output", "var = 35", fLinesRead.get(0));
392 	    } finally {
393 	        ConsoleLineTracker.setDelegate(null);
394 	        removeAllBreakpoints();
395 	        terminateAndRemove(fTarget);
396 	    }
397 	}
398 
dumpOnError(int expectedLines)399 	private void dumpOnError(int expectedLines) {
400         if (fLinesRead.size() != expectedLines) {
401 	    	Iterator<String> lines = fLinesRead.iterator();
402 	    	while (lines.hasNext()) {
403 				String line = lines.next();
404 				System.out.println(line);
405 			}
406         }
407 	}
408 }
409