blob: bc5e9ff8321377fdf7a2dda7df1b6379cfc044a4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Patrik Suzzi <psuzzi@gmail.com> - Bug 504029
******************************************************************************/
package org.eclipse.ui.tests.quickaccess;
import static org.junit.Assert.assertNotEquals;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.eclipse.jface.dialogs.DialogSettings;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.internal.quickaccess.QuickAccessDialog;
import org.eclipse.ui.internal.quickaccess.QuickAccessMessages;
import org.eclipse.ui.tests.harness.util.DisplayHelper;
import org.eclipse.ui.tests.harness.util.UITestCase;
/**
* Tests the quick access UI
* @since 3.4
*/
public class QuickAccessDialogTest extends UITestCase {
private static final int TIMEOUT = 3000;
// As defined in QuickAccessDialog and in SearchField
private static final int MAXIMUM_NUMBER_OF_ELEMENTS = 60;
private static final Predicate<Shell> isQuickAccessShell = shell -> shell.getText()
.equals(QuickAccessMessages.QuickAccessContents_QuickAccess);
private IDialogSettings dialogSettings;
private IWorkbenchWindow activeWorkbenchWindow;
/**
* @param testName
*/
public QuickAccessDialogTest(String testName) {
super(testName);
}
@Override
protected void doSetUp() throws Exception {
super.doSetUp();
Arrays.stream(Display.getDefault().getShells()).filter(isQuickAccessShell).forEach(Shell::close);
dialogSettings = new DialogSettings("QuickAccessDialogTest" + System.currentTimeMillis());
activeWorkbenchWindow = getWorkbench().getActiveWorkbenchWindow();
QuickAccessDialog warmupDialog = new QuickAccessDialog(activeWorkbenchWindow, null);
warmupDialog.open();
warmupDialog.close();
}
static Optional<QuickAccessDialog> findQuickAccessDialog() {
return Arrays.stream(Display.getDefault().getShells()).filter(isQuickAccessShell).findAny().map(Shell::getData)
.map(QuickAccessDialog.class::cast);
}
@Override
protected void doTearDown() throws Exception {
Arrays.stream(Display.getDefault().getShells()).filter(isQuickAccessShell)
.forEach(Shell::close);
}
/**
* Tests that the shell opens when the command is activated
* @throws Exception
*/
public void testOpenByCommand() throws Exception {
IHandlerService handlerService = getWorkbench().getActiveWorkbenchWindow()
.getService(IHandlerService.class);
Set<Shell> formerShells = new HashSet<>(Arrays.asList(Display.getDefault().getShells()));
handlerService.executeCommand("org.eclipse.ui.window.quickAccess", null); //$NON-NLS-1$
Set<Shell> newShells = new HashSet<>(Arrays.asList(Display.getDefault().getShells()));
assertEquals(formerShells.size() + 1, newShells.size());
newShells.removeAll(formerShells);
assertEquals(1, newShells.size());
assertTrue(isQuickAccessShell.test(newShells.iterator().next()));
}
/**
* Test that changing the filter text works correctly
*/
public void testTextFilter(){
QuickAccessDialog dialog = new QuickAccessDialog(activeWorkbenchWindow, null) {
@Override
protected IDialogSettings getDialogSettings() {
return dialogSettings;
}
};
dialog.open();
Text text = dialog.getQuickAccessContents().getFilterText();
Table table = dialog.getQuickAccessContents().getTable();
assertTrue("Quick access filter should be empty", text.getText().isEmpty());
assertTrue("Quick access table should be empty", table.getItemCount() == 0);
text.setText("T");
processEventsUntil(() -> table.getItemCount() > 1, TIMEOUT);
int oldCount = table.getItemCount();
assertTrue("Not enough quick access items for simple filter", oldCount > 3);
assertTrue("Too many quick access items for size of table", oldCount < MAXIMUM_NUMBER_OF_ELEMENTS);
final String oldFirstItemText = table.getItem(0).getText(1);
text.setText("B"); // The letter mustn't be part of the previous 1st proposal
assertTrue("The quick access items should have changed", DisplayHelper.waitForCondition(table.getDisplay(),
TIMEOUT,
() -> table.getItemCount() > 1 && !table.getItem(0).getText(1).equals(oldFirstItemText)));
int newCount = table.getItemCount();
assertTrue("Not enough quick access items for simple filter", newCount > 3);
assertTrue("Too many quick access items for size of table", newCount < MAXIMUM_NUMBER_OF_ELEMENTS);
}
public void testContributedElement() {
QuickAccessDialog dialog = new QuickAccessDialog(activeWorkbenchWindow, null) {
@Override
protected IDialogSettings getDialogSettings() {
return dialogSettings;
}
};
dialog.open();
final Table table = dialog.getQuickAccessContents().getTable();
Text text = dialog.getQuickAccessContents().getFilterText();
assertTrue("Quick access filter should be empty", text.getText().isEmpty());
assertTrue("Quick access table should be empty", table.getItemCount() == 0);
text.setText(TestQuickAccessComputer.TEST_QUICK_ACCESS_PROPOSAL_LABEL);
assertTrue("Missing contributed element", DisplayHelper.waitForCondition(dialog.getShell().getDisplay(), TIMEOUT, () ->
dialogContains(dialog, TestQuickAccessComputer.TEST_QUICK_ACCESS_PROPOSAL_LABEL))
);
}
public void testLongRunningComputerDoesntFreezeUI() throws InterruptedException {
QuickAccessDialog dialog = new QuickAccessDialog(activeWorkbenchWindow, null) {
@Override
protected IDialogSettings getDialogSettings() {
return dialogSettings;
}
};
dialog.open();
final Table table = dialog.getQuickAccessContents().getTable();
Text text = dialog.getQuickAccessContents().getFilterText();
long duration = System.currentTimeMillis();
text.setText(TestLongRunningQuickAccessComputer.THE_ELEMENT.getId());
assertTrue("UI Frozen on text change",
System.currentTimeMillis() - duration < TestLongRunningQuickAccessComputer.DELAY);
assertTrue("Missing contributed element", DisplayHelper.waitForCondition(dialog.getShell().getDisplay(), TestLongRunningQuickAccessComputer.DELAY + TIMEOUT, () ->
dialogContains(dialog, TestLongRunningQuickAccessComputer.THE_ELEMENT.getLabel())
));
table.select(0);
activateCurrentElement(dialog);
duration = System.currentTimeMillis();
QuickAccessDialog secondDialog = new QuickAccessDialog(activeWorkbenchWindow, null) {
@Override
protected IDialogSettings getDialogSettings() {
return dialogSettings;
}
};
secondDialog.open();
assertTrue(System.currentTimeMillis() - duration < TestLongRunningQuickAccessComputer.DELAY);
AtomicLong tick = new AtomicLong(System.currentTimeMillis());
AtomicLong maxBlockedUIThread = new AtomicLong();
assertTrue("Missing contributed element as previous pick", DisplayHelper.waitForCondition(
secondDialog.getShell().getDisplay(), TestLongRunningQuickAccessComputer.DELAY + TIMEOUT, () -> {
long currentTick = System.currentTimeMillis();
long previousTick = tick.getAndSet(currentTick);
long currentDelayInUIThread = currentTick - previousTick;
maxBlockedUIThread.set(Math.max(maxBlockedUIThread.get(), currentDelayInUIThread));
return dialogContains(secondDialog,
TestLongRunningQuickAccessComputer.THE_ELEMENT.getLabel());
}));
assertTrue(maxBlockedUIThread.get() < TestLongRunningQuickAccessComputer.DELAY);
}
/**
* Tests that activating the handler again toggles the show all setting and that the setting changes the results
* Also tests that closing and reopening the shell resets show all
*/
public void testShowAll() throws Exception {
// Open the shell
QuickAccessDialog dialog = new QuickAccessDialog(activeWorkbenchWindow, null) {
@Override
protected IDialogSettings getDialogSettings() {
return dialogSettings;
}
};
dialog.open();
Text text = dialog.getQuickAccessContents().getFilterText();
final Table table = dialog.getQuickAccessContents().getTable();
assertTrue("Quick access filter should be empty", text.getText().isEmpty());
assertTrue("Quick access table should be empty", table.getItemCount() == 0);
// Set a filter to get some items
text.setText("T");
processEventsUntil(() -> table.getItemCount() > 1, TIMEOUT);
final int defaultCount = table.getItemCount();
assertTrue("Not enough quick access items for simple filter", defaultCount > 3);
assertTrue("Too many quick access items for size of table", defaultCount < MAXIMUM_NUMBER_OF_ELEMENTS);
final String oldFirstItemText = table.getItem(0).getText(1);
IHandlerService handlerService = getWorkbench().getActiveWorkbenchWindow().getService(IHandlerService.class);
// Run the handler to turn on show all
handlerService.executeCommand("org.eclipse.ui.window.quickAccess", null); //$NON-NLS-1$
processEventsUntil(() -> table.getItemCount() != defaultCount, TIMEOUT);
final int allCount = table.getItemCount();
assertTrue("Turning on show all should display more items", allCount > defaultCount);
assertEquals("Turning on show all should not change the top item", oldFirstItemText, table.getItem(0).getText(1));
// Run the handler to turn off show all
handlerService.executeCommand("org.eclipse.ui.window.quickAccess", null); //$NON-NLS-1$
processEventsUntil(() -> table.getItemCount() != allCount, TIMEOUT);
// Note: The table count may one off from the old count because of shell resizing (scroll bars being added then removed)
assertTrue("Turning off show all should limit items shown", table.getItemCount() < allCount);
assertEquals("Turning off show all should not change the top item", oldFirstItemText, table.getItem(0).getText(1));
// Run the handler to turn on show all
handlerService.executeCommand("org.eclipse.ui.window.quickAccess", null); //$NON-NLS-1$
processEventsUntil(() -> table.getItemCount() == allCount, TIMEOUT);
assertEquals("Turning on show all twice shouldn't change the items", allCount, table.getItemCount());
assertEquals("Turning on show all twice shouldn't change the top item", oldFirstItemText, table.getItem(0).getText(1));
// Close and reopen the shell
dialog.close();
handlerService.executeCommand("org.eclipse.ui.window.quickAccess", null); //$NON-NLS-1$
dialog = findQuickAccessDialog().get();
text = dialog.getQuickAccessContents().getFilterText();
Table newTable = dialog.getQuickAccessContents().getTable();
text.setText("T");
processEventsUntil(() -> newTable.getItemCount() > 1, TIMEOUT);
// Note: The table count may one off from the old count because of shell resizing (scroll bars being added then removed)
assertTrue("Show all should be turned off when the shell is closed and reopened",
newTable.getItemCount() < allCount);
}
public void testPreviousChoicesAvailable() {
// add one selection to history
QuickAccessDialog dialog = new QuickAccessDialog(activeWorkbenchWindow, null) {
@Override
protected IDialogSettings getDialogSettings() {
return dialogSettings;
}
};
dialog.open();
Text text = dialog.getQuickAccessContents().getFilterText();
Table firstTable = dialog.getQuickAccessContents().getTable();
String quickAccessElementText = "Project Explorer";
text.setText(quickAccessElementText);
assertTrue("Missing entry", DisplayHelper.waitForCondition(firstTable.getDisplay(), TIMEOUT, () ->
dialogContains(dialog, quickAccessElementText)
));
// it seems like the 1st proposal isn't really "Project Explorer" on "official"
// tests?
System.out.println("Quick Access entries after querying '" + quickAccessElementText + '\'');
System.out.println(getAllEntries(firstTable).stream().map(s -> "* " + s).collect(Collectors.joining("\n")));
String selectedProposal = firstTable.getItem(0).getText(1);
firstTable.select(0);
activateCurrentElement(dialog);
assertNotEquals(0, dialogSettings.getArray("orderedElements").length);
// then try in a new SearchField
QuickAccessDialog secondDialog = new QuickAccessDialog(activeWorkbenchWindow,
null) {
@Override
protected IDialogSettings getDialogSettings() {
return dialogSettings;
}
};
secondDialog.open();
assertTrue("Missing entry in previous pick",
DisplayHelper.waitForCondition(secondDialog.getShell().getDisplay(), TIMEOUT,
() -> dialogContains(secondDialog, selectedProposal)));
}
private void activateCurrentElement(QuickAccessDialog dialog) {
Event enterPressed = new Event();
enterPressed.widget = dialog.getQuickAccessContents().getFilterText();
enterPressed.keyCode = SWT.CR;
enterPressed.widget.notifyListeners(SWT.KeyDown, enterPressed);
processEventsUntil(() -> enterPressed.widget.isDisposed(), 500);
}
public void testPreviousChoicesAvailableForExtension() {
// add one selection to history
QuickAccessDialog dialog = new QuickAccessDialog(activeWorkbenchWindow, null) {
@Override
protected IDialogSettings getDialogSettings() {
return dialogSettings;
}
};
dialog.open();
Text text = dialog.getQuickAccessContents().getFilterText();
text.setText(TestQuickAccessComputer.TEST_QUICK_ACCESS_PROPOSAL_LABEL);
final Table firstTable = dialog.getQuickAccessContents().getTable();
assertTrue(DisplayHelper.waitForCondition(text.getDisplay(), TIMEOUT,
() -> dialogContains(dialog, TestQuickAccessComputer.TEST_QUICK_ACCESS_PROPOSAL_LABEL)));
firstTable.select(0);
activateCurrentElement(dialog);
// then try in a new SearchField
QuickAccessDialog secondDialog = new QuickAccessDialog(activeWorkbenchWindow,
null) {
@Override
protected IDialogSettings getDialogSettings() {
return dialogSettings;
}
};
secondDialog.open();
assertTrue("Contributed item not found in previous choices",
DisplayHelper.waitForCondition(secondDialog.getShell().getDisplay(), TIMEOUT,
() -> getAllEntries(secondDialog.getQuickAccessContents().getTable()).stream()
.anyMatch(TestQuickAccessComputer::isContributedItem)));
}
public void testPreviousChoicesAvailableForIncrementalExtension() {
QuickAccessDialog dialog = new QuickAccessDialog(activeWorkbenchWindow, null) {
@Override
protected IDialogSettings getDialogSettings() {
return dialogSettings;
}
};
dialog.open();
Text text = dialog.getQuickAccessContents().getFilterText();
text.setText(TestIncrementalQuickAccessComputer.ENABLEMENT_QUERY);
final Table firstTable = dialog.getQuickAccessContents().getTable();
assertTrue(DisplayHelper.waitForCondition(text.getDisplay(), //
TIMEOUT, //
() -> firstTable.getItemCount() > 0
&& TestIncrementalQuickAccessComputer.isContributedItem(getAllEntries(firstTable).get(0))
));
firstTable.select(0);
activateCurrentElement(dialog);
// then try in a new SearchField
dialog = new QuickAccessDialog(activeWorkbenchWindow, null) {
@Override
protected IDialogSettings getDialogSettings() {
return dialogSettings;
}
};
dialog.open();
final Table secondTable = dialog.getQuickAccessContents().getTable();
assertTrue("Contributed item not found in previous choices",
DisplayHelper.waitForCondition(secondTable.getDisplay(), TIMEOUT, //
() -> getAllEntries(secondTable).stream()
.anyMatch(TestIncrementalQuickAccessComputer::isContributedItem)
));
}
public void testPrefixMatchHavePriority() {
QuickAccessDialog dialog = new QuickAccessDialog(activeWorkbenchWindow, null) {
@Override
protected IDialogSettings getDialogSettings() {
return dialogSettings;
}
};
dialog.open();
Text text = dialog.getQuickAccessContents().getFilterText();
Table table = dialog.getQuickAccessContents().getTable();
text.setText("P");
assertTrue("Not enough quick access items for simple filter",
DisplayHelper.waitForCondition(table.getDisplay(), TIMEOUT, () -> table.getItemCount() > 3));
assertTrue("Non-prefix match first", table.getItem(0).getText(1).toLowerCase().startsWith("p"));
}
private List<String> getAllEntries(Table table) {
if (table == null || table.isDisposed()) {
return Collections.emptyList();
}
final int nbColumns = table.getColumnCount();
return Arrays.stream(table.getItems()).map(item -> {
StringBuilder res = new StringBuilder();
for (int i = 0; i < nbColumns; i++) {
res.append(item.getText(i));
res.append(" | ");
}
return res.toString();
}).collect(Collectors.toList());
}
private boolean dialogContains(QuickAccessDialog dialog, String substring) {
return getAllEntries(dialog.getQuickAccessContents().getTable()).stream()
.anyMatch(label -> label.toLowerCase().contains(substring.toLowerCase()));
}
}