1 /* 2 * Copyright (c) 2006, 2007, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package test.java.awt.regtesthelpers; 25 /** 26 * <p>This class contains utilities useful for regression testing. 27 * <p>When using jtreg you would include this class into the build 28 * list via something like: 29 * <pre> 30 @library ../../../regtesthelpers 31 @build Util 32 @run main YourTest 33 </pre> 34 * Note that if you are about to create a test based on 35 * Applet-template, then put those lines into html-file, not in java-file. 36 * <p> And put an 37 * import test.java.awt.regtesthelpers.Util; 38 * into the java source of test. 39 */ 40 41 import java.awt.Component; 42 import java.awt.Frame; 43 import java.awt.Dialog; 44 import java.awt.Window; 45 import java.awt.Button; 46 import java.awt.Point; 47 import java.awt.Dimension; 48 import java.awt.Rectangle; 49 import java.awt.Robot; 50 import java.awt.Toolkit; 51 import java.awt.IllegalComponentStateException; 52 import java.awt.AWTException; 53 import java.awt.AWTEvent; 54 55 import java.awt.event.InputEvent; 56 import java.awt.event.WindowAdapter; 57 import java.awt.event.WindowEvent; 58 import java.awt.event.ActionEvent; 59 import java.awt.event.FocusEvent; 60 import java.awt.event.WindowListener; 61 import java.awt.event.WindowFocusListener; 62 import java.awt.event.FocusListener; 63 import java.awt.event.ActionListener; 64 65 import java.awt.peer.FramePeer; 66 67 import java.lang.reflect.Constructor; 68 import java.lang.reflect.Field; 69 import java.lang.reflect.InvocationTargetException; 70 import java.lang.reflect.Method; 71 72 import java.security.PrivilegedAction; 73 import java.security.AccessController; 74 75 import java.util.concurrent.atomic.AtomicBoolean; 76 77 public final class Util { 78 private Util() {} // this is a helper class with static methods :) 79 80 /* 81 * @throws RuntimeException when creation failed 82 */ 83 public static Robot createRobot() { 84 try { 85 return new Robot(); 86 } catch (AWTException e) { 87 throw new RuntimeException("Error: unable to create robot", e); 88 } 89 } 90 91 public static Frame createEmbeddedFrame(final Frame embedder) 92 throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, 93 InstantiationException, InvocationTargetException 94 { 95 Toolkit tk = Toolkit.getDefaultToolkit(); 96 FramePeer frame_peer = (FramePeer) embedder.getPeer(); 97 System.out.println("frame's peer = " + frame_peer); 98 if ("sun.awt.windows.WToolkit".equals(tk.getClass().getName())) { 99 Class comp_peer_class = 100 Class.forName("sun.awt.windows.WComponentPeer"); 101 System.out.println("comp peer class = " + comp_peer_class); 102 Field hwnd_field = comp_peer_class.getDeclaredField("hwnd"); 103 hwnd_field.setAccessible(true); 104 System.out.println("hwnd_field =" + hwnd_field); 105 long hwnd = hwnd_field.getLong(frame_peer); 106 System.out.println("hwnd = " + hwnd); 107 108 Class clazz = Class.forName("sun.awt.windows.WEmbeddedFrame"); 109 Constructor constructor = clazz.getConstructor (new Class [] {Long.TYPE}); 110 return (Frame) constructor.newInstance (new Object[] {hwnd}); 111 } else if ("sun.awt.X11.XToolkit".equals(tk.getClass().getName())) { 112 Class x_base_window_class = Class.forName("sun.awt.X11.XBaseWindow"); 113 System.out.println("x_base_window_class = " + x_base_window_class); 114 Method get_window = x_base_window_class.getMethod("getWindow", new Class[0]); 115 System.out.println("get_window = " + get_window); 116 long window = (Long) get_window.invoke(frame_peer, new Object[0]); 117 System.out.println("window = " + window); 118 Class clazz = Class.forName("sun.awt.X11.XEmbeddedFrame"); 119 Constructor constructor = clazz.getConstructor (new Class [] {Long.TYPE, Boolean.TYPE}); 120 return (Frame) constructor.newInstance (new Object[] {window, true}); 121 } 122 123 throw new RuntimeException("Unexpected toolkit - " + tk); 124 } 125 126 /** 127 * Makes the window visible and waits until it's shown. 128 */ 129 public static void showWindowWait(Window win) { 130 win.setVisible(true); 131 waitTillShown(win); 132 } 133 134 /** 135 * Moves mouse pointer in the center of given {@code comp} component 136 * using {@code robot} parameter. 137 */ 138 public static void pointOnComp(final Component comp, final Robot robot) { 139 Rectangle bounds = new Rectangle(comp.getLocationOnScreen(), comp.getSize()); 140 robot.mouseMove(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2); 141 } 142 143 /** 144 * Moves mouse pointer in the center of a given {@code comp} component 145 * and performs a left mouse button click using the {@code robot} parameter 146 * with the {@code delay} delay between press and release. 147 */ 148 public static void clickOnComp(final Component comp, final Robot robot, int delay) { 149 pointOnComp(comp, robot); 150 robot.delay(delay); 151 robot.mousePress(InputEvent.BUTTON1_MASK); 152 robot.delay(delay); 153 robot.mouseRelease(InputEvent.BUTTON1_MASK); 154 } 155 156 /** 157 * Moves mouse pointer in the center of a given {@code comp} component 158 * and performs a left mouse button click using the {@code robot} parameter 159 * with the default delay between press and release. 160 */ 161 public static void clickOnComp(final Component comp, final Robot robot) { 162 clickOnComp(comp, robot, 50); 163 } 164 165 /* 166 * Clicks on a title of Frame/Dialog. 167 * WARNING: it may fail on some platforms when the window is not wide enough. 168 */ 169 public static void clickOnTitle(final Window decoratedWindow, final Robot robot) { 170 Point p = decoratedWindow.getLocationOnScreen(); 171 Dimension d = decoratedWindow.getSize(); 172 173 if (decoratedWindow instanceof Frame || decoratedWindow instanceof Dialog) { 174 robot.mouseMove(p.x + (int)(d.getWidth()/2), p.y + (int)decoratedWindow.getInsets().top/2); 175 robot.delay(50); 176 robot.mousePress(InputEvent.BUTTON1_MASK); 177 robot.delay(50); 178 robot.mouseRelease(InputEvent.BUTTON1_MASK); 179 } 180 } 181 182 public static void waitForIdle(final Robot robot) { 183 // we do not use robot for now, use SunToolkit.realSync() instead 184 ((sun.awt.SunToolkit)Toolkit.getDefaultToolkit()).realSync(); 185 } 186 187 public static Field getField(final Class klass, final String fieldName) { 188 return AccessController.doPrivileged(new PrivilegedAction<Field>() { 189 public Field run() { 190 try { 191 Field field = klass.getDeclaredField(fieldName); 192 assert (field != null); 193 field.setAccessible(true); 194 return field; 195 } catch (SecurityException se) { 196 throw new RuntimeException("Error: unexpected exception caught!", se); 197 } catch (NoSuchFieldException nsfe) { 198 throw new RuntimeException("Error: unexpected exception caught!", nsfe); 199 } 200 } 201 }); 202 } 203 204 /* 205 * Waits for a notification and for a boolean condition to become true. 206 * The method returns when the above conditions are fullfilled or when the timeout 207 * occurs. 208 * 209 * @param condition the object to be notified and the booelan condition to wait for 210 * @param timeout the maximum time to wait in milliseconds 211 * @param catchExceptions if {@code true} the method catches InterruptedException 212 * @return the final boolean value of the {@code condition} 213 * @throws InterruptedException if the awaiting proccess has been interrupted 214 */ 215 public static boolean waitForConditionEx(final AtomicBoolean condition, long timeout) 216 throws InterruptedException 217 { 218 synchronized (condition) { 219 long startTime = System.currentTimeMillis(); 220 while (!condition.get()) { 221 condition.wait(timeout); 222 if (System.currentTimeMillis() - startTime >= timeout ) { 223 break; 224 } 225 } 226 } 227 return condition.get(); 228 } 229 230 /* 231 * The same as {@code waitForConditionEx(AtomicBoolean, long)} except that it 232 * doesn't throw InterruptedException. 233 */ 234 public static boolean waitForCondition(final AtomicBoolean condition, long timeout) { 235 try { 236 return waitForConditionEx(condition, timeout); 237 } catch (InterruptedException e) { 238 throw new RuntimeException("Error: unexpected exception caught!", e); 239 } 240 } 241 242 /* 243 * The same as {@code waitForConditionEx(AtomicBoolean, long)} but without a timeout. 244 */ 245 public static void waitForConditionEx(final AtomicBoolean condition) 246 throws InterruptedException 247 { 248 synchronized (condition) { 249 while (!condition.get()) { 250 condition.wait(); 251 } 252 } 253 } 254 255 /* 256 * The same as {@code waitForConditionEx(AtomicBoolean)} except that it 257 * doesn't throw InterruptedException. 258 */ 259 public static void waitForCondition(final AtomicBoolean condition) { 260 try { 261 waitForConditionEx(condition); 262 } catch (InterruptedException e) { 263 throw new RuntimeException("Error: unexpected exception caught!", e); 264 } 265 } 266 267 public static void waitTillShownEx(final Component comp) throws InterruptedException { 268 while (true) { 269 try { 270 Thread.sleep(100); 271 comp.getLocationOnScreen(); 272 break; 273 } catch (IllegalComponentStateException e) {} 274 } 275 } 276 public static void waitTillShown(final Component comp) { 277 try { 278 waitTillShownEx(comp); 279 } catch (InterruptedException e) { 280 throw new RuntimeException("Error: unexpected exception caught!", e); 281 } 282 } 283 284 /** 285 * Drags from one point to another with the specified mouse button pressed. 286 * 287 * @param robot a robot to use for moving the mouse, etc. 288 * @param startPoint a start point of the drag 289 * @param endPoint an end point of the drag 290 * @param button one of {@code InputEvent.BUTTON1_MASK}, 291 * {@code InputEvent.BUTTON2_MASK}, {@code InputEvent.BUTTON3_MASK} 292 * 293 * @throws IllegalArgumentException if {@code button} is not one of 294 * {@code InputEvent.BUTTON1_MASK}, {@code InputEvent.BUTTON2_MASK}, 295 * {@code InputEvent.BUTTON3_MASK} 296 */ 297 public static void drag(Robot robot, Point startPoint, Point endPoint, int button) { 298 if (!(button == InputEvent.BUTTON1_MASK || button == InputEvent.BUTTON2_MASK 299 || button == InputEvent.BUTTON3_MASK)) 300 { 301 throw new IllegalArgumentException("invalid mouse button"); 302 } 303 304 robot.mouseMove(startPoint.x, startPoint.y); 305 robot.mousePress(button); 306 try { 307 mouseMove(robot, startPoint, endPoint); 308 } finally { 309 robot.mouseRelease(button); 310 } 311 } 312 313 /** 314 * Moves the mouse pointer from one point to another. 315 * Uses Bresenham's algorithm. 316 * 317 * @param robot a robot to use for moving the mouse 318 * @param startPoint a start point of the drag 319 * @param endPoint an end point of the drag 320 */ 321 public static void mouseMove(Robot robot, Point startPoint, Point endPoint) { 322 int dx = endPoint.x - startPoint.x; 323 int dy = endPoint.y - startPoint.y; 324 325 int ax = Math.abs(dx) * 2; 326 int ay = Math.abs(dy) * 2; 327 328 int sx = signWOZero(dx); 329 int sy = signWOZero(dy); 330 331 int x = startPoint.x; 332 int y = startPoint.y; 333 334 int d = 0; 335 336 if (ax > ay) { 337 d = ay - ax/2; 338 while (true){ 339 robot.mouseMove(x, y); 340 robot.delay(50); 341 342 if (x == endPoint.x){ 343 return; 344 } 345 if (d >= 0){ 346 y = y + sy; 347 d = d - ax; 348 } 349 x = x + sx; 350 d = d + ay; 351 } 352 } else { 353 d = ax - ay/2; 354 while (true){ 355 robot.mouseMove(x, y); 356 robot.delay(50); 357 358 if (y == endPoint.y){ 359 return; 360 } 361 if (d >= 0){ 362 x = x + sx; 363 d = d - ay; 364 } 365 y = y + sy; 366 d = d + ax; 367 } 368 } 369 } 370 371 private static int signWOZero(int i){ 372 return (i > 0)? 1: -1; 373 } 374 375 private static int sign(int n) { 376 return n < 0 ? -1 : n == 0 ? 0 : 1; 377 } 378 379 /** Returns {@code WindowListener} instance that diposes {@code Window} on 380 * "window closing" event. 381 * 382 * @return the {@code WindowListener} instance that could be set 383 * on a {@code Window}. After that 384 * the {@code Window} is disposed when "window closed" 385 * event is sent to the {@code Window} 386 */ 387 public static WindowListener getClosingWindowAdapter() { 388 return new WindowAdapter () { 389 public void windowClosing(WindowEvent e) { 390 e.getWindow().dispose(); 391 } 392 }; 393 } 394 395 /* 396 * The values directly map to the ones of 397 * sun.awt.X11.XWM & sun.awt.motif.MToolkit classes. 398 */ 399 public final static int 400 UNDETERMINED_WM = 1, 401 NO_WM = 2, 402 OTHER_WM = 3, 403 OPENLOOK_WM = 4, 404 MOTIF_WM = 5, 405 CDE_WM = 6, 406 ENLIGHTEN_WM = 7, 407 KDE2_WM = 8, 408 SAWFISH_WM = 9, 409 ICE_WM = 10, 410 METACITY_WM = 11, 411 COMPIZ_WM = 12, 412 LG3D_WM = 13; 413 414 /* 415 * Returns -1 in case of not X Window or any problems. 416 */ 417 public static int getWMID() { 418 Class clazz = null; 419 try { 420 if ("sun.awt.X11.XToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { 421 clazz = Class.forName("sun.awt.X11.XWM"); 422 } else if ("sun.awt.motif.MToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { 423 clazz = Class.forName("sun.awt.motif.MToolkit"); 424 } 425 } catch (ClassNotFoundException cnfe) { 426 cnfe.printStackTrace(); 427 } 428 if (clazz == null) { 429 return -1; 430 } 431 432 try { 433 final Class _clazz = clazz; 434 Method m_getWMID = (Method)AccessController.doPrivileged(new PrivilegedAction() { 435 public Object run() { 436 try { 437 Method method = _clazz.getDeclaredMethod("getWMID", new Class[] {}); 438 if (method != null) { 439 method.setAccessible(true); 440 } 441 return method; 442 } catch (NoSuchMethodException e) { 443 assert false; 444 } catch (SecurityException e) { 445 assert false; 446 } 447 return null; 448 } 449 }); 450 return ((Integer)m_getWMID.invoke(null, new Object[] {})).intValue(); 451 } catch (IllegalAccessException iae) { 452 iae.printStackTrace(); 453 } catch (InvocationTargetException ite) { 454 ite.printStackTrace(); 455 } 456 return -1; 457 } 458 459 460 //////////////////////////// 461 // Some stuff to test focus. 462 //////////////////////////// 463 464 private static WindowGainedFocusListener wgfListener = new WindowGainedFocusListener(); 465 private static FocusGainedListener fgListener = new FocusGainedListener(); 466 private static ActionPerformedListener apListener = new ActionPerformedListener(); 467 468 private abstract static class EventListener { 469 AtomicBoolean notifier = new AtomicBoolean(false); 470 Component comp; 471 boolean printEvent; 472 473 public void listen(Component comp, boolean printEvent) { 474 this.comp = comp; 475 this.printEvent = printEvent; 476 notifier.set(false); 477 setListener(comp); 478 } 479 480 public AtomicBoolean getNotifier() { 481 return notifier; 482 } 483 484 abstract void setListener(Component comp); 485 486 void printAndNotify(AWTEvent e) { 487 if (printEvent) { 488 System.err.println(e); 489 } 490 synchronized (notifier) { 491 notifier.set(true); 492 notifier.notifyAll(); 493 } 494 } 495 } 496 497 private static class WindowGainedFocusListener extends EventListener implements WindowFocusListener { 498 499 void setListener(Component comp) { 500 ((Window)comp).addWindowFocusListener(this); 501 } 502 503 public void windowGainedFocus(WindowEvent e) { 504 505 ((Window)comp).removeWindowFocusListener(this); 506 printAndNotify(e); 507 } 508 509 public void windowLostFocus(WindowEvent e) {} 510 } 511 512 private static class FocusGainedListener extends EventListener implements FocusListener { 513 514 void setListener(Component comp) { 515 comp.addFocusListener(this); 516 } 517 518 public void focusGained(FocusEvent e) { 519 comp.removeFocusListener(this); 520 printAndNotify(e); 521 } 522 523 public void focusLost(FocusEvent e) {} 524 } 525 526 private static class ActionPerformedListener extends EventListener implements ActionListener { 527 528 void setListener(Component comp) { 529 ((Button)comp).addActionListener(this); 530 } 531 532 public void actionPerformed(ActionEvent e) { 533 ((Button)comp).removeActionListener(this); 534 printAndNotify(e); 535 } 536 } 537 538 private static boolean trackEvent(int eventID, Component comp, Runnable action, int time, boolean printEvent) { 539 EventListener listener = null; 540 541 switch (eventID) { 542 case WindowEvent.WINDOW_GAINED_FOCUS: 543 listener = wgfListener; 544 break; 545 case FocusEvent.FOCUS_GAINED: 546 listener = fgListener; 547 break; 548 case ActionEvent.ACTION_PERFORMED: 549 listener = apListener; 550 break; 551 } 552 553 listener.listen(comp, printEvent); 554 action.run(); 555 return Util.waitForCondition(listener.getNotifier(), time); 556 } 557 558 /* 559 * Tracks WINDOW_GAINED_FOCUS event for a window caused by an action. 560 * @param window the window to track the event for 561 * @param action the action to perform 562 * @param time the max time to wait for the event 563 * @param printEvent should the event received be printed or doesn't 564 * @return true if the event has been received, otherwise false 565 */ 566 public static boolean trackWindowGainedFocus(Window window, Runnable action, int time, boolean printEvent) { 567 return trackEvent(WindowEvent.WINDOW_GAINED_FOCUS, window, action, time, printEvent); 568 } 569 570 /* 571 * Tracks FOCUS_GAINED event for a component caused by an action. 572 * @see #trackWindowGainedFocus 573 */ 574 public static boolean trackFocusGained(Component comp, Runnable action, int time, boolean printEvent) { 575 return trackEvent(FocusEvent.FOCUS_GAINED, comp, action, time, printEvent); 576 } 577 578 /* 579 * Tracks ACTION_PERFORMED event for a button caused by an action. 580 * @see #trackWindowGainedFocus 581 */ 582 public static boolean trackActionPerformed(Button button, Runnable action, int time, boolean printEvent) { 583 return trackEvent(ActionEvent.ACTION_PERFORMED, button, action, time, printEvent); 584 } 585 586 /* 587 * Requests focus on the component provided and waits for the result. 588 * @return true if the component has been focused, false otherwise. 589 */ 590 public static boolean focusComponent(Component comp, int time) { 591 return focusComponent(comp, time, false); 592 } 593 public static boolean focusComponent(final Component comp, int time, boolean printEvent) { 594 return trackFocusGained(comp, 595 new Runnable() { 596 public void run() { 597 comp.requestFocus(); 598 } 599 }, 600 time, printEvent); 601 602 } 603 }