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 }