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