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 8221823
39  * @summary Mixing test for HierarchyBoundsListener ancestors
40  * @build FrameBorderCounter
41  * @run main HierarchyBoundsListenerMixingTest
42  */
43 public class HierarchyBoundsListenerMixingTest {
44 
prepareControls()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 
frameBorderCounter()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 
readInputStream(InputStream is)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 
performTest()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 
resetValues()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 
keyType(int key, Robot robot)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
ancestorMoved(HierarchyEvent ce)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
ancestorResized(HierarchyEvent ce)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 
invoke()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      ******************************************************/
init()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.
main(String args[])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 
setTimeoutTo(int seconds)489     public static synchronized void setTimeoutTo(int seconds) {
490         sleepTime = seconds * 1000;
491     }
492 
pass()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 
fail()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 
fail(String whyFailed)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 }
530