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