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: + *

    + *
  1. Override the method createContentPane. All of your logic + * for setting up the test environment should go here. This method is + * invoked on the EDT. + *
  2. Implement the necessary onEDTXX and + * onBackgroundThreadXXX methods to do the actual testing. + *
  3. Make your main method look like: + * new MySwingTestHelper().run(args). This will block + * until the test fails or succeeds. + *
  4. To use this with jtreg you'll need to have something like: + *
    + *     @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.List methods; + + // The conditions the background thread is blocked on. + private java.util.List conditions; + + // Whether or not we've installed the AWTEventListener + private boolean installedEventListener; + + /** + * Instance of Robot 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 createContentPane. + */ + 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(); + } + conditions.add(runnable); + } + } + + /** + * Runs the test. This method blocks the caller until the test + * fails or succeeds. Recognized arguments are: + *

+ * "-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 allMethods = new HashSet(); + allMethods.addAll(Arrays.asList(getClass().getMethods())); + allMethods.addAll(Arrays.asList(getClass().getDeclaredMethods())); + + methods = new ArrayList(); + 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 comparator = new Comparator() { + 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; + } + } + } + } + } +} diff --git a/test/javax/swing/regtesthelpers/Test.java b/test/javax/swing/regtesthelpers/Test.java new file mode 100644 index 0000000000000000000000000000000000000000..2ce928737f04bed981acd5dc552a524e2bc84e4e --- /dev/null +++ b/test/javax/swing/regtesthelpers/Test.java @@ -0,0 +1,34 @@ +/* + * 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. + * + * 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.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Test { + int value(); + boolean onEDT() default true; +} \ No newline at end of file diff --git a/test/javax/swing/text/FlowView/LayoutTest.java b/test/javax/swing/text/FlowView/LayoutTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1cb9effba988afbba092a2b62cb7c34c86a99433 --- /dev/null +++ b/test/javax/swing/text/FlowView/LayoutTest.java @@ -0,0 +1,90 @@ +/* + * 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. + * + * 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. + */ + +/* @test + @bug 6452106 6606443 8161195 + @author Peter Zhelezniakov + @library ../../regtesthelpers + @build Test JRobot SwingTestHelper + @run main/timeout=300 LayoutTest +*/ + +import javax.swing.text.*; +import javax.swing.*; +import java.awt.event.*; +import java.awt.*; + +public class LayoutTest extends SwingTestHelper { + JTextPane text; + + public static void main(String[] args) throws Throwable { + new LayoutTest().run(args); + } + + protected Component createContentPane() { + return text = new JTextPane(); + } + + @Test(value=10, onEDT=true) + private void onEDT10() { + requestAndWaitForFocus(text); + } + + + @Test(value=100, onEDT=true) + private void prepare6452106() { + text.setText("This is easily generated on my\nmachine"); + Document doc = text.getDocument(); + + // wrap the long paragraph + Dimension d = text.getPreferredSize(); + Dimension size = new Dimension(d.width * 2 / 3, d.height * 5); + window.setSize(size); + + // place caret at the end of 2nd line + Element p1 = doc.getDefaultRootElement().getElement(0); + int pos = p1.getEndOffset(); + text.setCaretPosition(pos - 1); + } + + @Test(value=110, onEDT=false) + private void test6452106() { + robot.setDelay(300); + robot.hitKey(KeyEvent.VK_DELETE); + robot.hitKey(KeyEvent.VK_SPACE); + robot.hitKey(KeyEvent.VK_SPACE); + } + + + @Test(value=200, onEDT=true) + private void prepare6606443() { + text.setText("This is easily\ngenerated\non my machine"); + text.setSelectionStart(15); + text.setSelectionEnd(24); + } + + @Test(value=210, onEDT=false) + private void test6606443() { + robot.hitKey(KeyEvent.VK_ENTER); + } +}