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 public static Point getTitlePoint(Window decoratedWindow) { 166 Point p = decoratedWindow.getLocationOnScreen(); 167 Dimension d = decoratedWindow.getSize(); 168 return new Point(p.x + (int)(d.getWidth()/2), 169 p.y + (int)(decoratedWindow.getInsets().top/2)); 170 } 171 172 /* 173 * Clicks on a title of Frame/Dialog. 174 * WARNING: it may fail on some platforms when the window is not wide enough. 175 */ 176 public static void clickOnTitle(final Window decoratedWindow, final Robot robot) { 177 if (decoratedWindow instanceof Frame || decoratedWindow instanceof Dialog) { 178 Point p = getTitlePoint(decoratedWindow); 179 robot.mouseMove(p.x, p.y); 180 robot.delay(50); 181 robot.mousePress(InputEvent.BUTTON1_MASK); 182 robot.delay(50); 183 robot.mouseRelease(InputEvent.BUTTON1_MASK); 184 } 185 } 186 187 public static void waitForIdle(final Robot robot) { 188 // we do not use robot for now, use SunToolkit.realSync() instead 189 ((sun.awt.SunToolkit)Toolkit.getDefaultToolkit()).realSync(); 190 } 191 192 public static Field getField(final Class klass, final String fieldName) { 193 return AccessController.doPrivileged(new PrivilegedAction<Field>() { 194 public Field run() { 195 try { 196 Field field = klass.getDeclaredField(fieldName); 197 assert (field != null); 198 field.setAccessible(true); 199 return field; 200 } catch (SecurityException se) { 201 throw new RuntimeException("Error: unexpected exception caught!", se); 202 } catch (NoSuchFieldException nsfe) { 203 throw new RuntimeException("Error: unexpected exception caught!", nsfe); 204 } 205 } 206 }); 207 } 208 209 /* 210 * Waits for a notification and for a boolean condition to become true. 211 * The method returns when the above conditions are fullfilled or when the timeout 212 * occurs. 213 * 214 * @param condition the object to be notified and the booelan condition to wait for 215 * @param timeout the maximum time to wait in milliseconds 216 * @param catchExceptions if {@code true} the method catches InterruptedException 217 * @return the final boolean value of the {@code condition} 218 * @throws InterruptedException if the awaiting proccess has been interrupted 219 */ 220 public static boolean waitForConditionEx(final AtomicBoolean condition, long timeout) 221 throws InterruptedException 222 { 223 synchronized (condition) { 224 long startTime = System.currentTimeMillis(); 225 while (!condition.get()) { 226 condition.wait(timeout); 227 if (System.currentTimeMillis() - startTime >= timeout ) { 228 break; 229 } 230 } 231 } 232 return condition.get(); 233 } 234 235 /* 236 * The same as {@code waitForConditionEx(AtomicBoolean, long)} except that it 237 * doesn't throw InterruptedException. 238 */ 239 public static boolean waitForCondition(final AtomicBoolean condition, long timeout) { 240 try { 241 return waitForConditionEx(condition, timeout); 242 } catch (InterruptedException e) { 243 throw new RuntimeException("Error: unexpected exception caught!", e); 244 } 245 } 246 247 /* 248 * The same as {@code waitForConditionEx(AtomicBoolean, long)} but without a timeout. 249 */ 250 public static void waitForConditionEx(final AtomicBoolean condition) 251 throws InterruptedException 252 { 253 synchronized (condition) { 254 while (!condition.get()) { 255 condition.wait(); 256 } 257 } 258 } 259 260 /* 261 * The same as {@code waitForConditionEx(AtomicBoolean)} except that it 262 * doesn't throw InterruptedException. 263 */ 264 public static void waitForCondition(final AtomicBoolean condition) { 265 try { 266 waitForConditionEx(condition); 267 } catch (InterruptedException e) { 268 throw new RuntimeException("Error: unexpected exception caught!", e); 269 } 270 } 271 272 public static void waitTillShownEx(final Component comp) throws InterruptedException { 273 while (true) { 274 try { 275 Thread.sleep(100); 276 comp.getLocationOnScreen(); 277 break; 278 } catch (IllegalComponentStateException e) {} 279 } 280 } 281 public static void waitTillShown(final Component comp) { 282 try { 283 waitTillShownEx(comp); 284 } catch (InterruptedException e) { 285 throw new RuntimeException("Error: unexpected exception caught!", e); 286 } 287 } 288 289 /** 290 * Drags from one point to another with the specified mouse button pressed. 291 * 292 * @param robot a robot to use for moving the mouse, etc. 293 * @param startPoint a start point of the drag 294 * @param endPoint an end point of the drag 295 * @param button one of {@code InputEvent.BUTTON1_MASK}, 296 * {@code InputEvent.BUTTON2_MASK}, {@code InputEvent.BUTTON3_MASK} 297 * 298 * @throws IllegalArgumentException if {@code button} is not one of 299 * {@code InputEvent.BUTTON1_MASK}, {@code InputEvent.BUTTON2_MASK}, 300 * {@code InputEvent.BUTTON3_MASK} 301 */ 302 public static void drag(Robot robot, Point startPoint, Point endPoint, int button) { 303 if (!(button == InputEvent.BUTTON1_MASK || button == InputEvent.BUTTON2_MASK 304 || button == InputEvent.BUTTON3_MASK)) 305 { 306 throw new IllegalArgumentException("invalid mouse button"); 307 } 308 309 robot.mouseMove(startPoint.x, startPoint.y); 310 robot.mousePress(button); 311 try { 312 mouseMove(robot, startPoint, endPoint); 313 } finally { 314 robot.mouseRelease(button); 315 } 316 } 317 318 /** 319 * Moves the mouse pointer from one point to another. 320 * Uses Bresenham's algorithm. 321 * 322 * @param robot a robot to use for moving the mouse 323 * @param startPoint a start point of the drag 324 * @param endPoint an end point of the drag 325 */ 326 public static void mouseMove(Robot robot, Point startPoint, Point endPoint) { 327 int dx = endPoint.x - startPoint.x; 328 int dy = endPoint.y - startPoint.y; 329 330 int ax = Math.abs(dx) * 2; 331 int ay = Math.abs(dy) * 2; 332 333 int sx = signWOZero(dx); 334 int sy = signWOZero(dy); 335 336 int x = startPoint.x; 337 int y = startPoint.y; 338 339 int d = 0; 340 341 if (ax > ay) { 342 d = ay - ax/2; 343 while (true){ 344 robot.mouseMove(x, y); 345 robot.delay(50); 346 347 if (x == endPoint.x){ 348 return; 349 } 350 if (d >= 0){ 351 y = y + sy; 352 d = d - ax; 353 } 354 x = x + sx; 355 d = d + ay; 356 } 357 } else { 358 d = ax - ay/2; 359 while (true){ 360 robot.mouseMove(x, y); 361 robot.delay(50); 362 363 if (y == endPoint.y){ 364 return; 365 } 366 if (d >= 0){ 367 x = x + sx; 368 d = d - ay; 369 } 370 y = y + sy; 371 d = d + ax; 372 } 373 } 374 } 375 376 private static int signWOZero(int i){ 377 return (i > 0)? 1: -1; 378 } 379 380 private static int sign(int n) { 381 return n < 0 ? -1 : n == 0 ? 0 : 1; 382 } 383 384 /** Returns {@code WindowListener} instance that diposes {@code Window} on 385 * "window closing" event. 386 * 387 * @return the {@code WindowListener} instance that could be set 388 * on a {@code Window}. After that 389 * the {@code Window} is disposed when "window closed" 390 * event is sent to the {@code Window} 391 */ 392 public static WindowListener getClosingWindowAdapter() { 393 return new WindowAdapter () { 394 public void windowClosing(WindowEvent e) { 395 e.getWindow().dispose(); 396 } 397 }; 398 } 399 400 /* 401 * The values directly map to the ones of 402 * sun.awt.X11.XWM & sun.awt.motif.MToolkit classes. 403 */ 404 public final static int 405 UNDETERMINED_WM = 1, 406 NO_WM = 2, 407 OTHER_WM = 3, 408 OPENLOOK_WM = 4, 409 MOTIF_WM = 5, 410 CDE_WM = 6, 411 ENLIGHTEN_WM = 7, 412 KDE2_WM = 8, 413 SAWFISH_WM = 9, 414 ICE_WM = 10, 415 METACITY_WM = 11, 416 COMPIZ_WM = 12, 417 LG3D_WM = 13, 418 CWM_WM = 14, 419 MUTTER_WM = 15; 420 421 /* 422 * Returns -1 in case of not X Window or any problems. 423 */ 424 public static int getWMID() { 425 Class clazz = null; 426 try { 427 if ("sun.awt.X11.XToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { 428 clazz = Class.forName("sun.awt.X11.XWM"); 429 } else if ("sun.awt.motif.MToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { 430 clazz = Class.forName("sun.awt.motif.MToolkit"); 431 } 432 } catch (ClassNotFoundException cnfe) { 433 cnfe.printStackTrace(); 434 } 435 if (clazz == null) { 436 return -1; 437 } 438 439 try { 440 final Class _clazz = clazz; 441 Method m_getWMID = (Method)AccessController.doPrivileged(new PrivilegedAction() { 442 public Object run() { 443 try { 444 Method method = _clazz.getDeclaredMethod("getWMID", new Class[] {}); 445 if (method != null) { 446 method.setAccessible(true); 447 } 448 return method; 449 } catch (NoSuchMethodException e) { 450 assert false; 451 } catch (SecurityException e) { 452 assert false; 453 } 454 return null; 455 } 456 }); 457 return ((Integer)m_getWMID.invoke(null, new Object[] {})).intValue(); 458 } catch (IllegalAccessException iae) { 459 iae.printStackTrace(); 460 } catch (InvocationTargetException ite) { 461 ite.printStackTrace(); 462 } 463 return -1; 464 } 465 466 467 //////////////////////////// 468 // Some stuff to test focus. 469 //////////////////////////// 470 471 private static WindowGainedFocusListener wgfListener = new WindowGainedFocusListener(); 472 private static FocusGainedListener fgListener = new FocusGainedListener(); 473 private static ActionPerformedListener apListener = new ActionPerformedListener(); 474 475 private abstract static class EventListener { 476 AtomicBoolean notifier = new AtomicBoolean(false); 477 Component comp; 478 boolean printEvent; 479 480 public void listen(Component comp, boolean printEvent) { 481 this.comp = comp; 482 this.printEvent = printEvent; 483 notifier.set(false); 484 setListener(comp); 485 } 486 487 public AtomicBoolean getNotifier() { 488 return notifier; 489 } 490 491 abstract void setListener(Component comp); 492 493 void printAndNotify(AWTEvent e) { 494 if (printEvent) { 495 System.err.println(e); 496 } 497 synchronized (notifier) { 498 notifier.set(true); 499 notifier.notifyAll(); 500 } 501 } 502 } 503 504 private static class WindowGainedFocusListener extends EventListener implements WindowFocusListener { 505 506 void setListener(Component comp) { 507 ((Window)comp).addWindowFocusListener(this); 508 } 509 510 public void windowGainedFocus(WindowEvent e) { 511 512 ((Window)comp).removeWindowFocusListener(this); 513 printAndNotify(e); 514 } 515 516 public void windowLostFocus(WindowEvent e) {} 517 } 518 519 private static class FocusGainedListener extends EventListener implements FocusListener { 520 521 void setListener(Component comp) { 522 comp.addFocusListener(this); 523 } 524 525 public void focusGained(FocusEvent e) { 526 comp.removeFocusListener(this); 527 printAndNotify(e); 528 } 529 530 public void focusLost(FocusEvent e) {} 531 } 532 533 private static class ActionPerformedListener extends EventListener implements ActionListener { 534 535 void setListener(Component comp) { 536 ((Button)comp).addActionListener(this); 537 } 538 539 public void actionPerformed(ActionEvent e) { 540 ((Button)comp).removeActionListener(this); 541 printAndNotify(e); 542 } 543 } 544 545 private static boolean trackEvent(int eventID, Component comp, Runnable action, int time, boolean printEvent) { 546 EventListener listener = null; 547 548 switch (eventID) { 549 case WindowEvent.WINDOW_GAINED_FOCUS: 550 listener = wgfListener; 551 break; 552 case FocusEvent.FOCUS_GAINED: 553 listener = fgListener; 554 break; 555 case ActionEvent.ACTION_PERFORMED: 556 listener = apListener; 557 break; 558 } 559 560 listener.listen(comp, printEvent); 561 action.run(); 562 return Util.waitForCondition(listener.getNotifier(), time); 563 } 564 565 /* 566 * Tracks WINDOW_GAINED_FOCUS event for a window caused by an action. 567 * @param window the window to track the event for 568 * @param action the action to perform 569 * @param time the max time to wait for the event 570 * @param printEvent should the event received be printed or doesn't 571 * @return true if the event has been received, otherwise false 572 */ 573 public static boolean trackWindowGainedFocus(Window window, Runnable action, int time, boolean printEvent) { 574 return trackEvent(WindowEvent.WINDOW_GAINED_FOCUS, window, action, time, printEvent); 575 } 576 577 /* 578 * Tracks FOCUS_GAINED event for a component caused by an action. 579 * @see #trackWindowGainedFocus 580 */ 581 public static boolean trackFocusGained(Component comp, Runnable action, int time, boolean printEvent) { 582 return trackEvent(FocusEvent.FOCUS_GAINED, comp, action, time, printEvent); 583 } 584 585 /* 586 * Tracks ACTION_PERFORMED event for a button caused by an action. 587 * @see #trackWindowGainedFocus 588 */ 589 public static boolean trackActionPerformed(Button button, Runnable action, int time, boolean printEvent) { 590 return trackEvent(ActionEvent.ACTION_PERFORMED, button, action, time, printEvent); 591 } 592 593 /* 594 * Requests focus on the component provided and waits for the result. 595 * @return true if the component has been focused, false otherwise. 596 */ 597 public static boolean focusComponent(Component comp, int time) { 598 return focusComponent(comp, time, false); 599 } 600 public static boolean focusComponent(final Component comp, int time, boolean printEvent) { 601 return trackFocusGained(comp, 602 new Runnable() { 603 public void run() { 604 comp.requestFocus(); 605 } 606 }, 607 time, printEvent); 608 609 } 610 }