1 /*
   2  * Copyright (c) 2014, 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 @bug 6768230
  38 @summary Mixing test for HierarchyBoundsListener ancestors
  39 @build FrameBorderCounter
  40 @run main HierarchyBoundsListenerMixingTest
  41  */
  42 public class HierarchyBoundsListenerMixingTest {
  43 
  44     protected void prepareControls() {
  45         dummy = new Frame();
  46         dummy.setSize(100, 100);
  47         dummy.setLocation(0, 350);
  48         dummy.setVisible(true);
  49 
  50         frame = new Frame("Test Frame");
  51         frame.setLayout(new FlowLayout());
  52 
  53         panel = new Panel();
  54         button = new Button("Button");
  55         label = new Label("Label");
  56         list = new List();
  57         list.add("One");
  58         list.add("Two");
  59         list.add("Three");
  60         choice = new Choice();
  61         choice.add("Red");
  62         choice.add("Orange");
  63         choice.add("Yellow");
  64         checkbox = new Checkbox("Checkbox");
  65         scrollbar = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 255);
  66         textfield = new TextField(15);
  67         textarea = new TextArea(5, 15);
  68 
  69         components = new Component[] {
  70             panel, button, label, list, choice, checkbox, scrollbar, textfield, textarea
  71         };
  72         ancestorResized = new boolean[components.length];
  73         ancestorMoved = new boolean[components.length];
  74 
  75         frame.addWindowListener(new WindowAdapter() {
  76             @Override
  77             public void windowClosing(WindowEvent event) {
  78                 System.err.println("User closed the window");
  79             }
  80         });
  81 
  82         HierarchyBoundsListener listener = new HierarchyBoundsListenerImpl();
  83         for (int i = 0; i < components.length; i++) {
  84             components[i].addHierarchyBoundsListener(listener);
  85             frame.add(components[i]);
  86         }
  87         frame.setSize(300, 300);
  88         frame.setVisible(true);
  89     }
  90 
  91     private int frameBorderCounter() {
  92         String JAVA_HOME = System.getProperty("java.home");
  93 
  94         try {
  95             Process p = Runtime.getRuntime().exec(JAVA_HOME + "/bin/java FrameBorderCounter");
  96             try {
  97                 p.waitFor();
  98             } catch (InterruptedException e) {
  99                 e.printStackTrace();
 100                 throw new RuntimeException(e);
 101             }
 102             if (p.exitValue() != 0) {
 103                 throw new RuntimeException("FrameBorderCounter exited with not null code!\n" + readInputStream(p.getErrorStream()));
 104             }
 105             return Integer.parseInt(readInputStream(p.getInputStream()).trim());
 106         } catch (IOException e) {
 107             e.printStackTrace();
 108             throw new RuntimeException(e);
 109         }
 110     }
 111 
 112     private String readInputStream(InputStream is) throws IOException {
 113         byte[] buffer = new byte[4096];
 114         int len = 0;
 115         StringBuilder sb = new StringBuilder();
 116         try (InputStreamReader isr = new InputStreamReader(is)) {
 117             while ((len = is.read(buffer)) > 0) {
 118                 sb.append(new String(buffer, 0, len));
 119             }
 120         }
 121         return sb.toString();
 122     }
 123 
 124     protected boolean performTest() {
 125         int BORDER_SHIFT = frameBorderCounter();
 126         BORDER_SHIFT = Math.abs(BORDER_SHIFT) == 1 ? BORDER_SHIFT : BORDER_SHIFT / 2;
 127         Robot robot = null;
 128         try {
 129             robot = new Robot();
 130             Thread.sleep(delay * 10);
 131         } catch (Exception e) {
 132             e.printStackTrace();
 133             throw new RuntimeException("Robot creation exception.");
 134         }
 135 
 136         robot.mouseMove((int) components[0].getLocationOnScreen().x + components[0].getSize().width / 2,
 137                         (int) components[0].getLocationOnScreen().y + components[0].getSize().height / 2);
 138         robot.delay(delay);
 139         robot.mousePress(InputEvent.BUTTON1_MASK);
 140         robot.delay(delay);
 141         robot.mouseRelease(InputEvent.BUTTON1_MASK);
 142         robot.delay(delay);
 143 
 144         resetValues();
 145         try {
 146             EventQueue.invokeAndWait(new Runnable() {
 147                 public void run() {
 148                     frame.setSize(new Dimension(frame.getSize().width + 10, frame.getSize().height + 10));
 149                     frame.invalidate();
 150                     frame.validate();
 151                 }
 152             });
 153         } catch (Exception e) {
 154             e.printStackTrace();
 155             passed = false;
 156         }
 157         if (! resizeTriggered) {
 158             synchronized (resizeLock) {
 159                 try {
 160                     resizeLock.wait(delay * 10);
 161                 } catch (Exception e) {
 162                 }
 163             }
 164         }
 165         for (int i = 0; i < components.length; i++) {
 166             if (! ancestorResized[i]) {
 167                 System.err.println("FAIL: Frame resized using API call. " +
 168                                    "Ancestor resized event did not occur for " + components[i].getClass());
 169                 passed = false;
 170             }
 171         }
 172         if (moveCount > 0) {
 173             System.err.println("FAIL: Ancestor moved event occured when Frame resized using API");
 174             passed = false;
 175         }
 176         robot.delay(delay * 5);
 177 
 178         resetValues();
 179         int x = (int) frame.getLocationOnScreen().x;
 180         int y = (int) frame.getLocationOnScreen().y;
 181         int w = frame.getSize().width;
 182         int h = frame.getSize().height;
 183 
 184         robot.mouseMove(x + w + BORDER_SHIFT, y + h / 2);
 185         robot.delay(delay);
 186         robot.mousePress(InputEvent.BUTTON1_MASK);
 187         robot.delay(delay);
 188         for (int i = 0; i < 20; i++) {
 189             robot.mouseMove(x + w + i + BORDER_SHIFT, y + h / 2);
 190             robot.delay(50);
 191         }
 192         robot.delay(delay);
 193         robot.mouseRelease(InputEvent.BUTTON1_MASK);
 194 
 195         if (! resizeTriggered) {
 196             synchronized (resizeLock) {
 197                 try {
 198                     resizeLock.wait(delay * 10);
 199                 } catch (Exception e) {
 200                 }
 201             }
 202         }
 203 
 204         for (int i = 0; i < components.length; i++) {
 205             if (! ancestorResized[i]) {
 206                 System.err.println("FAIL: Frame resized using mouse action. " +
 207                                    "Ancestor resized event did not occur for " +
 208                                    components[i].getClass());
 209                 passed = false;
 210             }
 211         }
 212         if (moveCount > 0) {
 213             System.err.println("FAIL: Ancestor moved event occured when Frame resized using mouse");
 214             passed = false;
 215         }
 216 
 217         resetValues();
 218         try {
 219             EventQueue.invokeAndWait(new Runnable() {
 220                 public void run() {
 221                     frame.setLocation(frame.getLocation().x + 20, frame.getLocation().y + 20);
 222                     frame.invalidate();
 223                     frame.validate();
 224                 }
 225             });
 226         } catch (Exception e) {
 227             e.printStackTrace();
 228             passed = false;
 229         }
 230         if (! moveTriggered) {
 231             synchronized (moveLock) {
 232                 try {
 233                     moveLock.wait(delay * 10);
 234                 } catch (Exception e) {
 235                 }
 236             }
 237         }
 238         for (int i = 0; i < components.length; i++) {
 239             if (! ancestorMoved[i]) {
 240                 System.err.println("FAIL: Frame moved using API call. " +
 241                                    "Ancestor moved event did not occur for " + components[i].getClass());
 242                 passed = false;
 243             }
 244         }
 245         if (resizeCount > 0) {
 246             System.err.println("FAIL: Ancestor resized event occured when Frame moved using API");
 247             passed = false;
 248         }
 249         robot.delay(delay * 10);
 250 
 251         resetValues();
 252         x = (int) frame.getLocationOnScreen().x;
 253         y = (int) frame.getLocationOnScreen().y;
 254         w = frame.getSize().width;
 255         h = frame.getSize().height;
 256 
 257         //Click on the dummy frame so that the test frame loses focus. This is to workaround
 258         //a bug in Linux AS.
 259         robot.mouseMove((int) dummy.getLocationOnScreen().x + dummy.getSize().width / 2,
 260                         (int) dummy.getLocationOnScreen().y + dummy.getSize().height / 2);
 261         robot.delay(delay);
 262         robot.mousePress(InputEvent.BUTTON1_MASK);
 263         robot.delay(delay);
 264         robot.mouseRelease(InputEvent.BUTTON1_MASK);
 265         robot.delay(delay);
 266 
 267         robot.mouseMove(x + w / 2, y + 10);
 268         robot.delay(delay);
 269         robot.mousePress(InputEvent.BUTTON1_MASK);
 270         robot.delay(delay);
 271         for (int i = 1; i <= 20; i++) {
 272             robot.mouseMove(x + w / 2 + i, y + 10);
 273             robot.delay(50);
 274         }
 275         robot.delay(delay);
 276         robot.mouseRelease(InputEvent.BUTTON1_MASK);
 277 
 278         if (! moveTriggered) {
 279             synchronized (moveLock) {
 280                 try {
 281                     moveLock.wait(delay * 10);
 282                 } catch (Exception e) {
 283                 }
 284             }
 285         }
 286 
 287         for (int i = 0; i < components.length; i++) {
 288             if (! ancestorMoved[i]) {
 289                 System.err.println("FAIL: Frame moved using mouse action. " +
 290                                    "Ancestor moved event did not occur for " + components[i].getClass());
 291                 passed = false;
 292             }
 293         }
 294         if (resizeCount > 0) {
 295             System.err.println("FAIL: Ancestor resized event occured when Frame moved using mouse");
 296             passed = false;
 297         }
 298 
 299         return passed;
 300     }
 301 
 302     private void resetValues() {
 303         moveTriggered = false;
 304         resizeTriggered = false;
 305         moveCount = 0;
 306         resizeCount = 0;
 307         for (int i = 0; i < ancestorResized.length; i++) {
 308             ancestorResized[i] = false;
 309             ancestorMoved[i] = false;
 310         }
 311     }
 312 
 313     private void keyType(int key, Robot robot) throws Exception {
 314         robot.keyPress(key);
 315         robot.delay(keyDelay);
 316         robot.keyRelease(key);
 317         robot.delay(keyDelay);
 318     }
 319 
 320     class HierarchyBoundsListenerImpl implements HierarchyBoundsListener {
 321         // checks for Ancestor_Moved events
 322         public void ancestorMoved(HierarchyEvent ce) {
 323             if (check) {
 324                 System.out.println("Moved " + ce.getComponent());
 325             }
 326             for (int i = 0; i < components.length; i++) {
 327                 if (components[i].equals(ce.getComponent())) {
 328                     //setting this array for purpose of checking ancestor_moved.
 329                     ancestorMoved[i] = true;
 330                     moveCount++;
 331                     if (moveCount == components.length) {
 332                         moveTriggered = true;
 333                         synchronized (moveLock) {
 334                             try {
 335                                 moveLock.notifyAll();
 336                             } catch (Exception e) {
 337                             }
 338                         }
 339                     }
 340                 }
 341             }
 342         }
 343         // checks for Ancestor_Moved events
 344         public void ancestorResized(HierarchyEvent ce) {
 345             if (check) {
 346                 System.out.println("Resized " + ce.getComponent());
 347             }
 348             for (int i = 0; i < components.length; i++) {
 349                 if (components[i].equals(ce.getComponent())) {
 350                     if (! frame.equals(ce.getChanged()) || ce.getChangedParent() != null) {
 351                         System.err.println("FAIL: Invalid value of HierarchyEvent.getXXX");
 352                         System.err.println("ce.getChanged() : " + ce.getChanged());
 353                         System.err.println("ce.getChangedParent() : " + ce.getChangedParent());
 354                         passed = false;
 355                     }
 356                     //setting this array for purpose of checking ancestor_resized
 357                     ancestorResized[i] = true;
 358                     resizeCount++;
 359                     if (resizeCount == components.length) {
 360                         resizeTriggered = true;
 361                         synchronized (resizeLock) {
 362                             try {
 363                                 resizeLock.notifyAll();
 364                             } catch (Exception e) {
 365                             }
 366                         }
 367                     }
 368                 }
 369             }
 370         }
 371     }
 372 
 373     private Frame frame, dummy;
 374     private Panel panel;
 375     private Button button;
 376     private Label label;
 377     private List list;
 378     private Choice choice;
 379     private Checkbox checkbox;
 380     private Scrollbar scrollbar;
 381     private TextField textfield;
 382     private TextArea textarea;
 383     private Component[] components;
 384     private boolean[] ancestorResized;
 385     private boolean[] ancestorMoved;
 386 
 387     private int delay = 500;
 388     private int keyDelay = 50;
 389     private int moveCount = 0;
 390     private int resizeCount = 0;
 391 
 392     private boolean passed = true;
 393     private boolean moveTriggered = false;
 394     private boolean resizeTriggered = false;
 395     private final Object moveLock = new Object();
 396     private final Object resizeLock = new Object();
 397 
 398     private boolean check = false;
 399 
 400     private void invoke() throws InterruptedException {
 401         try {
 402             SwingUtilities.invokeAndWait(new Runnable() {
 403 
 404                 public void run() {
 405                     prepareControls();
 406                 }
 407             });
 408             try {
 409                 Thread.sleep(1000); // wait for graphic effects on systems like Win7
 410             } catch (InterruptedException ex) {
 411             }
 412             if (!performTest()) {
 413                 fail("Test failed");
 414             }
 415         } catch (InvocationTargetException ex) {
 416             fail(ex.getMessage());
 417         }
 418     }
 419 
 420     /*****************************************************
 421      * Standard Test Machinery Section
 422      * DO NOT modify anything in this section -- it's a
 423      * standard chunk of code which has all of the
 424      * synchronisation necessary for the test harness.
 425      * By keeping it the same in all tests, it is easier
 426      * to read and understand someone else's test, as
 427      * well as insuring that all tests behave correctly
 428      * with the test harness.
 429      * There is a section following this for test-
 430      * classes
 431      ******************************************************/
 432     private static void init() throws InterruptedException {
 433         //*** Create instructions for the user here ***
 434         //System.setProperty("sun.awt.disableMixing", "true");
 435 
 436         String[] instructions = {
 437             "This is an AUTOMATIC test, simply wait until it is done.",
 438             "The result (passed or failed) will be shown in the",
 439             "message window below."
 440         };
 441         Sysout.createDialog();
 442         Sysout.printInstructions(instructions);
 443 
 444         HierarchyBoundsListenerMixingTest instance = new HierarchyBoundsListenerMixingTest();
 445 
 446         instance.invoke();
 447 
 448         pass();
 449     }//End  init()
 450     private static boolean theTestPassed = false;
 451     private static boolean testGeneratedInterrupt = false;
 452     private static String failureMessage = "";
 453     private static Thread mainThread = null;
 454     private static int sleepTime = 300000;
 455 
 456     // Not sure about what happens if multiple of this test are
 457     //  instantiated in the same VM.  Being static (and using
 458     //  static vars), it aint gonna work.  Not worrying about
 459     //  it for now.
 460     public static void main(String args[]) throws InterruptedException {
 461         mainThread = Thread.currentThread();
 462         try {
 463             init();
 464         } catch (TestPassedException e) {
 465             //The test passed, so just return from main and harness will
 466             // interepret this return as a pass
 467             return;
 468         }
 469         //At this point, neither test pass nor test fail has been
 470         // called -- either would have thrown an exception and ended the
 471         // test, so we know we have multiple threads.
 472 
 473         //Test involves other threads, so sleep and wait for them to
 474         // called pass() or fail()
 475         try {
 476             Thread.sleep(sleepTime);
 477             //Timed out, so fail the test
 478             throw new RuntimeException("Timed out after " + sleepTime / 1000 + " seconds");
 479         } catch (InterruptedException e) {
 480             //The test harness may have interrupted the test.  If so, rethrow the exception
 481             // so that the harness gets it and deals with it.
 482             if (!testGeneratedInterrupt) {
 483                 throw e;
 484             }
 485 
 486             //reset flag in case hit this code more than once for some reason (just safety)
 487             testGeneratedInterrupt = false;
 488 
 489             if (theTestPassed == false) {
 490                 throw new RuntimeException(failureMessage);
 491             }
 492         }
 493 
 494     }//main
 495 
 496     public static synchronized void setTimeoutTo(int seconds) {
 497         sleepTime = seconds * 1000;
 498     }
 499 
 500     public static synchronized void pass() {
 501         Sysout.println("The test passed.");
 502         Sysout.println("The test is over, hit  Ctl-C to stop Java VM");
 503         //first check if this is executing in main thread
 504         if (mainThread == Thread.currentThread()) {
 505             //Still in the main thread, so set the flag just for kicks,
 506             // and throw a test passed exception which will be caught
 507             // and end the test.
 508             theTestPassed = true;
 509             throw new TestPassedException();
 510         }
 511         theTestPassed = true;
 512         testGeneratedInterrupt = true;
 513         mainThread.interrupt();
 514     }//pass()
 515 
 516     public static synchronized void fail() {
 517         //test writer didn't specify why test failed, so give generic
 518         fail("it just plain failed! :-)");
 519     }
 520 
 521     public static synchronized void fail(String whyFailed) {
 522         Sysout.println("The test failed: " + whyFailed);
 523         Sysout.println("The test is over, hit  Ctl-C to stop Java VM");
 524         //check if this called from main thread
 525         if (mainThread == Thread.currentThread()) {
 526             //If main thread, fail now 'cause not sleeping
 527             throw new RuntimeException(whyFailed);
 528         }
 529         theTestPassed = false;
 530         testGeneratedInterrupt = true;
 531         failureMessage = whyFailed;
 532         mainThread.interrupt();
 533     }//fail()
 534 }// class LWComboBox
 535 class TestPassedException extends RuntimeException {
 536 }
 537 
 538 //*********** End Standard Test Machinery Section **********
 539 //************ Begin classes defined for the test ****************
 540 // if want to make listeners, here is the recommended place for them, then instantiate
 541 //  them in init()
 542 
 543 /* Example of a class which may be written as part of a test
 544 class NewClass implements anInterface
 545 {
 546 static int newVar = 0;
 547 
 548 public void eventDispatched(AWTEvent e)
 549 {
 550 //Counting events to see if we get enough
 551 eventCount++;
 552 
 553 if( eventCount == 20 )
 554 {
 555 //got enough events, so pass
 556 
 557 LWComboBox.pass();
 558 }
 559 else if( tries == 20 )
 560 {
 561 //tried too many times without getting enough events so fail
 562 
 563 LWComboBox.fail();
 564 }
 565 
 566 }// eventDispatched()
 567 
 568 }// NewClass class
 569 
 570  */
 571 //************** End classes defined for the test *******************
 572 /****************************************************
 573 Standard Test Machinery
 574 DO NOT modify anything below -- it's a standard
 575 chunk of code whose purpose is to make user
 576 interaction uniform, and thereby make it simpler
 577 to read and understand someone else's test.
 578  ****************************************************/
 579 /**
 580 This is part of the standard test machinery.
 581 It creates a dialog (with the instructions), and is the interface
 582 for sending text messages to the user.
 583 To print the instructions, send an array of strings to Sysout.createDialog
 584 WithInstructions method.  Put one line of instructions per array entry.
 585 To display a message for the tester to see, simply call Sysout.println
 586 with the string to be displayed.
 587 This mimics System.out.println but works within the test harness as well
 588 as standalone.
 589  */
 590 class Sysout {
 591 
 592     private static TestDialog dialog;
 593 
 594     public static void createDialogWithInstructions(String[] instructions) {
 595         dialog = new TestDialog(new Frame(), "Instructions");
 596         dialog.printInstructions(instructions);
 597         //dialog.setVisible(true);
 598         println("Any messages for the tester will display here.");
 599     }
 600 
 601     public static void createDialog() {
 602         dialog = new TestDialog(new Frame(), "Instructions");
 603         String[] defInstr = {"Instructions will appear here. ", ""};
 604         dialog.printInstructions(defInstr);
 605         //dialog.setVisible(true);
 606         println("Any messages for the tester will display here.");
 607     }
 608 
 609     public static void printInstructions(String[] instructions) {
 610         dialog.printInstructions(instructions);
 611     }
 612 
 613     public static void println(String messageIn) {
 614         dialog.displayMessage(messageIn);
 615         System.out.println(messageIn);
 616     }
 617 }// Sysout  class
 618 
 619 /**
 620 This is part of the standard test machinery.  It provides a place for the
 621 test instructions to be displayed, and a place for interactive messages
 622 to the user to be displayed.
 623 To have the test instructions displayed, see Sysout.
 624 To have a message to the user be displayed, see Sysout.
 625 Do not call anything in this dialog directly.
 626  */
 627 class TestDialog extends Dialog {
 628 
 629     TextArea instructionsText;
 630     TextArea messageText;
 631     int maxStringLength = 80;
 632 
 633     //DO NOT call this directly, go through Sysout
 634     public TestDialog(Frame frame, String name) {
 635         super(frame, name);
 636         int scrollBoth = TextArea.SCROLLBARS_BOTH;
 637         instructionsText = new TextArea("", 15, maxStringLength, scrollBoth);
 638         add("North", instructionsText);
 639 
 640         messageText = new TextArea("", 5, maxStringLength, scrollBoth);
 641         add("Center", messageText);
 642 
 643         pack();
 644 
 645         //setVisible(true);
 646     }// TestDialog()
 647 
 648     //DO NOT call this directly, go through Sysout
 649     public void printInstructions(String[] instructions) {
 650         //Clear out any current instructions
 651         instructionsText.setText("");
 652 
 653         //Go down array of instruction strings
 654 
 655         String printStr, remainingStr;
 656         for (int i = 0; i < instructions.length; i++) {
 657             //chop up each into pieces maxSringLength long
 658             remainingStr = instructions[i];
 659             while (remainingStr.length() > 0) {
 660                 //if longer than max then chop off first max chars to print
 661                 if (remainingStr.length() >= maxStringLength) {
 662                     //Try to chop on a word boundary
 663                     int posOfSpace = remainingStr.lastIndexOf(' ', maxStringLength - 1);
 664 
 665                     if (posOfSpace <= 0) {
 666                         posOfSpace = maxStringLength - 1;
 667                     }
 668 
 669                     printStr = remainingStr.substring(0, posOfSpace + 1);
 670                     remainingStr = remainingStr.substring(posOfSpace + 1);
 671                 } //else just print
 672                 else {
 673                     printStr = remainingStr;
 674                     remainingStr = "";
 675                 }
 676 
 677                 instructionsText.append(printStr + "\n");
 678 
 679             }// while
 680 
 681         }// for
 682 
 683     }//printInstructions()
 684 
 685     //DO NOT call this directly, go through Sysout
 686     public void displayMessage(String messageIn) {
 687         messageText.append(messageIn + "\n");
 688         System.out.println(messageIn);
 689     }
 690 }// TestDialog  class
 691