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