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