6decea2e77dbd2d57a90fd4248ef0774f8dc9f70
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui.swtbot.tests / shared / org / eclipse / tracecompass / tmf / ui / swtbot / tests / shared / SWTBotUtils.java
1 /*******************************************************************************
2 * Copyright (c) 2014, 2015 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 *******************************************************************************/
12
13 package org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared;
14
15 import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.withMnemonic;
16 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
17 import static org.junit.Assert.assertNotNull;
18 import static org.junit.Assert.assertTrue;
19 import static org.junit.Assert.fail;
20
21 import java.util.Arrays;
22 import java.util.List;
23 import java.util.TimeZone;
24 import java.util.concurrent.atomic.AtomicBoolean;
25 import java.util.concurrent.atomic.AtomicInteger;
26
27 import org.apache.log4j.Logger;
28 import org.eclipse.core.commands.ExecutionException;
29 import org.eclipse.core.commands.NotEnabledException;
30 import org.eclipse.core.commands.NotHandledException;
31 import org.eclipse.core.commands.common.NotDefinedException;
32 import org.eclipse.core.resources.IFolder;
33 import org.eclipse.core.resources.IProject;
34 import org.eclipse.core.resources.IResource;
35 import org.eclipse.core.resources.IResourceVisitor;
36 import org.eclipse.core.resources.ResourcesPlugin;
37 import org.eclipse.core.runtime.CoreException;
38 import org.eclipse.core.runtime.IPath;
39 import org.eclipse.core.runtime.IStatus;
40 import org.eclipse.core.runtime.NullProgressMonitor;
41 import org.eclipse.core.runtime.Status;
42 import org.eclipse.jdt.annotation.NonNull;
43 import org.eclipse.jdt.annotation.Nullable;
44 import org.eclipse.jface.bindings.keys.IKeyLookup;
45 import org.eclipse.jface.bindings.keys.KeyStroke;
46 import org.eclipse.jface.bindings.keys.ParseException;
47 import org.eclipse.jface.dialogs.MessageDialogWithToggle;
48 import org.eclipse.jface.preference.IPreferenceStore;
49 import org.eclipse.swt.events.ControlAdapter;
50 import org.eclipse.swt.events.ControlEvent;
51 import org.eclipse.swt.events.KeyEvent;
52 import org.eclipse.swt.events.KeyListener;
53 import org.eclipse.swt.graphics.Point;
54 import org.eclipse.swt.graphics.Rectangle;
55 import org.eclipse.swt.widgets.Control;
56 import org.eclipse.swt.widgets.Display;
57 import org.eclipse.swt.widgets.Shell;
58 import org.eclipse.swt.widgets.Table;
59 import org.eclipse.swt.widgets.TableItem;
60 import org.eclipse.swt.widgets.TreeItem;
61 import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
62 import org.eclipse.swtbot.eclipse.finder.matchers.WidgetMatcherFactory;
63 import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotEditor;
64 import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
65 import org.eclipse.swtbot.swt.finder.SWTBot;
66 import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
67 import org.eclipse.swtbot.swt.finder.keyboard.Keyboard;
68 import org.eclipse.swtbot.swt.finder.keyboard.Keystrokes;
69 import org.eclipse.swtbot.swt.finder.results.IntResult;
70 import org.eclipse.swtbot.swt.finder.results.Result;
71 import org.eclipse.swtbot.swt.finder.results.VoidResult;
72 import org.eclipse.swtbot.swt.finder.utils.MessageFormat;
73 import org.eclipse.swtbot.swt.finder.utils.SWTUtils;
74 import org.eclipse.swtbot.swt.finder.waits.Conditions;
75 import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
76 import org.eclipse.swtbot.swt.finder.widgets.AbstractSWTBot;
77 import org.eclipse.swtbot.swt.finder.widgets.AbstractSWTBotControl;
78 import org.eclipse.swtbot.swt.finder.widgets.SWTBotButton;
79 import org.eclipse.swtbot.swt.finder.widgets.SWTBotCheckBox;
80 import org.eclipse.swtbot.swt.finder.widgets.SWTBotMenu;
81 import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell;
82 import org.eclipse.swtbot.swt.finder.widgets.SWTBotTable;
83 import org.eclipse.swtbot.swt.finder.widgets.SWTBotToolbarButton;
84 import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
85 import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem;
86 import org.eclipse.swtbot.swt.finder.widgets.TimeoutException;
87 import org.eclipse.tracecompass.internal.tmf.ui.Activator;
88 import org.eclipse.tracecompass.internal.tmf.ui.ITmfUIPreferences;
89 import org.eclipse.tracecompass.internal.tmf.ui.project.operations.NewExperimentOperation;
90 import org.eclipse.tracecompass.tmf.ui.editors.TmfEventsEditor;
91 import org.eclipse.tracecompass.tmf.ui.project.model.TmfExperimentElement;
92 import org.eclipse.tracecompass.tmf.ui.project.model.TmfExperimentFolder;
93 import org.eclipse.tracecompass.tmf.ui.project.model.TmfOpenTraceHelper;
94 import org.eclipse.tracecompass.tmf.ui.project.model.TmfProjectElement;
95 import org.eclipse.tracecompass.tmf.ui.project.model.TmfProjectRegistry;
96 import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceElement;
97 import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceFolder;
98 import org.eclipse.tracecompass.tmf.ui.project.model.TmfTracesFolder;
99 import org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared.ConditionHelpers.ProjectElementHasChild;
100 import org.eclipse.tracecompass.tmf.ui.tests.shared.WaitUtils;
101 import org.eclipse.tracecompass.tmf.ui.views.TracingPerspectiveFactory;
102 import org.eclipse.ui.IEditorPart;
103 import org.eclipse.ui.IEditorReference;
104 import org.eclipse.ui.IPageLayout;
105 import org.eclipse.ui.IViewPart;
106 import org.eclipse.ui.IWorkbenchCommandConstants;
107 import org.eclipse.ui.IWorkbenchPage;
108 import org.eclipse.ui.IWorkbenchPartSite;
109 import org.eclipse.ui.PartInitException;
110 import org.eclipse.ui.PlatformUI;
111 import org.eclipse.ui.WorkbenchException;
112 import org.eclipse.ui.handlers.IHandlerService;
113 import org.hamcrest.Matcher;
114
115 /**
116 * SWTBot Helper functions
117 *
118 * @author Matthew Khouzam
119 */
120 @SuppressWarnings("restriction")
121 public final class SWTBotUtils {
122
123 private static final String WINDOW_MENU = "Window";
124 private static final String PREFERENCES_MENU_ITEM = "Preferences";
125 private static boolean fPrintedEnvironment = false;
126 private static Logger log = Logger.getLogger(SWTBotUtils.class);
127
128 private SWTBotUtils() {
129
130 }
131
132 private static final String TRACING_PERSPECTIVE_ID = TracingPerspectiveFactory.ID;
133
134 /**
135 * Waits for all Eclipse jobs to finish. Times out after
136 * WaitUtils#MAX_JOBS_WAIT_TIME by default.
137 *
138 * @throws RuntimeException
139 * once the waiting time passes the default maximum value
140 *
141 * @deprecated Use {@link WaitUtils#waitForJobs()} instead
142 */
143 @Deprecated
144 public static void waitForJobs() {
145 WaitUtils.waitForJobs();
146 }
147
148 /**
149 * Sleeps current thread for a given time.
150 *
151 * @param waitTimeMillis
152 * time in milliseconds to wait
153 */
154 public static void delay(final long waitTimeMillis) {
155 try {
156 Thread.sleep(waitTimeMillis);
157 } catch (final InterruptedException e) {
158 // Ignored
159 }
160 }
161
162 /**
163 * Create a tracing project
164 *
165 * @param projectName
166 * the name of the tracing project
167 */
168 public static void createProject(final String projectName) {
169 /*
170 * Make a new test
171 */
172 UIThreadRunnable.syncExec(new VoidResult() {
173 @Override
174 public void run() {
175 IProject project = TmfProjectRegistry.createProject(projectName, null, new NullProgressMonitor());
176 assertNotNull(project);
177 }
178 });
179
180 WaitUtils.waitForJobs();
181 }
182
183 /**
184 * Deletes a project
185 *
186 * @param projectName
187 * the name of the tracing project
188 * @param deleteResources
189 * whether or not to deleted resources under the project
190 * @param bot
191 * the workbench bot
192 */
193 public static void deleteProject(final String projectName, boolean deleteResources, SWTWorkbenchBot bot) {
194 // Wait for any analysis to complete because it might create
195 // supplementary files
196 WaitUtils.waitForJobs();
197 try {
198 ResourcesPlugin.getWorkspace().getRoot().getProject(projectName).refreshLocal(IResource.DEPTH_INFINITE, null);
199 } catch (CoreException e) {
200 }
201
202 WaitUtils.waitForJobs();
203
204 closeSecondaryShells(bot);
205 WaitUtils.waitForJobs();
206
207 if (!ResourcesPlugin.getWorkspace().getRoot().getProject(projectName).exists()) {
208 return;
209 }
210
211 final SWTBotView projectViewBot = bot.viewById(IPageLayout.ID_PROJECT_EXPLORER);
212 projectViewBot.setFocus();
213
214 SWTBotTree treeBot = projectViewBot.bot().tree();
215 SWTBotTreeItem treeItem = treeBot.getTreeItem(projectName);
216 SWTBotMenu contextMenu = treeItem.contextMenu("Delete");
217 contextMenu.click();
218
219 if (deleteResources) {
220 bot.shell("Delete Resources").setFocus();
221 final SWTBotCheckBox checkBox = bot.checkBox();
222 bot.waitUntil(Conditions.widgetIsEnabled(checkBox));
223 checkBox.click();
224 }
225
226 final SWTBotButton okButton = bot.button("OK");
227 bot.waitUntil(Conditions.widgetIsEnabled(okButton));
228 okButton.click();
229
230 WaitUtils.waitForJobs();
231 }
232
233 /**
234 * Deletes a project and its resources
235 *
236 * @param projectName
237 * the name of the tracing project
238 * @param bot
239 * the workbench bot
240 */
241 public static void deleteProject(String projectName, SWTWorkbenchBot bot) {
242 deleteProject(projectName, true, bot);
243 }
244
245 /**
246 * Creates an experiment
247 *
248 * @param bot
249 * a given workbench bot
250 * @param projectName
251 * the name of the project, creates the project if needed
252 * @param expName
253 * the experiment name
254 */
255 public static void createExperiment(SWTWorkbenchBot bot, String projectName, final @NonNull String expName) {
256 IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
257 TmfProjectElement tmfProject = TmfProjectRegistry.getProject(project, true);
258 TmfExperimentFolder expFolder = tmfProject.getExperimentsFolder();
259 assertNotNull(expFolder);
260 NewExperimentOperation operation = new NewExperimentOperation(expFolder, expName);
261 operation.run(new NullProgressMonitor());
262
263 bot.waitUntil(new DefaultCondition() {
264 @Override
265 public boolean test() throws Exception {
266 TmfExperimentElement experiment = expFolder.getExperiment(expName);
267 return experiment != null;
268 }
269
270 @Override
271 public String getFailureMessage() {
272 return "Experiment (" + expName + ") couldn't be created";
273 }
274 });
275 }
276
277 /**
278 * Focus on the main window
279 *
280 * @param shellBots
281 * swtbotshells for all the shells
282 */
283 public static void focusMainWindow(SWTBotShell[] shellBots) {
284 SWTBotShell mainShell = getMainShell(shellBots);
285 if (mainShell != null) {
286 mainShell.activate();
287 }
288 }
289
290 private static SWTBotShell getMainShell(SWTBotShell[] shellBots) {
291 SWTBotShell mainShell = null;
292 for (SWTBotShell shellBot : shellBots) {
293 if (shellBot.getText().toLowerCase().contains("eclipse")) {
294 mainShell = shellBot;
295 }
296 }
297 return mainShell;
298 }
299
300 /**
301 * Close all non-main shells that are visible.
302 *
303 * @param bot
304 * the workbench bot
305 */
306 public static void closeSecondaryShells(SWTWorkbenchBot bot) {
307 SWTBotShell[] shells = bot.shells();
308 SWTBotShell mainShell = getMainShell(shells);
309 if (mainShell == null) {
310 return;
311 }
312
313 // Close all non-main shell but make sure we don't close an invisible
314 // shell such the special "limbo shell" that Eclipse needs to work
315 Arrays.stream(shells)
316 .filter(shell -> shell != mainShell)
317 .filter(s -> !s.widget.isDisposed())
318 .filter(SWTBotShell::isVisible)
319 .peek(shell -> log.debug(MessageFormat.format("Closing lingering shell with title {0}", shell.getText())))
320 .forEach(SWTBotShell::close);
321 }
322
323 /**
324 * Close a view with a title
325 *
326 * @param title
327 * the title, like "welcome"
328 * @param bot
329 * the workbench bot
330 */
331 public static void closeView(String title, SWTWorkbenchBot bot) {
332 final List<SWTBotView> openViews = bot.views();
333 for (SWTBotView view : openViews) {
334 if (view.getTitle().equalsIgnoreCase(title)) {
335 view.close();
336 bot.waitUntil(ConditionHelpers.ViewIsClosed(view));
337 }
338 }
339 }
340
341 /**
342 * Close a view with an id
343 *
344 * @param viewId
345 * the view id, like
346 * "org.eclipse.linuxtools.tmf.ui.views.histogram"
347 * @param bot
348 * the workbench bot
349 */
350 public static void closeViewById(String viewId, SWTWorkbenchBot bot) {
351 final SWTBotView view = bot.viewById(viewId);
352 view.close();
353 bot.waitUntil(ConditionHelpers.ViewIsClosed(view));
354 }
355
356 /**
357 * Switch to the tracing perspective
358 */
359 public static void switchToTracingPerspective() {
360 switchToPerspective(TRACING_PERSPECTIVE_ID);
361 }
362
363 /**
364 * Switch to a given perspective
365 *
366 * @param id
367 * the perspective id (like
368 * "org.eclipse.linuxtools.tmf.ui.perspective"
369 */
370 public static void switchToPerspective(final String id) {
371 UIThreadRunnable.syncExec(new VoidResult() {
372 @Override
373 public void run() {
374 try {
375 PlatformUI.getWorkbench().showPerspective(id, PlatformUI.getWorkbench().getActiveWorkbenchWindow());
376 } catch (WorkbenchException e) {
377 fail(e.getMessage());
378 }
379 }
380 });
381 }
382
383 /**
384 * Initialize the environment for SWTBot
385 */
386 public static void initialize() {
387 failIfUIThread();
388
389 SWTWorkbenchBot bot = new SWTWorkbenchBot();
390 UIThreadRunnable.syncExec(() -> {
391 printEnvironment();
392
393 // There seems to be problems on some system where the main shell is
394 // not in focus initially. This was seen using Xvfb and Xephyr on
395 // some occasions.
396 focusMainWindow(bot.shells());
397
398 Shell shell = bot.activeShell().widget;
399
400 // Only adjust shell if it appears to be the top-most
401 if (shell.getParent() == null) {
402 makeShellFullyVisible(shell);
403 }
404 });
405
406 /* Do not switch perspectives on trace open */
407 IPreferenceStore store = Activator.getDefault().getPreferenceStore();
408 store.setValue(ITmfUIPreferences.SWITCH_TO_PERSPECTIVE, MessageDialogWithToggle.NEVER);
409 }
410
411 private static void printEnvironment() {
412 if (fPrintedEnvironment) {
413 return;
414 }
415
416 // Print some information about the environment that could affect test
417 // outcome
418 Rectangle bounds = Display.getDefault().getBounds();
419 System.out.println("Display size: " + bounds.width + "x" + bounds.height);
420
421 String osVersion = System.getProperty("os.version");
422 if (osVersion != null) {
423 System.out.println("OS version=" + osVersion);
424 }
425 String gtkVersion = System.getProperty("org.eclipse.swt.internal.gtk.version");
426 if (gtkVersion != null) {
427 System.out.println("GTK version=" + gtkVersion);
428 // Try to print the GTK theme information as behavior can change
429 // depending on the theme
430 String gtkTheme = System.getProperty("org.eclipse.swt.internal.gtk.theme");
431 System.out.println("GTK theme=" + (gtkTheme == null ? "unknown" : gtkTheme));
432
433 String overlayScrollbar = System.getenv("LIBOVERLAY_SCROLLBAR");
434 if (overlayScrollbar != null) {
435 System.out.println("LIBOVERLAY_SCROLLBAR=" + overlayScrollbar);
436 }
437 String ubuntuMenuProxy = System.getenv("UBUNTU_MENUPROXY");
438 if (ubuntuMenuProxy != null) {
439 System.out.println("UBUNTU_MENUPROXY=" + ubuntuMenuProxy);
440 }
441 }
442
443 System.out.println("Time zone: " + TimeZone.getDefault().getDisplayName());
444
445 fPrintedEnvironment = true;
446 }
447
448 /**
449 * If the test is running in the UI thread then fail
450 */
451 private static void failIfUIThread() {
452 if (Display.getCurrent() != null && Display.getCurrent().getThread() == Thread.currentThread()) {
453 fail("SWTBot test needs to run in a non-UI thread. Make sure that \"Run in UI thread\" is unchecked in your launch configuration or"
454 + " that useUIThread is set to false in the pom.xml");
455 }
456 }
457
458 /**
459 * Try to make the shell fully visible in the display. If the shell cannot
460 * fit the display, it will be positioned so that top-left corner is at
461 * <code>(0, 0)</code> in display-relative coordinates.
462 *
463 * @param shell
464 * the shell to make fully visible
465 */
466 private static void makeShellFullyVisible(Shell shell) {
467 Rectangle displayBounds = shell.getDisplay().getBounds();
468 Point absCoord = shell.toDisplay(0, 0);
469 Point shellSize = shell.getSize();
470
471 Point newLocation = new Point(absCoord.x, absCoord.y);
472 newLocation.x = Math.max(0, Math.min(absCoord.x, displayBounds.width - shellSize.x));
473 newLocation.y = Math.max(0, Math.min(absCoord.y, displayBounds.height - shellSize.y));
474 if (!newLocation.equals(absCoord)) {
475 shell.setLocation(newLocation);
476 }
477 }
478
479 /**
480 * Open a trace, this does not perform any validation though
481 *
482 * @param projectName
483 * The project name
484 * @param tracePath
485 * the path of the trace file (absolute or relative)
486 * @param traceType
487 * the trace type id (eg: org.eclipse.linuxtools.btf.trace)
488 */
489 public static void openTrace(final String projectName, final String tracePath, final String traceType) {
490 openTrace(projectName, tracePath, traceType, true);
491 }
492
493 /**
494 * Open a trace, this does not perform any validation though
495 *
496 * @param projectName
497 * The project name
498 * @param tracePath
499 * the path of the trace file (absolute or relative)
500 * @param traceType
501 * the trace type id (eg: org.eclipse.linuxtools.btf.trace)
502 * @param delay
503 * delay and wait for jobs
504 */
505 public static void openTrace(final String projectName, final String tracePath, final String traceType, boolean delay) {
506 IStatus status = UIThreadRunnable.syncExec(new Result<IStatus>() {
507 @Override
508 public IStatus run() {
509 try {
510 IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
511 TmfTraceFolder destinationFolder = TmfProjectRegistry.getProject(project, true).getTracesFolder();
512 return TmfOpenTraceHelper.openTraceFromPath(destinationFolder, tracePath, PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), traceType);
513 } catch (CoreException e) {
514 return new Status(IStatus.ERROR, "", e.getMessage(), e);
515 }
516 }
517 });
518 if (!status.isOK()) {
519 fail(status.getMessage());
520 }
521
522 if (delay) {
523 delay(1000);
524 WaitUtils.waitForJobs();
525 }
526 }
527
528 /**
529 * Finds an editor and sets focus to the editor
530 *
531 * @param bot
532 * the workbench bot
533 * @param editorName
534 * the editor name
535 * @return the corresponding SWTBotEditor
536 */
537 public static SWTBotEditor activateEditor(SWTWorkbenchBot bot, String editorName) {
538 Matcher<IEditorReference> matcher = WidgetMatcherFactory.withPartName(editorName);
539 final SWTBotEditor editorBot = bot.editor(matcher);
540 IEditorPart iep = editorBot.getReference().getEditor(true);
541 final TmfEventsEditor tmfEd = (TmfEventsEditor) iep;
542 editorBot.show();
543 UIThreadRunnable.syncExec(new VoidResult() {
544 @Override
545 public void run() {
546 tmfEd.setFocus();
547 }
548 });
549
550 WaitUtils.waitForJobs();
551 SWTBotUtils.delay(1000);
552 assertNotNull(tmfEd);
553 return editorBot;
554 }
555
556 /**
557 * Opens a trace in an editor and get the TmfEventsEditor
558 *
559 * @param bot
560 * the workbench bot
561 * @param projectName
562 * the name of the project that contains the trace
563 * @param elementPath
564 * the trace element path (relative to Traces folder)
565 * @return TmfEventsEditor the opened editor
566 */
567 public static TmfEventsEditor openEditor(SWTWorkbenchBot bot, String projectName, IPath elementPath) {
568 final SWTBotView projectExplorerView = bot.viewById(IPageLayout.ID_PROJECT_EXPLORER);
569 projectExplorerView.setFocus();
570 SWTBot projectExplorerBot = projectExplorerView.bot();
571
572 final SWTBotTree tree = projectExplorerBot.tree();
573 projectExplorerBot.waitUntil(ConditionHelpers.IsTreeNodeAvailable(projectName, tree));
574 final SWTBotTreeItem treeItem = tree.getTreeItem(projectName);
575 treeItem.expand();
576
577 SWTBotTreeItem tracesNode = getTraceProjectItem(projectExplorerBot, treeItem, TmfTracesFolder.TRACES_FOLDER_NAME);
578 tracesNode.expand();
579
580 SWTBotTreeItem currentItem = tracesNode;
581 for (String segment : elementPath.segments()) {
582 currentItem = getTraceProjectItem(projectExplorerBot, currentItem, segment);
583 currentItem.doubleClick();
584 }
585
586 SWTBotEditor editor = bot.editorByTitle(elementPath.toString());
587 IEditorPart editorPart = editor.getReference().getEditor(false);
588 assertTrue(editorPart instanceof TmfEventsEditor);
589 return (TmfEventsEditor) editorPart;
590 }
591
592 /**
593 * Returns the child tree item of the specified item at the given sub-path.
594 * The project element labels may have a count suffix in the format ' [n]'.
595 *
596 * @param bot
597 * a given workbench bot
598 * @param parentItem
599 * the parent tree item
600 * @param path
601 * the desired child element sub-path (without suffix)
602 * @return the a {@link SWTBotTreeItem} with the specified name
603 */
604 public static SWTBotTreeItem getTraceProjectItem(SWTBot bot, final SWTBotTreeItem parentItem, final String... path) {
605 SWTBotTreeItem item = parentItem;
606 for (String name : path) {
607 item = getTraceProjectItem(bot, item, name);
608 }
609 return item;
610 }
611
612 /**
613 * Returns the child tree item of the specified item with the given name.
614 * The project element label may have a count suffix in the format ' [n]'.
615 *
616 * @param bot
617 * a given workbench bot
618 * @param parentItem
619 * the parent tree item
620 * @param name
621 * the desired child element name (without suffix)
622 * @return the a {@link SWTBotTreeItem} with the specified name
623 */
624 public static SWTBotTreeItem getTraceProjectItem(SWTBot bot, final SWTBotTreeItem parentItem, final String name) {
625 ProjectElementHasChild condition = new ProjectElementHasChild(parentItem, name);
626 bot.waitUntil(condition);
627 return condition.getItem();
628 }
629
630 /**
631 * Select the traces folder
632 *
633 * @param bot
634 * a given workbench bot
635 * @param projectName
636 * the name of the project (it needs to exist or else it would
637 * time out)
638 * @return a {@link SWTBotTreeItem} of the "Traces" folder
639 */
640 public static SWTBotTreeItem selectTracesFolder(SWTWorkbenchBot bot, String projectName) {
641 SWTBotTreeItem projectTreeItem = selectProject(bot, projectName);
642 projectTreeItem.select();
643 SWTBotTreeItem tracesFolderItem = getTraceProjectItem(bot, projectTreeItem, TmfTracesFolder.TRACES_FOLDER_NAME);
644 tracesFolderItem.select();
645 return tracesFolderItem;
646 }
647
648 /**
649 * Clear the traces folder
650 *
651 * @param bot
652 * a given workbench bot
653 * @param projectName
654 * the name of the project (needs to exist)
655 */
656 public static void clearTracesFolder(SWTWorkbenchBot bot, String projectName) {
657 IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
658 TmfProjectElement tmfProject = TmfProjectRegistry.getProject(project, false);
659 TmfTraceFolder tracesFolder = tmfProject.getTracesFolder();
660 if (tracesFolder == null) {
661 return;
662 }
663 try {
664 for (TmfTraceElement traceElement : tracesFolder.getTraces()) {
665 traceElement.delete(null);
666 }
667
668 final IFolder resource = tracesFolder.getResource();
669 resource.accept(new IResourceVisitor() {
670 @Override
671 public boolean visit(IResource visitedResource) throws CoreException {
672 if (visitedResource != resource) {
673 visitedResource.delete(true, null);
674 }
675 return true;
676 }
677 }, IResource.DEPTH_ONE, 0);
678 } catch (CoreException e) {
679 fail(e.getMessage());
680 }
681
682 bot.waitUntil(new DefaultCondition() {
683 private int fTraceNb = 0;
684
685 @Override
686 public boolean test() throws Exception {
687 List<TmfTraceElement> traces = tracesFolder.getTraces();
688 fTraceNb = traces.size();
689 return fTraceNb == 0;
690 }
691
692 @Override
693 public String getFailureMessage() {
694 return "Traces Folder not empty (" + fTraceNb + ")";
695 }
696 });
697 }
698
699 /**
700 * Clear the trace folder (using the UI)
701 *
702 * @param bot
703 * a given workbench bot
704 * @param projectName
705 * the name of the project (needs to exist)
706 */
707 public static void clearTracesFolderUI(SWTWorkbenchBot bot, String projectName) {
708 SWTBotTreeItem tracesFolder = selectTracesFolder(bot, projectName);
709 tracesFolder.contextMenu().menu("Clear").click();
710 String CONFIRM_CLEAR_DIALOG_TITLE = "Confirm Clear";
711 bot.waitUntil(Conditions.shellIsActive(CONFIRM_CLEAR_DIALOG_TITLE));
712
713 SWTBotShell shell = bot.shell(CONFIRM_CLEAR_DIALOG_TITLE);
714 shell.bot().button("Yes").click();
715 bot.waitUntil(Conditions.shellCloses(shell));
716 bot.waitWhile(ConditionHelpers.treeItemHasChildren(tracesFolder));
717 }
718
719 /**
720 * Clear the experiment folder
721 *
722 * @param bot
723 * a given workbench bot
724 * @param projectName
725 * the name of the project (needs to exist)
726 */
727 public static void clearExperimentFolder(SWTWorkbenchBot bot, String projectName) {
728 IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
729 TmfProjectElement tmfProject = TmfProjectRegistry.getProject(project, false);
730 TmfExperimentFolder expFolder = tmfProject.getExperimentsFolder();
731 if (expFolder == null) {
732 return;
733 }
734 expFolder.getExperiments().forEach(experiment -> {
735 IResource resource = experiment.getResource();
736 try {
737 // Close the experiment if open
738 experiment.closeEditors();
739
740 IPath path = resource.getLocation();
741 if (path != null) {
742 // Delete supplementary files
743 experiment.deleteSupplementaryFolder();
744 }
745 // Finally, delete the experiment
746 resource.delete(true, null);
747 } catch (CoreException e) {
748 fail(e.getMessage());
749 }
750 });
751
752 bot.waitUntil(new DefaultCondition() {
753 private int fExperimentNb = 0;
754
755 @Override
756 public boolean test() throws Exception {
757 List<TmfExperimentElement> experiments = expFolder.getExperiments();
758 fExperimentNb = experiments.size();
759 return fExperimentNb == 0;
760 }
761
762 @Override
763 public String getFailureMessage() {
764 return "Experiment Folder not empty (" + fExperimentNb + ")";
765 }
766 });
767 }
768
769 /**
770 * Select the project in Project Explorer
771 *
772 * @param bot
773 * a given workbench bot
774 * @param projectName
775 * the name of the project (it needs to exist or else it would
776 * time out)
777 * @return a {@link SWTBotTreeItem} of the project
778 */
779 public static SWTBotTreeItem selectProject(SWTWorkbenchBot bot, String projectName) {
780 SWTBotView projectExplorerBot = bot.viewByTitle("Project Explorer");
781 projectExplorerBot.show();
782 // FIXME: Bug 496519. Sometimes, the tree becomes disabled for a certain
783 // amount of time. This can happen during a long running operation
784 // (BusyIndicator.showWhile) which brings up the modal dialog "operation
785 // in progress" and this disables all shells
786 projectExplorerBot.bot().waitUntil(Conditions.widgetIsEnabled(projectExplorerBot.bot().tree()));
787 SWTBotTreeItem treeItem = projectExplorerBot.bot().tree().getTreeItem(projectName);
788 treeItem.select();
789 return treeItem;
790 }
791
792 /**
793 * Open a view by id.
794 *
795 * @param id
796 * view id.
797 */
798 public static void openView(final String id) {
799 openView(id, null);
800 }
801
802 /**
803 * Open a view by id and secondary id
804 *
805 * @param id
806 * view id.
807 * @param secondaryId
808 * The secondary ID
809 */
810 public static void openView(final String id, final @Nullable String secondaryId) {
811 final PartInitException res[] = new PartInitException[1];
812 UIThreadRunnable.syncExec(new VoidResult() {
813 @Override
814 public void run() {
815 try {
816 if (secondaryId == null) {
817 PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(id);
818 } else {
819 PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(id, secondaryId, IWorkbenchPage.VIEW_ACTIVATE);
820 }
821 } catch (PartInitException e) {
822 res[0] = e;
823 }
824 }
825 });
826 if (res[0] != null) {
827 fail(res[0].getMessage());
828 }
829 WaitUtils.waitForJobs();
830 }
831
832 /**
833 * Maximize a table
834 *
835 * @param tableBot
836 * the {@link SWTBotTable} table
837 */
838 public static void maximizeTable(SWTBotTable tableBot) {
839 final AtomicBoolean controlResized = new AtomicBoolean();
840 UIThreadRunnable.syncExec(new VoidResult() {
841 @Override
842 public void run() {
843 tableBot.widget.addControlListener(new ControlAdapter() {
844 @Override
845 public void controlResized(ControlEvent e) {
846 tableBot.widget.removeControlListener(this);
847 controlResized.set(true);
848 }
849 });
850 }
851 });
852 try {
853 tableBot.pressShortcut(KeyStroke.getInstance(IKeyLookup.CTRL_NAME + "+"), KeyStroke.getInstance("M"));
854 } catch (ParseException e) {
855 fail();
856 }
857 new SWTBot().waitUntil(new DefaultCondition() {
858 @Override
859 public boolean test() throws Exception {
860 return controlResized.get();
861 }
862
863 @Override
864 public String getFailureMessage() {
865 return "Control was not resized";
866 }
867 });
868 }
869
870 /**
871 * Get the bounds of a widget in display coordinates
872 *
873 * @param bot
874 * the widget bot
875 * @return the widget bounds, in display coordinates
876 */
877 public static Rectangle getBoundsToDisplay(AbstractSWTBot<?> bot) {
878 if (bot.widget instanceof Control) {
879 final Control control = checkNotNull((Control) bot.widget);
880 return UIThreadRunnable.syncExec(new Result<Rectangle>() {
881 @Override
882 public Rectangle run() {
883 Point location = control.toDisplay(0, 0);
884 Point size = control.getSize();
885 return new Rectangle(location.x, location.y, size.x, size.y);
886 }
887 });
888 }
889 throw new IllegalArgumentException(bot +" is not a Control widget");
890 }
891
892 /**
893 * Get the bounds of a cell (SWT.Rectangle) for the specified row and column
894 * index in a table
895 *
896 * @param table
897 * the table
898 * @param row
899 * the row of the table to look up
900 * @param col
901 * the column of the table to look up
902 * @return the bounds in display relative coordinates
903 */
904 public static Rectangle getCellBounds(final Table table, final int row, final int col) {
905 return UIThreadRunnable.syncExec(new Result<Rectangle>() {
906 @Override
907 public Rectangle run() {
908 TableItem item = table.getItem(row);
909 Rectangle bounds = item.getBounds(col);
910 Point p = table.toDisplay(bounds.x, bounds.y);
911 Rectangle rect = new Rectangle(p.x, p.y, bounds.width, bounds.height);
912 return rect;
913 }
914 });
915 }
916
917 /**
918 * Get the tree item from a tree at the specified location
919 *
920 * @param bot
921 * the SWTBot
922 * @param tree
923 * the tree to find the tree item in
924 * @param nodeNames
925 * the path to the tree item, in the form of node names (from
926 * parent to child).
927 * @return the tree item
928 */
929 public static SWTBotTreeItem getTreeItem(SWTBot bot, SWTBotTree tree, String... nodeNames) {
930 if (nodeNames.length == 0) {
931 return null;
932 }
933
934 bot.waitUntil(ConditionHelpers.IsTreeNodeAvailable(nodeNames[0], tree));
935 SWTBotTreeItem currentNode = tree.getTreeItem(nodeNames[0]);
936 return getTreeItem(bot, currentNode, Arrays.copyOfRange(nodeNames, 1, nodeNames.length));
937 }
938
939 /**
940 * Get the tree item from a parent tree item at the specified location
941 *
942 * @param bot
943 * the SWTBot
944 * @param treeItem
945 * the treeItem to find the tree item under
946 * @param nodeNames
947 * the path to the tree item, in the form of node names (from
948 * parent to child).
949 * @return the tree item
950 */
951 public static SWTBotTreeItem getTreeItem(SWTBot bot, SWTBotTreeItem treeItem, String... nodeNames) {
952 if (nodeNames.length == 0) {
953 return treeItem;
954 }
955
956 SWTBotTreeItem currentNode = treeItem;
957 for (int i = 0; i < nodeNames.length; i++) {
958 bot.waitUntil(ConditionHelpers.treeItemHasChildren(treeItem));
959 currentNode.expand();
960
961 String nodeName = nodeNames[i];
962 try {
963 bot.waitUntil(ConditionHelpers.IsTreeChildNodeAvailable(nodeName, currentNode));
964 } catch (TimeoutException e) {
965 // FIXME: Sometimes in a JFace TreeViewer, it expands to
966 // nothing. Need to find out why.
967 currentNode.collapse();
968 currentNode.expand();
969 bot.waitUntil(ConditionHelpers.IsTreeChildNodeAvailable(nodeName, currentNode));
970 }
971
972 SWTBotTreeItem newNode = currentNode.getNode(nodeName);
973 currentNode = newNode;
974 }
975
976 return currentNode;
977 }
978
979 /**
980 * Press the shortcut specified by the given keys. The method returns when
981 * the key events have been received by the focus control.
982 *
983 * @param keyboard
984 * the keyboard
985 * @param keys
986 * the keys to press
987 */
988 public static void pressShortcut(Keyboard keyboard, KeyStroke... keys) {
989 Control focusControl = UIThreadRunnable.syncExec(new Result<Control>() {
990 @Override
991 public Control run() {
992 return Display.getCurrent().getFocusControl();
993 }
994 });
995 pressShortcut(focusControl, () -> keyboard.pressShortcut(keys), keys);
996 }
997
998 /**
999 * Press the shortcut specified by the given keys. The method returns when
1000 * the key events have been received by the given control.
1001 *
1002 * @param bot
1003 * the control bot
1004 * @param keys
1005 * the keys to press
1006 */
1007 public static void pressShortcut(AbstractSWTBotControl<?> bot, KeyStroke... keys) {
1008 pressShortcut(bot.widget, () -> bot.pressShortcut(keys), keys);
1009 }
1010
1011 private static void pressShortcut(Control control, Runnable pressShortcut, KeyStroke... keys) {
1012 AtomicInteger keysPressed = new AtomicInteger();
1013 AtomicInteger keysReleased = new AtomicInteger();
1014 KeyListener keyListener = new KeyListener() {
1015 @Override
1016 public void keyPressed(KeyEvent e) {
1017 keysPressed.incrementAndGet();
1018 }
1019 @Override
1020 public void keyReleased(KeyEvent e) {
1021 keysReleased.incrementAndGet();
1022 }
1023 };
1024 UIThreadRunnable.syncExec(() -> control.addKeyListener(keyListener));
1025 pressShortcut.run();
1026 new SWTBot().waitUntil(new DefaultCondition() {
1027 @Override
1028 public boolean test() throws Exception {
1029 return keysPressed.get() > 0 && keysPressed.get() == keysReleased.get();
1030 }
1031 @Override
1032 public String getFailureMessage() {
1033 return "key press " + Arrays.toString(keys) + " not detected";
1034 }
1035 });
1036 UIThreadRunnable.syncExec(() -> control.removeKeyListener(keyListener));
1037 }
1038
1039 /**
1040 * Press the keyboard shortcut that goes to the top of a tree widget. The
1041 * key combination can differ on different platforms.
1042 *
1043 * @param keyboard
1044 * the keyboard to use
1045 */
1046 public static void pressShortcutGoToTreeTop(Keyboard keyboard) {
1047 if (SWTUtils.isMac()) {
1048 pressShortcut(keyboard, Keystrokes.ALT, Keystrokes.UP);
1049 } else {
1050 pressShortcut(keyboard, Keystrokes.HOME);
1051 }
1052 }
1053
1054 /**
1055 * Get the active events editor. Note that this will wait until such editor
1056 * is available.
1057 *
1058 * @param workbenchBot
1059 * a given workbench bot
1060 * @return the active events editor
1061 */
1062 public static SWTBotEditor activeEventsEditor(final SWTWorkbenchBot workbenchBot) {
1063 ConditionHelpers.ActiveEventsEditor condition = new ConditionHelpers.ActiveEventsEditor(workbenchBot, null);
1064 workbenchBot.waitUntil(condition);
1065 return condition.getActiveEditor();
1066 }
1067
1068 /**
1069 * Get the active events editor. Note that this will wait until such editor
1070 * is available.
1071 *
1072 * @param workbenchBot
1073 * a given workbench bot
1074 * @param editorTitle
1075 * the desired editor title. If null, any active events editor
1076 * will be considered valid.
1077 * @return the active events editor
1078 */
1079 public static SWTBotEditor activeEventsEditor(final SWTWorkbenchBot workbenchBot, String editorTitle) {
1080 ConditionHelpers.ActiveEventsEditor condition = new ConditionHelpers.ActiveEventsEditor(workbenchBot, editorTitle);
1081 workbenchBot.waitUntil(condition);
1082 return condition.getActiveEditor();
1083 }
1084
1085 /**
1086 * Open the preferences dialog and return the corresponding shell. See also
1087 * {@link #pressOKishButtonInPreferences(SWTBot)} to close the dialog.
1088 *
1089 * @param bot
1090 * a given workbench bot
1091 * @return the preferences shell
1092 */
1093 public static SWTBotShell openPreferences(SWTBot bot) {
1094 if (SWTUtils.isMac()) {
1095 // On Mac, the Preferences menu item is under the application name.
1096 // For some reason, we can't access the application menu anymore so
1097 // we use the keyboard shortcut.
1098 try {
1099 bot.activeShell().pressShortcut(KeyStroke.getInstance(IKeyLookup.COMMAND_NAME + "+"), KeyStroke.getInstance(","));
1100 } catch (ParseException e) {
1101 fail();
1102 }
1103 } else {
1104 bot.menu(WINDOW_MENU).menu(PREFERENCES_MENU_ITEM).click();
1105 }
1106
1107 bot.waitUntil(Conditions.shellIsActive(PREFERENCES_MENU_ITEM));
1108 return bot.activeShell();
1109 }
1110
1111 /**
1112 * Click the OK or "Apply and Close" button the preferences dialog. The
1113 * button label changed from OK to "Apply and Close" in Eclipse version
1114 * 4.7-I20170329-2000.
1115 *
1116 * @param bot
1117 * a given workbench bot
1118 */
1119 public static void pressOKishButtonInPreferences(SWTBot bot) {
1120 try {
1121 String okIshLabel = "Apply and Close";
1122 // We do it this more manual way in order to not have to timout and
1123 // wait 30 secs when the button is not there
1124 bot.waitUntil(Conditions.waitForWidget(withMnemonic(okIshLabel)), 100);
1125 bot.button(okIshLabel).click();
1126 } catch (TimeoutException e) {
1127 // Doesn't exist pre-4.7-I20170329-2000, try old "OK" button
1128 bot.button("OK").click();
1129 }
1130 }
1131
1132 /**
1133 * Maximize a view by reference. Calling this a second time will "un-maximize" a view.
1134 * <p>
1135 * TODO: if this is useful, maybe uplift to SWTViewBot
1136 *
1137 * @param view
1138 * the view reference
1139 */
1140 public static void maximize(@NonNull IViewPart view) {
1141 assertNotNull(view);
1142 IWorkbenchPartSite site = view.getSite();
1143 assertNotNull(site);
1144 // The annotation is to make the compiler not complain.
1145 @Nullable
1146 Object handlerServiceObject = site.getService(IHandlerService.class);
1147 assertTrue(handlerServiceObject instanceof IHandlerService);
1148 IHandlerService handlerService = (IHandlerService) handlerServiceObject;
1149 try {
1150 handlerService.executeCommand(IWorkbenchCommandConstants.WINDOW_MAXIMIZE_ACTIVE_VIEW_OR_EDITOR, null);
1151 } catch (ExecutionException | NotDefinedException | NotEnabledException | NotHandledException e) {
1152 fail(e.getMessage());
1153 }
1154 }
1155
1156 /**
1157 * Get the number of checked items of a tree
1158 *
1159 * @param tree
1160 * The tree bot
1161 * @return The number of checked items
1162 */
1163 public static int getTreeCheckedItemCount(SWTBotTree tree) {
1164 return UIThreadRunnable.syncExec(new IntResult() {
1165
1166 @Override
1167 public Integer run() {
1168 int checked = 0;
1169 for (TreeItem item : tree.widget.getItems()) {
1170 checked += getChecked(item);
1171 }
1172 return checked;
1173 }
1174
1175 private int getChecked(TreeItem item) {
1176 int total = 0;
1177 if (item.getChecked()) {
1178 total++;
1179 }
1180 for (TreeItem child : item.getItems()) {
1181 total += getChecked(child);
1182 }
1183 return total;
1184 }
1185 });
1186 }
1187
1188 /**
1189 * Filter the specified list of items in a time graph view.
1190 *
1191 * @param viewBot
1192 * the view
1193 * @param filterItems
1194 * the list of filter dialog item tree paths
1195 * @param checkSubTree
1196 * true if the filter items sub-trees should be checked, or false
1197 * to only check the filter items
1198 */
1199 public static void applyTimeGraphFilter(SWTBotView viewBot, List<String[]> filterItems, boolean checkSubTree) {
1200 final String FILTER_ACTION = "Show View Filters";
1201 final String FILTER_DIALOG_TITLE = "Filter";
1202 final String UNCHECK_ALL = "Uncheck all";
1203 final String CHECK_SUBTREE = "Check subtree";
1204 final String OK_BUTTON = "OK";
1205
1206 SWTBotToolbarButton filterButton = viewBot.toolbarButton(FILTER_ACTION);
1207 filterButton.click();
1208 SWTBotShell shell = viewBot.bot().shell(FILTER_DIALOG_TITLE).activate();
1209
1210 SWTBot bot = shell.bot();
1211 SWTBotTree treeBot = bot.tree();
1212 bot.button(UNCHECK_ALL).click();
1213
1214 for (String[] filterItem : filterItems) {
1215 SWTBotTreeItem item = SWTBotUtils.getTreeItem(bot, treeBot, filterItem);
1216 if (checkSubTree) {
1217 item.select();
1218 bot.button(CHECK_SUBTREE).click();
1219 } else {
1220 item.check();
1221 }
1222 }
1223
1224 bot.button(OK_BUTTON).click();
1225 }
1226 }
This page took 0.081321 seconds and 5 git commands to generate.