Coverage Report - org.eclipse.swtbot.swt.finder.utils.SWTUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
SWTUtils
66%
80/120
59%
39/66
2.636
SWTUtils$1
100%
3/3
100%
2/2
2.636
SWTUtils$2
60%
3/5
N/A
2.636
SWTUtils$3
100%
3/3
N/A
2.636
SWTUtils$4
0%
0/8
0%
0/2
2.636
SWTUtils$5
0%
0/3
N/A
2.636
SWTUtils$6
0%
0/4
0%
0/2
2.636
 
 1  52635
 /*******************************************************************************
 2  
  * Copyright (c) 2008 Ketan Padegaonkar and others.
 3  
  * All rights reserved. This program and the accompanying materials
 4  
  * are made available under the terms of the Eclipse Public License v1.0
 5  
  * which accompanies this distribution, and is available at
 6  
  * http://www.eclipse.org/legal/epl-v10.html
 7  
  *
 8  
  * Contributors:
 9  
  *     Ketan Padegaonkar - initial API and implementation
 10  
  *     Hans Schwaebli - http://swtbot.org/bugzilla/show_bug.cgi?id=108
 11  
  *******************************************************************************/
 12  
 package org.eclipse.swtbot.swt.finder.utils;
 13  
 
 14  
 import java.io.File;
 15  
 import java.lang.reflect.InvocationTargetException;
 16  
 import java.lang.reflect.Method;
 17  
 import java.text.MessageFormat;
 18  
 
 19  
 import org.apache.log4j.Logger;
 20  
 import org.eclipse.swt.SWT;
 21  
 import org.eclipse.swt.graphics.GC;
 22  
 import org.eclipse.swt.graphics.Image;
 23  
 import org.eclipse.swt.graphics.ImageData;
 24  
 import org.eclipse.swt.graphics.ImageLoader;
 25  
 import org.eclipse.swt.graphics.Rectangle;
 26  
 import org.eclipse.swt.widgets.Control;
 27  
 import org.eclipse.swt.widgets.Display;
 28  
 import org.eclipse.swt.widgets.Shell;
 29  
 import org.eclipse.swt.widgets.Text;
 30  
 import org.eclipse.swt.widgets.Widget;
 31  
 import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
 32  
 import org.eclipse.swtbot.swt.finder.results.BoolResult;
 33  
 import org.eclipse.swtbot.swt.finder.results.Result;
 34  
 import org.eclipse.swtbot.swt.finder.utils.internal.Assert;
 35  
 import org.eclipse.swtbot.swt.finder.utils.internal.NextWidgetFinder;
 36  
 import org.eclipse.swtbot.swt.finder.utils.internal.PreviousWidgetFinder;
 37  
 import org.eclipse.swtbot.swt.finder.utils.internal.ReflectionInvoker;
 38  
 import org.eclipse.swtbot.swt.finder.utils.internal.SiblingFinder;
 39  
 import org.eclipse.swtbot.swt.finder.utils.internal.WidgetIndexFinder;
 40  
 import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
 41  
 import org.eclipse.swtbot.swt.finder.waits.ICondition;
 42  
 import org.eclipse.swtbot.swt.finder.widgets.TimeoutException;
 43  
 
 44  
 /**
 45  
  * @author Ketan Padegaonkar <KetanPadegaonkar [at] gmail [dot] com>
 46  
  * @version $Id$
 47  
  */
 48  1
 public abstract class SWTUtils {
 49  
 
 50  
         /** The logger. */
 51  1
         private static Logger        log        = Logger.getLogger(SWTUtils.class);
 52  
 
 53  
         /**
 54  
          * The display used for the GUI.
 55  
          */
 56  
         private static Display        display;
 57  
 
 58  
         /**
 59  
          * @param w the widget
 60  
          * @return the siblings of the widget, or an empty array, if there are none.
 61  
          */
 62  
         public static Widget[] siblings(final Widget w) {
 63  17
                 if ((w == null) || w.isDisposed())
 64  0
                         return new Widget[] {};
 65  17
                 return UIThreadRunnable.syncExec(w.getDisplay(), new SiblingFinder(w));
 66  
         }
 67  
 
 68  
         /**
 69  
          * Gets the index of the given widget in its current container.
 70  
          * 
 71  
          * @param w the widget
 72  
          * @return -1 if the the widget is <code>null</code> or if the widget does not have a parent; a number greater than
 73  
          *         or equal to zero indicating the index of the widget among its siblings
 74  
          */
 75  
         public static int widgetIndex(final Widget w) {
 76  1720
                 if ((w == null) || w.isDisposed())
 77  1
                         return -1;
 78  1719
                 return UIThreadRunnable.syncExec(w.getDisplay(), new WidgetIndexFinder(w));
 79  
         }
 80  
 
 81  
         /**
 82  
          * Gets the previous sibling of the passed in widget.
 83  
          * 
 84  
          * @param w the widget
 85  
          * @return the previous sibling of w
 86  
          */
 87  
         public static Widget previousWidget(final Widget w) {
 88  668
                 if ((w == null) || w.isDisposed())
 89  0
                         return null;
 90  668
                 return UIThreadRunnable.syncExec(w.getDisplay(), new PreviousWidgetFinder(w));
 91  
         }
 92  
 
 93  
         /**
 94  
          * Gets the next sibling of this passed in widget.
 95  
          * 
 96  
          * @param w the widget.
 97  
          * @return the sibling of the specified widget, or <code>null</code> if it has none.
 98  
          */
 99  
         public static Widget nextWidget(Widget w) {
 100  2
                 if ((w == null) || w.isDisposed())
 101  0
                         return null;
 102  2
                 return UIThreadRunnable.syncExec(w.getDisplay(), new NextWidgetFinder(w));
 103  
         }
 104  
 
 105  
         /**
 106  
          * Gets the text of the given object.
 107  
          * 
 108  
          * @param obj the object which should be a widget.
 109  
          * @return the result of invocation of Widget#getText()
 110  
          */
 111  
         public static String getText(final Object obj) {
 112  1628
                 if ((obj instanceof Widget) && !((Widget) obj).isDisposed()) {
 113  1628
                         Widget widget = (Widget) obj;
 114  1628
                         String text = UIThreadRunnable.syncExec(widget.getDisplay(), new ReflectionInvoker(obj, "getText")); //$NON-NLS-1$
 115  1628
                         text = text.replaceAll(Text.DELIMITER, "\n"); //$NON-NLS-1$
 116  1628
                         return text;
 117  
                 }
 118  0
                 return ""; //$NON-NLS-1$
 119  
         }
 120  
 
 121  
         /**
 122  
          * Gets the tooltip text for the given object.
 123  
          * 
 124  
          * @param obj the object which should be a widget.
 125  
          * @return the result of invocation of Widget#getToolTipText()
 126  
          * @since 1.0
 127  
          */
 128  
         public static String getToolTipText(final Object obj) {
 129  1
                 if ((obj instanceof Widget) && !((Widget) obj).isDisposed()) {
 130  1
                         Widget widget = (Widget) obj;
 131  1
                         return UIThreadRunnable.syncExec(widget.getDisplay(), new ReflectionInvoker(obj, "getToolTipText")); //$NON-NLS-1$
 132  
                 }
 133  0
                 return ""; //$NON-NLS-1$
 134  
         }
 135  
 
 136  
         /**
 137  
          * Converts the given widget to a string.
 138  
          * 
 139  
          * @param w the widget.
 140  
          * @return the string representation of the widget.
 141  
          */
 142  
         public static String toString(final Widget w) {
 143  947
                 if ((w == null) || w.isDisposed())
 144  2
                         return ""; //$NON-NLS-1$
 145  945
                 return toString(w.getDisplay(), w);
 146  
         }
 147  
 
 148  
         /**
 149  
          * Convers the display and object to a string.
 150  
          * 
 151  
          * @param display the display on which the object should be evaluated.
 152  
          * @param o the object to evaluate.
 153  
          * @return the string representation of the object when evaluated in the display thread.
 154  
          */
 155  
         public static String toString(Display display, final Object o) {
 156  945
                 return ClassUtils.simpleClassName(o) + " {" + trimToLength(getText(o), 20) + "}"; //$NON-NLS-1$ //$NON-NLS-2$
 157  
 
 158  
         }
 159  
 
 160  
         /**
 161  
          * Trims the string to a given length, adds an ellipsis("...") if the string is trimmed.
 162  
          * 
 163  
          * @param result The string to limit.
 164  
          * @param maxLength The length to limit it to.
 165  
          * @return The resulting string.
 166  
          */
 167  
         private static String trimToLength(String result, int maxLength) {
 168  945
                 if (result.length() > maxLength)
 169  29
                         result = result.substring(0, maxLength) + "..."; //$NON-NLS-1$
 170  945
                 return result;
 171  
         }
 172  
 
 173  
         /**
 174  
          * Checks if the widget has the given style.
 175  
          * 
 176  
          * @param w the widget.
 177  
          * @param style the style.
 178  
          * @return <code>true</code> if the widget has the specified style bit set. Otherwise <code>false</code>.
 179  
          */
 180  
         public static boolean hasStyle(final Widget w, final int style) {
 181  2617
                 if ((w == null) || w.isDisposed())
 182  1
                         return false;
 183  2616
                 if (style == SWT.NONE)
 184  1
                         return true;
 185  2615
                 return UIThreadRunnable.syncExec(w.getDisplay(), new BoolResult() {
 186  
                         public Boolean run() {
 187  2615
                                 return (w.getStyle() & style) != 0;
 188  
                         }
 189  
                 });
 190  
         }
 191  
 
 192  
         /**
 193  
          * Sleeps for the given number of milliseconds.
 194  
          * 
 195  
          * @param millis the time in milliseconds to sleep.
 196  
          */
 197  
         public static void sleep(long millis) {
 198  
                 try {
 199  260
                         Thread.sleep(millis);
 200  0
                 } catch (InterruptedException e) {
 201  0
                         throw new RuntimeException("Could not sleep", e); //$NON-NLS-1$
 202  
                 }
 203  260
         }
 204  
 
 205  
         /**
 206  
          * Gets all the thread in the VM.
 207  
          * 
 208  
          * @return all the threads in the VM.
 209  
          */
 210  
         public static Thread[] allThreads() {
 211  1
                 ThreadGroup threadGroup = primaryThreadGroup();
 212  
 
 213  1
                 Thread[] threads = new Thread[64];
 214  1
                 int enumerate = threadGroup.enumerate(threads, true);
 215  
 
 216  1
                 Thread[] result = new Thread[enumerate];
 217  1
                 System.arraycopy(threads, 0, result, 0, enumerate);
 218  
 
 219  1
                 return result;
 220  
         }
 221  
 
 222  
         /**
 223  
          * Gets the primary thread group.
 224  
          * 
 225  
          * @return the top level thread group.
 226  
          */
 227  
         public static ThreadGroup primaryThreadGroup() {
 228  1
                 ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
 229  3
                 while (threadGroup.getParent() != null)
 230  1
                         threadGroup = threadGroup.getParent();
 231  1
                 return threadGroup;
 232  
         }
 233  
 
 234  
         /**
 235  
          * Caches the display for later use.
 236  
          * 
 237  
          * @return the display.
 238  
          */
 239  
         public static Display display() {
 240  18675
                 if ((display == null) || display.isDisposed()) {
 241  1
                         display = null;
 242  1
                         Thread[] allThreads = allThreads();
 243  8
                         for (Thread thread : allThreads) {
 244  7
                                 Display d = Display.findDisplay(thread);
 245  7
                                 if (d != null)
 246  1
                                         display = d;
 247  
                         }
 248  1
                         if (display == null)
 249  0
                                 throw new IllegalStateException("Could not find a display"); //$NON-NLS-1$
 250  
                 }
 251  18675
                 return display;
 252  
         }
 253  
 
 254  
         /**
 255  
          * Checks if the widget text is an empty string.
 256  
          * 
 257  
          * @param w the widget
 258  
          * @return <code>true</code> if the widget does not have any text set on it. Othrewise <code>false</code>.
 259  
          */
 260  
         // TODO recommend changing the name to isEmptyText since null isn't being check and if getText returned a null an
 261  
         // exception would be thrown.
 262  
         public static boolean isEmptyOrNullText(Widget w) {
 263  0
                 return getText(w).trim().equals(""); //$NON-NLS-1$
 264  
         }
 265  
 
 266  
         /**
 267  
          * Invokes the specified methodName on the object, and returns the result, or <code>null</code> if the method
 268  
          * returns void.
 269  
          * 
 270  
          * @param object the object
 271  
          * @param methodName the method name
 272  
          * @return the result of invoking the method on the object
 273  
          * @throws NoSuchMethodException if the method methodName does not exist.
 274  
          * @throws IllegalAccessException if the java access control does not allow invocation.
 275  
          * @throws InvocationTargetException if the method methodName throws an exception.
 276  
          * @see Method#invoke(Object, Object[])
 277  
          * @since 1.0
 278  
          */
 279  
         public static Object invokeMethod(final Object object, String methodName) throws NoSuchMethodException, IllegalAccessException,
 280  
                         InvocationTargetException {
 281  53293
                 final Method method = object.getClass().getMethod(methodName, new Class[0]);
 282  52649
                 Widget widget = null;
 283  
                 final Object result;
 284  52649
                 if (object instanceof Widget) {
 285  52635
                         widget = (Widget) object;
 286  52635
                         result = UIThreadRunnable.syncExec(widget.getDisplay(), new Result<Object>() {
 287  
                                 public Object run() {
 288  
                                         try {
 289  52635
                                                 return method.invoke(object, new Object[0]);
 290  0
                                         } catch (Exception niceTry) {
 291  
                                         }
 292  0
                                         return null;
 293  
                                 }
 294  
                         });
 295  
                 } else
 296  14
                         result = method.invoke(object, new Object[0]);
 297  
 
 298  52649
                 return result;
 299  
         }
 300  
 
 301  
         /**
 302  
          * This captures a screen shot and saves it to the given file.
 303  
          * 
 304  
          * @param fileName the filename to save screenshot to.
 305  
          * @return <code>true</code> if the screenshot was created and saved, <code>false</code> otherwise.
 306  
          * @since 1.0
 307  
          */
 308  
         public static boolean captureScreenshot(final String fileName) {
 309  1
                 new ImageFormatConverter().imageTypeOf(fileName.substring(fileName.lastIndexOf('.') + 1));
 310  1
                 return UIThreadRunnable.syncExec(new BoolResult() {
 311  
                         public Boolean run() {
 312  1
                                 return captureScreenshotInternal(fileName);
 313  
                         }
 314  
                 });
 315  
         }
 316  
 
 317  
         /**
 318  
          * This captures a screen shot of a widget and saves it to the given file.
 319  
          * 
 320  
          * @param fileName the filename to save screenshot to.
 321  
          * @param control the control
 322  
          * @return <code>true</code> if the screenshot was created and saved, <code>false</code> otherwise.
 323  
          * @since 2.0
 324  
          */
 325  
         public static boolean captureScreenshot(final String fileName, final Control control) {
 326  0
                 new ImageFormatConverter().imageTypeOf(fileName.substring(fileName.lastIndexOf('.') + 1));
 327  0
                 return UIThreadRunnable.syncExec(new BoolResult() {
 328  
                         public Boolean run() {
 329  0
                                 if (control instanceof Shell)
 330  0
                                         return captureScreenshotInternal(fileName, control.getBounds());
 331  0
                                 Display display = control.getDisplay();
 332  0
                                 Rectangle bounds = control.getBounds();
 333  0
                                 Rectangle mappedToDisplay = display.map(control.getParent(), null, bounds);
 334  
 
 335  0
                                 return captureScreenshotInternal(fileName, mappedToDisplay);
 336  
                         }
 337  
                 });
 338  
         }
 339  
 
 340  
         /**
 341  
          * This captures a screen shot of an area and saves it to the given file.
 342  
          * 
 343  
          * @param fileName the filename to save screenshot to.
 344  
          * @param bounds the area to capture.
 345  
          * @return <code>true</code> if the screenshot was created and saved, <code>false</code> otherwise.
 346  
          * @since 2.0
 347  
          */
 348  
         public static boolean captureScreenshot(final String fileName, final Rectangle bounds) {
 349  0
                 new ImageFormatConverter().imageTypeOf(fileName.substring(fileName.lastIndexOf('.') + 1));
 350  0
                 return UIThreadRunnable.syncExec(new BoolResult() {
 351  
                         public Boolean run() {
 352  0
                                 return captureScreenshotInternal(fileName, bounds);
 353  
                         }
 354  
                 });
 355  
         }
 356  
 
 357  
         /**
 358  
          * Captures a screen shot. Used internally.
 359  
          * <p>
 360  
          * NOTE: This method is not thread safe. Clients must ensure that they do invoke this from a UI thread.
 361  
          * </p>
 362  
          * 
 363  
          * @param fileName the filename to save screenshot to.
 364  
          * @return <code>true</code> if the screenshot was created and saved, <code>false</code> otherwise.
 365  
          */
 366  1
         private static boolean captureScreenshotInternal(final String fileName) {
 367  1
                 return captureScreenshotInternal(fileName, display.getBounds());
 368  
         }
 369  
 
 370  
         /**
 371  
          * Captures a screen shot. Used internally.
 372  
          * <p>
 373  
          * NOTE: This method is not thread safe. Clients must ensure that they do invoke this from a UI thread.
 374  
          * </p>
 375  
          * 
 376  
          * @param fileName the filename to save screenshot to.
 377  
          * @param bounds the area relative to the display that should be captured.
 378  
          * @return <code>true</code> if the screenshot was created and saved, <code>false</code> otherwise.
 379  
          */
 380  0
         private static boolean captureScreenshotInternal(final String fileName, Rectangle bounds) {
 381  1
                 GC gc = new GC(display);
 382  1
                 Image image = null;
 383  1
                 File file = new File(fileName);
 384  1
                 File parentDir = file.getParentFile();
 385  1
                 if (parentDir != null)
 386  1
                         parentDir.mkdirs();
 387  
                 try {
 388  1
                         log.debug(MessageFormat.format("Capturing screenshot ''{0}''", fileName)); //$NON-NLS-1$
 389  
 
 390  1
                         image = new Image(display, bounds.width, bounds.height);
 391  1
                         gc.copyArea(image, bounds.x, bounds.y);
 392  1
                         ImageLoader imageLoader = new ImageLoader();
 393  1
                         imageLoader.data = new ImageData[] { image.getImageData() };
 394  1
                         imageLoader.save(fileName, new ImageFormatConverter().imageTypeOf(fileName.substring(fileName.lastIndexOf('.') + 1)));
 395  1
                         return true;
 396  0
                 } catch (Exception e) {
 397  0
                         log.warn("Could not capture screenshot: " + fileName + "'", e); //$NON-NLS-1$ //$NON-NLS-2$
 398  0
                         File brokenImage = file.getAbsoluteFile();
 399  0
                         if (brokenImage.exists()) {
 400  
                                 try {
 401  0
                                         log.trace(MessageFormat.format("Broken screenshot set to be deleted on exit: {0}", fileName)); //$NON-NLS-1$
 402  0
                                         brokenImage.deleteOnExit();
 403  0
                                 } catch (Exception ex) {
 404  0
                                         log.info(MessageFormat.format("Could not set broken screenshot to be deleted on exit: {0}", fileName), ex); //$NON-NLS-1$
 405  
                                 }
 406  
                         }
 407  0
                         return false;
 408  0
                 } finally {
 409  1
                         gc.dispose();
 410  1
                         if (image != null) {
 411  1
                                 image.dispose();
 412  
                         }
 413  0
                 }
 414  
         }
 415  
 
 416  
         /**
 417  
          * Waits until a display appears.
 418  
          * 
 419  
          * @throws TimeoutException if the condition does not evaluate to true after {@link SWTBotPreferences#TIMEOUT}
 420  
          *             milliseconds.
 421  
          */
 422  
         public static void waitForDisplayToAppear() {
 423  0
                 waitForDisplayToAppear(SWTBotPreferences.TIMEOUT);
 424  0
         }
 425  
 
 426  
         /**
 427  
          * Waits until a display appears.
 428  
          * 
 429  
          * @param timeout the timeout in ms.
 430  
          * @throws TimeoutException if the condition does not evaluate to true after {@code timeout}ms milliseconds.
 431  
          */
 432  
         public static void waitForDisplayToAppear(long timeout) {
 433  0
                 waitUntil(new DefaultCondition() {
 434  
 
 435  
                         public String getFailureMessage() {
 436  0
                                 return "Could not find a display"; //$NON-NLS-1$
 437  
                         }
 438  
 
 439  
                         public boolean test() throws Exception {
 440  0
                                 return SWTUtils.display() != null;
 441  
                         }
 442  
 
 443  0
                 }, timeout, 500);
 444  0
         }
 445  
 
 446  
         private static void waitUntil(ICondition condition, long timeout, long interval) throws TimeoutException {
 447  0
                 Assert.isTrue(interval >= 0, "interval value is negative"); //$NON-NLS-1$
 448  0
                 Assert.isTrue(timeout >= 0, "timeout value is negative"); //$NON-NLS-1$
 449  0
                 long limit = System.currentTimeMillis() + timeout;
 450  
                 while (true) {
 451  
                         try {
 452  0
                                 if (condition.test())
 453  0
                                         return;
 454  0
                         } catch (Throwable e) {
 455  
                                 // do nothing
 456  
                         }
 457  0
                         sleep(interval);
 458  0
                         if (System.currentTimeMillis() > limit)
 459  0
                                 throw new TimeoutException("Timeout after: " + timeout + " ms.: " + condition.getFailureMessage()); //$NON-NLS-1$ //$NON-NLS-2$
 460  
                 }
 461  
         }
 462  
 
 463  
         /**
 464  
          * Return true if the current thread is the UI thread.
 465  
          * 
 466  
          * @param display the display
 467  
          * @return <code>true</code> if the current thread is the UI thread, <code>false</code> otherwise.
 468  
          */
 469  
         public static boolean isUIThread(Display display) {
 470  94262
                 return display.getThread() == Thread.currentThread();
 471  
         }
 472  
 
 473  
         /**
 474  
          * Return true if the current thread is the UI thread.
 475  
          * 
 476  
          * @return <code>true</code> if this instance is running in the UI thread, <code>false</code> otherwise.
 477  
          */
 478  
         public static boolean isUIThread() {
 479  0
                 return isUIThread(display);
 480  
         }
 481  
 }