1 /* 2 * Copyright (c) 2007, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 import java.awt.*; 27 import java.awt.event.*; 28 import java.beans.*; 29 import java.lang.reflect.*; 30 import java.util.*; 31 32 import javax.swing.*; 33 34 /** 35 * SwingTestHelper is a utility class for writing AWT/Swing regression 36 * tests that require interacting with the UI. Typically such tests 37 * consist of executing a chunk of code, waiting on an event, executing 38 * more code ... This is painful in that you typically have to use various 39 * invokeLaters and threading to handle that interaction. SwingTestHelper 40 * strealines this process. 41 * <p> 42 * SwingTestHelper uses reflection to invoke all methods starting with 43 * the name <code>onEDT</code> on the EDT and all methods starting with 44 * <code>onBackgroundThread</code> on a background thread. Between each method 45 * invocation all pending events on the EDT are processed. The methods 46 * are first sorted based on an integer after the method names and invoked 47 * in that order. For example, the following subclass: 48 * <pre> 49 * class Test extends SwingTestHelper { 50 * private void onEDT10(); 51 * private void onBackgroundThread20(); 52 * private void onBackgroundThread30(); 53 * private void onEDT40(); 54 * private void onBackgroundThread50(); 55 * } 56 * </pre> 57 * Will have the methods invoked in the order <code>onEDT10</code>, 58 * <code>onBackgroundThread20</code>, <code>onBackgroundThread30</code>, 59 * <code>onEDT40</code>, <code>onBackgroundThread50</code>. 60 * <p> 61 * If you're not happy with method mangling you can also use annotations. 62 * The following gives the same result as the previous example: 63 * <pre> 64 * class Test extends SwingTestHelper { 65 * @Test(10) 66 * private void foo(); // Was onEDT10 67 * 68 * @Test(value=20, onEDT=false) 69 * private void bar(); // Was onBackgroundThread20 70 * 71 * @Test(value=30, onEDT=false) 72 * private void baz(); // Was onBackgroundThread30 73 * 74 * @Test(40) 75 * private void zed(); // Was onEDT40 76 * 77 * @Test(value=50, onEDT=false) 78 * private void onBackgroundThread50(); // Was onBackgroundThread50 79 * } 80 * </pre> 81 * <p> 82 * It is recommended that you increment the value in increments of 83 * 10. This makes it easier to add methods at a later date without 84 * having to change all method names/annotations after the newly added 85 * method. 86 * <p> 87 * Between each of the methods, all pending events (native and Java) 88 * are processed. 89 * <p> 90 * Failure of the test is signaled by any method throwing 91 * an exception, directly invoking <code>fail</code> or one of the 92 * <code>assert</code> variants. If no methods throw an exception the test is 93 * assumed to have passed. 94 * <p> 95 * Often times it is necessary to block until focus has been gained on a 96 * particular widget. This can be handled by the 97 * <code>requestAndWaitForFocus</code> method. It will invoke 98 * <code>requestFocus</code> and block the test (not the EDT) until focus 99 * has been granted to the widget. 100 * <p> 101 * Care must be taken when using <code>Robot</code> directly. For 102 * example, it's tempting to flood <code>Robot</code> with events and 103 * assume they will be received after some delay. Depending upon the 104 * machine you may need to increase the delay. Instead it's 105 * preferrable to block test execution until the event has been 106 * received and processed. This can be done using the method 107 * <code>waitForEvent</code>. For example, to block until a key typed 108 * event has been processed do the following: 109 * <pre> 110 * private void onEDT() { 111 * robot.moveMouseTo(myComponent); 112 * robot.mousePress(xxx); 113 * robot.mouseRelease(xxx); 114 * waitForEvent(myComponent, MouseEvent.MOUSE_RELEASED); 115 * } 116 * </pre> 117 * <p> 118 * Waiting for focus and events are specific examples of a more 119 * general problem. Often times you need the EDT to continue processing 120 * events, but want to block test execution until something happens. 121 * In the case of focus you want to block test execution until focus 122 * is gained. The method <code>waitForCondition</code> can be used to 123 * block test execution until the supplied <code>Runnable</code> returns. The 124 * <code>Runnable</code> is invoked on the background thread. 125 * <p> 126 * To use this class you will need to do the following: 127 * <ol> 128 * <li>Override the method <code>createContentPane</code>. All of your logic 129 * for setting up the test environment should go here. This method is 130 * invoked on the EDT. 131 * <li>Implement the necessary <code>onEDTXX</code> and 132 * <code>onBackgroundThreadXXX</code> methods to do the actual testing. 133 * <li>Make your <code>main</code> method look like: 134 * <code>new MySwingTestHelper().run(args)</code>. This will block 135 * until the test fails or succeeds. 136 * <li>To use this with jtreg you'll need to have something like: 137 * <pre> 138 * @library ../../../regtesthelpers 139 * @build Test JRobot Assert SwingTestHelper 140 * @run main MySwingTestHelper 141 * * </pre> 142 * </ol> 143 * <p> 144 * Here's a complete example: 145 * <pre> 146 * public class bug4852305 extends SwingTestHelper { 147 * private JTable table; 148 * 149 * public static void main(String[] args) throws Throwable { 150 * new bug4852305().run(args); 151 * } 152 * 153 * protected Component createContentPane() { 154 * DefaultTableModel model = new DefaultTableModel(1, 2); 155 * model.setValueAt("x", 0, 0); 156 * model.setValueAt("z", 0, 1); 157 * table = new JTable(model); 158 * table.setDefaultEditor(Object.class, new DefaultCellEditor(new JTextField()) { 159 * public boolean isCellEditable(EventObject anEvent) { 160 * if ((anEvent instanceof KeyEvent) || 161 * (anEvent instanceof ActionEvent)) { 162 * return false; 163 * } 164 * return true; 165 * } 166 * }); 167 * return new JScrollPane(table); 168 * } 169 * 170 * private void onEDT10() { 171 * requestAndWaitForFocus(table); 172 * } 173 * 174 * private void onEDT20() { 175 * robot.keyPress(KeyEvent.VK_A); 176 * robot.keyRelease(KeyEvent.VK_A); 177 * waitForEvent(table, KeyEvent.KEY_RELEASED); 178 * } 179 * 180 * private void onEDT30() { 181 * if (table.isEditing()) { 182 * fail("Should not be editing"); 183 * } 184 * } 185 * } 186 * </pre> 187 * 188 * 189 * @author Scott Violet 190 */ 191 public abstract class SwingTestHelper { 192 private static final String ON_EDT_METHOD_NAME = "onEDT"; 193 private static final String IN_BACKGROUND_METHOD_NAME = "onBackgroundThread"; 194 195 // Whether or not we've installed a PropertyChangeListener on the 196 // KeyboardFocusManager 197 private boolean installedFocusListener; 198 // Component currently blocking on until focus has been received. 199 private Component componentWaitingForFocus; 200 201 // Set to true when done. 202 private boolean done; 203 // If failed, this gives the exception. Only the first exception is 204 // kept. 205 private Throwable error; 206 207 // List of methods to invoke 208 private java.util.List<Method> methods; 209 210 // The conditions the background thread is blocked on. 211 private java.util.List<Runnable> conditions; 212 213 // Whether or not we've installed the AWTEventListener 214 private boolean installedEventListener; 215 216 /** 217 * Instance of <code>Robot</code> returned from <code>createRobot</code>. 218 */ 219 protected JRobot robot; 220 221 /** 222 * <code>Window</code> returned from <code>createWindow</code>. 223 */ 224 protected Window window; 225 226 // Listens for the first paint event 227 private AWTEventListener paintListener; 228 // Whether or not we've received a paint event. 229 private boolean receivedFirstPaint; 230 231 // used if the user wants to slow down method processing 232 private PauseCondition delay = null; 233 234 private boolean showProgress; 235 private JProgressBar progBar; 236 237 SwingTestHelper()238 public SwingTestHelper() { 239 paintListener = new AWTEventListener() { 240 public void eventDispatched(AWTEvent ev) { 241 if ((ev.getID() & PaintEvent.PAINT) != 0 && 242 ev.getSource() == window) { 243 synchronized(SwingTestHelper.this) { 244 if (receivedFirstPaint) { 245 return; 246 } 247 receivedFirstPaint = true; 248 } 249 Toolkit.getDefaultToolkit().removeAWTEventListener( 250 paintListener); 251 startControlLoop(); 252 } 253 } 254 }; 255 Toolkit.getDefaultToolkit().addAWTEventListener( 256 paintListener, AWTEvent.PAINT_EVENT_MASK); 257 } 258 259 /** 260 * Sets whether SwingTestHelper should use {@code SunToolkit.realSync} 261 * to wait for events to finish, or {@code Robot.waitForIdle}. The default 262 * is to use realSync. 263 * Nov 2014: no realSync any more, just robot.waitForIdle which actually 264 * _is_ realSync on all platforms but OS X (and thus cannot be used on EDT). 265 */ setUseRealSync(boolean useRealSync)266 public void setUseRealSync(boolean useRealSync) { 267 //NOOP 268 } 269 270 /** 271 * Set the amount of time to delay between invoking methods in 272 * the control loop. Useful to slow down testing. 273 */ setDelay(int delay)274 protected void setDelay(int delay) { 275 if (delay <= 0) { 276 this.delay = null; 277 } else { 278 this.delay = new PauseCondition(delay); 279 } 280 } 281 282 /** 283 * Sets whether or not progress through the list of methods is 284 * shown by a progress bar at the bottom of the window created 285 * by {@code createWindow}. 286 */ setShowProgress(boolean showProgress)287 protected void setShowProgress(boolean showProgress) { 288 this.showProgress = showProgress; 289 } 290 291 /** 292 * Creates and returns the <code>Window</code> for the test. This 293 * implementation returns a JFrame with a default close operation 294 * of <code>EXIT_ON_CLOSE</code>. The <code>Component</code> 295 * returned from <code>createContentPane</code> is added the 296 * <code>JFrame</code> and the the frame is packed. 297 * <p> 298 * Typically you only need override <code>createContentPane</code>. 299 */ createWindow()300 protected Window createWindow() { 301 JFrame frame = new JFrame("Test: " + getClass().getName()); 302 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 303 frame.add(createContentPane()); 304 if (showProgress) { 305 progBar = new JProgressBar(); 306 progBar.setString(""); 307 progBar.setStringPainted(true); 308 frame.add(progBar, BorderLayout.SOUTH); 309 } 310 frame.pack(); 311 return frame; 312 } 313 314 /** 315 * Returns the <code>Component</code> to place in the frame. 316 * Override this or the <code>createWindow</code> method. 317 */ createContentPane()318 protected Component createContentPane() { 319 return null; 320 } 321 322 /** 323 * Invokes <code>requestFocus</code> on the passed in component (assuming 324 * it doesn't already have focus). Test execution is blocked until focus 325 * has been gained on the component. This method <b>must</b> be invoked 326 * on the EDT, if you do not invoke it from the edt the test will fail. 327 * 328 * @param c the <code>Component</code> to wait for focus on 329 */ requestAndWaitForFocus(Component c)330 protected void requestAndWaitForFocus(Component c) { 331 requestAndWaitForFocus(c, true); 332 } 333 334 /** 335 * Blocks test execution until focus is gained on the component. 336 * This method <b>must</b> be invoked 337 * on the EDT, if you do not invoke it from the edt the test will fail. 338 */ waitForFocusGained(Component c)339 protected void waitForFocusGained(Component c) { 340 requestAndWaitForFocus(c, false); 341 } 342 requestAndWaitForFocus(Component c, boolean requestFocus)343 private void requestAndWaitForFocus(Component c, boolean requestFocus) { 344 if (!EventQueue.isDispatchThread()) { 345 System.out.println( 346 "requestAndWaitForFocus should be invoked on EDT"); 347 throw new RuntimeException(); 348 } 349 if (componentWaitingForFocus != null) { 350 System.out.println("Already waiting for focus"); 351 throw new RuntimeException(); 352 } 353 if (!installedFocusListener) { 354 installedFocusListener = true; 355 KeyboardFocusManager.getCurrentKeyboardFocusManager(). 356 addPropertyChangeListener(new FocusListener()); 357 } 358 synchronized(this) { 359 if (c.hasFocus()) { 360 return; 361 } 362 componentWaitingForFocus = c; 363 } 364 if (requestFocus) { 365 c.requestFocus(); 366 } 367 waitForCondition(new FocusCondition()); 368 } 369 370 /** 371 * Blocks test execution until the specified event has been received. 372 * This method immediately returns and the EDT will continue to 373 * process events, but test execution is blocked until 374 * the event is received. 375 * 376 * @param event the event type to wait for 377 */ waitForEvent(int event)378 protected void waitForEvent(int event) { 379 waitForEvent(null, event); 380 } 381 382 /** 383 * Blocks test execution until the specified event has been received. 384 * This method immediately returns and the EDT will continue to 385 * process events, but test execution is blocked until 386 * the event is received. 387 * 388 * @param target the <code>Component</code> to wait for the event on; 389 * <code>null</code> indicates it does not matter which 390 * component the event is received on 391 * @param event the event type to wait for 392 */ waitForEvent(Component target, int event)393 protected void waitForEvent(Component target, int event) { 394 waitForCondition(new EventCondition(target, event)); 395 if (!installedEventListener) { 396 installedEventListener = true; 397 Toolkit.getDefaultToolkit().addAWTEventListener( 398 new EventListener(), 0xFFFFFFFFFFFFFFFFl); 399 } 400 } 401 402 /** 403 * Paused test execution for the specified amount of time. The caller 404 * immediately returns and the EDT can process events. 405 * 406 * @param time the amount of time, in milliseconds, to pause for 407 */ pause(int time)408 protected void pause(int time) { 409 waitForCondition(new PauseCondition(time)); 410 } 411 412 /** 413 * Schedules a <code>Runnable</code> that will be processed in the 414 * background thread. This method immediately returns, and the 415 * EDT is free to continue processing events. Test execution is 416 * blocked until the <code>Runnable</code> completes. 417 */ waitForCondition(Runnable runnable)418 protected void waitForCondition(Runnable runnable) { 419 synchronized(this) { 420 if (conditions == null) { 421 conditions = new LinkedList<Runnable>(); 422 } 423 conditions.add(runnable); 424 } 425 } 426 427 /** 428 * Runs the test. This method blocks the caller until the test 429 * fails or succeeds. Recognized arguments are: 430 * <p> 431 * "-exit": Causes main to exit when the test is done. 432 * "-showProg": Indicate the progress of the test with a 433 * progress bar in the main window. Only works 434 * if the test hasn't overridden {@code createWindow}. 435 * "-delay int": Sets the delay between executing methods. 436 * Useful when you want to slow a test to watch it. 437 * 438 * @param args the arguments from main, it's ok to pass in null 439 */ run(String[] args)440 protected final void run(String[] args) throws Throwable { 441 boolean exit = false; 442 if (args != null) { 443 for (int i = 0; i < args.length; i++) { 444 if (args[i].equals("-exit")) { 445 exit = true; 446 } else if (args[i].equals("-delay")) { 447 try { 448 setDelay(Integer.parseInt(args[++i])); 449 } catch (NumberFormatException ne) { 450 throw new RuntimeException("-delay requires an integer value"); 451 } catch (ArrayIndexOutOfBoundsException ae) { 452 throw new RuntimeException("-delay requires an integer value"); 453 } 454 } else if (args[i].equals("-showProg")) { 455 setShowProgress(true); 456 } else { 457 throw new RuntimeException("Invalid argument \"" + args[i] + "\""); 458 } 459 } 460 } 461 462 createWindow0(); 463 synchronized(this) { 464 while(!done) { 465 wait(); 466 } 467 } 468 if (exit) { 469 // Not in harness 470 if (error != null) { 471 System.out.println("FAILED: " + error); 472 error.printStackTrace(); 473 } 474 System.exit(0); 475 } 476 if (error != null) { 477 throw error; 478 } 479 } 480 481 /** 482 * Creates the window, on the EDT. 483 */ createWindow0()484 private void createWindow0() { 485 EventQueue.invokeLater(new Runnable() { 486 public void run() { 487 window = createWindow(); 488 window.show(); 489 } 490 }); 491 } 492 493 /** 494 * Initializes the progress bar if necessary. 495 */ initProgressBar(final int size)496 private void initProgressBar(final int size) { 497 EventQueue.invokeLater(new Runnable() { 498 public void run() { 499 if (progBar != null) { 500 progBar.setMaximum(size); 501 } 502 } 503 }); 504 } 505 506 /** 507 * Starst the control loop. 508 */ startControlLoop()509 private void startControlLoop() { 510 robot = createRobot(); 511 if (robot != null) { 512 calculateMethods(); 513 initProgressBar(methods.size()); 514 new Thread(new Runnable() { 515 public void run() { 516 controlLoop(); 517 } 518 }).start(); 519 } 520 } 521 522 /** 523 * Increment the progress bar. 524 */ nextProgress(final String name)525 private void nextProgress(final String name) { 526 EventQueue.invokeLater(new Runnable() { 527 public void run() { 528 if (progBar != null) { 529 progBar.setString(name); 530 progBar.setValue(progBar.getValue() + 1); 531 } 532 } 533 }); 534 } 535 currentCondition()536 private synchronized Runnable currentCondition() { 537 if (conditions != null && conditions.size() > 0) { 538 return conditions.get(0); 539 } 540 return null; 541 } 542 nextCondition()543 private synchronized Runnable nextCondition() { 544 return conditions.remove(0); 545 } 546 controlLoop()547 private void controlLoop() { 548 int methodIndex = 0; 549 while (methodIndex < methods.size()) { 550 // Wait for any pending conditions 551 Runnable condition; 552 while ((condition = currentCondition()) != null) { 553 try { 554 condition.run(); 555 } catch (Exception e) { 556 fail(e); 557 return; 558 } 559 waitForEDTToFinish(); 560 synchronized(this) { 561 if (done) { 562 return; 563 } 564 } 565 // Advance to next condition 566 nextCondition(); 567 } 568 569 // Let all events on the EDT finish 570 waitForEDTToFinish(); 571 572 if (delay != null) { 573 delay.run(); 574 } 575 576 // Invoke the next method 577 Method method = methods.get(methodIndex++); 578 Test test = method.getAnnotation(Test.class); 579 boolean onEDT = true; 580 if (test != null) { 581 onEDT = test.onEDT(); 582 } 583 else if (!method.getName().startsWith(ON_EDT_METHOD_NAME)) { 584 onEDT = false; 585 } 586 if (onEDT) { 587 invokeOnEDT(method); 588 } 589 else { 590 invoke(method); 591 } 592 593 // Let all events on the EDT finish 594 waitForEDTToFinish(); 595 596 nextProgress(method.getName()); 597 598 // If done, stop. 599 synchronized(this) { 600 if (done) { 601 return; 602 } 603 } 604 } 605 606 // No more methods, if we get and done isn't true, set it true 607 // so that the main thread wakes up. 608 synchronized(this) { 609 if (!done) { 610 done = true; 611 notifyAll(); 612 } 613 } 614 } 615 waitForEDTToFinish()616 private void waitForEDTToFinish() { 617 robot.waitForIdle(); 618 } 619 invokeOnEDT(final Method method)620 private void invokeOnEDT(final Method method) { 621 try { 622 EventQueue.invokeAndWait(new Runnable() { 623 public void run() { 624 invoke(method); 625 } 626 }); 627 } catch (InvocationTargetException ite) { 628 fail(ite); 629 } catch (InterruptedException ie) { 630 fail(ie); 631 } 632 } 633 invoke(Method method)634 private void invoke(Method method) { 635 System.out.println("invoking: " + method.getName()); 636 try { 637 if (Modifier.isPrivate(method.getModifiers())) { 638 method.setAccessible(true); 639 } 640 method.invoke(this); 641 } catch (Exception e) { 642 fail(e); 643 } 644 } 645 646 // Determines the methods to execute. calculateMethods()647 private void calculateMethods() { 648 // Using a Set avoids duplicating methods returned by both 649 // getMethods() and getDeclaredMethods(). 650 HashSet<Method> allMethods = new HashSet<Method>(); 651 allMethods.addAll(Arrays.asList(getClass().getMethods())); 652 allMethods.addAll(Arrays.asList(getClass().getDeclaredMethods())); 653 654 methods = new ArrayList<Method>(); 655 for (Method method : allMethods) { 656 Test test = method.getAnnotation(Test.class); 657 if (test != null) { 658 methods.add(method); 659 } 660 else if (method.getName().startsWith(ON_EDT_METHOD_NAME)) { 661 methods.add(method); 662 } 663 else if (method.getName().startsWith(IN_BACKGROUND_METHOD_NAME)) { 664 methods.add(method); 665 } 666 } 667 Comparator<Method> comparator = new Comparator<Method>() { 668 public int compare(Method m1, Method m2) { 669 int index1 = getIndex(m1); 670 int index2 = getIndex(m2); 671 return index1 - index2; 672 } 673 private int getIndex(Method m) { 674 String name = m.getName(); 675 String indexAsString; 676 Test test = m.getAnnotation(Test.class); 677 if (test != null) { 678 return test.value(); 679 } 680 if (name.startsWith(ON_EDT_METHOD_NAME)) { 681 indexAsString = name.substring( 682 ON_EDT_METHOD_NAME.length()); 683 } 684 else { 685 indexAsString = name.substring( 686 IN_BACKGROUND_METHOD_NAME.length()); 687 } 688 if (indexAsString.length() == 0) { 689 System.out.println( 690 "onEDT and onBackgroundThread must be " + 691 "followed by an integer specifying " + 692 "order."); 693 System.exit(0); 694 } 695 return Integer.parseInt(indexAsString); 696 } 697 }; 698 Collections.sort(methods, comparator); 699 } 700 701 /** 702 * Invoke if the test should be considered to have failed. This will 703 * stop test execution. 704 */ fail(String reason)705 public void fail(String reason) { 706 fail(new RuntimeException(reason)); 707 } 708 709 /** 710 * Invoke if the test should be considered to have failed. This will 711 * stop test execution. 712 */ fail(Throwable error)713 public void fail(Throwable error) { 714 synchronized(this) { 715 if (this.error == null) { 716 if (error instanceof InvocationTargetException) { 717 this.error = ((InvocationTargetException)error). 718 getCause(); 719 } 720 else { 721 this.error = error; 722 } 723 this.done = true; 724 notifyAll(); 725 } 726 } 727 } 728 729 /** 730 * Invoke to prematurely stop test execution while there are remaining 731 * methods. You typically don't invoke this, instead if all methods have 732 * been executed and fail hasn't been invoked, the test is considered to 733 * have passed. 734 */ succeeded()735 public void succeeded() { 736 synchronized(this) { 737 this.done = true; 738 notifyAll(); 739 } 740 } 741 742 /** 743 * Creates and returns the Robot that will be used. You generally don't 744 * need to override this. 745 */ createRobot()746 protected JRobot createRobot() { 747 JRobot robot = JRobot.getRobot(false); 748 return robot; 749 } 750 751 752 private class FocusListener implements PropertyChangeListener { propertyChange(PropertyChangeEvent e)753 public void propertyChange(PropertyChangeEvent e) { 754 if (componentWaitingForFocus != null && 755 "focusOwner".equals(e.getPropertyName()) && 756 componentWaitingForFocus == e.getNewValue()) { 757 synchronized(SwingTestHelper.this) { 758 componentWaitingForFocus = null; 759 SwingTestHelper.this.notifyAll(); 760 } 761 } 762 } 763 } 764 765 766 private class EventCondition implements Runnable { 767 private Component component; 768 private int eventID; 769 private boolean received; 770 EventCondition(Component component, int eventID)771 EventCondition(Component component, int eventID) { 772 this.component = component; 773 this.eventID = eventID; 774 } 775 getEventID()776 public int getEventID() { 777 return eventID; 778 } 779 getComponent()780 public Component getComponent() { 781 return component; 782 } 783 received()784 public void received() { 785 synchronized(SwingTestHelper.this) { 786 this.received = true; 787 SwingTestHelper.this.notifyAll(); 788 } 789 } 790 isWaiting()791 public boolean isWaiting() { 792 return !received; 793 } 794 run()795 public void run() { 796 synchronized(SwingTestHelper.this) { 797 while (!received) { 798 try { 799 SwingTestHelper.this.wait(); 800 } catch (InterruptedException ie) { 801 fail(ie); 802 } 803 } 804 } 805 } 806 } 807 808 809 private class FocusCondition implements Runnable { run()810 public void run() { 811 synchronized(SwingTestHelper.this) { 812 while (componentWaitingForFocus != null) { 813 try { 814 SwingTestHelper.this.wait(); 815 } catch (InterruptedException ie) { 816 fail(ie); 817 } 818 } 819 } 820 } 821 } 822 823 824 private class PauseCondition implements Runnable { 825 private int time; PauseCondition(int time)826 PauseCondition(int time) { 827 this.time = time; 828 } run()829 public void run() { 830 try { 831 Thread.sleep(time); 832 } catch (InterruptedException ie) { 833 fail(ie); 834 } 835 } 836 } 837 838 839 private class EventListener implements AWTEventListener { eventDispatched(AWTEvent ev)840 public void eventDispatched(AWTEvent ev) { 841 int eventID = ev.getID(); 842 synchronized (SwingTestHelper.this) { 843 for (Runnable condition : conditions) { 844 if (condition instanceof EventCondition) { 845 EventCondition ec = (EventCondition)condition; 846 if (ec.isWaiting()) { 847 if (eventID == ec.getEventID() && 848 (ec.getComponent() == null || 849 ev.getSource() == ec.getComponent())) { 850 ec.received(); 851 } 852 return; 853 } 854 } 855 else { 856 return; 857 } 858 } 859 } 860 } 861 } 862 } 863