1 /*
   2  * Copyright (c) 2003, 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 /*
  25  * @test
  26  * @key headful
  27  * @bug 4799136
  28  * @summary Tests that type-ahead for dialog works and doesn't block program
  29  * @author Dmitry.Cherepanov@SUN.COM area=awt.focus
  30  * @run main EnqueueWithDialogButtonTest
  31  */
  32 
  33 import java.awt.*;
  34 import java.lang.reflect.InvocationTargetException;
  35 import java.awt.event.*;
  36 import java.util.concurrent.CountDownLatch;
  37 import java.util.concurrent.TimeUnit;
  38 
  39 /*
  40  * Tests that type-ahead works correctly. That means
  41  * that the key events are not delivered until a focus
  42  * transfer is completed.
  43  * There is another pretty similar test EnqueueWithDialogTest
  44  * written in time before 6347235 resolution. We'll keep it
  45  * to track quite unrelated suspicious waitForIdle behavior.
  46  */
  47 
  48 public class EnqueueWithDialogButtonTest
  49 {
  50     static Frame f;
  51     static Button b;
  52     static Dialog d;
  53     static Button ok;
  54     static CountDownLatch pressLatch = new CountDownLatch(1);
  55     static CountDownLatch robotLatch = new CountDownLatch(1);
  56     static volatile boolean gotFocus = false;
  57     static Robot robot;
  58     public static void main(String args[]) throws Exception {
  59         EnqueueWithDialogButtonTest test = new EnqueueWithDialogButtonTest();
  60         test.init();
  61         test.start();
  62     }
  63     public void init()
  64     {
  65         Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
  66                 public void eventDispatched(AWTEvent e) {
  67                     if (e instanceof InputEvent){
  68                         System.err.println(e.toString()+","+((InputEvent)e).getWhen());
  69                     }else{
  70                         System.err.println(e.toString());
  71                     }
  72                  }
  73             }, AWTEvent.KEY_EVENT_MASK | AWTEvent.FOCUS_EVENT_MASK);
  74 
  75 
  76         f = new Frame("frame");
  77         f.setPreferredSize(new Dimension(100,100));
  78         f.setLocation(100,50);
  79         b = new Button("press");
  80         d = new Dialog(f, "dialog", true);
  81         d.setPreferredSize(new Dimension(70,70));
  82         ok = new Button("ok");
  83         d.add(ok);
  84         d.pack();
  85         ok.addKeyListener(new KeyAdapter() {
  86                 public void keyPressed(KeyEvent e) {
  87                     System.err.println("OK pressed: should arrive after got focus");
  88                     d.dispose();
  89                     f.dispose();
  90                     // Typed-ahead key events should only be accepted if
  91                     // they arrive after FOCUS_GAINED
  92                     if (gotFocus) {
  93                         pressLatch.countDown();
  94                     }
  95                 }
  96             });
  97         ok.addFocusListener(new FocusAdapter() {
  98                 public void focusGained(FocusEvent e) {
  99                     gotFocus = true;
 100                     System.err.println("OK got focus");
 101                 }
 102             });
 103         f.add(b);
 104         f.pack();
 105         b.addActionListener(new ActionListener() {
 106                 public void actionPerformed(ActionEvent e) {
 107                     System.err.println(e.toString()+","+e.getWhen());
 108                     System.err.println("B pressed");
 109                     robotLatch.countDown();
 110 
 111                     EventQueue.invokeLater(new Runnable() {
 112                             public void run() {
 113                                 waitTillShown(d);
 114                                 EnqueueWithDialogButtonTest.this.d.toFront();
 115                                 EnqueueWithDialogButtonTest.this.moveMouseOver(d);
 116                             }
 117                         });
 118 
 119                     // This will cause enqueue the following key events
 120                     d.setVisible(true);
 121                 }
 122             });
 123 
 124     }//End  init()
 125 
 126     public void start () throws Exception
 127     {
 128 
 129         robot = new Robot();
 130         robot.setAutoDelay(50);
 131 
 132         f.setVisible(true);
 133         waitTillShown(b);
 134         System.err.println("b is shown");
 135         f.toFront();
 136         moveMouseOver(f);
 137         robot.waitForIdle();
 138         robot.delay(100);
 139         makeFocused(b);
 140         robot.waitForIdle();
 141         robot.delay(100);
 142         System.err.println("b is focused");
 143 
 144         robot.keyPress(KeyEvent.VK_SPACE);
 145         robot.keyRelease(KeyEvent.VK_SPACE);
 146         boolean ok = robotLatch.await(1, TimeUnit.SECONDS);
 147         if(!ok) {
 148             throw new RuntimeException("Was B button pressed?");
 149         }
 150 
 151         robot.keyPress(KeyEvent.VK_SPACE);
 152         robot.keyRelease(KeyEvent.VK_SPACE);
 153         robot.delay(500);
 154         ok = pressLatch.await(3, TimeUnit.SECONDS);
 155         if(!ok) {
 156             throw new RuntimeException("Type-ahead doesn't work");
 157         }
 158 
 159     }// start()
 160 
 161     private void moveMouseOver(Container c) {
 162         Point p = c.getLocationOnScreen();
 163         Dimension d = c.getSize();
 164         robot.mouseMove(p.x + (int)(d.getWidth()/2), p.y + (int)(d.getHeight()/2));
 165     }
 166 
 167     private void waitTillShown(Component c) {
 168         while (true) {
 169             try {
 170                 Thread.sleep(100);
 171                 c.getLocationOnScreen();
 172                 break;
 173             } catch (InterruptedException ie) {
 174                 ie.printStackTrace();
 175                 break;
 176             } catch (Exception e) {
 177             }
 178         }
 179     }
 180     private void makeFocused(Component comp) {
 181         if (comp.isFocusOwner()) {
 182             return;
 183         }
 184         final Semaphore sema = new Semaphore();
 185         final FocusAdapter fa = new FocusAdapter() {
 186                 public void focusGained(FocusEvent fe) {
 187                     sema.raise();
 188                 }
 189             };
 190         comp.addFocusListener(fa);
 191         comp.requestFocusInWindow();
 192         if (comp.isFocusOwner()) {
 193             return;
 194         }
 195         try {
 196             sema.doWait(3000);
 197         } catch (InterruptedException ie) {
 198             ie.printStackTrace();
 199         }
 200         comp.removeFocusListener(fa);
 201         if (!comp.isFocusOwner()) {
 202             throw new RuntimeException("Can't make " + comp + " focused, current owner is " + KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());
 203         }
 204     }
 205 
 206 static class Semaphore {
 207     boolean state = false;
 208     int waiting = 0;
 209     public Semaphore() {
 210     }
 211     public synchronized void doWait() throws InterruptedException {
 212         if (state) {
 213             return;
 214         }
 215         waiting++;
 216         wait();
 217         waiting--;
 218     }
 219     public synchronized void doWait(int timeout) throws InterruptedException {
 220         if (state) {
 221             return;
 222         }
 223         waiting++;
 224         wait(timeout);
 225         waiting--;
 226     }
 227     public synchronized void raise() {
 228         state = true;
 229         if (waiting > 0) {
 230             notifyAll();
 231         }
 232     }
 233     public synchronized boolean getState() {
 234         return state;
 235     }
 236 }
 237 }// class TestDialogTypeAhead
 238 
 239 
 240 /****************************************************
 241  Standard Test Machinery
 242  DO NOT modify anything below -- it's a standard
 243   chunk of code whose purpose is to make user
 244   interaction uniform, and thereby make it simpler
 245   to read and understand someone else's test.
 246  ****************************************************/
 247 
 248 /**
 249  This is part of the standard test machinery.
 250  It creates a dialog (with the instructions), and is the interface
 251   for sending text messages to the user.
 252  To print the instructions, send an array of strings to Sysout.createDialog
 253   WithInstructions method.  Put one line of instructions per array entry.
 254  To display a message for the tester to see, simply call Sysout.println
 255   with the string to be displayed.
 256  This mimics System.out.println but works within the test harness as well
 257   as standalone.
 258  */
 259 
 260 class Sysout
 261 {
 262     private static TestDialog dialog;
 263 
 264     public static void createDialogWithInstructions( String[] instructions )
 265     {
 266         dialog = new TestDialog( new Frame(), "Instructions" );
 267         dialog.printInstructions( instructions );
 268         dialog.setVisible(true);
 269         println( "Any messages for the tester will display here." );
 270     }
 271 
 272     public static void createDialog( )
 273     {
 274         dialog = new TestDialog( new Frame(), "Instructions" );
 275         String[] defInstr = { "Instructions will appear here. ", "" } ;
 276         dialog.printInstructions( defInstr );
 277         dialog.setVisible(true);
 278         println( "Any messages for the tester will display here." );
 279     }
 280 
 281 
 282     public static void printInstructions( String[] instructions )
 283     {
 284         dialog.printInstructions( instructions );
 285     }
 286 
 287 
 288     public static void println( String messageIn )
 289     {
 290         dialog.displayMessage( messageIn );
 291     }
 292 
 293 }// Sysout  class
 294 
 295 /**
 296   This is part of the standard test machinery.  It provides a place for the
 297    test instructions to be displayed, and a place for interactive messages
 298    to the user to be displayed.
 299   To have the test instructions displayed, see Sysout.
 300   To have a message to the user be displayed, see Sysout.
 301   Do not call anything in this dialog directly.
 302   */
 303 class TestDialog extends Dialog
 304 {
 305 
 306     TextArea instructionsText;
 307     TextArea messageText;
 308     int maxStringLength = 80;
 309 
 310     //DO NOT call this directly, go through Sysout
 311     public TestDialog( Frame frame, String name )
 312     {
 313         super( frame, name );
 314         int scrollBoth = TextArea.SCROLLBARS_BOTH;
 315         instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth );
 316         add( "North", instructionsText );
 317 
 318         messageText = new TextArea( "", 5, maxStringLength, scrollBoth );
 319         add("Center", messageText);
 320 
 321         pack();
 322 
 323         show();
 324     }// TestDialog()
 325 
 326     //DO NOT call this directly, go through Sysout
 327     public void printInstructions( String[] instructions )
 328     {
 329         //Clear out any current instructions
 330         instructionsText.setText( "" );
 331 
 332         //Go down array of instruction strings
 333 
 334         String printStr, remainingStr;
 335         for( int i=0; i < instructions.length; i++ )
 336         {
 337             //chop up each into pieces maxSringLength long
 338             remainingStr = instructions[ i ];
 339             while( remainingStr.length() > 0 )
 340             {
 341                 //if longer than max then chop off first max chars to print
 342                 if( remainingStr.length() >= maxStringLength )
 343                 {
 344                     //Try to chop on a word boundary
 345                     int posOfSpace = remainingStr.
 346                         lastIndexOf( ' ', maxStringLength - 1 );
 347 
 348                     if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1;
 349 
 350                     printStr = remainingStr.substring( 0, posOfSpace + 1 );
 351                     remainingStr = remainingStr.substring( posOfSpace + 1 );
 352                 }
 353                 //else just print
 354                 else
 355                 {
 356                     printStr = remainingStr;
 357                     remainingStr = "";
 358                 }
 359 
 360                 instructionsText.append( printStr + "\n" );
 361 
 362             }// while
 363 
 364         }// for
 365 
 366     }//printInstructions()
 367 
 368     //DO NOT call this directly, go through Sysout
 369     public void displayMessage( String messageIn )
 370     {
 371         messageText.append( messageIn + "\n" );
 372         System.out.println(messageIn);
 373     }
 374 
 375 }// TestDialog  class