changeset 12607:d640834171d3

8161195: Regression: closed/javax/swing/text/FlowView/LayoutTest.java Reviewed-by: serb
author mcherkas
date Thu, 26 Jan 2017 17:26:29 +0300
parents c0f664ed780a
children a3dc113274df
files src/windows/native/sun/windows/awt_Component.cpp test/javax/swing/regtesthelpers/JRobot.java test/javax/swing/regtesthelpers/SwingTestHelper.java test/javax/swing/regtesthelpers/Test.java test/javax/swing/text/FlowView/LayoutTest.java
diffstat 5 files changed, 1284 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/windows/native/sun/windows/awt_Component.cpp	Wed Dec 21 10:15:49 2016 -0500
+++ b/src/windows/native/sun/windows/awt_Component.cpp	Thu Jan 26 17:26:29 2017 +0300
@@ -3822,10 +3822,12 @@
     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;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/swing/regtesthelpers/JRobot.java	Thu Jan 26 17:26:29 2017 +0300
@@ -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.
+ * <p>When using jtreg you would include this class via something like:
+ * <pre>
+ * @library ../../../regtesthelpers
+ * @build JRobot
+ * </pre>
+ *
+ */
+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)));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/swing/regtesthelpers/SwingTestHelper.java	Thu Jan 26 17:26:29 2017 +0300
@@ -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.
+ * <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 {
+ *   &#064;Test(10)
+ *   private void foo(); // Was onEDT10
+ *
+ *   &#064;Test(value=20, onEDT=false)
+ *   private void bar(); // Was onBackgroundThread20
+ *
+ *   &#064;Test(value=30, onEDT=false)
+ *   private void baz(); // Was onBackgroundThread30
+ *
+ *   &#064;Test(40)
+ *   private void zed(); // Was onEDT40
+ *
+ *   &#064;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>
+ *     &#064;library ../../../regtesthelpers
+ *     &#064;build Test JRobot Assert SwingTestHelper
+ *     &#064;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 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;
+                    }
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/swing/regtesthelpers/Test.java	Thu Jan 26 17:26:29 2017 +0300
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/swing/text/FlowView/LayoutTest.java	Thu Jan 26 17:26:29 2017 +0300
@@ -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);
+    }
+}