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