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