1 /*
   2  * Copyright (c) 2014, 2018, 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 import java.awt.*;
  26 import java.awt.event.*;
  27 import java.lang.reflect.InvocationTargetException;
  28 import javax.swing.SwingUtilities;
  29 import java.io.*;
  30 
  31 /**
  32  * AWT Mixing test for HierarchyBoundsListener ancestors.
  33  * <p>See <a href="https://bugs.openjdk.java.net/browse/JDK-6768230">CR6768230</a> for details.
  34  */
  35 /*
  36  * @test
  37  * @key headful
  38  * @bug 6768230
  39  * @summary Mixing test for HierarchyBoundsListener ancestors
  40  * @build FrameBorderCounter
  41  * @run main HierarchyBoundsListenerMixingTest
  42  */
  43 public class HierarchyBoundsListenerMixingTest {
  44 
  45     protected void prepareControls() {
  46         dummy = new Frame();
  47         dummy.setSize(100, 100);
  48         dummy.setLocation(0, 350);
  49         dummy.setVisible(true);
  50 
  51         frame = new Frame("Test Frame");
  52         frame.setLayout(new FlowLayout());
  53 
  54         panel = new Panel();
  55         button = new Button("Button");
  56         label = new Label("Label");
  57         list = new List();
  58         list.add("One");
  59         list.add("Two");
  60         list.add("Three");
  61         choice = new Choice();
  62         choice.add("Red");
  63         choice.add("Orange");
  64         choice.add("Yellow");
  65         checkbox = new Checkbox("Checkbox");
  66         scrollbar = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 255);
  67         textfield = new TextField(15);
  68         textarea = new TextArea(5, 15);
  69 
  70         components = new Component[] {
  71             panel, button, label, list, choice, checkbox, scrollbar, textfield, textarea
  72         };
  73         ancestorResized = new boolean[components.length];
  74         ancestorMoved = new boolean[components.length];
  75 
  76         frame.addWindowListener(new WindowAdapter() {
  77             @Override
  78             public void windowClosing(WindowEvent event) {
  79                 System.err.println("User closed the window");
  80             }
  81         });
  82 
  83         HierarchyBoundsListener listener = new HierarchyBoundsListenerImpl();
  84         for (int i = 0; i < components.length; i++) {
  85             components[i].addHierarchyBoundsListener(listener);
  86             frame.add(components[i]);
  87         }
  88         frame.setBounds(100, 100, 300, 300);
  89         frame.setVisible(true);
  90     }
  91 
  92     private int frameBorderCounter() {
  93         String JAVA_HOME = System.getProperty("java.home");
  94 
  95         try {
  96             Process p = Runtime.getRuntime().exec(JAVA_HOME + "/bin/java FrameBorderCounter");
  97             try {
  98                 p.waitFor();
  99             } catch (InterruptedException e) {
 100                 e.printStackTrace();
 101                 throw new RuntimeException(e);
 102             }
 103             if (p.exitValue() != 0) {
 104                 throw new RuntimeException("FrameBorderCounter exited with not null code!\n" + readInputStream(p.getErrorStream()));
 105             }
 106             return Integer.parseInt(readInputStream(p.getInputStream()).trim());
 107         } catch (IOException e) {
 108             e.printStackTrace();
 109             throw new RuntimeException(e);
 110         }
 111     }
 112 
 113     private String readInputStream(InputStream is) throws IOException {
 114         byte[] buffer = new byte[4096];
 115         int len = 0;
 116         StringBuilder sb = new StringBuilder();
 117         try (InputStreamReader isr = new InputStreamReader(is)) {
 118             while ((len = is.read(buffer)) > 0) {
 119                 sb.append(new String(buffer, 0, len));
 120             }
 121         }
 122         return sb.toString();
 123     }
 124 
 125     protected boolean performTest() {
 126         int BORDER_SHIFT = frameBorderCounter();
 127         BORDER_SHIFT = Math.abs(BORDER_SHIFT) == 1 ? BORDER_SHIFT : BORDER_SHIFT / 2;
 128         Robot robot = null;
 129         try {
 130             robot = new Robot();
 131             Thread.sleep(delay * 10);
 132         } catch (Exception e) {
 133             e.printStackTrace();
 134             throw new RuntimeException("Robot creation exception.");
 135         }
 136 
 137         robot.mouseMove((int) components[0].getLocationOnScreen().x + components[0].getSize().width / 2,
 138                         (int) components[0].getLocationOnScreen().y + components[0].getSize().height / 2);
 139         robot.delay(delay);
 140         robot.mousePress(InputEvent.BUTTON1_MASK);
 141         robot.delay(delay);
 142         robot.mouseRelease(InputEvent.BUTTON1_MASK);
 143         robot.delay(delay);
 144 
 145         resetValues();
 146         try {
 147             EventQueue.invokeAndWait(new Runnable() {
 148                 public void run() {
 149                     frame.setSize(new Dimension(frame.getSize().width + 10, frame.getSize().height + 10));
 150                     frame.invalidate();
 151                     frame.validate();
 152                 }
 153             });
 154         } catch (Exception e) {
 155             e.printStackTrace();
 156             passed = false;
 157         }
 158         if (! resizeTriggered) {
 159             synchronized (resizeLock) {
 160                 try {
 161                     resizeLock.wait(delay * 10);
 162                 } catch (Exception e) {
 163                 }
 164             }
 165         }
 166         for (int i = 0; i < components.length; i++) {
 167             if (! ancestorResized[i]) {
 168                 System.err.println("FAIL: Frame resized using API call. " +
 169                                    "Ancestor resized event did not occur for " + components[i].getClass());
 170                 passed = false;
 171             }
 172         }
 173         if (moveCount > 0) {
 174             System.err.println("FAIL: Ancestor moved event occured when Frame resized using API");
 175             passed = false;
 176         }
 177         robot.delay(delay * 5);
 178 
 179         resetValues();
 180         int x = (int) frame.getLocationOnScreen().x;
 181         int y = (int) frame.getLocationOnScreen().y;
 182         int w = frame.getSize().width;
 183         int h = frame.getSize().height;
 184 
 185         robot.mouseMove(x + w + BORDER_SHIFT, y + h / 2);
 186         robot.delay(delay);
 187         robot.mousePress(InputEvent.BUTTON1_MASK);
 188         robot.delay(delay);
 189         for (int i = 0; i < 20; i++) {
 190             robot.mouseMove(x + w + i + BORDER_SHIFT, y + h / 2);
 191             robot.delay(50);
 192         }
 193         robot.delay(delay);
 194         robot.mouseRelease(InputEvent.BUTTON1_MASK);
 195 
 196         if (! resizeTriggered) {
 197             synchronized (resizeLock) {
 198                 try {
 199                     resizeLock.wait(delay * 10);
 200                 } catch (Exception e) {
 201                 }
 202             }
 203         }
 204 
 205         for (int i = 0; i < components.length; i++) {
 206             if (! ancestorResized[i]) {
 207                 System.err.println("FAIL: Frame resized using mouse action. " +
 208                                    "Ancestor resized event did not occur for " +
 209                                    components[i].getClass());
 210                 passed = false;
 211             }
 212         }
 213         if (moveCount > 0) {
 214             System.err.println("FAIL: Ancestor moved event occured when Frame resized using mouse");
 215             passed = false;
 216         }
 217 
 218         resetValues();
 219         try {
 220             EventQueue.invokeAndWait(new Runnable() {
 221                 public void run() {
 222                     frame.setLocation(frame.getLocation().x + 20, frame.getLocation().y + 20);
 223                     frame.invalidate();
 224                     frame.validate();
 225                 }
 226             });
 227         } catch (Exception e) {
 228             e.printStackTrace();
 229             passed = false;
 230         }
 231         if (! moveTriggered) {
 232             synchronized (moveLock) {
 233                 try {
 234                     moveLock.wait(delay * 10);
 235                 } catch (Exception e) {
 236                 }
 237             }
 238         }
 239         for (int i = 0; i < components.length; i++) {
 240             if (! ancestorMoved[i]) {
 241                 System.err.println("FAIL: Frame moved using API call. " +
 242                                    "Ancestor moved event did not occur for " + components[i].getClass());
 243                 passed = false;
 244             }
 245         }
 246         if (resizeCount > 0) {
 247             System.err.println("FAIL: Ancestor resized event occured when Frame moved using API");
 248             passed = false;
 249         }
 250         robot.delay(delay * 10);
 251 
 252         resetValues();
 253         x = (int) frame.getLocationOnScreen().x;
 254         y = (int) frame.getLocationOnScreen().y;
 255         w = frame.getSize().width;
 256         h = frame.getSize().height;
 257 
 258         //Click on the dummy frame so that the test frame loses focus. This is to workaround
 259         //a bug in Linux AS.
 260         robot.mouseMove((int) dummy.getLocationOnScreen().x + dummy.getSize().width / 2,
 261                         (int) dummy.getLocationOnScreen().y + dummy.getSize().height / 2);
 262         robot.delay(delay);
 263         robot.mousePress(InputEvent.BUTTON1_MASK);
 264         robot.delay(delay);
 265         robot.mouseRelease(InputEvent.BUTTON1_MASK);
 266         robot.delay(delay);
 267 
 268         robot.mouseMove(x + w / 2, y + 10);
 269         robot.delay(delay);
 270         robot.mousePress(InputEvent.BUTTON1_MASK);
 271         robot.delay(delay);
 272         for (int i = 1; i <= 20; i++) {
 273             robot.mouseMove(x + w / 2 + i, y + 10);
 274             robot.delay(50);
 275         }
 276         robot.delay(delay);
 277         robot.mouseRelease(InputEvent.BUTTON1_MASK);
 278 
 279         if (! moveTriggered) {
 280             synchronized (moveLock) {
 281                 try {
 282                     moveLock.wait(delay * 10);
 283                 } catch (Exception e) {
 284                 }
 285             }
 286         }
 287 
 288         for (int i = 0; i < components.length; i++) {
 289             if (! ancestorMoved[i]) {
 290                 System.err.println("FAIL: Frame moved using mouse action. " +
 291                                    "Ancestor moved event did not occur for " + components[i].getClass());
 292                 passed = false;
 293             }
 294         }
 295         if (resizeCount > 0) {
 296             System.err.println("FAIL: Ancestor resized event occured when Frame moved using mouse");
 297             passed = false;
 298         }
 299 
 300         return passed;
 301     }
 302 
 303     private void resetValues() {
 304         moveTriggered = false;
 305         resizeTriggered = false;
 306         moveCount = 0;
 307         resizeCount = 0;
 308         for (int i = 0; i < ancestorResized.length; i++) {
 309             ancestorResized[i] = false;
 310             ancestorMoved[i] = false;
 311         }
 312     }
 313 
 314     private void keyType(int key, Robot robot) throws Exception {
 315         robot.keyPress(key);
 316         robot.delay(keyDelay);
 317         robot.keyRelease(key);
 318         robot.delay(keyDelay);
 319     }
 320 
 321     class HierarchyBoundsListenerImpl implements HierarchyBoundsListener {
 322         // checks for Ancestor_Moved events
 323         public void ancestorMoved(HierarchyEvent ce) {
 324             if (check) {
 325                 System.out.println("Moved " + ce.getComponent());
 326             }
 327             for (int i = 0; i < components.length; i++) {
 328                 if (components[i].equals(ce.getComponent())) {
 329                     //setting this array for purpose of checking ancestor_moved.
 330                     ancestorMoved[i] = true;
 331                     moveCount++;
 332                     if (moveCount == components.length) {
 333                         moveTriggered = true;
 334                         synchronized (moveLock) {
 335                             try {
 336                                 moveLock.notifyAll();
 337                             } catch (Exception e) {
 338                             }
 339                         }
 340                     }
 341                 }
 342             }
 343         }
 344         // checks for Ancestor_Moved events
 345         public void ancestorResized(HierarchyEvent ce) {
 346             if (check) {
 347                 System.out.println("Resized " + ce.getComponent());
 348             }
 349             for (int i = 0; i < components.length; i++) {
 350                 if (components[i].equals(ce.getComponent())) {
 351                     if (! frame.equals(ce.getChanged()) || ce.getChangedParent() != null) {
 352                         System.err.println("FAIL: Invalid value of HierarchyEvent.getXXX");
 353                         System.err.println("ce.getChanged() : " + ce.getChanged());
 354                         System.err.println("ce.getChangedParent() : " + ce.getChangedParent());
 355                         passed = false;
 356                     }
 357                     //setting this array for purpose of checking ancestor_resized
 358                     ancestorResized[i] = true;
 359                     resizeCount++;
 360                     if (resizeCount == components.length) {
 361                         resizeTriggered = true;
 362                         synchronized (resizeLock) {
 363                             try {
 364                                 resizeLock.notifyAll();
 365                             } catch (Exception e) {
 366                             }
 367                         }
 368                     }
 369                 }
 370             }
 371         }
 372     }
 373 
 374     private Frame frame, dummy;
 375     private Panel panel;
 376     private Button button;
 377     private Label label;
 378     private List list;
 379     private Choice choice;
 380     private Checkbox checkbox;
 381     private Scrollbar scrollbar;
 382     private TextField textfield;
 383     private TextArea textarea;
 384     private Component[] components;
 385     private boolean[] ancestorResized;
 386     private boolean[] ancestorMoved;
 387 
 388     private int delay = 500;
 389     private int keyDelay = 50;
 390     private int moveCount = 0;
 391     private int resizeCount = 0;
 392 
 393     private boolean passed = true;
 394     private volatile boolean moveTriggered = false;
 395     private volatile boolean resizeTriggered = false;
 396     private final Object moveLock = new Object();
 397     private final Object resizeLock = new Object();
 398 
 399     private boolean check = false;
 400 
 401     private void invoke() throws InterruptedException {
 402         try {
 403             SwingUtilities.invokeAndWait(new Runnable() {
 404 
 405                 public void run() {
 406                     prepareControls();
 407                 }
 408             });
 409             try {
 410                 Thread.sleep(1000); // wait for graphic effects on systems like Win7
 411             } catch (InterruptedException ex) {
 412             }
 413             if (!performTest()) {
 414                 fail("Test failed");
 415             }
 416         } catch (InvocationTargetException ex) {
 417             fail(ex.getMessage());
 418         }
 419     }
 420 
 421     /*****************************************************
 422      * Standard Test Machinery Section
 423      * DO NOT modify anything in this section -- it's a
 424      * standard chunk of code which has all of the
 425      * synchronisation necessary for the test harness.
 426      * By keeping it the same in all tests, it is easier
 427      * to read and understand someone else's test, as
 428      * well as insuring that all tests behave correctly
 429      * with the test harness.
 430      * There is a section following this for test-
 431      * classes
 432      ******************************************************/
 433     private static void init() throws InterruptedException {
 434         //*** Create instructions for the user here ***
 435         //System.setProperty("sun.awt.disableMixing", "true");
 436 
 437         HierarchyBoundsListenerMixingTest instance = new HierarchyBoundsListenerMixingTest();
 438 
 439         instance.invoke();
 440 
 441         pass();
 442     }//End  init()
 443     private static boolean theTestPassed = false;
 444     private static boolean testGeneratedInterrupt = false;
 445     private static String failureMessage = "";
 446     private static Thread mainThread = null;
 447     private static int sleepTime = 300000;
 448 
 449     // Not sure about what happens if multiple of this test are
 450     //  instantiated in the same VM.  Being static (and using
 451     //  static vars), it aint gonna work.  Not worrying about
 452     //  it for now.
 453     public static void main(String args[]) throws InterruptedException {
 454         mainThread = Thread.currentThread();
 455         try {
 456             init();
 457         } catch (TestPassedException e) {
 458             //The test passed, so just return from main and harness will
 459             // interepret this return as a pass
 460             return;
 461         }
 462         //At this point, neither test pass nor test fail has been
 463         // called -- either would have thrown an exception and ended the
 464         // test, so we know we have multiple threads.
 465 
 466         //Test involves other threads, so sleep and wait for them to
 467         // called pass() or fail()
 468         try {
 469             Thread.sleep(sleepTime);
 470             //Timed out, so fail the test
 471             throw new RuntimeException("Timed out after " + sleepTime / 1000 + " seconds");
 472         } catch (InterruptedException e) {
 473             //The test harness may have interrupted the test.  If so, rethrow the exception
 474             // so that the harness gets it and deals with it.
 475             if (!testGeneratedInterrupt) {
 476                 throw e;
 477             }
 478 
 479             //reset flag in case hit this code more than once for some reason (just safety)
 480             testGeneratedInterrupt = false;
 481 
 482             if (theTestPassed == false) {
 483                 throw new RuntimeException(failureMessage);
 484             }
 485         }
 486 
 487     }//main
 488 
 489     public static synchronized void setTimeoutTo(int seconds) {
 490         sleepTime = seconds * 1000;
 491     }
 492 
 493     public static synchronized void pass() {
 494         System.out.println("The test passed.");
 495         System.out.println("The test is over, hit  Ctl-C to stop Java VM");
 496         //first check if this is executing in main thread
 497         if (mainThread == Thread.currentThread()) {
 498             //Still in the main thread, so set the flag just for kicks,
 499             // and throw a test passed exception which will be caught
 500             // and end the test.
 501             theTestPassed = true;
 502             throw new TestPassedException();
 503         }
 504         theTestPassed = true;
 505         testGeneratedInterrupt = true;
 506         mainThread.interrupt();
 507     }//pass()
 508 
 509     public static synchronized void fail() {
 510         //test writer didn't specify why test failed, so give generic
 511         fail("it just plain failed! :-)");
 512     }
 513 
 514     public static synchronized void fail(String whyFailed) {
 515         System.out.println("The test failed: " + whyFailed);
 516         System.out.println("The test is over, hit  Ctl-C to stop Java VM");
 517         //check if this called from main thread
 518         if (mainThread == Thread.currentThread()) {
 519             //If main thread, fail now 'cause not sleeping
 520             throw new RuntimeException(whyFailed);
 521         }
 522         theTestPassed = false;
 523         testGeneratedInterrupt = true;
 524         failureMessage = whyFailed;
 525         mainThread.interrupt();
 526     }//fail()
 527 }// class LWComboBox
 528 class TestPassedException extends RuntimeException {
 529 }