/******************************************************************************* * Copyright (c) 2014, 2015 Red Hat Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.autotools.ui.tests; import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.allOf; import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.inGroup; import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.widgetOfType; import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.withRegex; import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.withStyle; import static org.eclipse.swtbot.swt.finder.waits.Conditions.waitForWidget; import static org.eclipse.swtbot.swt.finder.waits.Conditions.widgetIsEnabled; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import java.util.regex.Pattern; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectNature; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.bindings.keys.KeyStroke; import org.eclipse.jface.bindings.keys.ParseException; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot; import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView; import org.eclipse.swtbot.swt.finder.SWTBot; import org.eclipse.swtbot.swt.finder.exceptions.WidgetNotFoundException; import org.eclipse.swtbot.swt.finder.finders.ContextMenuHelper; import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable; import org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory; import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences; import org.eclipse.swtbot.swt.finder.waits.Conditions; import org.eclipse.swtbot.swt.finder.waits.DefaultCondition; import org.eclipse.swtbot.swt.finder.waits.ICondition; import org.eclipse.swtbot.swt.finder.widgets.AbstractSWTBot; import org.eclipse.swtbot.swt.finder.widgets.SWTBotCheckBox; import org.eclipse.swtbot.swt.finder.widgets.SWTBotMenu; import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; import org.eclipse.swtbot.swt.finder.widgets.SWTBotToolbarButton; import org.eclipse.swtbot.swt.finder.widgets.SWTBotToolbarDropDownButton; import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree; import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.hamcrest.Matcher; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; public abstract class AbstractTest { private static final String PROJECT_NAME = "GnuProject"; protected static SWTWorkbenchBot bot; protected static String projectName; protected static SWTBotShell mainShell; protected static SWTBotView projectExplorer; @BeforeClass public static void initAbstractClass() throws Exception { AbstractTest.init(PROJECT_NAME); } @AfterClass public static void afterClass() throws Exception { AbstractTest.deleteProject(PROJECT_NAME); } public static void init(String projectName) throws Exception { SWTBotPreferences.KEYBOARD_LAYOUT = "EN_US"; SWTBotPreferences.PLAYBACK_DELAY = 10; bot = new SWTWorkbenchBot(); bot.sleep(5000); mainShell = getMainShell(); // Close the Welcome view if it exists try { bot.viewByTitle("Welcome").close(); } catch (Exception e) { // do nothing } // Turn off automatic building by default if (Platform.getOS().equals(Platform.OS_MACOSX)) { // On Mac, the Preferences menu is under the system menu final IWorkbench workbench = PlatformUI.getWorkbench(); workbench.getDisplay().asyncExec(() -> { IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); if (window != null) { Menu appMenu = workbench.getDisplay().getSystemMenu(); for (MenuItem item : appMenu.getItems()) { if (item.getText().startsWith("Preferences")) { Event event = new Event(); event.time = (int) System.currentTimeMillis(); event.widget = item; event.display = workbench.getDisplay(); item.setSelection(true); item.notifyListeners(SWT.Selection, event); break; } } } }); } else { clickMainMenu("Window", "Preferences"); } SWTBotShell shell = bot.shell("Preferences"); shell.activate(); bot.text().setText("Workspace"); bot.waitUntil(new NodeAvailableAndSelect(bot.tree(), "General", "Workspace")); SWTBotCheckBox buildAuto = bot.checkBox("Build automatically"); if (buildAuto != null && buildAuto.isChecked()) { buildAuto.click(); } bot.button("Apply").click(); // Ensure that the C/C++ perspective is chosen automatically // and doesn't require user intervention bot.text().setText("Perspectives"); bot.waitUntil(new NodeAvailableAndSelect(bot.tree(), "General", "Perspectives")); clickRadioButtonInGroup("Always open", "Open the associated perspective when creating a new project"); bot.button("OK").click(); AbstractTest.projectName = projectName; clickMainMenu("File", "New", "Project..."); shell = bot.shell("New Project"); shell.activate(); bot.text().setText("C Project"); bot.waitUntil(new NodeAvailableAndSelect(bot.tree(), "C/C++", "C Project")); bot.button("Next >").click(); bot.textWithLabel("Project name:").setText(projectName); bot.tree().expandNode("GNU Autotools") .select("Hello World ANSI C Autotools Project"); bot.button("Finish").click(); bot.waitUntil(Conditions.shellCloses(shell)); IProjectNature nature = checkProject().getNature( "org.eclipse.cdt.autotools.core.autotoolsNatureV2"); assertNotNull(nature); projectExplorer = bot.viewByTitle("Project Explorer"); } public static void deleteProject(String projectName) throws CoreException { ResourcesPlugin.getWorkspace().getRoot().getProject(projectName).delete(true, null); } public static class NodeAvailableAndSelect extends DefaultCondition { private SWTBotTree tree; private String parent; private String node; /** * Wait for a tree node (with a known parent) to become visible, and * select it when it does. Note that this wait condition should only be * used after having made an attempt to reveal the node. * * @param tree * The SWTBotTree that contains the node to select. * @param parent * The text of the parent node that contains the node to * select. * @param node * The text of the node to select. */ public NodeAvailableAndSelect(SWTBotTree tree, String parent, String node) { this.tree = tree; this.node = node; this.parent = parent; } @Override public boolean test() { try { SWTBotTreeItem parentNode = tree.getTreeItem(parent); parentNode.getNode(node).select(); return true; } catch (WidgetNotFoundException e) { return false; } } @Override public String getFailureMessage() { return "Timed out waiting for " + node; //$NON-NLS-1$ } } /** * Enter the project folder so as to avoid expanding trees later */ public static void enterProjectFolder() { projectExplorer.setFocus(); projectExplorer.bot().tree().select(projectName).contextMenu("Go Into") .click(); bot.waitUntil(waitForWidget(WidgetMatcherFactory.withText(projectName), projectExplorer.getWidget())); } /** * Exit from the project tree. */ public static void exitProjectFolder() { projectExplorer.setFocus(); SWTBotToolbarButton forwardButton; try { forwardButton = projectExplorer.toolbarPushButton("Forward"); } catch (WidgetNotFoundException e) { // If the "Forward" button is not found, already at the top level. return; } SWTBotToolbarButton backButton = projectExplorer .toolbarPushButton("Back to Workspace"); if (backButton.isEnabled()) { backButton.click(); bot.waitUntil(widgetIsEnabled(forwardButton)); } } /** * Selects a radio button with the given <code>mnemonicText</code> in a * group with the given label <inGroup>, while also deselecting whatever * other radio button in the group that is already selected. Workaround for * https://bugs.eclipse.org/bugs/show_bug.cgi?id=344484 */ public static void clickRadioButtonInGroup(String mnemonicText, final String inGroup) { UIThreadRunnable.syncExec(() -> { @SuppressWarnings("unchecked") Matcher<Button> matcher = allOf(inGroup(inGroup), widgetOfType(Button.class), withStyle(SWT.RADIO, "SWT.RADIO")); int i = 0; while (true) { Button b; try { b = bot.widget(matcher, i++); } catch (IndexOutOfBoundsException e) { return; } if (b.getSelection()) { b.setSelection(false); return; } } }); bot.radioInGroup(mnemonicText, inGroup).click(); } public static void clickContextMenu(AbstractSWTBot<? extends Control> bot, String... texts) { new SWTBotMenu(ContextMenuHelper.contextMenu(bot, texts)).click(); } public static void clickVolatileContextMenu( AbstractSWTBot<? extends Control> bot, String... texts) { int tries = 0; final int maxTries = 2; while (true) { try { clickContextMenu(bot, texts); return; } catch (Exception e) { if (++tries > maxTries) { throw e; } } } } public static void clickProjectContextMenu(String... texts) { clickVolatileContextMenu(bot.viewByTitle("Project Explorer").bot() .tree().select(projectName), texts); } /** * Click an item from the main Eclipse menu, with a guarantee that the main * shell will be in focus. * * @param items */ public static void clickMainMenu(String... items) { if (items.length == 0) { return; } mainShell.setFocus(); SWTBotMenu menu = bot.menu(items[0]); for (int i = 1; i < items.length; i++) { menu = menu.menu(items[i]); } menu.click(); } public static SWTBotShell openProperties(String parentCategory, String category) { clickContextMenu(projectExplorer.bot().tree().select(projectName), "Properties"); SWTBotShell shell = bot.shell("Properties for " + projectName); shell.activate(); bot.text().setText(category); bot.waitUntil(new NodeAvailableAndSelect(bot.tree(), parentCategory, category)); shell.activate(); return shell; } public static IProject checkProject() { IWorkspace workspace = ResourcesPlugin.getWorkspace(); assertNotNull(workspace); IWorkspaceRoot root = workspace.getRoot(); assertNotNull(root); IProject project = root.getProject(projectName); assertNotNull(project); return project; } public static SWTBotView viewConsole(String consoleType) { SWTBotView view = bot.viewByPartName("Console"); view.setFocus(); SWTBotToolbarDropDownButton b = view .toolbarDropDownButton("Display Selected Console"); org.hamcrest.Matcher<MenuItem> withRegex = withRegex(".*" + consoleType + ".*"); focusMainShell(); b.menuItem(withRegex).click(); try { b.pressShortcut(KeyStroke.getInstance("ESC")); } catch (ParseException e) { } view.setFocus(); return view; } /** * Focus on the main window */ public static void focusMainShell() { SWTBotShell shell = getMainShell(); shell.activate(); } private static SWTBotShell getMainShell() { for (SWTBotShell shellBot : bot.shells()) { if (shellBot.getText().toLowerCase().contains("eclipse")) { return shellBot; } } fail("Could not find main shell"); return null; } @After public void cleanUp() { exitProjectFolder(); SWTBotShell[] shells = bot.shells(); for (final SWTBotShell shell : shells) { if (!shell.equals(mainShell)) { String shellTitle = shell.getText(); if (shellTitle.length() > 0 && !shellTitle.startsWith("Quick Access")) { UIThreadRunnable.syncExec(() -> { if (shell.widget.getParent() != null && !shell.isOpen()) { shell.close(); } }); } } } bot.closeAllEditors(); mainShell.activate(); } protected ICondition consoleTextMatches(SWTBotView consoleView, Pattern pattern) { return new ConsoleTextMatches(consoleView, pattern); } protected class ConsoleTextMatches implements ICondition { private final SWTBotView view; private Pattern pattern; public ConsoleTextMatches(SWTBotView view, Pattern pattern) { this.view = view; this.pattern = pattern; } @Override public boolean test() { if (view.isActive()) { String output = view.bot().styledText().getText(); java.util.regex.Matcher m = pattern.matcher(output); return m.matches(); } return false; } @Override public void init(SWTBot bot) { } @Override public String getFailureMessage() { return null; } } }