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