diff --git a/src/windows/native/sun/windows/awt_Component.cpp b/src/windows/native/sun/windows/awt_Component.cpp index c383d54206af2cb419c71624f3dce0fd1a818313..c6df7885ae79eb6c0f6964518edd0084d30469b0 100644 --- a/src/windows/native/sun/windows/awt_Component.cpp +++ b/src/windows/native/sun/windows/awt_Component.cpp @@ -3813,10 +3813,12 @@ MsgRouting AwtComponent::WmImeNotify(WPARAM subMsg, LPARAM bitsCandType) if (!m_useNativeCompWindow) { if (subMsg == IMN_OPENCANDIDATE) { m_bitsCandType = subMsg; - } else if (subMsg != IMN_SETCANDIDATEPOS) { + InquireCandidatePosition(); + } else if (subMsg == IMN_OPENSTATUSWINDOW || + subMsg == WM_IME_STARTCOMPOSITION) { m_bitsCandType = 0; + InquireCandidatePosition(); } - InquireCandidatePosition(); return mrConsume; } return mrDoDefault; diff --git a/test/javax/swing/regtesthelpers/JRobot.java b/test/javax/swing/regtesthelpers/JRobot.java new file mode 100644 index 0000000000000000000000000000000000000000..aba18c3535b6f75a53374821f98254d39f86f6bd --- /dev/null +++ b/test/javax/swing/regtesthelpers/JRobot.java @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2007, 2016, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +/** + * JRobot is a wrapper around java.awt.Robot that provides some convenience + * methods. + *
When using jtreg you would include this class via something like: + *
+ * @library ../../../regtesthelpers + * @build JRobot + *+ * + */ +import java.awt.AWTException; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import javax.swing.SwingUtilities; + +public class JRobot extends java.awt.Robot { + private static int DEFAULT_DELAY = 550; + private static int INTERNAL_DELAY = 250; + + private int delay; + private boolean delaysEnabled; + + protected JRobot(boolean enableDelays) throws AWTException { + super(); + delaysEnabled = enableDelays; + setAutoWaitForIdle(enableDelays); + if (enableDelays) { + setAutoDelay(INTERNAL_DELAY); + setDelay(DEFAULT_DELAY); + } + } + + /** + * Return a JRobot. Delays are enabled by default. + * @return a JRobot + */ + public static JRobot getRobot() { + return getRobot(true); + } + + /** + * Create a JRobot. The parameter controls whether delays are enabled. + * @param enableDelays controls whether delays are enabled. + * @return a JRobot + */ + public static JRobot getRobot(boolean enableDelays) { + JRobot robot = null; + try { + robot = new JRobot(enableDelays); + } catch (AWTException e) { + System.err.println("Coudn't create Robot, details below"); + throw new Error(e); + } + return robot; + } + + /** + * Press and release a key. + * @param keycode which key to press. For example, KeyEvent.VK_DOWN + */ + public void hitKey(int keycode) { + keyPress(keycode); + keyRelease(keycode); + delay(); + } + + /** + * Press and release a key with modifiers. + * @param keys keys to press. Keys are pressed in order they are passed as + * parameters to this method. All keys except the last one are considered + * modifiers. For example, to press Ctrl+Shift+T, call: + * hitKey(KeyEvent.VK_CONTROL, KeyEvent.VK_SHIFT, KeyEvent.VK_T); + */ + public void hitKey(int... keys) { + for (int i = 0; i < keys.length; i++) { + keyPress(keys[i]); + } + + for (int i = keys.length - 1; i >= 0; i--) { + keyRelease(keys[i]); + } + delay(); + } + + /** + * Move mouse cursor to the center of the Component. + * @param c Component the mouse is placed over + */ + public void moveMouseTo(Component c) { + Point p = c.getLocationOnScreen(); + Dimension size = c.getSize(); + p.x += size.width / 2; + p.y += size.height / 2; + mouseMove(p.x, p.y); + delay(); + } + + /** + * Move mouse smoothly from (x0, y0) to (x1, y1). + */ + public void glide(int x0, int y0, int x1, int y1) { + float dmax = (float)Math.max(Math.abs(x1 - x0), Math.abs(y1 - y0)); + float dx = (x1 - x0) / dmax; + float dy = (y1 - y0) / dmax; + + mouseMove(x0, y0); + for (int i=1; i<=dmax; i++) { + mouseMove((int)(x0 + dx*i), (int)(y0 + dy*i)); + } + delay(); + } + + /** + * Perform a mouse click, i.e. press and release mouse button(s). + * @param buttons mouse button(s). + * For example, MouseEvent.BUTTON1_MASK + */ + public void clickMouse(int buttons) { + mousePress(buttons); + mouseRelease(buttons); + delay(); + } + + /** + * Perform a click with the first mouse button. + */ + public void clickMouse() { + clickMouse(InputEvent.BUTTON1_MASK); + } + + /** + * Click in the center of the given Component + * @param c the Component to click on + * @param buttons mouse button(s). + */ + public void clickMouseOn(Component c, int buttons) { + moveMouseTo(c); + clickMouse(buttons); + } + + /** + * Click the first mouse button in the center of the given Component + * @param c the Component to click on + */ + public void clickMouseOn(Component c) { + clickMouseOn(c, InputEvent.BUTTON1_MASK); + } + + /** + * Return whether delays are enabled + * @return whether delays are enabled + */ + public boolean getDelaysEnabled() { + return delaysEnabled; + } + + /** + * Delay execution by delay milliseconds + */ + public void delay() { + delay(delay); + } + + /** + * Return the delay amount, in milliseconds + */ + public int getDelay() { + return delay; + } + + /** + * Set the delay amount, in milliseconds + */ + public void setDelay(int delay) { + this.delay = delay; + } + + /** + * Waits until all events currently on the event queue have been processed. + * Does nothing if called on EDT + */ + public synchronized void waitForIdle() { + if (!EventQueue.isDispatchThread()) { + super.waitForIdle(); + } + } + + /** + * Calculate the center of the Rectangle passed, and return them + * in a Point object. + * @param r a non-null Rectangle + * @return a new Point object containing coordinates of r's center + */ + public Point centerOf(Rectangle r) { + return new Point(r.x + r.width / 2, r.y + r.height / 2); + } + + /** + * Calculate the center of the Rectangle passed, and store it in p. + * @param r a non-null Rectangle + * @param p a non-null Point that receives coordinates of r's center + * @return p + */ + public Point centerOf(Rectangle r, Point p) { + p.x = r.x + r.width / 2; + p.y = r.y + r.height / 2; + return p; + } + + /** + * Convert a rectangle from coordinate system of Component c to + * screen coordinate system. + * @param r a non-null Rectangle + * @param c a Component whose coordinate system is used for conversion + */ + public void convertRectToScreen(Rectangle r, Component c) { + Point p = new Point(r.x, r.y); + SwingUtilities.convertPointToScreen(p, c); + r.x = p.x; + r.y = p.y; + } + + /** + * Compares two rectangles pixel-by-pixel. + * @param r0 the first area + * @param r1 the second area + * return true if all pixels in the two areas are identical + */ + public boolean compareRects(Rectangle r0, Rectangle r1) { + int xShift = r1.x - r0.x; + int yShift = r1.y - r0.y; + + for (int y = r0.y; y < r0.y + r0.height; y++) { + for (int x = r0.x; x < r0.x + r0.width; x++) { + if (!comparePixels(x, y, x + xShift, y + yShift)) { + return false; + } + } + } + return true; + } + + /** + * Compares colors of two points on the screen. + * @param p0 the first point + * @param p1 the second point + * return true if the two points have the same color + */ + public boolean comparePixels(Point p0, Point p1) { + return comparePixels(p0.x, p0.y, p1.x, p1.y); + } + + /** + * Compares colors of two points on the screen. + * @param x0 the x coordinate of the first point + * @param y0 the y coordinate of the first point + * @param x1 the x coordinate of the second point + * @param y1 the y coordinate of the second point + * return true if the two points have the same color + */ + public boolean comparePixels(int x0, int y0, int x1, int y1) { + return (getPixelColor(x0, y0).equals(getPixelColor(x1, y1))); + } +} diff --git a/test/javax/swing/regtesthelpers/SwingTestHelper.java b/test/javax/swing/regtesthelpers/SwingTestHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..5b6d35fa49a0aa48a7a73f16075ccece21c054f6 --- /dev/null +++ b/test/javax/swing/regtesthelpers/SwingTestHelper.java @@ -0,0 +1,862 @@ +/* + * Copyright (c) 2007, 2016, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.*; +import java.awt.event.*; +import java.beans.*; +import java.lang.reflect.*; +import java.util.*; + +import javax.swing.*; + +/** + * 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. + *
+ * SwingTestHelper uses reflection to invoke all methods starting with
+ * the name onEDT on the EDT and all methods starting with
+ * onBackgroundThread 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:
+ *
+ * class Test extends SwingTestHelper {
+ * private void onEDT10();
+ * private void onBackgroundThread20();
+ * private void onBackgroundThread30();
+ * private void onEDT40();
+ * private void onBackgroundThread50();
+ * }
+ *
+ * Will have the methods invoked in the order onEDT10,
+ * onBackgroundThread20, onBackgroundThread30,
+ * onEDT40, onBackgroundThread50.
+ * + * If you're not happy with method mangling you can also use annotations. + * The following gives the same result as the previous example: + *
+ * 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
+ * }
+ *
+ * + * 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. + *
+ * Between each of the methods, all pending events (native and Java) + * are processed. + *
+ * Failure of the test is signaled by any method throwing
+ * an exception, directly invoking fail or one of the
+ * assert variants. If no methods throw an exception the test is
+ * assumed to have passed.
+ *
+ * Often times it is necessary to block until focus has been gained on a
+ * particular widget. This can be handled by the
+ * requestAndWaitForFocus method. It will invoke
+ * requestFocus and block the test (not the EDT) until focus
+ * has been granted to the widget.
+ *
+ * Care must be taken when using Robot directly. For
+ * example, it's tempting to flood Robot 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
+ * waitForEvent. For example, to block until a key typed
+ * event has been processed do the following:
+ *
+ * private void onEDT() {
+ * robot.moveMouseTo(myComponent);
+ * robot.mousePress(xxx);
+ * robot.mouseRelease(xxx);
+ * waitForEvent(myComponent, MouseEvent.MOUSE_RELEASED);
+ * }
+ *
+ *
+ * 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 waitForCondition can be used to
+ * block test execution until the supplied Runnable returns. The
+ * Runnable is invoked on the background thread.
+ *
+ * To use this class you will need to do the following: + *
createContentPane. All of your logic
+ * for setting up the test environment should go here. This method is
+ * invoked on the EDT.
+ * onEDTXX and
+ * onBackgroundThreadXXX methods to do the actual testing.
+ * main method look like:
+ * new MySwingTestHelper().run(args). This will block
+ * until the test fails or succeeds.
+ * + * @library ../../../regtesthelpers + * @build Test JRobot Assert SwingTestHelper + * @run main MySwingTestHelper + * *+ *
+ * Here's a complete example: + *
+ * 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");
+ * }
+ * }
+ * }
+ *
+ *
+ *
+ * @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.ListRobot returned from createRobot.
+ */
+ protected JRobot robot;
+
+ /**
+ * Window returned from createWindow.
+ */
+ 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 Window for the test. This
+ * implementation returns a JFrame with a default close operation
+ * of EXIT_ON_CLOSE. The Component
+ * returned from createContentPane is added the
+ * JFrame and the the frame is packed.
+ *
+ * Typically you only need override
+ * "-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().
+ HashSetcreateContentPane.
+ */
+ 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 Component to place in the frame.
+ * Override this or the createWindow method.
+ */
+ protected Component createContentPane() {
+ return null;
+ }
+
+ /**
+ * Invokes requestFocus 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 must be invoked
+ * on the EDT, if you do not invoke it from the edt the test will fail.
+ *
+ * @param c the Component 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 must 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 Component to wait for the event on;
+ * null 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 Runnable 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 Runnable completes.
+ */
+ protected void waitForCondition(Runnable runnable) {
+ synchronized(this) {
+ if (conditions == null) {
+ conditions = new LinkedList