| /* | |
| * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| import java.awt.AWTEvent; | |
| import java.awt.BorderLayout; | |
| import java.awt.Component; | |
| import java.awt.EventQueue; | |
| import java.awt.KeyboardFocusManager; | |
| import java.awt.Toolkit; | |
| import java.awt.Window; | |
| import java.awt.event.AWTEventListener; | |
| import java.awt.event.PaintEvent; | |
| import java.beans.PropertyChangeEvent; | |
| import java.beans.PropertyChangeListener; | |
| import java.lang.reflect.InvocationTargetException; | |
| import java.lang.reflect.Method; | |
| import java.lang.reflect.Modifier; | |
| import java.util.ArrayList; | |
| import java.util.Arrays; | |
| import java.util.Collections; | |
| import java.util.Comparator; | |
| import java.util.HashSet; | |
| import java.util.LinkedList; | |
| import javax.swing.JFrame; | |
| import javax.swing.JProgressBar; | |
| /** | |
| * SwingTestHelper is a utility class for writing AWT/Swing regression | |
| * tests that require interacting with the UI. Typically such tests | |
| * consist of executing a chunk of code, waiting on an event, executing | |
| * more code ... This is painful in that you typically have to use various | |
| * invokeLaters and threading to handle that interaction. SwingTestHelper | |
| * strealines this process. | |
| * <p> | |
| * SwingTestHelper uses reflection to invoke all methods starting with | |
| * the name <code>onEDT</code> on the EDT and all methods starting with | |
| * <code>onBackgroundThread</code> on a background thread. Between each method | |
| * invocation all pending events on the EDT are processed. The methods | |
| * are first sorted based on an integer after the method names and invoked | |
| * in that order. For example, the following subclass: | |
| * <pre> | |
| * class Test extends SwingTestHelper { | |
| * private void onEDT10(); | |
| * private void onBackgroundThread20(); | |
| * private void onBackgroundThread30(); | |
| * private void onEDT40(); | |
| * private void onBackgroundThread50(); | |
| * } | |
| * </pre> | |
| * Will have the methods invoked in the order <code>onEDT10</code>, | |
| * <code>onBackgroundThread20</code>, <code>onBackgroundThread30</code>, | |
| * <code>onEDT40</code>, <code>onBackgroundThread50</code>. | |
| * <p> | |
| * If you're not happy with method mangling you can also use annotations. | |
| * The following gives the same result as the previous example: | |
| * <pre> | |
| * class Test extends SwingTestHelper { | |
| * @Test(10) | |
| * private void foo(); // Was onEDT10 | |
| * | |
| * @Test(value=20, onEDT=false) | |
| * private void bar(); // Was onBackgroundThread20 | |
| * | |
| * @Test(value=30, onEDT=false) | |
| * private void baz(); // Was onBackgroundThread30 | |
| * | |
| * @Test(40) | |
| * private void zed(); // Was onEDT40 | |
| * | |
| * @Test(value=50, onEDT=false) | |
| * private void onBackgroundThread50(); // Was onBackgroundThread50 | |
| * } | |
| * </pre> | |
| * <p> | |
| * It is recommended that you increment the value in increments of | |
| * 10. This makes it easier to add methods at a later date without | |
| * having to change all method names/annotations after the newly added | |
| * method. | |
| * <p> | |
| * Between each of the methods, all pending events (native and Java) | |
| * are processed. | |
| * <p> | |
| * Failure of the test is signaled by any method throwing | |
| * an exception, directly invoking <code>fail</code> or one of the | |
| * <code>assert</code> variants. If no methods throw an exception the test is | |
| * assumed to have passed. | |
| * <p> | |
| * Often times it is necessary to block until focus has been gained on a | |
| * particular widget. This can be handled by the | |
| * <code>requestAndWaitForFocus</code> method. It will invoke | |
| * <code>requestFocus</code> and block the test (not the EDT) until focus | |
| * has been granted to the widget. | |
| * <p> | |
| * Care must be taken when using <code>Robot</code> directly. For | |
| * example, it's tempting to flood <code>Robot</code> with events and | |
| * assume they will be received after some delay. Depending upon the | |
| * machine you may need to increase the delay. Instead it's | |
| * preferrable to block test execution until the event has been | |
| * received and processed. This can be done using the method | |
| * <code>waitForEvent</code>. For example, to block until a key typed | |
| * event has been processed do the following: | |
| * <pre> | |
| * private void onEDT() { | |
| * robot.moveMouseTo(myComponent); | |
| * robot.mousePress(xxx); | |
| * robot.mouseRelease(xxx); | |
| * waitForEvent(myComponent, MouseEvent.MOUSE_RELEASED); | |
| * } | |
| * </pre> | |
| * <p> | |
| * Waiting for focus and events are specific examples of a more | |
| * general problem. Often times you need the EDT to continue processing | |
| * events, but want to block test execution until something happens. | |
| * In the case of focus you want to block test execution until focus | |
| * is gained. The method <code>waitForCondition</code> can be used to | |
| * block test execution until the supplied <code>Runnable</code> returns. The | |
| * <code>Runnable</code> is invoked on the background thread. | |
| * <p> | |
| * To use this class you will need to do the following: | |
| * <ol> | |
| * <li>Override the method <code>createContentPane</code>. All of your logic | |
| * for setting up the test environment should go here. This method is | |
| * invoked on the EDT. | |
| * <li>Implement the necessary <code>onEDTXX</code> and | |
| * <code>onBackgroundThreadXXX</code> methods to do the actual testing. | |
| * <li>Make your <code>main</code> method look like: | |
| * <code>new MySwingTestHelper().run(args)</code>. This will block | |
| * until the test fails or succeeds. | |
| * <li>To use this with jtreg you'll need to have something like: | |
| * <pre> | |
| * @library ../../../regtesthelpers | |
| * @build Test JRobot Assert SwingTestHelper | |
| * @run main MySwingTestHelper | |
| * * </pre> | |
| * </ol> | |
| * <p> | |
| * Here's a complete example: | |
| * <pre> | |
| * public class bug4852305 extends SwingTestHelper { | |
| * private JTable table; | |
| * | |
| * public static void main(String[] args) throws Throwable { | |
| * new bug4852305().run(args); | |
| * } | |
| * | |
| * protected Component createContentPane() { | |
| * DefaultTableModel model = new DefaultTableModel(1, 2); | |
| * model.setValueAt("x", 0, 0); | |
| * model.setValueAt("z", 0, 1); | |
| * table = new JTable(model); | |
| * table.setDefaultEditor(Object.class, new DefaultCellEditor(new JTextField()) { | |
| * public boolean isCellEditable(EventObject anEvent) { | |
| * if ((anEvent instanceof KeyEvent) || | |
| * (anEvent instanceof ActionEvent)) { | |
| * return false; | |
| * } | |
| * return true; | |
| * } | |
| * }); | |
| * return new JScrollPane(table); | |
| * } | |
| * | |
| * private void onEDT10() { | |
| * requestAndWaitForFocus(table); | |
| * } | |
| * | |
| * private void onEDT20() { | |
| * robot.keyPress(KeyEvent.VK_A); | |
| * robot.keyRelease(KeyEvent.VK_A); | |
| * waitForEvent(table, KeyEvent.KEY_RELEASED); | |
| * } | |
| * | |
| * private void onEDT30() { | |
| * if (table.isEditing()) { | |
| * fail("Should not be editing"); | |
| * } | |
| * } | |
| * } | |
| * </pre> | |
| * | |
| * | |
| * @author Scott Violet | |
| */ | |
| public abstract class SwingTestHelper { | |
| private static final String ON_EDT_METHOD_NAME = "onEDT"; | |
| private static final String IN_BACKGROUND_METHOD_NAME = "onBackgroundThread"; | |
| // Whether or not we've installed a PropertyChangeListener on the | |
| // KeyboardFocusManager | |
| private boolean installedFocusListener; | |
| // Component currently blocking on until focus has been received. | |
| private Component componentWaitingForFocus; | |
| // Set to true when done. | |
| private boolean done; | |
| // If failed, this gives the exception. Only the first exception is | |
| // kept. | |
| private Throwable error; | |
| // List of methods to invoke | |
| private java.util.List<Method> methods; | |
| // The conditions the background thread is blocked on. | |
| private java.util.List<Runnable> conditions; | |
| // Whether or not we've installed the AWTEventListener | |
| private boolean installedEventListener; | |
| /** | |
| * Instance of <code>Robot</code> returned from <code>createRobot</code>. | |
| */ | |
| protected JRobot robot; | |
| /** | |
| * <code>Window</code> returned from <code>createWindow</code>. | |
| */ | |
| protected Window window; | |
| // Listens for the first paint event | |
| private AWTEventListener paintListener; | |
| // Whether or not we've received a paint event. | |
| private boolean receivedFirstPaint; | |
| // used if the user wants to slow down method processing | |
| private PauseCondition delay = null; | |
| private boolean showProgress; | |
| private JProgressBar progBar; | |
| public SwingTestHelper() { | |
| paintListener = new AWTEventListener() { | |
| public void eventDispatched(AWTEvent ev) { | |
| if ((ev.getID() & PaintEvent.PAINT) != 0 && | |
| ev.getSource() == window) { | |
| synchronized(SwingTestHelper.this) { | |
| if (receivedFirstPaint) { | |
| return; | |
| } | |
| receivedFirstPaint = true; | |
| } | |
| Toolkit.getDefaultToolkit().removeAWTEventListener( | |
| paintListener); | |
| startControlLoop(); | |
| } | |
| } | |
| }; | |
| Toolkit.getDefaultToolkit().addAWTEventListener( | |
| paintListener, AWTEvent.PAINT_EVENT_MASK); | |
| } | |
| /** | |
| * Sets whether SwingTestHelper should use {@code SunToolkit.realSync} | |
| * to wait for events to finish, or {@code Robot.waitForIdle}. The default | |
| * is to use realSync. | |
| * Nov 2014: no realSync any more, just robot.waitForIdle which actually | |
| * _is_ realSync on all platforms but OS X (and thus cannot be used on EDT). | |
| */ | |
| public void setUseRealSync(boolean useRealSync) { | |
| //NOOP | |
| } | |
| /** | |
| * Set the amount of time to delay between invoking methods in | |
| * the control loop. Useful to slow down testing. | |
| */ | |
| protected void setDelay(int delay) { | |
| if (delay <= 0) { | |
| this.delay = null; | |
| } else { | |
| this.delay = new PauseCondition(delay); | |
| } | |
| } | |
| /** | |
| * Sets whether or not progress through the list of methods is | |
| * shown by a progress bar at the bottom of the window created | |
| * by {@code createWindow}. | |
| */ | |
| protected void setShowProgress(boolean showProgress) { | |
| this.showProgress = showProgress; | |
| } | |
| /** | |
| * Creates and returns the <code>Window</code> for the test. This | |
| * implementation returns a JFrame with a default close operation | |
| * of <code>EXIT_ON_CLOSE</code>. The <code>Component</code> | |
| * returned from <code>createContentPane</code> is added the | |
| * <code>JFrame</code> and the frame is packed. | |
| * <p> | |
| * Typically you only need override <code>createContentPane</code>. | |
| */ | |
| protected Window createWindow() { | |
| JFrame frame = new JFrame("Test: " + getClass().getName()); | |
| frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | |
| frame.add(createContentPane()); | |
| if (showProgress) { | |
| progBar = new JProgressBar(); | |
| progBar.setString(""); | |
| progBar.setStringPainted(true); | |
| frame.add(progBar, BorderLayout.SOUTH); | |
| } | |
| frame.pack(); | |
| return frame; | |
| } | |
| /** | |
| * Returns the <code>Component</code> to place in the frame. | |
| * Override this or the <code>createWindow</code> method. | |
| */ | |
| protected Component createContentPane() { | |
| return null; | |
| } | |
| /** | |
| * Invokes <code>requestFocus</code> on the passed in component (assuming | |
| * it doesn't already have focus). Test execution is blocked until focus | |
| * has been gained on the component. This method <b>must</b> be invoked | |
| * on the EDT, if you do not invoke it from the edt the test will fail. | |
| * | |
| * @param c the <code>Component</code> to wait for focus on | |
| */ | |
| protected void requestAndWaitForFocus(Component c) { | |
| requestAndWaitForFocus(c, true); | |
| } | |
| /** | |
| * Blocks test execution until focus is gained on the component. | |
| * This method <b>must</b> be invoked | |
| * on the EDT, if you do not invoke it from the edt the test will fail. | |
| */ | |
| protected void waitForFocusGained(Component c) { | |
| requestAndWaitForFocus(c, false); | |
| } | |
| private void requestAndWaitForFocus(Component c, boolean requestFocus) { | |
| if (!EventQueue.isDispatchThread()) { | |
| System.out.println( | |
| "requestAndWaitForFocus should be invoked on EDT"); | |
| throw new RuntimeException(); | |
| } | |
| if (componentWaitingForFocus != null) { | |
| System.out.println("Already waiting for focus"); | |
| throw new RuntimeException(); | |
| } | |
| if (!installedFocusListener) { | |
| installedFocusListener = true; | |
| KeyboardFocusManager.getCurrentKeyboardFocusManager(). | |
| addPropertyChangeListener(new FocusListener()); | |
| } | |
| synchronized(this) { | |
| if (c.hasFocus()) { | |
| return; | |
| } | |
| componentWaitingForFocus = c; | |
| } | |
| if (requestFocus) { | |
| c.requestFocus(); | |
| } | |
| waitForCondition(new FocusCondition()); | |
| } | |
| /** | |
| * Blocks test execution until the specified event has been received. | |
| * This method immediately returns and the EDT will continue to | |
| * process events, but test execution is blocked until | |
| * the event is received. | |
| * | |
| * @param event the event type to wait for | |
| */ | |
| protected void waitForEvent(int event) { | |
| waitForEvent(null, event); | |
| } | |
| /** | |
| * Blocks test execution until the specified event has been received. | |
| * This method immediately returns and the EDT will continue to | |
| * process events, but test execution is blocked until | |
| * the event is received. | |
| * | |
| * @param target the <code>Component</code> to wait for the event on; | |
| * <code>null</code> indicates it does not matter which | |
| * component the event is received on | |
| * @param event the event type to wait for | |
| */ | |
| protected void waitForEvent(Component target, int event) { | |
| waitForCondition(new EventCondition(target, event)); | |
| if (!installedEventListener) { | |
| installedEventListener = true; | |
| Toolkit.getDefaultToolkit().addAWTEventListener( | |
| new EventListener(), 0xFFFFFFFFFFFFFFFFl); | |
| } | |
| } | |
| /** | |
| * Paused test execution for the specified amount of time. The caller | |
| * immediately returns and the EDT can process events. | |
| * | |
| * @param time the amount of time, in milliseconds, to pause for | |
| */ | |
| protected void pause(int time) { | |
| waitForCondition(new PauseCondition(time)); | |
| } | |
| /** | |
| * Schedules a <code>Runnable</code> that will be processed in the | |
| * background thread. This method immediately returns, and the | |
| * EDT is free to continue processing events. Test execution is | |
| * blocked until the <code>Runnable</code> completes. | |
| */ | |
| protected void waitForCondition(Runnable runnable) { | |
| synchronized(this) { | |
| if (conditions == null) { | |
| conditions = new LinkedList<Runnable>(); | |
| } | |
| conditions.add(runnable); | |
| } | |
| } | |
| /** | |
| * Runs the test. This method blocks the caller until the test | |
| * fails or succeeds. Recognized arguments are: | |
| * <p> | |
| * "-exit": Causes main to exit when the test is done. | |
| * "-showProg": Indicate the progress of the test with a | |
| * progress bar in the main window. Only works | |
| * if the test hasn't overridden {@code createWindow}. | |
| * "-delay int": Sets the delay between executing methods. | |
| * Useful when you want to slow a test to watch it. | |
| * | |
| * @param args the arguments from main, it's ok to pass in null | |
| */ | |
| protected final void run(String[] args) throws Throwable { | |
| boolean exit = false; | |
| if (args != null) { | |
| for (int i = 0; i < args.length; i++) { | |
| if (args[i].equals("-exit")) { | |
| exit = true; | |
| } else if (args[i].equals("-delay")) { | |
| try { | |
| setDelay(Integer.parseInt(args[++i])); | |
| } catch (NumberFormatException ne) { | |
| throw new RuntimeException("-delay requires an integer value"); | |
| } catch (ArrayIndexOutOfBoundsException ae) { | |
| throw new RuntimeException("-delay requires an integer value"); | |
| } | |
| } else if (args[i].equals("-showProg")) { | |
| setShowProgress(true); | |
| } else { | |
| throw new RuntimeException("Invalid argument \"" + args[i] + "\""); | |
| } | |
| } | |
| } | |
| createWindow0(); | |
| synchronized(this) { | |
| while(!done) { | |
| wait(); | |
| } | |
| } | |
| if (exit) { | |
| // Not in harness | |
| if (error != null) { | |
| System.out.println("FAILED: " + error); | |
| error.printStackTrace(); | |
| } | |
| System.exit(0); | |
| } | |
| if (error != null) { | |
| throw error; | |
| } | |
| } | |
| /** | |
| * Creates the window, on the EDT. | |
| */ | |
| private void createWindow0() { | |
| EventQueue.invokeLater(new Runnable() { | |
| public void run() { | |
| window = createWindow(); | |
| window.show(); | |
| } | |
| }); | |
| } | |
| /** | |
| * Initializes the progress bar if necessary. | |
| */ | |
| private void initProgressBar(final int size) { | |
| EventQueue.invokeLater(new Runnable() { | |
| public void run() { | |
| if (progBar != null) { | |
| progBar.setMaximum(size); | |
| } | |
| } | |
| }); | |
| } | |
| /** | |
| * Starst the control loop. | |
| */ | |
| private void startControlLoop() { | |
| robot = createRobot(); | |
| if (robot != null) { | |
| calculateMethods(); | |
| initProgressBar(methods.size()); | |
| new Thread(new Runnable() { | |
| public void run() { | |
| controlLoop(); | |
| } | |
| }).start(); | |
| } | |
| } | |
| /** | |
| * Increment the progress bar. | |
| */ | |
| private void nextProgress(final String name) { | |
| EventQueue.invokeLater(new Runnable() { | |
| public void run() { | |
| if (progBar != null) { | |
| progBar.setString(name); | |
| progBar.setValue(progBar.getValue() + 1); | |
| } | |
| } | |
| }); | |
| } | |
| private synchronized Runnable currentCondition() { | |
| if (conditions != null && conditions.size() > 0) { | |
| return conditions.get(0); | |
| } | |
| return null; | |
| } | |
| private synchronized Runnable nextCondition() { | |
| return conditions.remove(0); | |
| } | |
| private void controlLoop() { | |
| int methodIndex = 0; | |
| while (methodIndex < methods.size()) { | |
| // Wait for any pending conditions | |
| Runnable condition; | |
| while ((condition = currentCondition()) != null) { | |
| try { | |
| condition.run(); | |
| } catch (Exception e) { | |
| fail(e); | |
| return; | |
| } | |
| waitForEDTToFinish(); | |
| synchronized(this) { | |
| if (done) { | |
| return; | |
| } | |
| } | |
| // Advance to next condition | |
| nextCondition(); | |
| } | |
| // Let all events on the EDT finish | |
| waitForEDTToFinish(); | |
| if (delay != null) { | |
| delay.run(); | |
| } | |
| // Invoke the next method | |
| Method method = methods.get(methodIndex++); | |
| Test test = method.getAnnotation(Test.class); | |
| boolean onEDT = true; | |
| if (test != null) { | |
| onEDT = test.onEDT(); | |
| } | |
| else if (!method.getName().startsWith(ON_EDT_METHOD_NAME)) { | |
| onEDT = false; | |
| } | |
| if (onEDT) { | |
| invokeOnEDT(method); | |
| } | |
| else { | |
| invoke(method); | |
| } | |
| // Let all events on the EDT finish | |
| waitForEDTToFinish(); | |
| nextProgress(method.getName()); | |
| // If done, stop. | |
| synchronized(this) { | |
| if (done) { | |
| return; | |
| } | |
| } | |
| } | |
| // No more methods, if we get and done isn't true, set it true | |
| // so that the main thread wakes up. | |
| synchronized(this) { | |
| if (!done) { | |
| done = true; | |
| notifyAll(); | |
| } | |
| } | |
| } | |
| private void waitForEDTToFinish() { | |
| robot.waitForIdle(); | |
| } | |
| private void invokeOnEDT(final Method method) { | |
| try { | |
| EventQueue.invokeAndWait(new Runnable() { | |
| public void run() { | |
| invoke(method); | |
| } | |
| }); | |
| } catch (InvocationTargetException ite) { | |
| fail(ite); | |
| } catch (InterruptedException ie) { | |
| fail(ie); | |
| } | |
| } | |
| private void invoke(Method method) { | |
| System.out.println("invoking: " + method.getName()); | |
| try { | |
| if (Modifier.isPrivate(method.getModifiers())) { | |
| method.setAccessible(true); | |
| } | |
| method.invoke(this); | |
| } catch (Exception e) { | |
| fail(e); | |
| } | |
| } | |
| // Determines the methods to execute. | |
| private void calculateMethods() { | |
| // Using a Set avoids duplicating methods returned by both | |
| // getMethods() and getDeclaredMethods(). | |
| HashSet<Method> allMethods = new HashSet<Method>(); | |
| allMethods.addAll(Arrays.asList(getClass().getMethods())); | |
| allMethods.addAll(Arrays.asList(getClass().getDeclaredMethods())); | |
| methods = new ArrayList<Method>(); | |
| for (Method method : allMethods) { | |
| Test test = method.getAnnotation(Test.class); | |
| if (test != null) { | |
| methods.add(method); | |
| } | |
| else if (method.getName().startsWith(ON_EDT_METHOD_NAME)) { | |
| methods.add(method); | |
| } | |
| else if (method.getName().startsWith(IN_BACKGROUND_METHOD_NAME)) { | |
| methods.add(method); | |
| } | |
| } | |
| Comparator<Method> comparator = new Comparator<Method>() { | |
| public int compare(Method m1, Method m2) { | |
| int index1 = getIndex(m1); | |
| int index2 = getIndex(m2); | |
| return index1 - index2; | |
| } | |
| private int getIndex(Method m) { | |
| String name = m.getName(); | |
| String indexAsString; | |
| Test test = m.getAnnotation(Test.class); | |
| if (test != null) { | |
| return test.value(); | |
| } | |
| if (name.startsWith(ON_EDT_METHOD_NAME)) { | |
| indexAsString = name.substring( | |
| ON_EDT_METHOD_NAME.length()); | |
| } | |
| else { | |
| indexAsString = name.substring( | |
| IN_BACKGROUND_METHOD_NAME.length()); | |
| } | |
| if (indexAsString.length() == 0) { | |
| System.out.println( | |
| "onEDT and onBackgroundThread must be " + | |
| "followed by an integer specifying " + | |
| "order."); | |
| System.exit(0); | |
| } | |
| return Integer.parseInt(indexAsString); | |
| } | |
| }; | |
| Collections.sort(methods, comparator); | |
| } | |
| /** | |
| * Invoke if the test should be considered to have failed. This will | |
| * stop test execution. | |
| */ | |
| public void fail(String reason) { | |
| fail(new RuntimeException(reason)); | |
| } | |
| /** | |
| * Invoke if the test should be considered to have failed. This will | |
| * stop test execution. | |
| */ | |
| public void fail(Throwable error) { | |
| synchronized(this) { | |
| if (this.error == null) { | |
| if (error instanceof InvocationTargetException) { | |
| this.error = ((InvocationTargetException)error). | |
| getCause(); | |
| } | |
| else { | |
| this.error = error; | |
| } | |
| this.done = true; | |
| notifyAll(); | |
| } | |
| } | |
| } | |
| /** | |
| * Invoke to prematurely stop test execution while there are remaining | |
| * methods. You typically don't invoke this, instead if all methods have | |
| * been executed and fail hasn't been invoked, the test is considered to | |
| * have passed. | |
| */ | |
| public void succeeded() { | |
| synchronized(this) { | |
| this.done = true; | |
| notifyAll(); | |
| } | |
| } | |
| /** | |
| * Creates and returns the Robot that will be used. You generally don't | |
| * need to override this. | |
| */ | |
| protected JRobot createRobot() { | |
| JRobot robot = JRobot.getRobot(false); | |
| return robot; | |
| } | |
| private class FocusListener implements PropertyChangeListener { | |
| public void propertyChange(PropertyChangeEvent e) { | |
| if (componentWaitingForFocus != null && | |
| "focusOwner".equals(e.getPropertyName()) && | |
| componentWaitingForFocus == e.getNewValue()) { | |
| synchronized(SwingTestHelper.this) { | |
| componentWaitingForFocus = null; | |
| SwingTestHelper.this.notifyAll(); | |
| } | |
| } | |
| } | |
| } | |
| private class EventCondition implements Runnable { | |
| private Component component; | |
| private int eventID; | |
| private boolean received; | |
| EventCondition(Component component, int eventID) { | |
| this.component = component; | |
| this.eventID = eventID; | |
| } | |
| public int getEventID() { | |
| return eventID; | |
| } | |
| public Component getComponent() { | |
| return component; | |
| } | |
| public void received() { | |
| synchronized(SwingTestHelper.this) { | |
| this.received = true; | |
| SwingTestHelper.this.notifyAll(); | |
| } | |
| } | |
| public boolean isWaiting() { | |
| return !received; | |
| } | |
| public void run() { | |
| synchronized(SwingTestHelper.this) { | |
| while (!received) { | |
| try { | |
| SwingTestHelper.this.wait(); | |
| } catch (InterruptedException ie) { | |
| fail(ie); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| private class FocusCondition implements Runnable { | |
| public void run() { | |
| synchronized(SwingTestHelper.this) { | |
| while (componentWaitingForFocus != null) { | |
| try { | |
| SwingTestHelper.this.wait(); | |
| } catch (InterruptedException ie) { | |
| fail(ie); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| private class PauseCondition implements Runnable { | |
| private int time; | |
| PauseCondition(int time) { | |
| this.time = time; | |
| } | |
| public void run() { | |
| try { | |
| Thread.sleep(time); | |
| } catch (InterruptedException ie) { | |
| fail(ie); | |
| } | |
| } | |
| } | |
| private class EventListener implements AWTEventListener { | |
| public void eventDispatched(AWTEvent ev) { | |
| int eventID = ev.getID(); | |
| synchronized (SwingTestHelper.this) { | |
| for (Runnable condition : conditions) { | |
| if (condition instanceof EventCondition) { | |
| EventCondition ec = (EventCondition)condition; | |
| if (ec.isWaiting()) { | |
| if (eventID == ec.getEventID() && | |
| (ec.getComponent() == null || | |
| ev.getSource() == ec.getComponent())) { | |
| ec.received(); | |
| } | |
| return; | |
| } | |
| } | |
| else { | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } |