tmf.ui: Add a SWTbot condition for XY charts ready
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui.swtbot.tests / shared / org / eclipse / tracecompass / tmf / ui / swtbot / tests / shared / ConditionHelpers.java
1 /*******************************************************************************
2 * Copyright (c) 2013, 2016 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Matthew Khouzam - Initial API and implementation
11 * Alexandre Montplaisir - Replaced separate Condition objects by anonymous classes
12 * Patrick Tasse - Add projectElementHasChild and isEditorOpened conditions
13 *******************************************************************************/
14
15 package org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared;
16
17 import static org.eclipse.swtbot.eclipse.finder.matchers.WidgetMatcherFactory.withPartName;
18
19 import java.util.Arrays;
20 import java.util.List;
21 import java.util.regex.Pattern;
22
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.eclipse.jface.viewers.StructuredSelection;
25 import org.eclipse.jface.wizard.IWizardContainer;
26 import org.eclipse.jface.wizard.IWizardPage;
27 import org.eclipse.jface.wizard.Wizard;
28 import org.eclipse.osgi.util.NLS;
29 import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
30 import org.eclipse.swtbot.eclipse.finder.matchers.WidgetMatcherFactory;
31 import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotEditor;
32 import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
33 import org.eclipse.swtbot.swt.finder.SWTBot;
34 import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
35 import org.eclipse.swtbot.swt.finder.results.Result;
36 import org.eclipse.swtbot.swt.finder.utils.TableCollection;
37 import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
38 import org.eclipse.swtbot.swt.finder.waits.ICondition;
39 import org.eclipse.swtbot.swt.finder.widgets.SWTBotTable;
40 import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
41 import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem;
42 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
43 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
44 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
45 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
46 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
47 import org.eclipse.tracecompass.tmf.ui.editors.TmfEventsEditor;
48 import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.TmfXYChartViewer;
49 import org.eclipse.tracecompass.tmf.ui.views.timegraph.AbstractTimeGraphView;
50 import org.eclipse.ui.IEditorReference;
51 import org.hamcrest.Matcher;
52 import org.swtchart.Chart;
53
54 /**
55 * Is a tree node available
56 *
57 * @author Matthew Khouzam
58 */
59 public final class ConditionHelpers {
60
61 private ConditionHelpers() {}
62
63 /**
64 * Provide default implementations for some {@link ICondition} methods.
65 */
66 public abstract static class SWTBotTestCondition implements ICondition {
67
68 @Override
69 public abstract boolean test() throws Exception;
70
71 @Override
72 public final void init(SWTBot bot) {
73 }
74
75 @Override
76 public String getFailureMessage() {
77 return null;
78 }
79 }
80
81 /**
82 * Is a tree node available
83 *
84 * @param name
85 * the name of the node
86 * @param tree
87 * the parent tree
88 * @return true or false, it should swallow all exceptions
89 */
90 public static ICondition IsTreeNodeAvailable(final String name, final SWTBotTree tree) {
91 return new SWTBotTestCondition() {
92 @Override
93 public boolean test() throws Exception {
94 try {
95 final SWTBotTreeItem[] treeItems = tree.getAllItems();
96 for (SWTBotTreeItem ti : treeItems) {
97 final String text = ti.getText();
98 if (text.equals(name)) {
99 return true;
100 }
101 }
102 } catch (Exception e) {
103 }
104 return false;
105 }
106
107 @Override
108 public String getFailureMessage() {
109 return NLS.bind("No child of tree {0} found with text {1}. Child items: {2}",
110 new String[] { tree.toString(), name, Arrays.toString(tree.getAllItems()) });
111 }
112 };
113 }
114
115 /**
116 * Is a table item available
117 *
118 * @param name
119 * the name of the item
120 * @param table
121 * the parent table
122 * @return true or false, it should swallow all exceptions
123 */
124 public static ICondition isTableItemAvailable(final String name, final SWTBotTable table) {
125 return new SWTBotTestCondition() {
126 @Override
127 public boolean test() throws Exception {
128 try {
129 return table.containsItem(name);
130 } catch (Exception e) {
131 }
132 return false;
133 }
134
135 @Override
136 public String getFailureMessage() {
137 return NLS.bind("No child of table {0} found with text ''{1}''.", table, name);
138 }
139 };
140 }
141
142 /**
143 * Is the treeItem's node available
144 *
145 * @param name
146 * the name of the node
147 * @param treeItem
148 * the treeItem
149 * @return true or false, it should swallow all exceptions
150 */
151 public static ICondition IsTreeChildNodeAvailable(final String name, final SWTBotTreeItem treeItem) {
152 return new SWTBotTestCondition() {
153 @Override
154 public boolean test() throws Exception {
155 try {
156 return treeItem.getNode(name) != null;
157 } catch (Exception e) {
158 }
159 return false;
160 }
161
162 @Override
163 public String getFailureMessage() {
164 return NLS.bind("No child of tree item {0} found with text ''{1}''. Child items: {2}",
165 new String[] { treeItem.toString(), name, Arrays.toString(treeItem.getItems()) });
166 }
167 };
168 }
169
170 /**
171 * Is the treeItem's node removed
172 *
173 * @param length
174 * length of the node after removal
175 * @param treeItem
176 * the treeItem
177 * @return true or false, it should swallow all exceptions
178 */
179 public static ICondition isTreeChildNodeRemoved(final int length, final SWTBotTreeItem treeItem) {
180 return new SWTBotTestCondition() {
181 @Override
182 public boolean test() throws Exception {
183 try {
184 return treeItem.getNodes().size() == length;
185 } catch (Exception e) {
186 }
187 return false;
188 }
189
190 @Override
191 public String getFailureMessage() {
192 return NLS.bind("Child of tree item {0} found with text ''{1}'' not removed. Child items: {2}",
193 new String[] { treeItem.toString(), String.valueOf(length), Arrays.toString(treeItem.getItems()) });
194 }
195 };
196 }
197
198 /**
199 * Condition to check if the number of direct children of the
200 * provided tree item equals the specified count.
201 *
202 * @param treeItem
203 * the SWTBot tree item
204 * @param count
205 * the expected count
206 * @return ICondition for verification
207 */
208 public static ICondition treeItemCount(final SWTBotTreeItem treeItem, int count) {
209 return new SWTBotTestCondition() {
210 @Override
211 public boolean test() throws Exception {
212 return treeItem.rowCount() == count;
213 }
214
215 @Override
216 public String getFailureMessage() {
217 return NLS.bind("Tree item count: {0} expected: {1}",
218 treeItem.rowCount(), count);
219 }
220 };
221 }
222
223 /**
224 * Checks if the wizard's shell is null
225 *
226 * @param wizard
227 * the null
228 * @return false if either are null
229 */
230 public static ICondition isWizardReady(final Wizard wizard) {
231 return new SWTBotTestCondition() {
232 @Override
233 public boolean test() throws Exception {
234 if (wizard.getShell() == null) {
235 return false;
236 }
237 return true;
238 }
239 };
240 }
241
242 /**
243 * Is the wizard on the page you want?
244 *
245 * @param wizard
246 * wizard
247 * @param page
248 * the desired page
249 * @return true or false
250 */
251 public static ICondition isWizardOnPage(final Wizard wizard, final IWizardPage page) {
252 return new SWTBotTestCondition() {
253 @Override
254 public boolean test() throws Exception {
255 if (wizard == null || page == null) {
256 return false;
257 }
258 final IWizardContainer container = wizard.getContainer();
259 if (container == null) {
260 return false;
261 }
262 IWizardPage currentPage = container.getCurrentPage();
263 return page.equals(currentPage);
264 }
265 };
266 }
267
268 /**
269 * Wait for a view to close
270 *
271 * @param view
272 * bot view for the view
273 * @return true if the view is closed, false if it's active.
274 */
275 public static ICondition ViewIsClosed(final SWTBotView view) {
276 return new SWTBotTestCondition() {
277 @Override
278 public boolean test() throws Exception {
279 return (view != null) && (!view.isActive());
280 }
281 };
282 }
283
284 /**
285 * Wait till table cell has a given content.
286 *
287 * @param table
288 * the table bot reference
289 * @param content
290 * the content to check
291 * @param row
292 * the row of the cell
293 * @param column
294 * the column of the cell
295 * @return ICondition for verification
296 */
297 public static ICondition isTableCellFilled(final SWTBotTable table,
298 final String content, final int row, final int column) {
299 return new SWTBotTestCondition() {
300 @Override
301 public boolean test() throws Exception {
302 try {
303 String cell = table.cell(row, column);
304 if( cell == null ) {
305 return false;
306 }
307 return cell.contains(content);
308 } catch (Exception e) {
309 }
310 return false;
311 }
312 };
313 }
314
315 /**
316 * Condition to check if a tracing project element has a child with the
317 * specified name. A project element label may have a count suffix in the
318 * format ' [n]'.
319 */
320 public static class ProjectElementHasChild extends DefaultCondition {
321
322 private final SWTBotTreeItem fParentItem;
323 private final String fName;
324 private final String fRegex;
325 private SWTBotTreeItem fItem = null;
326
327 /**
328 * Constructor.
329 *
330 * @param parentItem
331 * the parent item
332 * @param name
333 * the child name to look for
334 */
335 public ProjectElementHasChild(final SWTBotTreeItem parentItem, final String name) {
336 fParentItem = parentItem;
337 fName = name;
338 /* Project element labels may have count suffix */
339 fRegex = Pattern.quote(name) + "(\\s\\[(\\d)+\\])?";
340 }
341
342 @Override
343 public boolean test() throws Exception {
344 fParentItem.expand();
345 for (SWTBotTreeItem item : fParentItem.getItems()) {
346 if (item.getText().matches(fRegex)) {
347 fItem = item;
348 return true;
349 }
350 }
351 return false;
352 }
353
354 @Override
355 public String getFailureMessage() {
356 return NLS.bind("No child of {0} found with name {1}", fParentItem.getText(), fName);
357 }
358
359 /**
360 * Returns the matching child item if the condition returned true.
361 *
362 * @return the matching item
363 */
364 public SWTBotTreeItem getItem() {
365 return fItem;
366 }
367 }
368
369 /**
370 * Condition to check if an editor with the specified title is opened.
371 *
372 * @param bot
373 * a workbench bot
374 * @param title
375 * the editor title
376 * @return ICondition for verification
377 */
378 public static ICondition isEditorOpened(final SWTWorkbenchBot bot, final String title) {
379 return new SWTBotTestCondition() {
380 @Override
381 public boolean test() throws Exception {
382 Matcher<IEditorReference> withPartName = withPartName(title);
383 return !bot.editors(withPartName).isEmpty();
384 }
385 };
386 }
387
388 /**
389 * Condition to check if the selection range equals the specified range.
390 *
391 * @param range
392 * the selection range
393 * @return ICondition for verification
394 */
395 public static ICondition selectionRange(final TmfTimeRange range) {
396 return new SWTBotTestCondition() {
397 @Override
398 public boolean test() throws Exception {
399 return TmfTraceManager.getInstance().getCurrentTraceContext().getSelectionRange().equals(range);
400 }
401
402 @Override
403 public String getFailureMessage() {
404 return NLS.bind("Selection range: {0} expected: {1}",
405 TmfTraceManager.getInstance().getCurrentTraceContext().getSelectionRange(), range);
406 }
407 };
408 }
409
410 /**
411 * Condition to check if the window range equals the specified range.
412 *
413 * @param range
414 * the window range
415 * @return ICondition for verification
416 */
417 public static ICondition windowRange(final TmfTimeRange range) {
418 return new SWTBotTestCondition() {
419 @Override
420 public boolean test() throws Exception {
421 return TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange().equals(range);
422 }
423
424 @Override
425 public String getFailureMessage() {
426 return NLS.bind("Window range: {0} expected: {1}",
427 TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange(), range);
428 }
429 };
430 }
431
432 /**
433 * Condition to check if the selection contains the specified text at the
434 * specified column. The text is checked in any item of the tree selection.
435 *
436 * @param tree
437 * the SWTBot tree
438 * @param column
439 * the column index
440 * @param text
441 * the expected text
442 * @return ICondition for verification
443 */
444 public static ICondition treeSelectionContains(final SWTBotTree tree, final int column, final String text) {
445 return new SWTBotTestCondition() {
446 @Override
447 public boolean test() throws Exception {
448 TableCollection selection = tree.selection();
449 for (int row = 0; row < selection.rowCount(); row++) {
450 if (selection.get(row, column).equals(text)) {
451 return true;
452 }
453 }
454 return false;
455 }
456
457 @Override
458 public String getFailureMessage() {
459 return NLS.bind("Tree selection [0,{0}]: {1} expected: {2}",
460 new Object[] { column, tree.selection().get(0, column), text});
461 }
462 };
463 }
464
465 private static class EventsTableSelectionCondition extends DefaultCondition {
466 private long fSelectionTime;
467 private SWTWorkbenchBot fBot;
468 private long fCurValue;
469
470 private EventsTableSelectionCondition(SWTWorkbenchBot bot, long selectionTime) {
471 fBot = bot;
472 fSelectionTime = selectionTime;
473 }
474
475 @Override
476 public boolean test() throws Exception {
477 StructuredSelection eventsTableSelection = getEventsTableSelection();
478 if (eventsTableSelection.isEmpty()) {
479 return false;
480 }
481 fCurValue = ((ITmfEvent) eventsTableSelection.getFirstElement()).getTimestamp().getValue();
482 return fCurValue == fSelectionTime;
483 }
484
485 @Override
486 public String getFailureMessage() {
487 return "The selection in the table was not an event with timestamp " + fSelectionTime + ". Actual is " + fCurValue;
488 }
489
490 private StructuredSelection getEventsTableSelection() {
491 return UIThreadRunnable.syncExec(new Result<StructuredSelection>() {
492
493 @Override
494 public StructuredSelection run() {
495 SWTBotEditor eventsEditor = SWTBotUtils.activeEventsEditor(fBot);
496 TmfEventsEditor part = (TmfEventsEditor) eventsEditor.getReference().getPart(false);
497 StructuredSelection selection = (StructuredSelection) part.getSelection();
498 return selection;
499 }
500 });
501 }
502 }
503
504 /**
505 * Wait until the events table selection matches the specified time stamp.
506 *
507 * @param bot
508 * a workbench bot
509 *
510 * @param selectionTime
511 * the selection time
512 * @return ICondition for verification
513 */
514 public static ICondition selectionInEventsTable(final SWTWorkbenchBot bot, long selectionTime) {
515 return new EventsTableSelectionCondition(bot, selectionTime);
516 }
517
518 private static class TimeGraphIsReadyCondition extends DefaultCondition {
519
520 private @NonNull TmfTimeRange fSelectionRange;
521 private @NonNull ITmfTimestamp fVisibleTime;
522 private AbstractTimeGraphView fView;
523 private String fFailureMessage;
524
525 private TimeGraphIsReadyCondition(AbstractTimeGraphView view, @NonNull TmfTimeRange selectionRange, @NonNull ITmfTimestamp visibleTime) {
526 fView = view;
527 fSelectionRange = selectionRange;
528 fVisibleTime = visibleTime;
529 }
530
531 @Override
532 public boolean test() throws Exception {
533 ICondition selectionRangeCondition = ConditionHelpers.selectionRange(fSelectionRange);
534 if (!selectionRangeCondition.test()) {
535 fFailureMessage = selectionRangeCondition.getFailureMessage();
536 return false;
537 }
538 @NonNull TmfTimeRange curWindowRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange();
539 if (!curWindowRange.contains(fVisibleTime)) {
540 fFailureMessage = "Current window range " + curWindowRange + " does not contain " + fVisibleTime;
541 return false;
542 }
543
544 if (fView.isDirty()) {
545 fFailureMessage = "Time graph is dirty";
546 return false;
547
548 }
549 return true;
550 }
551
552 @Override
553 public String getFailureMessage() {
554 return fFailureMessage;
555 }
556 }
557
558 /**
559 *
560 * Wait until the Time Graph view is ready. The Time Graph view is
561 * considered ready if the selectionRange is selected, the visibleTime is
562 * visible and the view is not dirty (its model is done updating).
563 *
564 * @param view
565 * the time graph view
566 * @param selectionRange
567 * the selection that the time graph should have
568 * @param visibleTime
569 * the visible time that the time graph should have
570 * @return ICondition for verification
571 */
572 public static ICondition timeGraphIsReadyCondition(AbstractTimeGraphView view, @NonNull TmfTimeRange selectionRange, @NonNull ITmfTimestamp visibleTime) {
573 return new TimeGraphIsReadyCondition(view, selectionRange, visibleTime);
574 }
575
576 private static class XYViewerIsReadyCondition extends DefaultCondition {
577
578 private TmfXYChartViewer fViewer;
579 private String fFailureMessage;
580
581 private XYViewerIsReadyCondition(TmfXYChartViewer view) {
582 fViewer = view;
583 }
584
585 @Override
586 public boolean test() throws Exception {
587
588 if (fViewer.isDirty()) {
589 fFailureMessage = "Time graph is dirty";
590 return false;
591 }
592 return true;
593 }
594
595 @Override
596 public String getFailureMessage() {
597 return fFailureMessage;
598 }
599 }
600
601 /**
602 *
603 * Wait until the XY chart viewer is ready. The XY chart viewer is
604 * considered ready when it is not updating.
605 *
606 * @param viewer
607 * the XY chart viewer
608 * @return ICondition for verification
609 */
610 public static ICondition xyViewerIsReadyCondition(TmfXYChartViewer viewer) {
611 return new XYViewerIsReadyCondition(viewer);
612 }
613
614 private static class NumberOfEventsCondition extends DefaultCondition {
615
616 private ITmfTrace fTrace;
617 private long fNbEvents;
618
619 private NumberOfEventsCondition(ITmfTrace trace, long nbEvents) {
620 fTrace = trace;
621 fNbEvents = nbEvents;
622 }
623
624 @Override
625 public boolean test() throws Exception {
626 return fTrace.getNbEvents() == fNbEvents;
627 }
628
629 @Override
630 public String getFailureMessage() {
631 return fTrace.getName() + " did not contain the expected number of " + fNbEvents + " events. Current: " + fTrace.getNbEvents();
632 }
633 }
634
635 /**
636 * Wait until the trace contains the specified number of events.
637 *
638 * @param trace
639 * the trace
640 * @param nbEvents
641 * the number of events to wait for
642 * @return ICondition for verification
643 */
644 public static ICondition numberOfEventsInTrace(ITmfTrace trace, long nbEvents) {
645 return new NumberOfEventsCondition(trace, nbEvents);
646 }
647
648 /**
649 * Wait until there is an active events editor. A title can also be
650 * specified to wait until a more specific editor.
651 */
652 public static final class ActiveEventsEditor extends DefaultCondition {
653 private final SWTWorkbenchBot fWorkbenchBot;
654 private SWTBotEditor fEditor;
655 private String fEditorTitle;
656
657 /**
658 * Wait until there is an active events editor.
659 *
660 * @param workbenchBot
661 * a workbench bot
662 * @param editorTitle
663 * If specified, wait until an active events editor with this
664 * title. Can be set to null.
665 */
666 public ActiveEventsEditor(SWTWorkbenchBot workbenchBot, String editorTitle) {
667 fWorkbenchBot = workbenchBot;
668 fEditorTitle = editorTitle;
669 }
670
671 @Override
672 public boolean test() throws Exception {
673 List<SWTBotEditor> editors = fWorkbenchBot.editors(WidgetMatcherFactory.withPartId(TmfEventsEditor.ID));
674 for (SWTBotEditor e : editors) {
675 // We are careful not to call e.getWidget() here because it actually forces the editor to show.
676 // This is especially a problem for cases where we wait until there is no active editor.
677 if (e.isActive()) {
678 if (fEditorTitle != null && !fEditorTitle.equals(e.getTitle())) {
679 return false;
680 }
681 fEditor = e;
682 return true;
683 }
684 }
685 return false;
686 }
687
688 @Override
689 public String getFailureMessage() {
690 String editorMessage = fEditorTitle != null ? " " + fEditorTitle : "";
691 return "Active events editor" + editorMessage + " not found";
692 }
693
694 /**
695 * @return The active editor found
696 */
697 public SWTBotEditor getActiveEditor() {
698 return fEditor;
699 }
700 }
701
702 private static class NumberOfSeries extends DefaultCondition {
703 private String fFailureMessage;
704 private Chart fChart;
705 private final int fNumberOfSeries;
706
707 public NumberOfSeries(Chart chart, int numberOfSeries) {
708 fChart = chart;
709 fNumberOfSeries = numberOfSeries;
710 }
711
712 @Override
713 public boolean test() throws Exception {
714 int length = fChart.getSeriesSet().getSeries().length;
715 if (length != fNumberOfSeries){
716 fFailureMessage = "Chart did not contain the expected number series. Actual " + length + ", expected " + fNumberOfSeries;
717 return false;
718 }
719
720 return true;
721 }
722
723 @Override
724 public String getFailureMessage() {
725 return fFailureMessage;
726 }
727 }
728
729 /**
730 * Wait until the chart has the specified number of series.
731 *
732 * @param chart
733 * the chart
734 * @param numberOfSeries
735 * the number of expected series
736 *
737 * @return ICondition for verification
738 */
739 public static ICondition numberOfSeries(Chart chart, int numberOfSeries) {
740 return new NumberOfSeries(chart, numberOfSeries);
741 }
742
743 /**
744 * Condition to check if the tree item has children
745 *
746 * @param treeItem
747 * the tree item that should have children
748 * @return ICondition for verification
749 */
750 public static ICondition treeItemHasChildren(SWTBotTreeItem treeItem) {
751 return new TreeItemHasChildren(treeItem);
752 }
753
754 private static final class TreeItemHasChildren extends DefaultCondition {
755 private SWTBotTreeItem fTreeItem;
756
757 public TreeItemHasChildren(SWTBotTreeItem treeItem) {
758 fTreeItem = treeItem;
759 }
760
761 @Override
762 public boolean test() throws Exception {
763 return fTreeItem.getItems().length > 0;
764 }
765
766 @Override
767 public String getFailureMessage() {
768 return NLS.bind("No child of tree item {0} found.", new String[] { fTreeItem.toString() });
769 }
770 }
771 }
This page took 0.048325 seconds and 5 git commands to generate.