/******************************************************************************* * Copyright (c) 2013, 2016 Ericsson * * 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: * Matthew Khouzam - Initial API and implementation * Alexandre Montplaisir - Replaced separate Condition objects by anonymous classes * Patrick Tasse - Add projectElementHasChild and isEditorOpened conditions *******************************************************************************/ package org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared; import static org.eclipse.swtbot.eclipse.finder.matchers.WidgetMatcherFactory.withPartName; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.wizard.IWizardContainer; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.jface.wizard.Wizard; import org.eclipse.osgi.util.NLS; import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot; import org.eclipse.swtbot.eclipse.finder.matchers.WidgetMatcherFactory; 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.results.Result; import org.eclipse.swtbot.swt.finder.utils.TableCollection; import org.eclipse.swtbot.swt.finder.waits.DefaultCondition; import org.eclipse.swtbot.swt.finder.waits.ICondition; 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.tracecompass.tmf.core.event.ITmfEvent; import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; import org.eclipse.tracecompass.tmf.ui.editors.TmfEventsEditor; import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.TmfXYChartViewer; import org.eclipse.tracecompass.tmf.ui.views.timegraph.AbstractTimeGraphView; import org.eclipse.ui.IEditorReference; import org.hamcrest.Matcher; import org.swtchart.Chart; /** * Is a tree node available * * @author Matthew Khouzam */ public final class ConditionHelpers { private ConditionHelpers() {} /** * Provide default implementations for some {@link ICondition} methods. */ public abstract static class SWTBotTestCondition implements ICondition { @Override public abstract boolean test() throws Exception; @Override public final void init(SWTBot bot) { } @Override public String getFailureMessage() { return null; } } /** * Is a tree node available * * @param name * the name of the node * @param tree * the parent tree * @return true or false, it should swallow all exceptions */ public static ICondition IsTreeNodeAvailable(final String name, final SWTBotTree tree) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { try { final SWTBotTreeItem[] treeItems = tree.getAllItems(); for (SWTBotTreeItem ti : treeItems) { final String text = ti.getText(); if (text.equals(name)) { return true; } } } catch (Exception e) { } return false; } @Override public String getFailureMessage() { return NLS.bind("No child of tree {0} found with text {1}. Child items: {2}", new String[] { tree.toString(), name, Arrays.toString(tree.getAllItems()) }); } }; } /** * Is a table item available * * @param name * the name of the item * @param table * the parent table * @return true or false, it should swallow all exceptions */ public static ICondition isTableItemAvailable(final String name, final SWTBotTable table) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { try { return table.containsItem(name); } catch (Exception e) { } return false; } @Override public String getFailureMessage() { return NLS.bind("No child of table {0} found with text ''{1}''.", table, name); } }; } /** * Is the treeItem's node available * * @param name * the name of the node * @param treeItem * the treeItem * @return true or false, it should swallow all exceptions */ public static ICondition IsTreeChildNodeAvailable(final String name, final SWTBotTreeItem treeItem) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { try { return treeItem.getNode(name) != null; } catch (Exception e) { } return false; } @Override public String getFailureMessage() { return NLS.bind("No child of tree item {0} found with text ''{1}''. Child items: {2}", new String[] { treeItem.toString(), name, Arrays.toString(treeItem.getItems()) }); } }; } /** * Is the treeItem's node removed * * @param length * length of the node after removal * @param treeItem * the treeItem * @return true or false, it should swallow all exceptions */ public static ICondition isTreeChildNodeRemoved(final int length, final SWTBotTreeItem treeItem) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { try { return treeItem.getNodes().size() == length; } catch (Exception e) { } return false; } @Override public String getFailureMessage() { return NLS.bind("Child of tree item {0} found with text ''{1}'' not removed. Child items: {2}", new String[] { treeItem.toString(), String.valueOf(length), Arrays.toString(treeItem.getItems()) }); } }; } /** * Condition to check if the number of direct children of the * provided tree item equals the specified count. * * @param treeItem * the SWTBot tree item * @param count * the expected count * @return ICondition for verification */ public static ICondition treeItemCount(final SWTBotTreeItem treeItem, int count) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { return treeItem.rowCount() == count; } @Override public String getFailureMessage() { return NLS.bind("Tree item count: {0} expected: {1}", treeItem.rowCount(), count); } }; } /** * Checks if the wizard's shell is null * * @param wizard * the null * @return false if either are null */ public static ICondition isWizardReady(final Wizard wizard) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { if (wizard.getShell() == null) { return false; } return true; } }; } /** * Is the wizard on the page you want? * * @param wizard * wizard * @param page * the desired page * @return true or false */ public static ICondition isWizardOnPage(final Wizard wizard, final IWizardPage page) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { if (wizard == null || page == null) { return false; } final IWizardContainer container = wizard.getContainer(); if (container == null) { return false; } IWizardPage currentPage = container.getCurrentPage(); return page.equals(currentPage); } }; } /** * Wait for a view to close * * @param view * bot view for the view * @return true if the view is closed, false if it's active. */ public static ICondition ViewIsClosed(final SWTBotView view) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { return (view != null) && (!view.isActive()); } }; } /** * Wait till table cell has a given content. * * @param table * the table bot reference * @param content * the content to check * @param row * the row of the cell * @param column * the column of the cell * @return ICondition for verification */ public static ICondition isTableCellFilled(final SWTBotTable table, final String content, final int row, final int column) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { try { String cell = table.cell(row, column); if( cell == null ) { return false; } return cell.contains(content); } catch (Exception e) { } return false; } @Override public String getFailureMessage() { String cell = table.cell(row, column); if (cell == null) { return NLS.bind("Cell absent, expected: {0}", content); } return NLS.bind("Cell content: {0} expected: {1}", cell, content); } }; } /** * Condition to check if a tracing project element has a child with the * specified name. A project element label may have a count suffix in the * format ' [n]'. */ public static class ProjectElementHasChild extends DefaultCondition { private final SWTBotTreeItem fParentItem; private final String fName; private final String fRegex; private SWTBotTreeItem fItem = null; /** * Constructor. * * @param parentItem * the parent item * @param name * the child name to look for */ public ProjectElementHasChild(final SWTBotTreeItem parentItem, final String name) { fParentItem = parentItem; fName = name; /* Project element labels may have count suffix */ fRegex = Pattern.quote(name) + "(\\s\\[(\\d)+\\])?"; } @Override public boolean test() throws Exception { fParentItem.expand(); for (SWTBotTreeItem item : fParentItem.getItems()) { if (item.getText().matches(fRegex)) { fItem = item; return true; } } return false; } @Override public String getFailureMessage() { return NLS.bind("No child of {0} found with name {1}", fParentItem.getText(), fName); } /** * Returns the matching child item if the condition returned true. * * @return the matching item */ public SWTBotTreeItem getItem() { return fItem; } } /** * Condition to check if an editor with the specified title is opened. * * @param bot * a workbench bot * @param title * the editor title * @return ICondition for verification */ public static ICondition isEditorOpened(final SWTWorkbenchBot bot, final String title) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { Matcher<IEditorReference> withPartName = withPartName(title); return !bot.editors(withPartName).isEmpty(); } }; } /** * Condition to check if the selection range equals the specified range. * * @param range * the selection range * @return ICondition for verification */ public static ICondition selectionRange(final TmfTimeRange range) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { return TmfTraceManager.getInstance().getCurrentTraceContext().getSelectionRange().equals(range); } @Override public String getFailureMessage() { return NLS.bind("Selection range: {0} expected: {1}", TmfTraceManager.getInstance().getCurrentTraceContext().getSelectionRange(), range); } }; } /** * Condition to check if the window range equals the specified range. * * @param range * the window range * @return ICondition for verification */ public static ICondition windowRange(final TmfTimeRange range) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { return TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange().equals(range); } @Override public String getFailureMessage() { return NLS.bind("Window range: {0} expected: {1}", TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange(), range); } }; } /** * Condition to check if the selection contains the specified text at the * specified column. The text is checked in any item of the tree selection. * * @param tree * the SWTBot tree * @param column * the column index * @param text * the expected text * @return ICondition for verification */ public static ICondition treeSelectionContains(final SWTBotTree tree, final int column, final String text) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { TableCollection selection = tree.selection(); for (int row = 0; row < selection.rowCount(); row++) { if (selection.get(row, column).equals(text)) { return true; } } return false; } @Override public String getFailureMessage() { return NLS.bind("Tree selection [0,{0}]: {1} expected: {2}", new Object[] { column, tree.selection().get(0, column), text}); } }; } /** * Condition to check if the selection contains the specified text at the * specified column. The text is checked in any item of the tree selection. * * @param timeGraph * the {@link SWTBotTimeGraph} * @param column * the column index * @param text * the expected text * @return ICondition for verification */ public static ICondition timeGraphSelectionContains(final SWTBotTimeGraph timeGraph, final int column, final String text) { return new SWTBotTestCondition() { @Override public boolean test() throws Exception { TableCollection selection = timeGraph.selection(); for (int row = 0; row < selection.rowCount(); row++) { if (selection.get(row, column).equals(text)) { return true; } } return false; } @Override public String getFailureMessage() { return NLS.bind("Time graph selection [0,{0}]: {1} expected: {2}", new Object[] { column, timeGraph.selection().get(0, column), text}); } }; } private static class EventsTableSelectionCondition extends DefaultCondition { private long fSelectionTime; private SWTWorkbenchBot fBot; private long fCurValue; private EventsTableSelectionCondition(SWTWorkbenchBot bot, long selectionTime) { fBot = bot; fSelectionTime = selectionTime; } @Override public boolean test() throws Exception { StructuredSelection eventsTableSelection = getEventsTableSelection(); if (eventsTableSelection.isEmpty()) { return false; } fCurValue = ((ITmfEvent) eventsTableSelection.getFirstElement()).getTimestamp().getValue(); return fCurValue == fSelectionTime; } @Override public String getFailureMessage() { return "The selection in the table was not an event with timestamp " + fSelectionTime + ". Actual is " + fCurValue; } private StructuredSelection getEventsTableSelection() { return UIThreadRunnable.syncExec(new Result<StructuredSelection>() { @Override public StructuredSelection run() { SWTBotEditor eventsEditor = SWTBotUtils.activeEventsEditor(fBot); TmfEventsEditor part = (TmfEventsEditor) eventsEditor.getReference().getPart(false); StructuredSelection selection = (StructuredSelection) part.getSelection(); return selection; } }); } } /** * Wait until the events table selection matches the specified time stamp. * * @param bot * a workbench bot * * @param selectionTime * the selection time * @return ICondition for verification */ public static ICondition selectionInEventsTable(final SWTWorkbenchBot bot, long selectionTime) { return new EventsTableSelectionCondition(bot, selectionTime); } private static class TimeGraphIsReadyCondition extends DefaultCondition { private @NonNull TmfTimeRange fSelectionRange; private @NonNull ITmfTimestamp fVisibleTime; private AbstractTimeGraphView fView; private String fFailureMessage; private TimeGraphIsReadyCondition(AbstractTimeGraphView view, @NonNull TmfTimeRange selectionRange, @NonNull ITmfTimestamp visibleTime) { fView = view; fSelectionRange = selectionRange; fVisibleTime = visibleTime; } @Override public boolean test() throws Exception { ICondition selectionRangeCondition = ConditionHelpers.selectionRange(fSelectionRange); if (!selectionRangeCondition.test()) { fFailureMessage = selectionRangeCondition.getFailureMessage(); return false; } @NonNull TmfTimeRange curWindowRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange(); if (!curWindowRange.contains(fVisibleTime)) { fFailureMessage = "Current window range " + curWindowRange + " does not contain " + fVisibleTime; return false; } if (fView.isDirty()) { fFailureMessage = "Time graph is dirty"; return false; } return true; } @Override public String getFailureMessage() { return fFailureMessage; } } /** * * Wait until the Time Graph view is ready. The Time Graph view is * considered ready if the selectionRange is selected, the visibleTime is * visible and the view is not dirty (its model is done updating). * * @param view * the time graph view * @param selectionRange * the selection that the time graph should have * @param visibleTime * the visible time that the time graph should have * @return ICondition for verification */ public static ICondition timeGraphIsReadyCondition(AbstractTimeGraphView view, @NonNull TmfTimeRange selectionRange, @NonNull ITmfTimestamp visibleTime) { return new TimeGraphIsReadyCondition(view, selectionRange, visibleTime); } private static class XYViewerIsReadyCondition extends DefaultCondition { private TmfXYChartViewer fViewer; private String fFailureMessage; private XYViewerIsReadyCondition(TmfXYChartViewer view) { fViewer = view; } @Override public boolean test() throws Exception { if (fViewer.isDirty()) { fFailureMessage = "Time graph is dirty"; return false; } return true; } @Override public String getFailureMessage() { return fFailureMessage; } } /** * * Wait until the XY chart viewer is ready. The XY chart viewer is * considered ready when it is not updating. * * @param viewer * the XY chart viewer * @return ICondition for verification */ public static ICondition xyViewerIsReadyCondition(TmfXYChartViewer viewer) { return new XYViewerIsReadyCondition(viewer); } private static class NumberOfEventsCondition extends DefaultCondition { private ITmfTrace fTrace; private long fNbEvents; private NumberOfEventsCondition(ITmfTrace trace, long nbEvents) { fTrace = trace; fNbEvents = nbEvents; } @Override public boolean test() throws Exception { return fTrace.getNbEvents() == fNbEvents; } @Override public String getFailureMessage() { return fTrace.getName() + " did not contain the expected number of " + fNbEvents + " events. Current: " + fTrace.getNbEvents(); } } /** * Wait until the trace contains the specified number of events. * * @param trace * the trace * @param nbEvents * the number of events to wait for * @return ICondition for verification */ public static ICondition numberOfEventsInTrace(ITmfTrace trace, long nbEvents) { return new NumberOfEventsCondition(trace, nbEvents); } /** * Wait until there is an active events editor. A title can also be * specified to wait until a more specific editor. */ public static final class ActiveEventsEditor extends DefaultCondition { private final SWTWorkbenchBot fWorkbenchBot; private SWTBotEditor fEditor; private String fEditorTitle; /** * Wait until there is an active events editor. * * @param workbenchBot * a workbench bot * @param editorTitle * If specified, wait until an active events editor with this * title. Can be set to null. */ public ActiveEventsEditor(SWTWorkbenchBot workbenchBot, String editorTitle) { fWorkbenchBot = workbenchBot; fEditorTitle = editorTitle; } @Override public boolean test() throws Exception { List<SWTBotEditor> editors = fWorkbenchBot.editors(WidgetMatcherFactory.withPartId(TmfEventsEditor.ID)); for (SWTBotEditor e : editors) { // We are careful not to call e.getWidget() here because it actually forces the editor to show. // This is especially a problem for cases where we wait until there is no active editor. if (e.isActive()) { if (fEditorTitle != null && !fEditorTitle.equals(e.getTitle())) { return false; } fEditor = e; return true; } } return false; } @Override public String getFailureMessage() { String editorMessage = fEditorTitle != null ? " " + fEditorTitle : ""; return "Active events editor" + editorMessage + " not found"; } /** * @return The active editor found */ public SWTBotEditor getActiveEditor() { return fEditor; } } private static class NumberOfSeries extends DefaultCondition { private String fFailureMessage; private Chart fChart; private final int fNumberOfSeries; public NumberOfSeries(Chart chart, int numberOfSeries) { fChart = chart; fNumberOfSeries = numberOfSeries; } @Override public boolean test() throws Exception { int length = fChart.getSeriesSet().getSeries().length; if (length != fNumberOfSeries){ fFailureMessage = "Chart did not contain the expected number series. Actual " + length + ", expected " + fNumberOfSeries; return false; } return true; } @Override public String getFailureMessage() { return fFailureMessage; } } /** * Wait until the chart has the specified number of series. * * @param chart * the chart * @param numberOfSeries * the number of expected series * * @return ICondition for verification */ public static ICondition numberOfSeries(Chart chart, int numberOfSeries) { return new NumberOfSeries(chart, numberOfSeries); } /** * Condition to check if the tree item has children * * @param treeItem * the tree item that should have children * @return ICondition for verification */ public static ICondition treeItemHasChildren(SWTBotTreeItem treeItem) { return new TreeItemHasChildren(treeItem); } private static final class TreeItemHasChildren extends DefaultCondition { private SWTBotTreeItem fTreeItem; public TreeItemHasChildren(SWTBotTreeItem treeItem) { fTreeItem = treeItem; } @Override public boolean test() throws Exception { return fTreeItem.getItems().length > 0; } @Override public String getFailureMessage() { return NLS.bind("No child of tree item {0} found.", new String[] { fTreeItem.toString() }); } } }