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  * @test
  25  * @key headful
  26  * @bug 6236247
  27  * @summary Test that setting of always-on-top flags before showing window works
  28  * @author dom@sparc.spb.su: area=awt.toplevel
  29  * @run main TestAlwaysOnTopBeforeShow
  30  */
  31 
  32 /**
  33  * TestAlwaysOnTopBeforeShow.java
  34  *
  35  * summary:  Test that always-on-top works in the following situations:
  36  * - when set on a window before showing
  37  * - when set on a child dialog
  38  * - that it doesn't generate focus event when set on an invisible window
  39  */
  40 
  41 import java.awt.*;
  42 import java.awt.event.*;
  43 import java.util.concurrent.atomic.AtomicBoolean;
  44 import sun.awt.SunToolkit;
  45 
  46 
  47 //*** global search and replace TestAlwaysOnTopBeforeShow with name of the test ***
  48 
  49 public class TestAlwaysOnTopBeforeShow
  50 {
  51 
  52     //*** test-writer defined static variables go here ***
  53 
  54     private static AtomicBoolean focused = new AtomicBoolean();
  55     private static AtomicBoolean pressed = new AtomicBoolean();
  56     private static volatile Object pressedTarget;
  57     private static void init()
  58     {
  59         //*** Create instructions for the user here ***
  60 
  61         Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
  62                 public void eventDispatched(AWTEvent e) {
  63                     if (e.getID() == MouseEvent.MOUSE_PRESSED) {
  64                         synchronized(pressed) {
  65                             pressed.set(true);
  66                             pressedTarget = e.getSource();
  67                             pressed.notifyAll();
  68                         }
  69                     }
  70                 }
  71             }, AWTEvent.MOUSE_EVENT_MASK);
  72 
  73         Frame f = new Frame("always-on-top");
  74         f.setBounds(0, 0, 200, 200);
  75         f.addFocusListener(new FocusAdapter() {
  76                 public void focusGained(FocusEvent e) {
  77                     synchronized(focused) {
  78                         focused.set(true);
  79                         focused.notifyAll();
  80                     }
  81                 }
  82             });
  83 
  84         f.setAlwaysOnTop(true);
  85 
  86         waitForIdle(1000);
  87         if (focused.get()) {
  88             throw new RuntimeException("Always-on-top generated focus event");
  89         }
  90 
  91         f.setVisible(true);
  92 
  93         waitFocused(f, focused);
  94         focused.set(false);
  95 
  96         Frame f2 = new Frame("auxilary");
  97         f2.setBounds(100, 0, 200, 100);
  98         f2.setVisible(true);
  99         f2.toFront();
 100         waitForIdle(1000);
 101 
 102         Point location = f.getLocationOnScreen();
 103         Dimension size = f.getSize();
 104         checkOnTop(f, f2, location.x + size.width / 2, location.y + size.height / 2);
 105 
 106         Dialog d = new Dialog(f, "Always-on-top");
 107         d.pack();
 108         d.setBounds(0, 0, 100, 100);
 109 
 110         waitForIdle(1000);
 111         checkOnTop(f, f2, location.x + size.width / 2, location.y + size.height / 2);
 112         waitForIdle(1000);
 113 
 114         focused.set(false);
 115         f.setVisible(false);
 116         f.setAlwaysOnTop(false);
 117         waitForIdle(1000);
 118         if (focused.get()) {
 119             throw new RuntimeException("Always-on-top generated focus event");
 120         }
 121 
 122         TestAlwaysOnTopBeforeShow.pass();
 123 
 124     }//End  init()
 125 
 126     private static void waitForIdle(int mls) {
 127         ((SunToolkit)Toolkit.getDefaultToolkit()).realSync();
 128         try {
 129             Thread.sleep(mls);
 130         } catch (InterruptedException e) {
 131             e.printStackTrace();
 132         }
 133     }
 134 
 135     static void waitFocused(Window w, AtomicBoolean b) {
 136         try {
 137             synchronized(b) {
 138                 if (w.isFocusOwner()) {
 139                     return;
 140                 }
 141                 b.wait(3000);
 142             }
 143         } catch (Exception e) {
 144             throw new RuntimeException(e);
 145         }
 146         if (!w.isFocusOwner()) {
 147             throw new RuntimeException("Can't make " + w + " focus owner");
 148         }
 149     }
 150 
 151     static void checkOnTop(Window ontop, Window under, int x, int y) {
 152         under.toFront();
 153         try {
 154             Robot robot = new Robot();
 155             robot.mouseMove(x, y);
 156             robot.mousePress(InputEvent.BUTTON1_MASK);
 157             robot.mouseRelease(InputEvent.BUTTON1_MASK);
 158             synchronized(pressed) {
 159                 if (pressed.get()) {
 160                     if (pressedTarget != ontop) {
 161                         throw new RuntimeException("Pressed at wrong location: " + pressedTarget);
 162                     }
 163                 } else {
 164                     pressed.wait(5000);
 165                 }
 166             }
 167             if (!pressed.get() || pressedTarget != ontop) {
 168                 throw new RuntimeException("Pressed at wrong location: " + pressedTarget);
 169             }
 170         } catch (Exception e) {
 171             throw new RuntimeException(e);
 172         }
 173     }
 174 
 175     /*****************************************************
 176      * Standard Test Machinery Section
 177      * DO NOT modify anything in this section -- it's a
 178      * standard chunk of code which has all of the
 179      * synchronisation necessary for the test harness.
 180      * By keeping it the same in all tests, it is easier
 181      * to read and understand someone else's test, as
 182      * well as insuring that all tests behave correctly
 183      * with the test harness.
 184      * There is a section following this for test-
 185      * classes
 186      ******************************************************/
 187     private static boolean theTestPassed = false;
 188     private static boolean testGeneratedInterrupt = false;
 189     private static String failureMessage = "";
 190 
 191     private static Thread mainThread = null;
 192 
 193     private static int sleepTime = 300000;
 194 
 195     // Not sure about what happens if multiple of this test are
 196     //  instantiated in the same VM.  Being static (and using
 197     //  static vars), it aint gonna work.  Not worrying about
 198     //  it for now.
 199     public static void main( String args[] ) throws InterruptedException
 200     {
 201         mainThread = Thread.currentThread();
 202         try
 203         {
 204             init();
 205         }
 206         catch( TestPassedException e )
 207         {
 208             //The test passed, so just return from main and harness will
 209             // interepret this return as a pass
 210             return;
 211         }
 212         //At this point, neither test pass nor test fail has been
 213         // called -- either would have thrown an exception and ended the
 214         // test, so we know we have multiple threads.
 215 
 216         //Test involves other threads, so sleep and wait for them to
 217         // called pass() or fail()
 218         try
 219         {
 220             Thread.sleep( sleepTime );
 221             //Timed out, so fail the test
 222             throw new RuntimeException( "Timed out after " + sleepTime/1000 + " seconds" );
 223         }
 224         catch (InterruptedException e)
 225         {
 226             //The test harness may have interrupted the test.  If so, rethrow the exception
 227             // so that the harness gets it and deals with it.
 228             if( ! testGeneratedInterrupt ) throw e;
 229 
 230             //reset flag in case hit this code more than once for some reason (just safety)
 231             testGeneratedInterrupt = false;
 232 
 233             if ( theTestPassed == false )
 234             {
 235                 throw new RuntimeException( failureMessage );
 236             }
 237         }
 238 
 239     }//main
 240 
 241     public static synchronized void setTimeoutTo( int seconds )
 242     {
 243         sleepTime = seconds * 1000;
 244     }
 245 
 246     public static synchronized void pass()
 247     {
 248         Sysout.println( "The test passed." );
 249         Sysout.println( "The test is over, hit  Ctl-C to stop Java VM" );
 250         //first check if this is executing in main thread
 251         if ( mainThread == Thread.currentThread() )
 252         {
 253             //Still in the main thread, so set the flag just for kicks,
 254             // and throw a test passed exception which will be caught
 255             // and end the test.
 256             theTestPassed = true;
 257             throw new TestPassedException();
 258         }
 259         theTestPassed = true;
 260         testGeneratedInterrupt = true;
 261         mainThread.interrupt();
 262     }//pass()
 263 
 264     public static synchronized void fail()
 265     {
 266         //test writer didn't specify why test failed, so give generic
 267         fail( "it just plain failed! :-)" );
 268     }
 269 
 270     public static synchronized void fail( String whyFailed )
 271     {
 272         Sysout.println( "The test failed: " + whyFailed );
 273         Sysout.println( "The test is over, hit  Ctl-C to stop Java VM" );
 274         //check if this called from main thread
 275         if ( mainThread == Thread.currentThread() )
 276         {
 277             //If main thread, fail now 'cause not sleeping
 278             throw new RuntimeException( whyFailed );
 279         }
 280         theTestPassed = false;
 281         testGeneratedInterrupt = true;
 282         failureMessage = whyFailed;
 283         mainThread.interrupt();
 284     }//fail()
 285 
 286 }// class TestAlwaysOnTopBeforeShow
 287 
 288 //This exception is used to exit from any level of call nesting
 289 // when it's determined that the test has passed, and immediately
 290 // end the test.
 291 class TestPassedException extends RuntimeException
 292 {
 293 }
 294 
 295 //*********** End Standard Test Machinery Section **********
 296 
 297 
 298 //************ Begin classes defined for the test ****************
 299 
 300 // if want to make listeners, here is the recommended place for them, then instantiate
 301 //  them in init()
 302 
 303 /* Example of a class which may be written as part of a test
 304 class NewClass implements anInterface
 305  {
 306    static int newVar = 0;
 307 
 308    public void eventDispatched(AWTEvent e)
 309     {
 310       //Counting events to see if we get enough
 311       eventCount++;
 312 
 313       if( eventCount == 20 )
 314        {
 315          //got enough events, so pass
 316 
 317          TestAlwaysOnTopBeforeShow.pass();
 318        }
 319       else if( tries == 20 )
 320        {
 321          //tried too many times without getting enough events so fail
 322 
 323          TestAlwaysOnTopBeforeShow.fail();
 324        }
 325 
 326     }// eventDispatched()
 327 
 328  }// NewClass class
 329 
 330 */
 331 
 332 
 333 //************** End classes defined for the test *******************
 334 
 335 
 336 
 337 
 338 /****************************************************
 339  Standard Test Machinery
 340  DO NOT modify anything below -- it's a standard
 341   chunk of code whose purpose is to make user
 342   interaction uniform, and thereby make it simpler
 343   to read and understand someone else's test.
 344  ****************************************************/
 345 
 346 /**
 347  This is part of the standard test machinery.
 348  It creates a dialog (with the instructions), and is the interface
 349   for sending text messages to the user.
 350  To print the instructions, send an array of strings to Sysout.createDialog
 351   WithInstructions method.  Put one line of instructions per array entry.
 352  To display a message for the tester to see, simply call Sysout.println
 353   with the string to be displayed.
 354  This mimics System.out.println but works within the test harness as well
 355   as standalone.
 356  */
 357 
 358 class Sysout
 359 {
 360     private static TestDialog dialog;
 361 
 362     public static void createDialogWithInstructions( String[] instructions )
 363     {
 364         dialog = new TestDialog( new Frame(), "Instructions" );
 365         dialog.printInstructions( instructions );
 366         dialog.setVisible(true);
 367         println( "Any messages for the tester will display here." );
 368     }
 369 
 370     public static void createDialog( )
 371     {
 372         dialog = new TestDialog( new Frame(), "Instructions" );
 373         String[] defInstr = { "Instructions will appear here. ", "" } ;
 374         dialog.printInstructions( defInstr );
 375         dialog.setVisible(true);
 376         println( "Any messages for the tester will display here." );
 377     }
 378 
 379 
 380     public static void printInstructions( String[] instructions )
 381     {
 382         dialog.printInstructions( instructions );
 383     }
 384 
 385 
 386     public static void println( String messageIn )
 387     {
 388         System.out.println(messageIn);
 389     }
 390 
 391 }// Sysout  class
 392 
 393 /**
 394   This is part of the standard test machinery.  It provides a place for the
 395    test instructions to be displayed, and a place for interactive messages
 396    to the user to be displayed.
 397   To have the test instructions displayed, see Sysout.
 398   To have a message to the user be displayed, see Sysout.
 399   Do not call anything in this dialog directly.
 400   */
 401 class TestDialog extends Dialog
 402 {
 403 
 404     TextArea instructionsText;
 405     TextArea messageText;
 406     int maxStringLength = 80;
 407 
 408     //DO NOT call this directly, go through Sysout
 409     public TestDialog( Frame frame, String name )
 410     {
 411         super( frame, name );
 412         int scrollBoth = TextArea.SCROLLBARS_BOTH;
 413         instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth );
 414         add( "North", instructionsText );
 415 
 416         messageText = new TextArea( "", 5, maxStringLength, scrollBoth );
 417         add("Center", messageText);
 418 
 419         pack();
 420 
 421         setVisible(true);
 422     }// TestDialog()
 423 
 424     //DO NOT call this directly, go through Sysout
 425     public void printInstructions( String[] instructions )
 426     {
 427         //Clear out any current instructions
 428         instructionsText.setText( "" );
 429 
 430         //Go down array of instruction strings
 431 
 432         String printStr, remainingStr;
 433         for( int i=0; i < instructions.length; i++ )
 434         {
 435             //chop up each into pieces maxSringLength long
 436             remainingStr = instructions[ i ];
 437             while( remainingStr.length() > 0 )
 438             {
 439                 //if longer than max then chop off first max chars to print
 440                 if( remainingStr.length() >= maxStringLength )
 441                 {
 442                     //Try to chop on a word boundary
 443                     int posOfSpace = remainingStr.
 444                         lastIndexOf( ' ', maxStringLength - 1 );
 445 
 446                     if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1;
 447 
 448                     printStr = remainingStr.substring( 0, posOfSpace + 1 );
 449                     remainingStr = remainingStr.substring( posOfSpace + 1 );
 450                 }
 451                 //else just print
 452                 else
 453                 {
 454                     printStr = remainingStr;
 455                     remainingStr = "";
 456                 }
 457 
 458                 instructionsText.append( printStr + "\n" );
 459 
 460             }// while
 461 
 462         }// for
 463 
 464     }//printInstructions()
 465 
 466     //DO NOT call this directly, go through Sysout
 467     public void displayMessage( String messageIn )
 468     {
 469         messageText.append( messageIn + "\n" );
 470         System.out.println(messageIn);
 471     }
 472 
 473 }// TestDialog  class