tmf: Add waitUntil / condition to tmf.ui.tests
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui.swtbot.tests / shared / org / eclipse / tracecompass / tmf / ui / swtbot / tests / shared / SWTBotUtils.java
index b8bbe60a511dbdb8a62c5a4b3cacfa748e603ea3..48f24b5c9824a0491d0f5e7eac01b871fb2ad7de 100644 (file)
@@ -16,18 +16,26 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.util.Arrays;
 import java.util.List;
+import java.util.TimeZone;
+import java.util.concurrent.atomic.AtomicBoolean;
 
+import org.apache.log4j.Logger;
+import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceVisitor;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jface.bindings.keys.IKeyLookup;
 import org.eclipse.jface.bindings.keys.KeyStroke;
 import org.eclipse.jface.bindings.keys.ParseException;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.widgets.Display;
@@ -40,8 +48,12 @@ import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotEditor;
 import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
 import org.eclipse.swtbot.swt.finder.SWTBot;
 import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
+import org.eclipse.swtbot.swt.finder.keyboard.Keyboard;
+import org.eclipse.swtbot.swt.finder.keyboard.Keystrokes;
 import org.eclipse.swtbot.swt.finder.results.Result;
 import org.eclipse.swtbot.swt.finder.results.VoidResult;
+import org.eclipse.swtbot.swt.finder.utils.MessageFormat;
+import org.eclipse.swtbot.swt.finder.utils.SWTUtils;
 import org.eclipse.swtbot.swt.finder.waits.Conditions;
 import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
 import org.eclipse.swtbot.swt.finder.widgets.SWTBotButton;
@@ -51,12 +63,19 @@ import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell;
 import org.eclipse.swtbot.swt.finder.widgets.SWTBotTable;
 import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
 import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem;
+import org.eclipse.swtbot.swt.finder.widgets.TimeoutException;
+import org.eclipse.tracecompass.internal.tmf.ui.project.operations.NewExperimentOperation;
 import org.eclipse.tracecompass.tmf.ui.editors.TmfEventsEditor;
+import org.eclipse.tracecompass.tmf.ui.project.model.TmfExperimentElement;
+import org.eclipse.tracecompass.tmf.ui.project.model.TmfExperimentFolder;
 import org.eclipse.tracecompass.tmf.ui.project.model.TmfOpenTraceHelper;
+import org.eclipse.tracecompass.tmf.ui.project.model.TmfProjectElement;
 import org.eclipse.tracecompass.tmf.ui.project.model.TmfProjectRegistry;
+import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceElement;
 import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceFolder;
 import org.eclipse.tracecompass.tmf.ui.project.model.TmfTracesFolder;
 import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.ConditionHelpers.ProjectElementHasChild;
+import org.eclipse.tracecompass.tmf.ui.tests.shared.WaitUtils;
 import org.eclipse.tracecompass.tmf.ui.views.TracingPerspectiveFactory;
 import org.eclipse.ui.IEditorPart;
 import org.eclipse.ui.IEditorReference;
@@ -71,20 +90,32 @@ import org.hamcrest.Matcher;
  *
  * @author Matthew Khouzam
  */
+@SuppressWarnings("restriction")
 public final class SWTBotUtils {
 
+    private static final String WINDOW_MENU = "Window";
+    private static final String PREFERENCES_MENU_ITEM = "Preferences";
+    private static boolean fPrintedEnvironment = false;
+    private static Logger log = Logger.getLogger(SWTBotUtils.class);
+
     private SWTBotUtils() {
+
     }
 
     private static final String TRACING_PERSPECTIVE_ID = TracingPerspectiveFactory.ID;
 
     /**
-     * Waits for all Eclipse jobs to finish
+     * Waits for all Eclipse jobs to finish. Times out after
+     * WaitUtils#MAX_JOBS_WAIT_TIME by default.
+     *
+     * @throws RuntimeException
+     *             once the waiting time passes the default maximum value
+     *
+     * @deprecated Use {@link WaitUtils#waitForJobs()} instead
      */
+    @Deprecated
     public static void waitForJobs() {
-        while (!Job.getJobManager().isIdle()) {
-            delay(100);
-        }
+        WaitUtils.waitForJobs();
     }
 
     /**
@@ -119,7 +150,7 @@ public final class SWTBotUtils {
             }
         });
 
-        SWTBotUtils.waitForJobs();
+        WaitUtils.waitForJobs();
     }
 
     /**
@@ -135,13 +166,16 @@ public final class SWTBotUtils {
     public static void deleteProject(final String projectName, boolean deleteResources, SWTWorkbenchBot bot) {
         // Wait for any analysis to complete because it might create
         // supplementary files
-        SWTBotUtils.waitForJobs();
+        WaitUtils.waitForJobs();
         try {
             ResourcesPlugin.getWorkspace().getRoot().getProject(projectName).refreshLocal(IResource.DEPTH_INFINITE, null);
         } catch (CoreException e) {
         }
 
-        SWTBotUtils.waitForJobs();
+        WaitUtils.waitForJobs();
+
+        closeSecondaryShells(bot);
+        WaitUtils.waitForJobs();
 
         final SWTBotView projectViewBot = bot.viewById(IPageLayout.ID_PROJECT_EXPLORER);
         projectViewBot.setFocus();
@@ -162,7 +196,7 @@ public final class SWTBotUtils {
         bot.waitUntil(Conditions.widgetIsEnabled(okButton));
         okButton.click();
 
-        SWTBotUtils.waitForJobs();
+        WaitUtils.waitForJobs();
     }
 
     /**
@@ -177,6 +211,39 @@ public final class SWTBotUtils {
         deleteProject(projectName, true, bot);
     }
 
+    /**
+     * Creates an experiment
+     *
+     * @param bot
+     *            a given workbench bot
+     * @param projectName
+     *            the name of the project, creates the project if needed
+     * @param expName
+     *            the experiment name
+     */
+    public static void createExperiment(SWTWorkbenchBot bot, String projectName, final @NonNull String expName) {
+        IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+        TmfProjectElement tmfProject = TmfProjectRegistry.getProject(project, true);
+        TmfExperimentFolder expFolder = tmfProject.getExperimentsFolder();
+        assertNotNull(expFolder);
+        NewExperimentOperation operation = new NewExperimentOperation(expFolder, expName);
+        operation.run(new NullProgressMonitor());
+
+        bot.waitUntil(new DefaultCondition() {
+            @Override
+            public boolean test() throws Exception {
+                TmfExperimentElement experiment = expFolder.getExperiment(expName);
+                return experiment != null;
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "Experiment (" + expName + ") couldn't be created";
+            }
+        });
+    }
+
+
     /**
      * Focus on the main window
      *
@@ -184,11 +251,42 @@ public final class SWTBotUtils {
      *            swtbotshells for all the shells
      */
     public static void focusMainWindow(SWTBotShell[] shellBots) {
+        SWTBotShell mainShell = getMainShell(shellBots);
+        if (mainShell != null) {
+            mainShell.activate();
+        }
+    }
+
+    private static SWTBotShell getMainShell(SWTBotShell[] shellBots) {
+        SWTBotShell mainShell = null;
         for (SWTBotShell shellBot : shellBots) {
             if (shellBot.getText().toLowerCase().contains("eclipse")) {
-                shellBot.activate();
+                mainShell = shellBot;
             }
         }
+        return mainShell;
+    }
+
+    /**
+     * Close all non-main shells that are visible.
+     *
+     * @param bot
+     *            the workbench bot
+     */
+    public static void closeSecondaryShells(SWTWorkbenchBot bot) {
+        SWTBotShell[] shells = bot.shells();
+        SWTBotShell mainShell = getMainShell(shells);
+        if (mainShell == null) {
+            return;
+        }
+
+        // Close all non-main shell but make sure we don't close an invisible
+        // shell such the special "limbo shell" that Eclipse needs to work
+        Arrays.stream(shells)
+                .filter(shell -> shell != mainShell)
+                .filter(SWTBotShell::isVisible)
+                .peek(shell -> log.debug(MessageFormat.format("Closing lingering shell with title {0}", shell.getText())))
+                .forEach(SWTBotShell::close);
     }
 
     /**
@@ -258,6 +356,12 @@ public final class SWTBotUtils {
 
         SWTWorkbenchBot bot = new SWTWorkbenchBot();
         UIThreadRunnable.syncExec(() -> {
+            printEnvironment();
+
+            // There seems to be problems on some system where the main shell is
+            // not in focus initially. This was seen using Xvfb and Xephyr on some occasions.
+            focusMainWindow(bot.shells());
+
             Shell shell = bot.activeShell().widget;
 
             // Only adjust shell if it appears to be the top-most
@@ -267,6 +371,41 @@ public final class SWTBotUtils {
         });
     }
 
+    private static void printEnvironment() {
+        if (fPrintedEnvironment) {
+            return;
+        }
+
+        // Print some information about the environment that could affect test outcome
+        Rectangle bounds = Display.getDefault().getBounds();
+        System.out.println("Display size: " + bounds.width + "x" + bounds.height);
+
+        String osVersion = System.getProperty("os.version");
+        if (osVersion != null) {
+            System.out.println("OS version=" + osVersion);
+        }
+        String gtkVersion = System.getProperty("org.eclipse.swt.internal.gtk.version");
+        if (gtkVersion != null) {
+            System.out.println("GTK version=" + gtkVersion);
+            // Try to print the GTK theme information as behavior can change depending on the theme
+            String gtkTheme = System.getProperty("org.eclipse.swt.internal.gtk.theme");
+            System.out.println("GTK theme=" + (gtkTheme == null ? "unknown" : gtkTheme));
+
+            String overlayScrollbar = System.getenv("LIBOVERLAY_SCROLLBAR");
+            if (overlayScrollbar != null) {
+                System.out.println("LIBOVERLAY_SCROLLBAR=" + overlayScrollbar);
+            }
+            String ubuntuMenuProxy = System.getenv("UBUNTU_MENUPROXY");
+            if (ubuntuMenuProxy != null) {
+                System.out.println("UBUNTU_MENUPROXY=" + ubuntuMenuProxy);
+            }
+        }
+
+        System.out.println("Time zone: " + TimeZone.getDefault().getDisplayName());
+
+        fPrintedEnvironment = true;
+    }
+
     /**
      * If the test is running in the UI thread then fail
      */
@@ -345,7 +484,7 @@ public final class SWTBotUtils {
 
         if (delay) {
             delay(1000);
-            waitForJobs();
+            WaitUtils.waitForJobs();
         }
     }
 
@@ -371,7 +510,7 @@ public final class SWTBotUtils {
             }
         });
 
-        SWTBotUtils.waitForJobs();
+        WaitUtils.waitForJobs();
         SWTBotUtils.delay(1000);
         assertNotNull(tmfEd);
         return editorBot;
@@ -404,7 +543,6 @@ public final class SWTBotUtils {
         SWTBotTreeItem currentItem = tracesNode;
         for (String segment : elementPath.segments()) {
             currentItem = getTraceProjectItem(projectExplorerBot, currentItem, segment);
-            currentItem.select();
             currentItem.doubleClick();
         }
 
@@ -450,6 +588,121 @@ public final class SWTBotUtils {
         return tracesFolderItem;
     }
 
+    /**
+     * Clear the traces folder
+     *
+     * @param bot
+     *            a given workbench bot
+     * @param projectName
+     *            the name of the project (needs to exist)
+     */
+    public static void clearTracesFolder(SWTWorkbenchBot bot, String projectName) {
+        IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+        TmfProjectElement tmfProject = TmfProjectRegistry.getProject(project, false);
+        TmfTraceFolder tracesFolder = tmfProject.getTracesFolder();
+        try {
+            for (TmfTraceElement traceElement : tracesFolder.getTraces()) {
+                traceElement.delete(null);
+            }
+
+            final IFolder resource = tracesFolder.getResource();
+            resource.accept(new IResourceVisitor() {
+                @Override
+                public boolean visit(IResource visitedResource) throws CoreException {
+                    if (visitedResource != resource) {
+                        visitedResource.delete(true, null);
+                    }
+                    return true;
+                }
+            }, IResource.DEPTH_ONE, 0);
+        } catch (CoreException e) {
+            fail(e.getMessage());
+        }
+
+        bot.waitUntil(new DefaultCondition() {
+            private int fTraceNb = 0;
+
+            @Override
+            public boolean test() throws Exception {
+                List<TmfTraceElement> traces = tracesFolder.getTraces();
+                fTraceNb = traces.size();
+                return fTraceNb == 0;
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "Traces Folder not empty (" + fTraceNb + ")";
+            }
+        });
+    }
+
+    /**
+     * Clear the trace folder (using the UI)
+     *
+     * @param bot
+     *            a given workbench bot
+     * @param projectName
+     *            the name of the project (needs to exist)
+     */
+    public static void clearTracesFolderUI(SWTWorkbenchBot bot, String projectName) {
+        SWTBotTreeItem tracesFolder = selectTracesFolder(bot, projectName);
+        tracesFolder.contextMenu().menu("Clear").click();
+        String CONFIRM_CLEAR_DIALOG_TITLE = "Confirm Clear";
+        bot.waitUntil(Conditions.shellIsActive(CONFIRM_CLEAR_DIALOG_TITLE));
+
+        SWTBotShell shell = bot.shell(CONFIRM_CLEAR_DIALOG_TITLE);
+        shell.bot().button("Yes").click();
+        bot.waitUntil(Conditions.shellCloses(shell));
+        bot.waitWhile(ConditionHelpers.treeItemHasChildren(tracesFolder));
+    }
+
+    /**
+     * Clear the experiment folder
+     *
+     * @param bot
+     *            a given workbench bot
+     * @param projectName
+     *            the name of the project (needs to exist)
+     */
+    public static void clearExperimentFolder(SWTWorkbenchBot bot, String projectName) {
+        IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+        TmfProjectElement tmfProject = TmfProjectRegistry.getProject(project, false);
+        TmfExperimentFolder expFolder = tmfProject.getExperimentsFolder();
+        expFolder.getExperiments().forEach(experiment -> {
+            IResource resource = experiment.getResource();
+            try {
+                // Close the experiment if open
+                experiment.closeEditors();
+
+                IPath path = resource.getLocation();
+                if (path != null) {
+                    // Delete supplementary files
+                    experiment.deleteSupplementaryFolder();
+                }
+                // Finally, delete the experiment
+                resource.delete(true, null);
+            } catch (CoreException e) {
+                fail(e.getMessage());
+            }
+        });
+
+        bot.waitUntil(new DefaultCondition() {
+            private int fExperimentNb = 0;
+
+            @Override
+            public boolean test() throws Exception {
+                List<TmfExperimentElement> experiments = expFolder.getExperiments();
+                fExperimentNb = experiments.size();
+                return fExperimentNb == 0;
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "Experiment Folder not empty (" + fExperimentNb + ")";
+            }
+        });
+    }
+
     /**
      * Select the project in Project Explorer
      *
@@ -462,6 +715,11 @@ public final class SWTBotUtils {
     public static SWTBotTreeItem selectProject(SWTWorkbenchBot bot, String projectName) {
         SWTBotView projectExplorerBot = bot.viewByTitle("Project Explorer");
         projectExplorerBot.show();
+        // FIXME: Bug 496519. Sometimes, the tree becomes disabled for a certain
+        // amount of time. This can happen during a long running operation
+        // (BusyIndicator.showWhile) which brings up the modal dialog "operation
+        // in progress" and this disables all shells
+        projectExplorerBot.bot().waitUntil(Conditions.widgetIsEnabled(projectExplorerBot.bot().tree()));
         SWTBotTreeItem treeItem = projectExplorerBot.bot().tree().getTreeItem(projectName);
         treeItem.select();
         return treeItem;
@@ -488,7 +746,7 @@ public final class SWTBotUtils {
         if (res[0] != null) {
             fail(res[0].getMessage());
         }
-        waitForJobs();
+        WaitUtils.waitForJobs();
     }
 
     /**
@@ -498,11 +756,35 @@ public final class SWTBotUtils {
      *            the {@link SWTBotTable} table
      */
     public static void maximizeTable(SWTBotTable tableBot) {
+        final AtomicBoolean controlResized = new AtomicBoolean();
+        UIThreadRunnable.syncExec(new VoidResult() {
+            @Override
+            public void run() {
+                tableBot.widget.addControlListener(new ControlAdapter() {
+                    @Override
+                    public void controlResized(ControlEvent e) {
+                        tableBot.widget.removeControlListener(this);
+                        controlResized.set(true);
+                    }
+                });
+            }
+        });
         try {
             tableBot.pressShortcut(KeyStroke.getInstance(IKeyLookup.CTRL_NAME + "+"), KeyStroke.getInstance("M"));
         } catch (ParseException e) {
             fail();
         }
+        new SWTBot().waitUntil(new DefaultCondition() {
+            @Override
+            public boolean test() throws Exception {
+                return controlResized.get();
+            }
+
+            @Override
+            public String getFailureMessage() {
+                return "Control was not resized";
+            }
+        });
     }
 
     /**
@@ -549,11 +831,41 @@ public final class SWTBotUtils {
 
         bot.waitUntil(ConditionHelpers.IsTreeNodeAvailable(nodeNames[0], tree));
         SWTBotTreeItem currentNode = tree.getTreeItem(nodeNames[0]);
-        for (int i = 1; i < nodeNames.length; i++) {
+        return getTreeItem(bot, currentNode, Arrays.copyOfRange(nodeNames, 1, nodeNames.length));
+    }
+
+    /**
+     * Get the tree item from a parent tree item at the specified location
+     *
+     * @param bot
+     *            the SWTBot
+     * @param treeItem
+     *            the treeItem to find the tree item under
+     * @param nodeNames
+     *            the path to the tree item, in the form of node names (from
+     *            parent to child).
+     * @return the tree item
+     */
+    public static SWTBotTreeItem getTreeItem(SWTBot bot, SWTBotTreeItem treeItem, String... nodeNames) {
+        if (nodeNames.length == 0) {
+            return treeItem;
+        }
+
+        SWTBotTreeItem currentNode = treeItem;
+        for (int i = 0; i < nodeNames.length; i++) {
+            bot.waitUntil(ConditionHelpers.treeItemHasChildren(treeItem));
             currentNode.expand();
 
             String nodeName = nodeNames[i];
-            bot.waitUntil(ConditionHelpers.IsTreeChildNodeAvailable(nodeName, currentNode));
+            try {
+                bot.waitUntil(ConditionHelpers.IsTreeChildNodeAvailable(nodeName, currentNode));
+            } catch (TimeoutException e) {
+                //FIXME: Sometimes in a JFace TreeViewer, it expands to nothing. Need to find out why.
+                currentNode.collapse();
+                currentNode.expand();
+                bot.waitUntil(ConditionHelpers.IsTreeChildNodeAvailable(nodeName, currentNode));
+            }
+
             SWTBotTreeItem newNode = currentNode.getNode(nodeName);
             currentNode = newNode;
         }
@@ -561,6 +873,21 @@ public final class SWTBotUtils {
         return currentNode;
     }
 
+    /**
+     * Press the keyboard shortcut that goes to the top of a tree widget. The
+     * key combination can differ on different platforms.
+     *
+     * @param keyboard
+     *            the keyboard to use
+     */
+    public static void pressShortcutGoToTreeTop(Keyboard keyboard) {
+        if (SWTUtils.isMac()) {
+            keyboard.pressShortcut(Keystrokes.ALT, Keystrokes.UP);
+        } else {
+            keyboard.pressShortcut(Keystrokes.HOME);
+        }
+    }
+
     /**
      * Get the active events editor. Note that this will wait until such editor
      * is available.
@@ -570,25 +897,50 @@ public final class SWTBotUtils {
      * @return the active events editor
      */
     public static SWTBotEditor activeEventsEditor(final SWTWorkbenchBot workbenchBot) {
-        final SWTBotEditor editor[] = new SWTBotEditor[1];
-        workbenchBot.waitUntil(new DefaultCondition() {
-            @Override
-            public boolean test() throws Exception {
-                List<SWTBotEditor> editors = workbenchBot.editors(WidgetMatcherFactory.withPartId(TmfEventsEditor.ID));
-                for (SWTBotEditor e : editors) {
-                    if (e.isActive() && !e.getWidget().isDisposed()) {
-                        editor[0] = e;
-                        return true;
-                    }
-                }
-                return false;
-            }
+        ConditionHelpers.ActiveEventsEditor condition = new ConditionHelpers.ActiveEventsEditor(workbenchBot, null);
+        workbenchBot.waitUntil(condition);
+        return condition.getActiveEditor();
+    }
 
-            @Override
-            public String getFailureMessage() {
-                return "Active events editor not found";
+    /**
+     * Get the active events editor. Note that this will wait until such editor
+     * is available.
+     *
+     * @param workbenchBot
+     *            a given workbench bot
+     * @param editorTitle
+     *            the desired editor title. If null, any active events editor
+     *            will be considered valid.
+     * @return the active events editor
+     */
+    public static SWTBotEditor activeEventsEditor(final SWTWorkbenchBot workbenchBot, String editorTitle) {
+        ConditionHelpers.ActiveEventsEditor condition = new ConditionHelpers.ActiveEventsEditor(workbenchBot, editorTitle);
+        workbenchBot.waitUntil(condition);
+        return condition.getActiveEditor();
+    }
+
+    /**
+     * Open the preferences dialog and return the corresponding shell.
+     *
+     * @param bot
+     *            a given workbench bot
+     * @return the preferences shell
+     */
+    public static SWTBotShell openPreferences(SWTBot bot) {
+        if (SWTUtils.isMac()) {
+            // On Mac, the Preferences menu item is under the application name.
+            // For some reason, we can't access the application menu anymore so
+            // we use the keyboard shortcut.
+            try {
+                bot.activeShell().pressShortcut(KeyStroke.getInstance(IKeyLookup.COMMAND_NAME + "+"), KeyStroke.getInstance(","));
+            } catch (ParseException e) {
+                fail();
             }
-        });
-        return editor[0];
+        } else {
+            bot.menu(WINDOW_MENU).menu(PREFERENCES_MENU_ITEM).click();
+        }
+
+        bot.waitUntil(Conditions.shellIsActive(PREFERENCES_MENU_ITEM));
+        return bot.activeShell();
     }
 }
This page took 0.029825 seconds and 5 git commands to generate.