1 /*******************************************************************************
2  * Copyright (c) 2019, 2020 Red Hat Inc., 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 package org.eclipse.jdt.text.tests.contentassist;
12 
13 import static org.junit.Assert.assertEquals;
14 import static org.junit.Assert.assertNull;
15 import static org.junit.Assert.assertTrue;
16 
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.Collections;
20 import java.util.List;
21 import java.util.Set;
22 import java.util.concurrent.atomic.AtomicReference;
23 import java.util.stream.Collectors;
24 
25 import org.junit.After;
26 import org.junit.Test;
27 
28 import org.eclipse.jdt.testplugin.JavaProjectHelper;
29 
30 import org.eclipse.swt.widgets.Composite;
31 import org.eclipse.swt.widgets.Display;
32 import org.eclipse.swt.widgets.Shell;
33 import org.eclipse.swt.widgets.Table;
34 import org.eclipse.swt.widgets.TableItem;
35 import org.eclipse.swt.widgets.Widget;
36 
37 import org.eclipse.core.runtime.IStatus;
38 import org.eclipse.core.runtime.NullProgressMonitor;
39 
40 import org.eclipse.jface.text.contentassist.ContentAssistant;
41 
42 import org.eclipse.ui.texteditor.ContentAssistAction;
43 import org.eclipse.ui.texteditor.ITextEditorActionConstants;
44 
45 import org.eclipse.jdt.core.ICompilationUnit;
46 import org.eclipse.jdt.core.IJavaProject;
47 import org.eclipse.jdt.core.IPackageFragment;
48 import org.eclipse.jdt.core.IPackageFragmentRoot;
49 
50 import org.eclipse.jdt.ui.JavaUI;
51 import org.eclipse.jdt.ui.PreferenceConstants;
52 import org.eclipse.jdt.ui.text.IJavaPartitions;
53 
54 import org.eclipse.jdt.internal.ui.JavaPlugin;
55 import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
56 import org.eclipse.jdt.internal.ui.text.java.JavaCompletionProcessor;
57 
58 public class ContentAssistAndThreadsTest extends AbstractCompletionTest {
59 	@After
resetPreference()60 	public void resetPreference() {
61 		JavaPlugin.getDefault().getPreferenceStore().setToDefault(PreferenceConstants.CODEASSIST_NONUITHREAD_COMPUTATION);
62 	}
63 
64 	@Test
testComputeCompletionInNonUIThread()65 	public void testComputeCompletionInNonUIThread() throws Exception {
66 		IJavaProject fJProject1= JavaProjectHelper.createJavaProject("TestProject1", "bin");
67 		JavaProjectHelper.addRTJar(fJProject1);
68 		IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src");
69 		IPackageFragment pack1= sourceFolder.createPackageFragment("test1", false, null);
70 		ICompilationUnit cu= pack1.createCompilationUnit("Blah.java", "", true, new NullProgressMonitor());
71 		JavaEditor part= (JavaEditor) JavaUI.openInEditor(cu);
72 		ContentAssistant assistant= new ContentAssistant();
73 		assistant.setDocumentPartitioning(IJavaPartitions.JAVA_PARTITIONING);
74 		JavaCompletionProcessor javaProcessor= new JavaCompletionProcessor(part, assistant, getContentType());
75 		AtomicReference<Throwable> exception = new AtomicReference<>();
76 		List<IStatus> errors = new ArrayList<>();
77 		JavaPlugin.getDefault().getLog().addLogListener((status, plugin) -> {
78 			if (status.getSeverity() >= IStatus.WARNING) {
79 				errors.add(status);
80 			}
81 		});
82 		Thread thread = new Thread(() -> {
83 			try {
84 				javaProcessor.computeCompletionProposals(part.getViewer(), 0);
85 				// a popup can be shown and block the thread in case of error
86 			} catch (Exception e) {
87 				exception.set(e);
88 			}
89 		});
90 		thread.start();
91 		thread.join();
92 		if (exception.get() != null) {
93 			exception.get().printStackTrace();
94 		}
95 		assertNull(exception.get());
96 		assertEquals(Collections.emptyList(), errors);
97 	}
98 
99 	@Test
testLongNonUIThreadContentAssistDoesntFreezeUI()100 	public void testLongNonUIThreadContentAssistDoesntFreezeUI() throws Exception {
101 		JavaPlugin.getDefault().getPreferenceStore().setValue(PreferenceConstants.CODEASSIST_NONUITHREAD_COMPUTATION, true);
102 		IJavaProject fJProject1= JavaProjectHelper.createJavaProject("TestProject1", "bin");
103 		JavaProjectHelper.addRTJar(fJProject1);
104 		IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src");
105 		IPackageFragment pack1= sourceFolder.createPackageFragment("test1", false, null);
106 		ICompilationUnit cu= pack1.createCompilationUnit("Blah.java", LongCompletionProposalComputer.CONTENT_TRIGGER_STRING, true, new NullProgressMonitor());
107 		JavaEditor part= (JavaEditor) JavaUI.openInEditor(cu);
108 		final Set<Shell> beforeShells = Arrays.stream(part.getSite().getShell().getDisplay().getShells()).filter(Shell::isVisible).collect(Collectors.toSet());
109 		Display display= part.getViewer().getTextWidget().getDisplay();
110 		ContentAssistAction action = (ContentAssistAction) part.getAction(ITextEditorActionConstants.CONTENT_ASSIST);
111 		action.update();
112 		CheckUIThreadReactivityThread thread = new CheckUIThreadReactivityThread(display);
113 		thread.start();
114 		display.asyncExec(() -> action.run()); // mustn't be synchronous or CheckUIThreadReactivityTest can miss it.
115 		try {
116 			assertTrue("Missing completion proposal", new org.eclipse.jdt.text.tests.performance.DisplayHelper() {
117 				@Override
118 				protected boolean condition() {
119 					Set<Shell> newShells = Arrays.stream(part.getSite().getShell().getDisplay().getShells()).filter(Shell::isVisible).collect(Collectors.toSet());
120 					newShells.removeAll(beforeShells);
121 					if (!newShells.isEmpty()) {
122 						Table completionTable = findCompletionSelectionControl(newShells.iterator().next());
123 						return Arrays.stream(completionTable.getItems()).map(TableItem::getText).anyMatch(LongCompletionProposalComputer.CONTENT_TRIGGER_STRING::equals);
124 					}
125 					return false;
126 				}
127 			}.waitForCondition(display, 3000));
128 		} finally {
129 			thread.interrupt();
130 		}
131 		assertTrue("UI was frozen for " + thread.getMaxDuration(), thread.getMaxDuration() < 1000);
132 	}
133 
findCompletionSelectionControl(Widget control)134 	private Table findCompletionSelectionControl(Widget control) {
135 		if (control instanceof Table) {
136 			return (Table)control;
137 		} else if (control instanceof Composite) {
138 			for (Widget child : ((Composite)control).getChildren()) {
139 				Table res = findCompletionSelectionControl(child);
140 				if (res != null) {
141 					return res;
142 				}
143 			}
144 		}
145 		return null;
146 	}
147 }
148