blob: 10028be30d53eca0be53169b7e9fcb0dbf170edf [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2007 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/*
25test
26@bug 4799136
27@summary Tests that type-ahead for dialog works and doesn't block program
28@author area=awt.focus
29@run applet TestDialogTypeAhead.html
30*/
31
32// Note there is no @ in front of test above. This is so that the
33// harness will not mistake this file as a test file. It should
34// only see the html file as a test file. (the harness runs all
35// valid test files, so it would run this test twice if this file
36// were valid as well as the html file.)
37// Also, note the area= after Your Name in the author tag. Here, you
38// should put which functional area the test falls in. See the
39// AWT-core home page -> test areas and/or -> AWT team for a list of
40// areas.
41// Note also the 'TestDialogTypeAhead.html' in the run tag. This should
42// be changed to the name of the test.
43
44
45/**
46 * TestDialogTypeAhead.java
47 *
48 * summary:
49 */
50
51import java.applet.Applet;
52import java.awt.*;
53import java.lang.reflect.InvocationTargetException;
54import java.awt.event.*;
55import java.awt.peer.DialogPeer;
56import java.awt.peer.ComponentPeer;
57import java.lang.reflect.Method;
58import java.lang.reflect.Proxy;
59import java.lang.reflect.InvocationHandler;
60import java.lang.reflect.InvocationTargetException;
61import test.java.awt.regtesthelpers.Util;
62
63//Automated tests should run as applet tests if possible because they
64// get their environments cleaned up, including AWT threads, any
65// test created threads, and any system resources used by the test
66// such as file descriptors. (This is normally not a problem as
67// main tests usually run in a separate VM, however on some platforms
68// such as the Mac, separate VMs are not possible and non-applet
69// tests will cause problems). Also, you don't have to worry about
70// synchronisation stuff in Applet tests they way you do in main
71// tests...
72
73
74public class TestDialogTypeAhead extends Applet
75{
76 //Declare things used in the test, like buttons and labels here
77 static Frame f;
78 static Button b;
79 static Dialog d;
80 static Button ok;
81 static Semaphore pressSema = new Semaphore();
82 static Semaphore robotSema = new Semaphore();
83 static volatile boolean gotFocus = false;
84 static Robot robot;
85 public void init()
86 {
87 //Create instructions for the user here, as well as set up
88 // the environment -- set the layout manager, add buttons,
89 // etc.
90
91 Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
92 public void eventDispatched(AWTEvent e) {
93 System.err.println(e.toString());
94 }
95 }, AWTEvent.KEY_EVENT_MASK);
96
97 this.setLayout (new BorderLayout ());
98
99 f = new Frame("frame");
100 b = new Button("press");
101 d = new TestDialog(f, "dialog", true, robotSema);
102 ok = new Button("ok");
103 d.add(ok);
104 d.pack();
105
106 ok.addKeyListener(new KeyAdapter() {
107 public void keyPressed(KeyEvent e) {
108 System.err.println("OK pressed");
109 d.dispose();
110 f.dispose();
111 // Typed-ahead key events should only be accepted if
112 // they arrive after FOCUS_GAINED
113 if (gotFocus) {
114 pressSema.raise();
115 }
116 }
117 });
118 ok.addFocusListener(new FocusAdapter() {
119 public void focusGained(FocusEvent e) {
120 gotFocus = true;
121 System.err.println("Ok got focus");
122 }
123 });
124 f.add(b);
125 f.pack();
126 b.addActionListener(new ActionListener() {
127 public void actionPerformed(ActionEvent e) {
128 System.err.println("B pressed");
129
130 EventQueue.invokeLater(new Runnable() {
131 public void run() {
132 waitTillShown(d);
133 TestDialogTypeAhead.this.d.toFront();
134 TestDialogTypeAhead.this.moveMouseOver(d);
135 }
136 });
137
138 d.setVisible(true);
139 }
140 });
141
142 }//End init()
143
144 public void start ()
145 {
146 //Get things going. Request focus, set size, et cetera
147 setSize (200,200);
148 setVisible(true);
149 validate();
150 try {
151 robot = new Robot();
152 } catch (Exception e) {
153 throw new RuntimeException("Can't create robot:" + e);
154 }
155
156 f.setVisible(true);
157 waitTillShown(b);
158 System.err.println("b is shown");
159 f.toFront();
160 moveMouseOver(f);
161 waitForIdle();
162 makeFocused(b);
163 waitForIdle();
164 System.err.println("b is focused");
165
166 robot.keyPress(KeyEvent.VK_SPACE);
167 robot.keyRelease(KeyEvent.VK_SPACE);
168 try {
169 robotSema.doWait(1000);
170 } catch (InterruptedException ie) {
171 throw new RuntimeException("Interrupted!");
172 }
173 robot.keyPress(KeyEvent.VK_SPACE);
174 robot.keyRelease(KeyEvent.VK_SPACE);
175 waitForIdle();
176 try {
177 pressSema.doWait(3000);
178 } catch (InterruptedException ie) {
179 throw new RuntimeException("Interrupted!");
180 }
181 if (!pressSema.getState()) {
182 throw new RuntimeException("Type-ahead doesn't work");
183 }
184
185 }// start()
186
187 private void moveMouseOver(Container c) {
188 Point p = c.getLocationOnScreen();
189 Dimension d = c.getSize();
190 robot.mouseMove(p.x + (int)(d.getWidth()/2), p.y + (int)(d.getHeight()/2));
191 }
192 private void waitForIdle() {
193 try {
194 Toolkit.getDefaultToolkit().sync();
195 sun.awt.SunToolkit.flushPendingEvents();
196 EventQueue.invokeAndWait( new Runnable() {
197 public void run() {
198 // dummy implementation
199 }
200 } );
201 } catch(InterruptedException ite) {
202 System.err.println("Robot.waitForIdle, non-fatal exception caught:");
203 ite.printStackTrace();
204 } catch(InvocationTargetException ine) {
205 System.err.println("Robot.waitForIdle, non-fatal exception caught:");
206 ine.printStackTrace();
207 }
208 }
209
210 private void waitTillShown(Component c) {
211 while (true) {
212 try {
213 Thread.sleep(100);
214 c.getLocationOnScreen();
215 break;
216 } catch (InterruptedException ie) {
217 ie.printStackTrace();
218 break;
219 } catch (Exception e) {
220 }
221 }
222 }
223 private void makeFocused(Component comp) {
224 if (comp.isFocusOwner()) {
225 return;
226 }
227 final Semaphore sema = new Semaphore();
228 final FocusAdapter fa = new FocusAdapter() {
229 public void focusGained(FocusEvent fe) {
230 sema.raise();
231 }
232 };
233 comp.addFocusListener(fa);
234 comp.requestFocusInWindow();
235 if (comp.isFocusOwner()) {
236 return;
237 }
238 try {
239 sema.doWait(3000);
240 } catch (InterruptedException ie) {
241 ie.printStackTrace();
242 }
243 comp.removeFocusListener(fa);
244 if (!comp.isFocusOwner()) {
245 throw new RuntimeException("Can't make " + comp + " focused, current owner is " + KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());
246 }
247 }
248
249 static class Semaphore {
250 boolean state = false;
251 int waiting = 0;
252 public Semaphore() {
253 }
254 public synchronized void doWait() throws InterruptedException {
255 if (state) {
256 return;
257 }
258 waiting++;
259 wait();
260 waiting--;
261 }
262 public synchronized void doWait(int timeout) throws InterruptedException {
263 if (state) {
264 return;
265 }
266 waiting++;
267 wait(timeout);
268 waiting--;
269 }
270 public synchronized void raise() {
271 state = true;
272 if (waiting > 0) {
273 notifyAll();
274 }
275 }
276 public synchronized boolean getState() {
277 return state;
278 }
279 }
280
281 // Fix for 6446952.
282 // In the process of showing the dialog we have to catch peer.show() call
283 // so that to trigger key events just before it gets invoked.
284 // We base on the fact that a modal dialog sets type-ahead markers
285 // before it calls 'show' on the peer.
286 // Posting the key events before dialog.setVisible(true) would be actually not
287 // good because it would be Ok to dispatch them to the current focus owner,
288 // not to the dialog.
289 class TestDialog extends Dialog {
290 ComponentPeer origDialogPeer;
291 ComponentPeer proxyInstPeer;
292 Semaphore trigger;
293
294 TestDialog(Frame owner, String title, boolean modal, Semaphore trigger) {
295 super(owner, title, modal);
296 this.trigger = trigger;
297 }
298 public ComponentPeer getPeer() {
299 ComponentPeer ret = super.getPeer();
300 if (ret == proxyInstPeer) {
301 return origDialogPeer;
302 } else {
303 return ret;
304 }
305 }
306
307 public void addNotify() {
308 super.addNotify();
309 replacePeer();
310 }
311
312 void replacePeer() {
313 origDialogPeer = getPeer();
314
315 InvocationHandler handler = new InvocationHandler() {
316 public Object invoke(Object proxy, Method method, Object[] args) {
317 if (method.getName() == "show") {
318 trigger.raise();
319 }
320
321 Object ret = null;
322 try {
323 ret = method.invoke(origDialogPeer, args);
324 } catch (IllegalAccessException iae) {
325 throw new Error("Test error.", iae);
326 } catch (InvocationTargetException ita) {
327 throw new Error("Test error.", ita);
328 }
329 return ret;
330 }
331 };
332
333 proxyInstPeer = (DialogPeer)Proxy.newProxyInstance(
334 DialogPeer.class.getClassLoader(), new Class[] {DialogPeer.class}, handler);
335
336 try {
337 Util.getField(Component.class, "peer").set(d, proxyInstPeer);
338 } catch (IllegalAccessException iae) {
339 throw new Error("Test error.", iae);
340 }
341 }
342 }
343}// class TestDialogTypeAhead
344
345
346/****************************************************
347 Standard Test Machinery
348 DO NOT modify anything below -- it's a standard
349 chunk of code whose purpose is to make user
350 interaction uniform, and thereby make it simpler
351 to read and understand someone else's test.
352 ****************************************************/
353
354/**
355 This is part of the standard test machinery.
356 It creates a dialog (with the instructions), and is the interface
357 for sending text messages to the user.
358 To print the instructions, send an array of strings to Sysout.createDialog
359 WithInstructions method. Put one line of instructions per array entry.
360 To display a message for the tester to see, simply call Sysout.println
361 with the string to be displayed.
362 This mimics System.out.println but works within the test harness as well
363 as standalone.
364 */
365
366class Sysout
367{
368 private static TestDialog dialog;
369
370 public static void createDialogWithInstructions( String[] instructions )
371 {
372 dialog = new TestDialog( new Frame(), "Instructions" );
373 dialog.printInstructions( instructions );
374 dialog.setVisible(true);
375 println( "Any messages for the tester will display here." );
376 }
377
378 public static void createDialog( )
379 {
380 dialog = new TestDialog( new Frame(), "Instructions" );
381 String[] defInstr = { "Instructions will appear here. ", "" } ;
382 dialog.printInstructions( defInstr );
383 dialog.setVisible(true);
384 println( "Any messages for the tester will display here." );
385 }
386
387
388 public static void printInstructions( String[] instructions )
389 {
390 dialog.printInstructions( instructions );
391 }
392
393
394 public static void println( String messageIn )
395 {
396 dialog.displayMessage( messageIn );
397 }
398
399}// Sysout class
400
401/**
402 This is part of the standard test machinery. It provides a place for the
403 test instructions to be displayed, and a place for interactive messages
404 to the user to be displayed.
405 To have the test instructions displayed, see Sysout.
406 To have a message to the user be displayed, see Sysout.
407 Do not call anything in this dialog directly.
408 */
409class TestDialog extends Dialog
410{
411
412 TextArea instructionsText;
413 TextArea messageText;
414 int maxStringLength = 80;
415
416 //DO NOT call this directly, go through Sysout
417 public TestDialog( Frame frame, String name )
418 {
419 super( frame, name );
420 int scrollBoth = TextArea.SCROLLBARS_BOTH;
421 instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth );
422 add( "North", instructionsText );
423
424 messageText = new TextArea( "", 5, maxStringLength, scrollBoth );
425 add("Center", messageText);
426
427 pack();
428
429 show();
430 }// TestDialog()
431
432 //DO NOT call this directly, go through Sysout
433 public void printInstructions( String[] instructions )
434 {
435 //Clear out any current instructions
436 instructionsText.setText( "" );
437
438 //Go down array of instruction strings
439
440 String printStr, remainingStr;
441 for( int i=0; i < instructions.length; i++ )
442 {
443 //chop up each into pieces maxSringLength long
444 remainingStr = instructions[ i ];
445 while( remainingStr.length() > 0 )
446 {
447 //if longer than max then chop off first max chars to print
448 if( remainingStr.length() >= maxStringLength )
449 {
450 //Try to chop on a word boundary
451 int posOfSpace = remainingStr.
452 lastIndexOf( ' ', maxStringLength - 1 );
453
454 if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1;
455
456 printStr = remainingStr.substring( 0, posOfSpace + 1 );
457 remainingStr = remainingStr.substring( posOfSpace + 1 );
458 }
459 //else just print
460 else
461 {
462 printStr = remainingStr;
463 remainingStr = "";
464 }
465
466 instructionsText.append( printStr + "\n" );
467
468 }// while
469
470 }// for
471
472 }//printInstructions()
473
474 //DO NOT call this directly, go through Sysout
475 public void displayMessage( String messageIn )
476 {
477 messageText.append( messageIn + "\n" );
478 System.out.println(messageIn);
479 }
480
481}// TestDialog class