1 /*
   2  * Copyright (c) 2011, 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  /*
  25  * @summary Utility routines that wait for a window to be displayed or for
  26 colors to be visible
  27  * @summary com.apple.junit.utils
  28  */
  29 package test.java.awt.regtesthelpers;
  30 
  31 import java.awt.*;
  32 import java.awt.event.*;
  33 
  34 //import junit.framework.Assert;
  35 public class VisibilityValidator {
  36 
  37     // Wait up to five seconds for our window events
  38     static final int SETUP_PERIOD = 5000;
  39     static final boolean DEBUG = false;
  40 
  41     volatile Window win = null;
  42     boolean activated = false;
  43     boolean opened = false;
  44     boolean focused = false;
  45     volatile boolean valid = false;
  46 
  47     //
  48     // Utility functions that encapsulates normal usage patterns
  49     //
  50     public static void setVisibleAndConfirm(Frame testframe) throws Exception {
  51         setVisibleAndConfirm(testframe, "Could not confirm test frame was "
  52                 + "visible");
  53     }
  54 
  55     public static void setVisibleAndConfirm(Frame testframe, String msg)
  56             throws Exception {
  57         if (testframe.isVisible()) {
  58             throw new RuntimeException("Frame is already visible");
  59         }
  60 
  61         VisibilityValidator checkpoint = new VisibilityValidator(testframe);
  62         testframe.setVisible(true);
  63         checkpoint.requireVisible();
  64         if (!checkpoint.isValid()) {
  65             //System.err.println(msg);
  66             throw new Exception("Frame not visible after " + SETUP_PERIOD
  67                     + " milliseconds");
  68         }
  69     }
  70 
  71     //
  72     // Add listeners to the window
  73     //
  74     public VisibilityValidator(Window win) {
  75         this.win = win;
  76         WindowAdapter watcher = new WindowAdapter() {
  77             public void windowOpened(WindowEvent e) {
  78                 doOpen();
  79             }
  80 
  81             public void windowActivated(WindowEvent e) {
  82                 doActivate();
  83             }
  84 
  85             public void windowGainedFocus(WindowEvent e) {
  86                 doGainedFocus();
  87             }
  88         };
  89 
  90         win.addWindowListener(watcher);
  91         win.addWindowFocusListener(watcher);
  92     }
  93 
  94     // Make the window visible
  95     //
  96     // The only way to make it through this routine is for the window to
  97     // generate BOTH a windowOpened, a windowActivated event and a
  98     // windowGainedFocus, or to timeout.
  99     //
 100     synchronized public void requireVisible() {
 101         int tries = 0;
 102 
 103         // wait for windowOpened and windowActivated events
 104         try {
 105             while ((opened == false)
 106                     || (activated == false)
 107                     || (focused == false)) {
 108                 if (tries < 4) {
 109                     tries += 1;
 110                     wait(SETUP_PERIOD);
 111                 } else {
 112                     break;
 113                 }
 114             }
 115 
 116             if (opened && activated) {
 117                 valid = true;
 118             } else {
 119                 valid = false;
 120             }
 121         } catch (InterruptedException ix) {
 122             valid = false;
 123         }
 124 
 125         // Extra-super paranoid checks
 126         if (win.isVisible() == false) {
 127             valid = false;
 128         }
 129 
 130         if (win.isShowing() == false) {
 131             valid = false;
 132         }
 133 
 134         if (win.isFocused() == false) {
 135             valid = false;
 136         }
 137 
 138         if (DEBUG) {
 139             if (!isValid()) {
 140                 System.out.println("\tactivated:" + new Boolean(activated));
 141                 System.out.println("\topened:" + new Boolean(opened));
 142                 System.out.println("\tfocused:" + new Boolean(focused));
 143                 System.out.println("\tvalid:" + new Boolean(valid));
 144                 System.out.println("\tisVisible():"
 145                         + new Boolean(win.isVisible()));
 146                 System.out.println("\tisShowing():"
 147                         + new Boolean(win.isShowing()));
 148                 System.out.println("\tisFocused():"
 149                         + new Boolean(win.isFocused()));
 150             }
 151         }
 152 
 153     }
 154 
 155     synchronized void doOpen() {
 156         opened = true;
 157         notify();
 158     }
 159 
 160     synchronized void doActivate() {
 161         activated = true;
 162         notify();
 163     }
 164 
 165     synchronized void doGainedFocus() {
 166         focused = true;
 167         notify();
 168     }
 169 
 170     public boolean isValid() {
 171         return valid;
 172     }
 173 
 174     public boolean isClear() {
 175         return valid;
 176     }
 177 
 178     volatile static Robot robot = null;
 179 
 180     // utility function that waits until a Component is shown with the
 181     // appropriate color
 182     public static boolean waitForColor(Component c,
 183             Color expected) throws AWTException,
 184             InterruptedException {
 185         Dimension dim = c.getSize();
 186         int xOff = dim.width / 2;
 187         int yOff = dim.height / 2;
 188         return waitForColor(c, xOff, yOff, expected);
 189     }
 190 
 191     // utility function that waits for 5 seconds for Component to be shown with
 192     // the appropriate color
 193     public static boolean waitForColor(Component c,
 194             int xOff,
 195             int yOff,
 196             Color expected) throws AWTException, InterruptedException {
 197         return waitForColor(c, xOff, yOff, expected, 5000L);
 198     }
 199 
 200     // utility function that waits until a Component is up with the appropriate
 201     // color
 202     public static boolean waitForColor(Component c,
 203             int xOff,
 204             int yOff,
 205             Color expected,
 206             long timeout) throws AWTException, InterruptedException {
 207         Point p = c.getLocationOnScreen();
 208         int x = (int) p.getX() + xOff;
 209         int y = (int) p.getY() + yOff;
 210         return waitForColor(x, y, expected, timeout);
 211     }
 212 
 213     // utility function that waits until specific screen coords have the
 214     // appropriate color
 215     public static boolean waitForColor(int locX,
 216             int locY,
 217             Color expected,
 218             long timeout) throws AWTException, InterruptedException {
 219         if (robot == null) {
 220             robot = new Robot();
 221         }
 222 
 223         long endtime = System.currentTimeMillis() + timeout;
 224         while (endtime > System.currentTimeMillis()) {
 225             if (colorMatch(robot.getPixelColor(locX, locY), expected)) {
 226                 return true;
 227             }
 228             Thread.sleep(50);
 229         }
 230 
 231         return false;
 232     }
 233 
 234     // utility function that asserts that two colors are similar to each other
 235     public static void assertColorEquals(final String message,
 236             final Color actual,
 237             final Color expected) {
 238         System.out.println("actual color: " + actual);
 239         System.out.println("expect color: " + expected);
 240         //Assert.assertTrue(message, colorMatch(actual, expected));
 241     }
 242 
 243     // determines if two colors are close in hue and brightness
 244     public static boolean colorMatch(final Color actual, final Color expected) {
 245         final float[] actualHSB = getHSB(actual);
 246         final float[] expectedHSB = getHSB(expected);
 247 
 248         final float actualHue = actualHSB[0];
 249         final float expectedHue = expectedHSB[0];
 250         final boolean hueMatched = closeMatchHue(actualHue, expectedHue, 0.17f);
 251         //System.out.println("hueMatched? " + hueMatched);
 252         final float actualBrightness = actualHSB[2];
 253         final float expectedBrightness = expectedHSB[2];
 254         final boolean brightnessMatched = closeMatch(actualBrightness,
 255                 expectedBrightness, 0.15f);
 256         //System.out.println("brightnessMatched? " + brightnessMatched);
 257 
 258         // check to see if the brightness was so low or so high that the hue
 259         // got clamped to red
 260         if (brightnessMatched && !hueMatched) {
 261             return (expectedBrightness < 0.15f);
 262         }
 263 
 264         return brightnessMatched && hueMatched;
 265     }
 266 
 267     static float[] getHSB(final Color color) {
 268         final float[] hsb = new float[3];
 269         Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), hsb);
 270         return hsb;
 271     }
 272 
 273     // matches hues from 0.0 to 1.0, accounting for wrap-around at the 1.0/0.0
 274     // boundry
 275     static boolean closeMatchHue(final float actual,
 276             final float expected,
 277             final float tolerance) {
 278         if (closeMatch(actual, expected, tolerance)) {
 279             return true;
 280         }
 281 
 282         // all that remains is the overflow and underflow cases
 283         final float expectedHigh = expected + tolerance;
 284         final float expectedLow = expected - tolerance;
 285 
 286         if (expectedHigh > 1.0f) {
 287             // expected is too high, and actual was too low
 288             //System.out.println("\thue expected too high, actual too low");
 289             return closeMatch(actual + 0.5f, expected - 0.5f, tolerance);
 290         }
 291 
 292         if (expectedLow < 0.0f) {
 293             // expected is too low, and actual was too high
 294             //System.out.println("\thue expected too low, actual too high");
 295             return closeMatch(actual - 0.5f, expected + 0.5f, tolerance);
 296         }
 297 
 298         //System.out.println("\tcloseMatchHue? " + false);
 299         return false;
 300     }
 301 
 302     static boolean closeMatch(final float actual,
 303             final float expected,
 304             final float tolerance) {
 305         return (expected + tolerance) > actual && (expected - tolerance) < actual;
 306     }
 307 }