1 /******************************************************************************* 2 * Copyright (c) 2016-2017 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 * Contributors: 12 * Mickael Istria, Sopot Cela (Red Hat Inc.) 13 *******************************************************************************/ 14 package org.eclipse.ui.genericeditor.tests; 15 16 import static org.junit.Assert.assertEquals; 17 import static org.junit.Assert.assertNotNull; 18 import static org.junit.Assert.assertTrue; 19 20 import java.util.Arrays; 21 import java.util.Set; 22 import java.util.stream.Collectors; 23 24 import org.junit.After; 25 import org.junit.Test; 26 27 import org.eclipse.swt.SWT; 28 import org.eclipse.swt.custom.ST; 29 import org.eclipse.swt.custom.StyledText; 30 import org.eclipse.swt.widgets.Composite; 31 import org.eclipse.swt.widgets.Control; 32 import org.eclipse.swt.widgets.Display; 33 import org.eclipse.swt.widgets.Event; 34 import org.eclipse.swt.widgets.Shell; 35 import org.eclipse.swt.widgets.Table; 36 import org.eclipse.swt.widgets.TableItem; 37 import org.eclipse.swt.widgets.Widget; 38 39 import org.eclipse.jface.text.ITextSelection; 40 import org.eclipse.jface.text.contentassist.ICompletionProposal; 41 import org.eclipse.jface.text.tests.util.DisplayHelper; 42 43 import org.eclipse.ui.genericeditor.tests.contributions.BarContentAssistProcessor; 44 import org.eclipse.ui.genericeditor.tests.contributions.EnabledPropertyTester; 45 import org.eclipse.ui.genericeditor.tests.contributions.LongRunningBarContentAssistProcessor; 46 47 import org.eclipse.ui.texteditor.ContentAssistAction; 48 import org.eclipse.ui.texteditor.ITextEditorActionConstants; 49 50 /** 51 * @since 1.0 52 */ 53 public class CompletionTest extends AbstratGenericEditorTest { 54 55 private Shell completionShell; 56 57 @Test testCompletion()58 public void testCompletion() throws Exception { 59 final Set<Shell> beforeShells = Arrays.stream(editor.getSite().getShell().getDisplay().getShells()).filter(Shell::isVisible).collect(Collectors.toSet()); 60 editor.selectAndReveal(3, 0); 61 openConentAssist(); 62 this.completionShell= findNewShell(beforeShells, editor.getSite().getShell().getDisplay()); 63 final Table completionProposalList = findCompletionSelectionControl(completionShell); 64 checkCompletionContent(completionProposalList); 65 // TODO find a way to actually trigger completion and verify result against Editor content 66 // Assert.assertEquals("Completion didn't complete", "bars are good for a beer.", ((StyledText)editor.getAdapter(Control.class)).getText()); 67 } 68 69 @Test testCompletionUsingViewerSelection()70 public void testCompletionUsingViewerSelection() throws Exception { 71 final Set<Shell> beforeShells = Arrays.stream(editor.getSite().getShell().getDisplay().getShells()).filter(Shell::isVisible).collect(Collectors.toSet()); 72 editor.getDocumentProvider().getDocument(editor.getEditorInput()).set("abc"); 73 editor.selectAndReveal(0, 3); 74 openConentAssist(); 75 this.completionShell= findNewShell(beforeShells, editor.getSite().getShell().getDisplay()); 76 final Table completionProposalList = findCompletionSelectionControl(completionShell); 77 assertTrue(new DisplayHelper() { 78 @Override 79 protected boolean condition() { 80 return Arrays.stream(completionProposalList.getItems()).map(TableItem::getText).anyMatch("ABC"::equals); 81 } 82 }.waitForCondition(completionProposalList.getDisplay(), 200)); 83 } 84 85 @Test testEnabledWhenCompletion()86 public void testEnabledWhenCompletion() throws Exception { 87 // Confirm that when disabled, a completion shell is present 88 EnabledPropertyTester.setEnabled(false); 89 createAndOpenFile("enabledWhen.txt", "bar 'bar'"); 90 final Set<Shell> beforeShells = Arrays.stream(editor.getSite().getShell().getDisplay().getShells()).filter(Shell::isVisible).collect(Collectors.toSet()); 91 editor.selectAndReveal(3, 0); 92 openConentAssist(); 93 Shell[] afterShells = Arrays.stream(editor.getSite().getShell().getDisplay().getShells()) 94 .filter(Shell::isVisible) 95 .filter(shell -> !beforeShells.contains(shell)) 96 .toArray(Shell[]::new); 97 assertEquals("A new shell was found", 0, afterShells.length); 98 cleanFileAndEditor(); 99 100 // Confirm that when enabled, a completion shell is present 101 EnabledPropertyTester.setEnabled(true); 102 createAndOpenFile("enabledWhen.txt", "bar 'bar'"); 103 final Set<Shell> beforeEnabledShells = Arrays.stream(editor.getSite().getShell().getDisplay().getShells()).filter(Shell::isVisible).collect(Collectors.toSet()); 104 editor.selectAndReveal(3, 0); 105 openConentAssist(); 106 assertNotNull(findNewShell(beforeEnabledShells, editor.getSite().getShell().getDisplay())); 107 } 108 openConentAssist()109 private void openConentAssist() { 110 ContentAssistAction action = (ContentAssistAction) editor.getAction(ITextEditorActionConstants.CONTENT_ASSIST); 111 action.update(); 112 action.run(); 113 waitAndDispatch(100); 114 } 115 116 /** 117 * Checks that completion behaves as expected: 118 * 1. Computing is shown instantaneously 119 * 2. 1st proposal shown instantaneously 120 * 3. 2s later, 2nd proposal is shown 121 * @param completionProposalList the completion list 122 */ checkCompletionContent(final Table completionProposalList)123 private void checkCompletionContent(final Table completionProposalList) { 124 // should be instantaneous, but happens to go asynchronous on CI so let's allow a wait 125 assertTrue(new DisplayHelper() { 126 @Override 127 protected boolean condition() { 128 return completionProposalList.getItemCount() == 2; 129 } 130 }.waitForCondition(completionProposalList.getDisplay(), 200)); 131 final TableItem computingItem = completionProposalList.getItem(0); 132 assertTrue("Missing computing info entry", computingItem.getText().contains("Computing")); //$NON-NLS-1$ //$NON-NLS-2$ 133 TableItem completionProposalItem = completionProposalList.getItem(1); 134 final ICompletionProposal selectedProposal = (ICompletionProposal)completionProposalItem.getData(); 135 assertTrue("Incorrect proposal content", BarContentAssistProcessor.PROPOSAL.endsWith(selectedProposal .getDisplayString())); 136 completionProposalList.setSelection(completionProposalItem); 137 // asynchronous 138 new DisplayHelper() { 139 @Override 140 protected boolean condition() { 141 return completionProposalList.getItem(0) != computingItem && completionProposalList.getItemCount() == 2; 142 } 143 }.waitForCondition(completionProposalList.getDisplay(), LongRunningBarContentAssistProcessor.DELAY + 200); 144 completionProposalItem = completionProposalList.getItem(0); 145 assertTrue("Proposal content seems incorrect", BarContentAssistProcessor.PROPOSAL.endsWith(((ICompletionProposal)completionProposalItem.getData()).getDisplayString())); 146 TableItem otherProposalItem = completionProposalList.getItem(1); 147 assertTrue("Proposal content seems incorrect", LongRunningBarContentAssistProcessor.PROPOSAL.endsWith(((ICompletionProposal)otherProposalItem.getData()).getDisplayString())); 148 assertEquals("Addition of completion proposal should keep selection", selectedProposal, completionProposalList.getSelection()[0].getData()); 149 } 150 findNewShell(Set<Shell> beforeShells, Display display)151 public static Shell findNewShell(Set<Shell> beforeShells, Display display) { 152 Shell[] afterShells = Arrays.stream(display.getShells()) 153 .filter(Shell::isVisible) 154 .filter(shell -> !beforeShells.contains(shell)) 155 .toArray(Shell[]::new); 156 assertEquals("No new shell found", 1, afterShells.length); 157 return afterShells[0]; 158 } 159 160 @Test testCompletionFreeze_bug521484()161 public void testCompletionFreeze_bug521484() throws Exception { 162 final Set<Shell> beforeShells = Arrays.stream(editor.getSite().getShell().getDisplay().getShells()).filter(Shell::isVisible).collect(Collectors.toSet()); 163 editor.selectAndReveal(3, 0); 164 openConentAssist(); 165 this.completionShell= findNewShell(beforeShells, editor.getSite().getShell().getDisplay()); 166 final Table completionProposalList = findCompletionSelectionControl(this.completionShell); 167 // should be instantaneous, but happens to go asynchronous on CI so let's allow a wait 168 new DisplayHelper() { 169 @Override 170 protected boolean condition() { 171 return completionProposalList.getItemCount() == 2; 172 } 173 }.waitForCondition(completionShell.getDisplay(), 200); 174 assertEquals(2, completionProposalList.getItemCount()); 175 final TableItem computingItem = completionProposalList.getItem(0); 176 assertTrue("Missing computing info entry", computingItem.getText().contains("Computing")); //$NON-NLS-1$ //$NON-NLS-2$ 177 // Some processors are long running, moving cursor can cause freeze (bug 521484) 178 // asynchronous 179 long timestamp = System.currentTimeMillis(); 180 emulatePressLeftArrowKey(); 181 DisplayHelper.sleep(editor.getSite().getShell().getDisplay(), 200); //give time to process events 182 long processingDuration = System.currentTimeMillis() - timestamp; 183 assertTrue("UI Thread frozen for " + processingDuration + "ms", processingDuration < LongRunningBarContentAssistProcessor.DELAY); 184 } 185 186 @Test testMoveCaretBackUsesAllProcessors_bug522255()187 public void testMoveCaretBackUsesAllProcessors_bug522255() throws Exception { 188 final Set<Shell> beforeShells = Arrays.stream(editor.getSite().getShell().getDisplay().getShells()).filter(Shell::isVisible).collect(Collectors.toSet()); 189 testCompletion(); 190 emulatePressLeftArrowKey(); 191 DisplayHelper.sleep(editor.getSite().getShell().getDisplay(), LongRunningBarContentAssistProcessor.DELAY + 500); // adding delay is a workaround for bug521484, use only 100ms without the bug 192 this.completionShell= findNewShell(beforeShells, editor.getSite().getShell().getDisplay()); 193 final Table completionProposalList = findCompletionSelectionControl(this.completionShell); 194 assertEquals("Missing proposals from a Processor", 2, completionProposalList.getItemCount()); // replace with line below when #5214894 is done 195 // checkCompletionContent(completionProposalList); // use this instead of assert above when #521484 is done 196 } 197 emulatePressLeftArrowKey()198 private void emulatePressLeftArrowKey() { 199 editor.selectAndReveal(((ITextSelection)editor.getSelectionProvider().getSelection()).getOffset() - 1, 0); 200 StyledText styledText = (StyledText) editor.getAdapter(Control.class); 201 Event e = new Event(); 202 e.type = ST.VerifyKey; 203 e.widget = styledText; 204 e.keyCode = SWT.ARROW_LEFT; 205 e.display = styledText.getDisplay(); 206 styledText.notifyListeners(ST.VerifyKey, e); 207 } 208 findCompletionSelectionControl(Widget control)209 public static Table findCompletionSelectionControl(Widget control) { 210 if (control instanceof Table) { 211 return (Table)control; 212 } else if (control instanceof Composite) { 213 for (Widget child : ((Composite)control).getChildren()) { 214 Table res = findCompletionSelectionControl(child); 215 if (res != null) { 216 return res; 217 } 218 } 219 } 220 return null; 221 } 222 223 @After closeShell()224 public void closeShell() { 225 if (this.completionShell != null && !completionShell.isDisposed()) { 226 completionShell.close(); 227 } 228 } 229 } 230