HierarchyBoundsListenerMixingTest.java revision 14851:980da45565c8
1/*
2 * Copyright (c) 2014, 2016, 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
25import java.awt.*;
26import java.awt.event.*;
27import java.lang.reflect.InvocationTargetException;
28import javax.swing.SwingUtilities;
29import 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 */
43public 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.setSize(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 boolean moveTriggered = false;
395    private 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        String[] instructions = {
438            "This is an AUTOMATIC test, simply wait until it is done.",
439            "The result (passed or failed) will be shown in the",
440            "message window below."
441        };
442        Sysout.createDialog();
443        Sysout.printInstructions(instructions);
444
445        HierarchyBoundsListenerMixingTest instance = new HierarchyBoundsListenerMixingTest();
446
447        instance.invoke();
448
449        pass();
450    }//End  init()
451    private static boolean theTestPassed = false;
452    private static boolean testGeneratedInterrupt = false;
453    private static String failureMessage = "";
454    private static Thread mainThread = null;
455    private static int sleepTime = 300000;
456
457    // Not sure about what happens if multiple of this test are
458    //  instantiated in the same VM.  Being static (and using
459    //  static vars), it aint gonna work.  Not worrying about
460    //  it for now.
461    public static void main(String args[]) throws InterruptedException {
462        mainThread = Thread.currentThread();
463        try {
464            init();
465        } catch (TestPassedException e) {
466            //The test passed, so just return from main and harness will
467            // interepret this return as a pass
468            return;
469        }
470        //At this point, neither test pass nor test fail has been
471        // called -- either would have thrown an exception and ended the
472        // test, so we know we have multiple threads.
473
474        //Test involves other threads, so sleep and wait for them to
475        // called pass() or fail()
476        try {
477            Thread.sleep(sleepTime);
478            //Timed out, so fail the test
479            throw new RuntimeException("Timed out after " + sleepTime / 1000 + " seconds");
480        } catch (InterruptedException e) {
481            //The test harness may have interrupted the test.  If so, rethrow the exception
482            // so that the harness gets it and deals with it.
483            if (!testGeneratedInterrupt) {
484                throw e;
485            }
486
487            //reset flag in case hit this code more than once for some reason (just safety)
488            testGeneratedInterrupt = false;
489
490            if (theTestPassed == false) {
491                throw new RuntimeException(failureMessage);
492            }
493        }
494
495    }//main
496
497    public static synchronized void setTimeoutTo(int seconds) {
498        sleepTime = seconds * 1000;
499    }
500
501    public static synchronized void pass() {
502        Sysout.println("The test passed.");
503        Sysout.println("The test is over, hit  Ctl-C to stop Java VM");
504        //first check if this is executing in main thread
505        if (mainThread == Thread.currentThread()) {
506            //Still in the main thread, so set the flag just for kicks,
507            // and throw a test passed exception which will be caught
508            // and end the test.
509            theTestPassed = true;
510            throw new TestPassedException();
511        }
512        theTestPassed = true;
513        testGeneratedInterrupt = true;
514        mainThread.interrupt();
515    }//pass()
516
517    public static synchronized void fail() {
518        //test writer didn't specify why test failed, so give generic
519        fail("it just plain failed! :-)");
520    }
521
522    public static synchronized void fail(String whyFailed) {
523        Sysout.println("The test failed: " + whyFailed);
524        Sysout.println("The test is over, hit  Ctl-C to stop Java VM");
525        //check if this called from main thread
526        if (mainThread == Thread.currentThread()) {
527            //If main thread, fail now 'cause not sleeping
528            throw new RuntimeException(whyFailed);
529        }
530        theTestPassed = false;
531        testGeneratedInterrupt = true;
532        failureMessage = whyFailed;
533        mainThread.interrupt();
534    }//fail()
535}// class LWComboBox
536class TestPassedException extends RuntimeException {
537}
538
539//*********** End Standard Test Machinery Section **********
540//************ Begin classes defined for the test ****************
541// if want to make listeners, here is the recommended place for them, then instantiate
542//  them in init()
543
544/* Example of a class which may be written as part of a test
545class NewClass implements anInterface
546{
547static int newVar = 0;
548
549public void eventDispatched(AWTEvent e)
550{
551//Counting events to see if we get enough
552eventCount++;
553
554if( eventCount == 20 )
555{
556//got enough events, so pass
557
558LWComboBox.pass();
559}
560else if( tries == 20 )
561{
562//tried too many times without getting enough events so fail
563
564LWComboBox.fail();
565}
566
567}// eventDispatched()
568
569}// NewClass class
570
571 */
572//************** End classes defined for the test *******************
573/****************************************************
574Standard Test Machinery
575DO NOT modify anything below -- it's a standard
576chunk of code whose purpose is to make user
577interaction uniform, and thereby make it simpler
578to read and understand someone else's test.
579 ****************************************************/
580/**
581This is part of the standard test machinery.
582It creates a dialog (with the instructions), and is the interface
583for sending text messages to the user.
584To print the instructions, send an array of strings to Sysout.createDialog
585WithInstructions method.  Put one line of instructions per array entry.
586To display a message for the tester to see, simply call Sysout.println
587with the string to be displayed.
588This mimics System.out.println but works within the test harness as well
589as standalone.
590 */
591class Sysout {
592
593    private static TestDialog dialog;
594
595    public static void createDialogWithInstructions(String[] instructions) {
596        dialog = new TestDialog(new Frame(), "Instructions");
597        dialog.printInstructions(instructions);
598        //dialog.setVisible(true);
599        println("Any messages for the tester will display here.");
600    }
601
602    public static void createDialog() {
603        dialog = new TestDialog(new Frame(), "Instructions");
604        String[] defInstr = {"Instructions will appear here. ", ""};
605        dialog.printInstructions(defInstr);
606        //dialog.setVisible(true);
607        println("Any messages for the tester will display here.");
608    }
609
610    public static void printInstructions(String[] instructions) {
611        dialog.printInstructions(instructions);
612    }
613
614    public static void println(String messageIn) {
615        dialog.displayMessage(messageIn);
616        System.out.println(messageIn);
617    }
618}// Sysout  class
619
620/**
621This is part of the standard test machinery.  It provides a place for the
622test instructions to be displayed, and a place for interactive messages
623to the user to be displayed.
624To have the test instructions displayed, see Sysout.
625To have a message to the user be displayed, see Sysout.
626Do not call anything in this dialog directly.
627 */
628class TestDialog extends Dialog {
629
630    TextArea instructionsText;
631    TextArea messageText;
632    int maxStringLength = 80;
633
634    //DO NOT call this directly, go through Sysout
635    public TestDialog(Frame frame, String name) {
636        super(frame, name);
637        int scrollBoth = TextArea.SCROLLBARS_BOTH;
638        instructionsText = new TextArea("", 15, maxStringLength, scrollBoth);
639        add("North", instructionsText);
640
641        messageText = new TextArea("", 5, maxStringLength, scrollBoth);
642        add("Center", messageText);
643
644        pack();
645
646        //setVisible(true);
647    }// TestDialog()
648
649    //DO NOT call this directly, go through Sysout
650    public void printInstructions(String[] instructions) {
651        //Clear out any current instructions
652        instructionsText.setText("");
653
654        //Go down array of instruction strings
655
656        String printStr, remainingStr;
657        for (int i = 0; i < instructions.length; i++) {
658            //chop up each into pieces maxSringLength long
659            remainingStr = instructions[i];
660            while (remainingStr.length() > 0) {
661                //if longer than max then chop off first max chars to print
662                if (remainingStr.length() >= maxStringLength) {
663                    //Try to chop on a word boundary
664                    int posOfSpace = remainingStr.lastIndexOf(' ', maxStringLength - 1);
665
666                    if (posOfSpace <= 0) {
667                        posOfSpace = maxStringLength - 1;
668                    }
669
670                    printStr = remainingStr.substring(0, posOfSpace + 1);
671                    remainingStr = remainingStr.substring(posOfSpace + 1);
672                } //else just print
673                else {
674                    printStr = remainingStr;
675                    remainingStr = "";
676                }
677
678                instructionsText.append(printStr + "\n");
679
680            }// while
681
682        }// for
683
684    }//printInstructions()
685
686    //DO NOT call this directly, go through Sysout
687    public void displayMessage(String messageIn) {
688        messageText.append(messageIn + "\n");
689        System.out.println(messageIn);
690    }
691}// TestDialog  class
692
693