This commit is contained in:
Jesper Wilhelmsson 2022-12-23 11:25:10 +00:00
commit 19ce23c645
15 changed files with 501 additions and 170 deletions

View File

@ -1705,7 +1705,7 @@ void PhaseIdealLoop::insert_pre_post_loops(IdealLoopTree *loop, Node_List &old_n
// pre-loop, the main-loop may not execute at all. Later in life this
// zero-trip guard will become the minimum-trip guard when we unroll
// the main-loop.
Node *min_opaq = new OpaqueZeroTripGuardNode(C, limit);
Node *min_opaq = new OpaqueZeroTripGuardNode(C, limit, b_test);
Node *min_cmp = new CmpINode(pre_incr, min_opaq);
Node *min_bol = new BoolNode(min_cmp, b_test);
register_new_node(min_opaq, new_pre_exit);
@ -1994,7 +1994,7 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new,
// (the previous loop trip-counter exit value) because we will be changing
// the exit value (via additional unrolling) so we cannot constant-fold away the zero
// trip guard until all unrolling is done.
Node *zer_opaq = new OpaqueZeroTripGuardNode(C, incr);
Node *zer_opaq = new OpaqueZeroTripGuardNode(C, incr, main_end->test_trip());
Node *zer_cmp = new CmpINode(zer_opaq, limit);
Node *zer_bol = new BoolNode(zer_cmp, main_end->test_trip());
register_new_node(zer_opaq, new_main_exit);

View File

@ -27,6 +27,7 @@
#include "opto/node.hpp"
#include "opto/opcodes.hpp"
#include "subnode.hpp"
//------------------------------Opaque1Node------------------------------------
// A node to prevent unwanted optimizations. Allows constant folding.
@ -72,9 +73,16 @@ class OpaqueLoopStrideNode : public Opaque1Node {
class OpaqueZeroTripGuardNode : public Opaque1Node {
public:
OpaqueZeroTripGuardNode(Compile* C, Node *n) : Opaque1Node(C, n) {
// This captures the test that returns true when the loop is entered. It depends on whether the loop goes up or down.
// This is used by CmpINode::Value.
BoolTest::mask _loop_entered_mask;
OpaqueZeroTripGuardNode(Compile* C, Node* n, BoolTest::mask loop_entered_test) :
Opaque1Node(C, n), _loop_entered_mask(loop_entered_test) {
}
virtual int Opcode() const;
virtual uint size_of() const {
return sizeof(*this);
}
};
//------------------------------Opaque3Node------------------------------------

View File

@ -1683,6 +1683,13 @@ void PhaseIterGVN::add_users_to_worklist( Node *n ) {
}
}
}
if (use->Opcode() == Op_OpaqueZeroTripGuard) {
assert(use->outcnt() <= 1, "OpaqueZeroTripGuard can't be shared");
if (use->outcnt() == 1) {
Node* cmp = use->unique_out();
_worklist.push(cmp);
}
}
}
}
@ -1901,6 +1908,7 @@ void PhaseCCP::push_more_uses(Unique_Node_List& worklist, Node* parent, const No
push_loadp(worklist, use);
push_and(worklist, parent, use);
push_cast_ii(worklist, parent, use);
push_opaque_zero_trip_guard(worklist, use);
}
@ -2021,6 +2029,12 @@ void PhaseCCP::push_cast_ii(Unique_Node_List& worklist, const Node* parent, cons
}
}
void PhaseCCP::push_opaque_zero_trip_guard(Unique_Node_List& worklist, const Node* use) const {
if (use->Opcode() == Op_OpaqueZeroTripGuard) {
push_if_not_bottom_type(worklist, use->unique_out());
}
}
//------------------------------do_transform-----------------------------------
// Top level driver for the recursive transformer
void PhaseCCP::do_transform() {

View File

@ -596,6 +596,7 @@ class PhaseCCP : public PhaseIterGVN {
static void push_load_barrier(Unique_Node_List& worklist, const BarrierSetC2* barrier_set, const Node* use);
void push_and(Unique_Node_List& worklist, const Node* parent, const Node* use) const;
void push_cast_ii(Unique_Node_List& worklist, const Node* parent, const Node* use) const;
void push_opaque_zero_trip_guard(Unique_Node_List& worklist, const Node* use) const;
public:
PhaseCCP( PhaseIterGVN *igvn ); // Compute conditional constants

View File

@ -34,6 +34,7 @@
#include "opto/matcher.hpp"
#include "opto/movenode.hpp"
#include "opto/mulnode.hpp"
#include "opto/opaquenode.hpp"
#include "opto/opcodes.hpp"
#include "opto/phaseX.hpp"
#include "opto/subnode.hpp"
@ -661,6 +662,47 @@ const Type *CmpINode::sub( const Type *t1, const Type *t2 ) const {
return TypeInt::CC; // else use worst case results
}
const Type* CmpINode::Value(PhaseGVN* phase) const {
Node* in1 = in(1);
Node* in2 = in(2);
// If this test is the zero trip guard for a main or post loop, check whether, with the opaque node removed, the test
// would constant fold so the loop is never entered. If so return the type of the test without the opaque node removed:
// make the loop unreachable.
// The reason for this is that the iv phi captures the bounds of the loop and if the loop becomes unreachable, it can
// become top. In that case, the loop must be removed.
// This is safe because:
// - as optimizations proceed, the range of iterations executed by the main loop narrows. If no iterations remain, then
// we're done with optimizations for that loop.
// - the post loop is initially not reachable but as long as there's a main loop, the zero trip guard for the post
// loop takes a phi that merges the pre and main loop's iv and can't constant fold the zero trip guard. Once, the main
// loop is removed, there's no need to preserve the zero trip guard for the post loop anymore.
if (in1 != NULL && in2 != NULL) {
uint input = 0;
Node* cmp = NULL;
BoolTest::mask test;
if (in1->Opcode() == Op_OpaqueZeroTripGuard && phase->type(in1) != Type::TOP) {
cmp = new CmpINode(in1->in(1), in2);
test = ((OpaqueZeroTripGuardNode*)in1)->_loop_entered_mask;
}
if (in2->Opcode() == Op_OpaqueZeroTripGuard && phase->type(in2) != Type::TOP) {
assert(cmp == NULL, "A cmp with 2 OpaqueZeroTripGuard inputs");
cmp = new CmpINode(in1, in2->in(1));
test = ((OpaqueZeroTripGuardNode*)in2)->_loop_entered_mask;
}
if (cmp != NULL) {
const Type* cmp_t = cmp->Value(phase);
const Type* t = BoolTest(test).cc2logical(cmp_t);
cmp->destruct(phase);
if (t == TypeInt::ZERO) {
return cmp_t;
}
}
}
return SubNode::Value(phase);
}
// Simplify a CmpU (compare 2 integers) node, based on local information.
// If both inputs are constants, compare them.
const Type *CmpUNode::sub( const Type *t1, const Type *t2 ) const {

View File

@ -153,6 +153,7 @@ public:
virtual int Opcode() const;
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
virtual const Type *sub( const Type *, const Type * ) const;
virtual const Type* Value(PhaseGVN* phase) const;
};
//------------------------------CmpUNode---------------------------------------

View File

@ -34,7 +34,7 @@ import java.util.Objects;
* Random Bit Generator).
* <p>
* According to
* <a href="http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf">
* <a href="https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf">
* NIST Special Publication 800-90A Revision 1, Recommendation for Random
* Number Generation Using Deterministic Random Bit Generators</a> (800-90Ar1),
* <blockquote>

View File

@ -43,7 +43,7 @@ import java.util.regex.Pattern;
*
* <p>A cryptographically strong random number minimally complies with the
* statistical random number generator tests specified in
* <a href="http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf">
* <a href="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf">
* <i>FIPS 140-2, Security Requirements for Cryptographic Modules</i></a>,
* section 4.9.1.
* Additionally, {@code SecureRandom} must produce non-deterministic output.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -366,6 +366,8 @@ public class DefaultCaret extends Rectangle implements Caret, FocusListener, Mou
}
}
private int savedBlinkRate = 0;
private boolean isBlinkRateSaved = false;
// --- FocusListener methods --------------------------
/**
@ -379,8 +381,21 @@ public class DefaultCaret extends Rectangle implements Caret, FocusListener, Mou
public void focusGained(FocusEvent e) {
if (component.isEnabled()) {
if (component.isEditable()) {
setVisible(true);
if (isBlinkRateSaved) {
setBlinkRate(savedBlinkRate);
savedBlinkRate = 0;
isBlinkRateSaved = false;
}
} else {
if (getBlinkRate() != 0) {
if (!isBlinkRateSaved) {
savedBlinkRate = getBlinkRate();
isBlinkRateSaved = true;
}
setBlinkRate(0);
}
}
setVisible(true);
setSelectionVisible(true);
updateSystemSelection();
}
@ -1031,17 +1046,29 @@ public class DefaultCaret extends Rectangle implements Caret, FocusListener, Mou
* @see Caret#setBlinkRate
*/
public void setBlinkRate(int rate) {
if (rate < 0) {
throw new IllegalArgumentException("Invalid blink rate: " + rate);
}
if (rate != 0) {
if (flasher == null) {
flasher = new Timer(rate, handler);
if (component.isEditable()) {
if (flasher == null) {
flasher = new Timer(rate, handler);
}
flasher.setDelay(rate);
} else {
savedBlinkRate = rate;
isBlinkRateSaved = true;
}
flasher.setDelay(rate);
} else {
if (flasher != null) {
flasher.stop();
flasher.removeActionListener(handler);
flasher = null;
}
if (component.isEditable() && isBlinkRateSaved) {
savedBlinkRate = 0;
isBlinkRateSaved = false;
}
}
}
@ -1053,6 +1080,9 @@ public class DefaultCaret extends Rectangle implements Caret, FocusListener, Mou
* @see Caret#getBlinkRate
*/
public int getBlinkRate() {
if (isBlinkRateSaved) {
return savedBlinkRate;
}
return (flasher == null) ? 0 : flasher.getDelay();
}

View File

@ -0,0 +1,122 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8298176
* @summary Must remove OpaqueZeroTripGuardPostLoop after main loop disappears else
* the zero-trip-guard of the post loop cannot die and leaves an inconsistent
* graph behind.
* @run main/othervm -Xcomp -XX:-TieredCompilation
* -XX:CompileCommand=compileonly,TestOpaqueZeroTripGuardPostLoopRemoval::test*
* -XX:CompileCommand=dontinline,TestOpaqueZeroTripGuardPostLoopRemoval::*
* TestOpaqueZeroTripGuardPostLoopRemoval
*/
public class TestOpaqueZeroTripGuardPostLoopRemoval {
static long x;
public static void main(String[] strArr) {
test_001();
test_002();
try {
test_003();
} catch (Exception e) {
// Expected
}
test_004();
test_005();
}
static void test_001() {
int b = 6;
for (long l = 1; l < 9; l++) {
b++;
}
for (int i = 1; i < 1000; i*=2) {
for (int j = 1; j < 2; j++) {
x = b + 1;
}
}
}
static void test_002() {
int b = 6;
for (long l = 60; l < 3000; l+=3) {
// bounds of loop: no work for post loop
b += 33; // any multiple of iv step
}
for (int i = 1; i < 1000; i*=2) {
for (int j = 1; j < 2; j++) {
x = b + 1;
}
}
}
static void dontInline() {
throw new RuntimeException();
}
static int test_003() {
int y = 3;
for (int i = 0; i < 9; ) {
for (long l = 1; l < 5; l++) {
y *= 2;
}
while (true) {
dontInline();
}
}
return y;
}
static void test_004() {
for (int i2 = 4; i2 < 13; i2++) {
double d = 56;
for (long l = 1; l < 5; l++) {
d = d + 3;
}
for (int i = 0; i < 10; i++) {
for (int d2 = i2; d2 < 2; d2 = 3) {
}
}
}
}
public static int test_005() {
long arr[]=new long[400];
for (int i = 3; i < 177; i++) {
for (int j = 0; j < 10; j++){}
}
int y = 0;
for (int i = 15; i < 356; i++) {
// Inner loop prevents strip-mining of outer loop
// later, inner loop is removed, so outer does pre-main-post without strip-mining
for (int j = 0; j < 10; j++){
y |= 1;
}
}
return y;
}
}

View File

@ -659,8 +659,6 @@ javax/swing/JPopupMenu/6800513/bug6800513.java 7184956 macosx-all
javax/swing/JTabbedPane/8007563/Test8007563.java 8051591 generic-all
javax/swing/JTabbedPane/4624207/bug4624207.java 8064922 macosx-all
javax/swing/SwingUtilities/TestBadBreak/TestBadBreak.java 8160720 generic-all
javax/swing/text/DefaultCaret/HidingSelection/HidingSelectionTest.java 8194048 windows-all
javax/swing/text/DefaultCaret/HidingSelection/MultiSelectionTest.java 8213562 linux-all
javax/swing/JFileChooser/6798062/bug6798062.java 8146446 windows-all
javax/swing/JPopupMenu/4870644/bug4870644.java 8194130 macosx-all,linux-all
javax/swing/dnd/8139050/NativeErrorsInTableDnD.java 8202765 macosx-all,linux-all

View File

@ -126,6 +126,7 @@ jdk_util = \
jdk_util_other = \
java/util \
sun/util \
jdk/internal/util \
-:jdk_collections \
-:jdk_concurrent \
-:jdk_stream

View File

@ -0,0 +1,150 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, JetBrains s.r.o.. 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
* @key headful
* @bug 8287600 8291266 8299207
* @requires os.family == "mac"
* @summary [macosx] Some primitives do not render in metal pipeline
* @run main DrawPrimitivesTest
*/
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.CountDownLatch;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public abstract class DrawPrimitivesTest extends JFrame {
private final static int W = 800;
private final static int H = 800;
private final static Color[] color = { Color.RED, Color.BLUE, Color.GREEN};
private final static int COLOR_TOLERANCE = 10;
private final CountDownLatch latchRender = new CountDownLatch(1);
private volatile int frameX0 = 0;
private volatile int frameY0 = 0;
private final String name;
private static boolean isAlmostEqual(Color c1, Color c2) {
return Math.abs(c1.getRed() - c2.getRed()) < COLOR_TOLERANCE &&
Math.abs(c1.getGreen() - c2.getGreen()) < COLOR_TOLERANCE &&
Math.abs(c1.getBlue() - c2.getBlue()) < COLOR_TOLERANCE;
}
public static void main(String[] args) throws InterruptedException, AWTException, InvocationTargetException {
new DrawPrimitivesTest("drawLine") {
public void renderPrimitive(Graphics2D g2d, int x0, int y0, int w, int h) {
g2d.drawLine(x0, y0, x0+w, y0+h);
}
}.runTest();
new DrawPrimitivesTest("fillRect") {
public void renderPrimitive(Graphics2D g2d, int x0, int y0, int w, int h) {
g2d.fillRect(x0, y0, w, h);
}
}.runTest();
new DrawPrimitivesTest("fillOvalAA") {
public void renderPrimitive(Graphics2D g2d, int x0, int y0, int w, int h) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.fillOval(x0, y0, w, h);
}
}.runTest();
}
public abstract void renderPrimitive(Graphics2D g2d, int x0, int y0, int w, int h);
public DrawPrimitivesTest(String name) {
super();
this.name = name;
}
public void runTest() throws InterruptedException, InvocationTargetException, AWTException {
SwingUtilities.invokeLater(() -> {
add(new JPanel() {
@Override
public Dimension getPreferredSize() {
return new Dimension(W, H);
}
@Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.YELLOW);
int c = 0;
for (int i = 0; i < W; i += 10) {
for (int j = 0; j < H; j += 10) {
c = (c + 1) % color.length;
g2d.setColor(color[c]);
renderPrimitive(g2d, i, j, 10, 10);
}
}
Point p = getLocationOnScreen();
frameX0 = p.x;
frameY0 = p.y - getInsets().top;
latchRender.countDown();
}
});
setPreferredSize(new Dimension(W, H));
pack();
setVisible(true);
});
latchRender.await();
Thread.sleep(1000);
Robot robot = new Robot();
boolean hasEmptyContent = true;
l:for (int i = frameX0 + W/3; i < frameX0 + (2*W)/3; i++) {
for (int j = 0; j < 10; j += 2) {
if (isAlmostEqual(robot.getPixelColor(i, frameY0 + H / 2 + j), Color.RED)) {
hasEmptyContent = false;
break l;
}
}
}
SwingUtilities.invokeAndWait(() -> {
setVisible(false);
dispose();
});
if (hasEmptyContent) {
throw new RuntimeException(name + ": Empty content");
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -21,10 +21,17 @@
* questions.
*/
import javax.swing.*;
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JTextField;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.awt.image.BufferedImage;
/**
* @test
@ -39,7 +46,6 @@ public class HidingSelectionTest {
private static JTextField field1;
private static JTextField field2;
private static JFrame frame;
private static Rectangle bounds;
private static JMenu menu;
private static JTextField anotherWindow;
private static Point menuLoc;
@ -67,17 +73,9 @@ public class HidingSelectionTest {
Robot robot = new Robot();
robot.waitForIdle();
robot.delay(200);
SwingUtilities.invokeAndWait(() -> {
bounds = field2.getBounds();
bounds.setLocation(field2.getLocationOnScreen());
});
BufferedImage nosel = robot.createScreenCapture(bounds);
SwingUtilities.invokeAndWait(field2::requestFocus);
SwingUtilities.invokeAndWait(field2::selectAll);
robot.waitForIdle();
robot.delay(200);
BufferedImage sel = robot.createScreenCapture(bounds);
SwingUtilities.invokeAndWait(() -> {
menuLoc = menu.getLocationOnScreen();
@ -89,7 +87,7 @@ public class HidingSelectionTest {
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
robot.waitForIdle();
robot.delay(200);
if (!biEqual(robot.createScreenCapture(bounds), sel)) {
if (!field2.getCaret().isSelectionVisible()) {
throw new RuntimeException("Test fails: menu hides selection");
}
@ -98,7 +96,7 @@ public class HidingSelectionTest {
SwingUtilities.invokeAndWait(field1::requestFocus);
robot.waitForIdle();
robot.delay(200);
if (!biEqual(robot.createScreenCapture(bounds), nosel)) {
if (field2.getCaret().isSelectionVisible()) {
throw new RuntimeException(
"Test fails: focus lost doesn't hide selection");
}
@ -119,35 +117,12 @@ public class HidingSelectionTest {
SwingUtilities.invokeAndWait(anotherWindow::requestFocus);
robot.waitForIdle();
robot.delay(200);
if (biEqual(robot.createScreenCapture(bounds), nosel)) {
if (!field2.getCaret().isSelectionVisible()) {
throw new RuntimeException(
"Test fails: switch window hides selection");
}
SwingUtilities.invokeAndWait(anotherWindow::selectAll);
robot.waitForIdle();
robot.delay(200);
if (biEqual(robot.createScreenCapture(bounds), sel)) {
throw new RuntimeException(
"Test fails: selection ownership is lost selection is shown");
}
SwingUtilities.invokeLater(frame2::dispose);
SwingUtilities.invokeLater(frame::dispose);
}
static boolean biEqual(BufferedImage i1, BufferedImage i2) {
if (i1.getWidth() == i2.getWidth() &&
i1.getHeight() == i2.getHeight()) {
for (int x = 0; x < i1.getWidth(); x++) {
for (int y = 0; y < i1.getHeight(); y++) {
if (i1.getRGB(x, y) != i2.getRGB(x, y)) {
return false;
}
}
}
return true;
}
return false;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -21,10 +21,17 @@
* questions.
*/
import javax.swing.*;
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JTextField;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.awt.image.BufferedImage;
/**
* @test
@ -40,131 +47,113 @@ public class MultiSelectionTest {
private static JTextField field1;
private static JTextField field2;
private static JFrame frame;
private static Rectangle bounds;
private static JMenu menu;
private static JTextField anotherWindow;
private static Point menuLoc;
private static JFrame frame2;
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(() -> {
frame = new JFrame();
field1 = new JTextField("field1 ");
field2 = new JTextField("field2 ");
field1.setEditable(false);
field2.setEditable(false);
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(field1);
frame.getContentPane().add(field2);
JMenuBar menuBar = new JMenuBar();
menu = new JMenu("menu");
menu.add(new JMenuItem("item"));
menuBar.add(menu);
frame.setJMenuBar(menuBar);
frame.pack();
frame.setVisible(true);
});
try {
SwingUtilities.invokeAndWait(() -> {
frame = new JFrame();
field1 = new JTextField("field1 ");
field2 = new JTextField("field2 ");
field1.setEditable(false);
field2.setEditable(false);
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(field1);
frame.getContentPane().add(field2);
JMenuBar menuBar = new JMenuBar();
menu = new JMenu("menu");
menu.add(new JMenuItem("item"));
menuBar.add(menu);
frame.setJMenuBar(menuBar);
frame.pack();
frame.setVisible(true);
});
Robot robot = new Robot();
robot.waitForIdle();
robot.delay(200);
SwingUtilities.invokeAndWait(() -> {
bounds = field2.getBounds();
bounds.setLocation(field2.getLocationOnScreen());
});
BufferedImage nosel = robot.createScreenCapture(bounds);
Robot robot = new Robot();
robot.waitForIdle();
robot.delay(200);
SwingUtilities.invokeAndWait(field2::requestFocus);
SwingUtilities.invokeAndWait(field2::selectAll);
robot.waitForIdle();
robot.delay(200);
BufferedImage sel = robot.createScreenCapture(bounds);
SwingUtilities.invokeAndWait(field2::requestFocus);
SwingUtilities.invokeAndWait(field2::selectAll);
robot.waitForIdle();
robot.delay(200);
SwingUtilities.invokeAndWait(() -> {
menuLoc = menu.getLocationOnScreen();
menuLoc.translate(10, 10);
});
robot.mouseMove(menuLoc.x, menuLoc.y);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.delay(50);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
robot.waitForIdle();
robot.delay(200);
if (!biEqual(robot.createScreenCapture(bounds), sel)) {
throw new RuntimeException("Test fails: menu hides selection");
}
SwingUtilities.invokeAndWait(
MenuSelectionManager.defaultManager()::clearSelectedPath);
SwingUtilities.invokeAndWait(field1::requestFocus);
robot.waitForIdle();
robot.delay(200);
if (!biEqual(robot.createScreenCapture(bounds), sel)) {
throw new RuntimeException(
"Test fails: focus lost hides single selection");
}
SwingUtilities.invokeAndWait(field1::selectAll);
robot.waitForIdle();
robot.delay(200);
if (!biEqual(robot.createScreenCapture(bounds), nosel)) {
throw new RuntimeException(
"Test fails: focus lost doesn't hide selection upon multi selection");
}
SwingUtilities.invokeAndWait(field2::requestFocus);
robot.waitForIdle();
robot.delay(200);
if (!biEqual(robot.createScreenCapture(bounds), sel)) {
throw new RuntimeException(
"Test fails: focus gain hides selection upon multi selection");
}
SwingUtilities.invokeAndWait(field2::requestFocus);
robot.waitForIdle();
SwingUtilities.invokeAndWait(() ->{
frame2 = new JFrame();
Point loc = frame.getLocationOnScreen();
loc.translate(0, frame.getHeight());
frame2.setLocation(loc);
anotherWindow = new JTextField("textField3");
frame2.add(anotherWindow);
frame2.pack();
frame2.setVisible(true);
});
robot.waitForIdle();
SwingUtilities.invokeAndWait(anotherWindow::requestFocus);
robot.waitForIdle();
robot.delay(200);
if (biEqual(robot.createScreenCapture(bounds), nosel)) {
throw new RuntimeException(
"Test fails: switch window hides selection");
}
SwingUtilities.invokeAndWait(anotherWindow::selectAll);
robot.waitForIdle();
robot.delay(200);
if (biEqual(robot.createScreenCapture(bounds), sel)) {
throw new RuntimeException(
"Test fails: selection ownership is lost selection is shown");
}
SwingUtilities.invokeLater(frame2::dispose);
SwingUtilities.invokeLater(frame::dispose);
}
static boolean biEqual(BufferedImage i1, BufferedImage i2) {
if (i1.getWidth() == i2.getWidth() &&
i1.getHeight() == i2.getHeight()) {
for (int x = 0; x < i1.getWidth(); x++) {
for (int y = 0; y < i1.getHeight(); y++) {
if (i1.getRGB(x, y) != i2.getRGB(x, y)) {
return false;
}
}
SwingUtilities.invokeAndWait(() -> {
menuLoc = menu.getLocationOnScreen();
menuLoc.translate(10, 10);
});
robot.mouseMove(menuLoc.x, menuLoc.y);
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.delay(50);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
robot.waitForIdle();
robot.delay(200);
if (!field2.getCaret().isSelectionVisible()) {
throw new RuntimeException("Test fails: menu hides selection");
}
return true;
SwingUtilities.invokeAndWait(
MenuSelectionManager.defaultManager()::clearSelectedPath);
SwingUtilities.invokeAndWait(field1::requestFocus);
robot.waitForIdle();
robot.delay(200);
if (field2.getSelectedText() == null || field2.getSelectedText().length() == 0) {
throw new RuntimeException(
"Test fails: focus lost hides single selection");
}
SwingUtilities.invokeAndWait(field1::selectAll);
robot.waitForIdle();
robot.delay(200);
if (field2.getCaret().isSelectionVisible()) {
throw new RuntimeException(
"Test fails: focus lost doesn't hide selection upon multi selection");
}
SwingUtilities.invokeAndWait(field2::requestFocus);
robot.waitForIdle();
robot.delay(200);
if (!field2.getCaret().isSelectionVisible()) {
throw new RuntimeException(
"Test fails: focus gain hides selection upon multi selection");
}
SwingUtilities.invokeAndWait(field2::requestFocus);
robot.waitForIdle();
SwingUtilities.invokeAndWait(() -> {
frame2 = new JFrame();
Point loc = frame.getLocationOnScreen();
loc.translate(0, frame.getHeight());
frame2.setLocation(loc);
anotherWindow = new JTextField("textField3");
frame2.add(anotherWindow);
frame2.pack();
frame2.setVisible(true);
});
robot.waitForIdle();
SwingUtilities.invokeAndWait(anotherWindow::requestFocus);
robot.waitForIdle();
robot.delay(200);
if (!field2.getCaret().isSelectionVisible()) {
throw new RuntimeException(
"Test fails: switch window hides selection");
}
SwingUtilities.invokeAndWait(anotherWindow::selectAll);
robot.waitForIdle();
robot.delay(200);
if (field2.getCaret().isSelectionVisible()) {
throw new RuntimeException(
"Test fails: selection ownership is lost selection is shown");
}
} finally {
if (frame2 != null) {
SwingUtilities.invokeLater(frame2::dispose);
}
SwingUtilities.invokeLater(frame::dispose);
}
return false;
}
}