blob: 91f5811eed02c97f4f2cef40b1d5e331fe0a367d [file] [log] [blame]
/*
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.awt.*;
import java.awt.event.*;
import java.awt.peer.ComponentPeer;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import javax.swing.*;
import sun.awt.AWTAccessor;
import sun.awt.EmbeddedFrame;
import java.io.*;
import test.java.awt.regtesthelpers.Util;
/**
* <p>This class provides basis for AWT Mixing testing.
* <p>It provides all standard test machinery and should be used by
* extending and overriding next methods:
* <li> {@link OverlappingTestBase#prepareControls()} - setup UI components
* <li> {@link OverlappingTestBase#performTest()} - run particular test
* Those methods would be run in the loop for each AWT component.
* <p>Current AWT component should be added to the tested UI by {@link OverlappingTestBase#propagateAWTControls(java.awt.Container) ()}.
* There AWT components are prepared to be tested as being overlayed by other (e.g. Swing) components - they are colored to
* {@link OverlappingTestBase#AWT_BACKGROUND_COLOR} and throws failure on catching mouse event.
* <p> Validation of component being overlayed should be tested by {@link OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point) }
* See each method javadoc for more details.
*
* <p>Due to test machinery limitations all test should be run from their own main() by calling next coe
* <code>
* public static void main(String args[]) throws InterruptedException {
* instance = new YourTestInstance();
* OverlappingTestBase.doMain(args);
* }
* </code>
*
* @author Sergey Grinev
*/
public abstract class OverlappingTestBase {
// working variables
private static volatile boolean wasHWClicked = false;
private static volatile boolean passed = true;
// constants
/**
* Default color for AWT component used for validate correct drawing of overlapping. <b>Never</b> use it for lightweight components.
*/
protected static final Color AWT_BACKGROUND_COLOR = new Color(21, 244, 54);
protected static Color AWT_VERIFY_COLOR = AWT_BACKGROUND_COLOR;
protected static final int ROBOT_DELAY = 500;
private static final String[] simpleAwtControls = {"Button", "Checkbox", "Label", "TextArea"};
/**
* Generic strings array. To be used for population of List based controls.
*/
protected static final String[] petStrings = {"Bird", "Cat", "Dog", "Rabbit", "Rhynocephalia Granda", "Bear", "Tiger", "Mustang"};
// "properties"
/**
* Tests customization. Set this variable to test only control from java.awt
* <p>Usage of this variable should be marked with CR being the reason.
* <p>Do not use this variable simultaneously with {@link OverlappingTestBase#skipClassNames}
*/
protected String onlyClassName = null;
/**
* For customizing tests. List classes' simple names to skip them from testings.
* <p>Usage of this variable should be marked with CR being the reason.
*/
protected String[] skipClassNames = null;
/**
* Set to false to avoid event delivery validation
* @see OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point, boolean)
*/
protected boolean useClickValidation = true;
/**
* Set to false if test doesn't supposed to verify EmbeddedFrame
*/
protected boolean testEmbeddedFrame = false;
/**
* Set this variable to true if testing embedded frame is impossible (on Mac, for instance, for now).
* The testEmbeddedFrame is explicitly set to true in dozen places.
*/
protected boolean skipTestingEmbeddedFrame = false;
public static final boolean isMac = System.getProperty("os.name").toLowerCase().contains("os x");
private boolean isFrameBorderCalculated;
private int borderShift;
{ if (Toolkit.getDefaultToolkit().getClass().getName().matches(".*L.*Toolkit")) {
// No EmbeddedFrame in LWToolkit/LWCToolkit, yet
// And it should be programmed some other way, too, in any case
//System.err.println("skipTestingEmbeddedFrame");
//skipTestingEmbeddedFrame = true;
}else {
System.err.println("do not skipTestingEmbeddedFrame");
}
}
protected int frameBorderCounter() {
if (!isFrameBorderCalculated) {
try {
new FrameBorderCounter(); // force compilation by jtreg
String JAVA_HOME = System.getProperty("java.home");
Process p = Runtime.getRuntime().exec(JAVA_HOME + "/bin/java FrameBorderCounter");
try {
p.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
if (p.exitValue() != 0) {
throw new RuntimeException("FrameBorderCounter exited with not null code!\n" + readInputStream(p.getErrorStream()));
}
borderShift = Integer.parseInt(readInputStream(p.getInputStream()).trim());
isFrameBorderCalculated = true;
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Problem calculating a native border size");
}
}
return borderShift;
}
public void getVerifyColor() {
try {
final int size = 200;
final Point[] p = new Point[1];
SwingUtilities.invokeAndWait(new Runnable() {
public void run(){
JFrame frame = new JFrame("set back");
frame.getContentPane().setBackground(AWT_BACKGROUND_COLOR);
frame.setSize(size, size);
frame.setUndecorated(true);
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
p[0] = frame.getLocation();
}
});
Robot robot = new Robot();
robot.waitForIdle();
Thread.sleep(ROBOT_DELAY);
AWT_VERIFY_COLOR = robot.getPixelColor(p[0].x+size/2, p[0].y+size/2);
System.out.println("Color will be compared with " + AWT_VERIFY_COLOR + " instead of " + AWT_BACKGROUND_COLOR);
} catch (Exception e) {
System.err.println("Cannot get verify color: "+e.getMessage());
}
}
private String readInputStream(InputStream is) throws IOException {
byte[] buffer = new byte[4096];
int len = 0;
StringBuilder sb = new StringBuilder();
try (InputStreamReader isr = new InputStreamReader(is)) {
while ((len = is.read(buffer)) > 0) {
sb.append(new String(buffer, 0, len));
}
}
return sb.toString();
}
private void setupControl(final Component control) {
if (useClickValidation) {
control.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
System.err.println("ERROR: " + control.getClass() + " received mouse click.");
wasHWClicked = true;
}
});
}
control.setBackground(AWT_BACKGROUND_COLOR);
control.setForeground(AWT_BACKGROUND_COLOR);
control.setPreferredSize(new Dimension(150, 150));
control.setFocusable(false);
}
private void addAwtControl(java.util.List<Component> container, final Component control) {
String simpleName = control.getClass().getSimpleName();
if (onlyClassName != null && !simpleName.equals(onlyClassName)) {
return;
}
if (skipClassNames != null) {
for (String skipMe : skipClassNames) {
if (simpleName.equals(skipMe)) {
return;
}
}
}
setupControl(control);
container.add(control);
}
private void addSimpleAwtControl(java.util.List<Component> container, String className) {
try {
Class definition = Class.forName("java.awt." + className);
Constructor constructor = definition.getConstructor(new Class[]{String.class});
java.awt.Component component = (java.awt.Component) constructor.newInstance(new Object[]{"AWT Component " + className});
addAwtControl(container, component);
} catch (Exception ex) {
System.err.println(ex.getMessage());
fail("Setup error, this jdk doesn't have awt conrol " + className);
}
}
/**
* Adds current AWT control to container
* <p>N.B.: if testEmbeddedFrame == true this method will also add EmbeddedFrame over Canvas
* and it should be called <b>after</b> Frame.setVisible(true) call
* @param container container to hold AWT component
*/
protected final void propagateAWTControls(Container container) {
if (currentAwtControl != null) {
container.add(currentAwtControl);
} else { // embedded frame
try {
//create embedder
Canvas embedder = new Canvas();
embedder.setBackground(Color.RED);
embedder.setPreferredSize(new Dimension(150, 150));
container.add(embedder);
container.setVisible(true); // create peer
long frameWindow = 0;
String getWindowMethodName = null;
String eframeClassName = null;
if (Toolkit.getDefaultToolkit().getClass().getName().contains("XToolkit")) {
java.awt.Helper.addExports("sun.awt.X11", OverlappingTestBase.class.getModule());
getWindowMethodName = "getWindow";
eframeClassName = "sun.awt.X11.XEmbeddedFrame";
}else if (Toolkit.getDefaultToolkit().getClass().getName().contains(".WToolkit")) {
java.awt.Helper.addExports("sun.awt.windows", OverlappingTestBase.class.getModule());
getWindowMethodName = "getHWnd";
eframeClassName = "sun.awt.windows.WEmbeddedFrame";
}else if (isMac) {
java.awt.Helper.addExports("sun.lwawt", OverlappingTestBase.class.getModule());
java.awt.Helper.addExports("sun.lwawt.macosx", OverlappingTestBase.class.getModule());
eframeClassName = "sun.lwawt.macosx.CViewEmbeddedFrame";
}
ComponentPeer peer = AWTAccessor.getComponentAccessor()
.getPeer(embedder);
if (!isMac) {
Method getWindowMethod = peer.getClass().getMethod(getWindowMethodName);
frameWindow = (Long) getWindowMethod.invoke(peer);
} else {
Method m_getPlatformWindowMethod = peer.getClass().getMethod("getPlatformWindow");
Object platformWindow = m_getPlatformWindowMethod.invoke(peer);
Class classPlatformWindow = Class.forName("sun.lwawt.macosx.CPlatformWindow");
Method m_getContentView = classPlatformWindow.getMethod("getContentView");
Object contentView = m_getContentView.invoke(platformWindow);
Class classContentView = Class.forName("sun.lwawt.macosx.CPlatformView");
Method m_getAWTView = classContentView.getMethod("getAWTView");
frameWindow = (Long) m_getAWTView.invoke(contentView);
}
Class eframeClass = Class.forName(eframeClassName);
Constructor eframeCtor = eframeClass.getConstructor(long.class);
EmbeddedFrame eframe = (EmbeddedFrame) eframeCtor.newInstance(frameWindow);
setupControl(eframe);
eframe.setSize(new Dimension(150, 150));
eframe.setVisible(true);
// System.err.println(eframe.getSize());
} catch (Exception ex) {
ex.printStackTrace();
fail("Failed to instantiate EmbeddedFrame: " + ex.getMessage());
}
}
}
private static final Font hugeFont = new Font("Arial", Font.BOLD, 70);
private java.util.List<Component> getAWTControls() {
java.util.List<Component> components = new ArrayList<Component>();
for (String clazz : simpleAwtControls) {
addSimpleAwtControl(components, clazz);
}
TextField tf = new TextField();
tf.setFont(hugeFont);
addAwtControl(components, tf);
// more complex controls
Choice c = new Choice();
for (int i = 0; i < petStrings.length; i++) {
c.add(petStrings[i]);
}
addAwtControl(components, c);
c.setPreferredSize(null);
c.setFont(hugeFont); // to make control bigger as setPrefferedSize don't do his job here
List l = new List(petStrings.length);
for (int i = 0; i < petStrings.length; i++) {
l.add(petStrings[i]);
}
addAwtControl(components, l);
Canvas canvas = new Canvas();
canvas.setSize(100, 200);
addAwtControl(components, canvas);
Scrollbar sb = new Scrollbar(Scrollbar.VERTICAL, 500, 1, 0, 500);
addAwtControl(components, sb);
Scrollbar sb2 = new Scrollbar(Scrollbar.HORIZONTAL, 500, 1, 0, 500);
addAwtControl(components, sb2);
return components;
}
/**
* Default shift for {@link OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point) }
*/
protected static Point shift = new Point(16, 16);
/**
* Verifies point using specified AWT Robot. Supposes <code>defaultShift == true</code> for {@link OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point, boolean) }.
* This method is used to verify controls by providing just their plain screen coordinates.
* @param robot AWT Robot. Usually created by {@link Util#createRobot() }
* @param lLoc point to verify
* @see OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point, boolean)
*/
protected void clickAndBlink(Robot robot, Point lLoc) {
clickAndBlink(robot, lLoc, true);
}
/**
* Default failure message for color check
* @see OverlappingTestBase#performTest()
*/
protected String failMessageColorCheck = "The LW component did not pass pixel color check and is overlapped";
/**
* Default failure message event check
* @see OverlappingTestBase#performTest()
*/
protected String failMessage = "The LW component did not received the click.";
private static boolean isValidForPixelCheck(Component component) {
if ((component instanceof java.awt.Scrollbar) || isMac && (component instanceof java.awt.Button)) {
return false;
}
return true;
}
/**
* Preliminary validation - should be run <b>before</b> overlapping happens to ensure test is correct.
* @param robot AWT Robot. Usually created by {@link Util#createRobot() }
* @param lLoc point to validate to be <b>of</b> {@link OverlappingTestBase#AWT_BACKGROUND_COLOR}
* @param component tested component, should be pointed out as not all components are valid for pixel check.
*/
protected void pixelPreCheck(Robot robot, Point lLoc, Component component) {
if (isValidForPixelCheck(component)) {
int tries = 10;
Color c = null;
while (tries-- > 0) {
c = robot.getPixelColor(lLoc.x, lLoc.y);
System.out.println("Precheck. color: "+c+" compare with "+AWT_VERIFY_COLOR);
if (c.equals(AWT_VERIFY_COLOR)) {
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
System.err.println(lLoc + ": " + c);
fail("Dropdown test setup failure, colored part of AWT component is not located at click area");
}
}
/**
* Verifies point using specified AWT Robot.
* <p>Firstly, verifies point by color pixel check
* <p>Secondly, verifies event delivery by mouse click
* @param robot AWT Robot. Usually created by {@link Util#createRobot() }
* @param lLoc point to verify
* @param defaultShift if true verified position will be shifted by {@link OverlappingTestBase#shift }.
*/
protected void clickAndBlink(Robot robot, Point lLoc, boolean defaultShift) {
Point loc = lLoc.getLocation();
//check color
Util.waitForIdle(robot);
try{
Thread.sleep(500);
}catch(Exception exx){
exx.printStackTrace();
}
if (defaultShift) {
loc.translate(shift.x, shift.y);
}
if (!(System.getProperty("os.name").toLowerCase().contains("os x"))) {
Color c = robot.getPixelColor(loc.x, loc.y);
System.out.println("C&B. color: "+c+" compare with "+AWT_VERIFY_COLOR);
if (c.equals(AWT_VERIFY_COLOR)) {
fail(failMessageColorCheck);
passed = false;
}
// perform click
Util.waitForIdle(robot);
}
robot.mouseMove(loc.x, loc.y);
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
Util.waitForIdle(robot);
}
/**
* This method should be overriden with code which setups UI for testing.
* Code in this method <b>will</b> be called only from AWT thread so Swing operations can be called directly.
*
* @see {@link OverlappingTestBase#propagateAWTControls(java.awt.Container) } for instructions about adding tested AWT control to UI
*/
protected abstract void prepareControls();
/**
* This method should be overriden with test execution. It will <b>not</b> be called from AWT thread so all Swing operations should be treated accordingly.
* @return true if test passed. Otherwise fail with default fail message.
* @see {@link OverlappingTestBase#failMessage} default fail message
*/
protected abstract boolean performTest();
/**
* This method can be overriden with cleanup routines. It will be called from AWT thread so all Swing operations should be treated accordingly.
*/
protected void cleanup() {
// intentionally do nothing
}
/**
* Currect tested AWT Control. Usually shouldn't be accessed directly.
*
* @see {@link OverlappingTestBase#propagateAWTControls(java.awt.Container) } for instructions about adding tested AWT control to UI
*/
protected Component currentAwtControl;
private void testComponent(Component component) throws InterruptedException, InvocationTargetException {
Robot robot = null;
try {
robot = new Robot();
}catch(Exception ignorex) {
}
currentAwtControl = component;
System.out.println("Testing " + currentAwtControl.getClass().getSimpleName());
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
prepareControls();
}
});
if (component != null) {
Util.waitTillShown(component);
}
Util.waitForIdle(robot);
try {
Thread.sleep(500); // wait for graphic effects on systems like Win7
} catch (InterruptedException ex) {
}
if (!instance.performTest()) {
fail(failMessage);
passed = false;
}
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
cleanup();
}
});
}
private void testEmbeddedFrame() throws InvocationTargetException, InterruptedException {
Robot robot = null;
try {
robot = new Robot();
}catch(Exception ignorex) {
}
System.out.println("Testing EmbeddedFrame");
currentAwtControl = null;
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
prepareControls();
}
});
Util.waitForIdle(robot);
try {
Thread.sleep(500); // wait for graphic effects on systems like Win7
} catch (InterruptedException ex) {
}
if (!instance.performTest()) {
fail(failMessage);
passed = false;
}
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
cleanup();
}
});
}
private void testAwtControls() throws InterruptedException {
try {
for (Component component : getAWTControls()) {
testComponent(component);
}
if (testEmbeddedFrame && !skipTestingEmbeddedFrame) {
testEmbeddedFrame();
}
} catch (InvocationTargetException ex) {
ex.printStackTrace();
fail(ex.getMessage());
}
}
/**
* Used by standard test machinery. See usage at {@link OverlappingTestBase }
*/
protected static OverlappingTestBase instance;
protected OverlappingTestBase() {
getVerifyColor();
}
/*****************************************************
* Standard Test Machinery Section
* DO NOT modify anything in this section -- it's a
* standard chunk of code which has all of the
* synchronisation necessary for the test harness.
* By keeping it the same in all tests, it is easier
* to read and understand someone else's test, as
* well as insuring that all tests behave correctly
* with the test harness.
* There is a section following this for test-
* classes
******************************************************/
private static void init() throws InterruptedException {
//System.setProperty("sun.awt.disableMixing", "true");
instance.testAwtControls();
if (wasHWClicked) {
fail("HW component received the click.");
passed = false;
}
if (passed) {
pass();
}
}//End init()
private static boolean theTestPassed = false;
private static boolean testGeneratedInterrupt = false;
private static String failureMessage = "";
private static Thread mainThread = null;
private static int sleepTime = 300000;
// Not sure about what happens if multiple of this test are
// instantiated in the same VM. Being static (and using
// static vars), it aint gonna work. Not worrying about
// it for now.
/**
* Starting point for test runs. See usage at {@link OverlappingTestBase }
* @param args regular main args, not used.
* @throws InterruptedException
*/
public static void doMain(String args[]) throws InterruptedException {
mainThread = Thread.currentThread();
try {
init();
} catch (TestPassedException e) {
//The test passed, so just return from main and harness will
// interepret this return as a pass
return;
}
//At this point, neither test pass nor test fail has been
// called -- either would have thrown an exception and ended the
// test, so we know we have multiple threads.
//Test involves other threads, so sleep and wait for them to
// called pass() or fail()
try {
Thread.sleep(sleepTime);
//Timed out, so fail the test
throw new RuntimeException("Timed out after " + sleepTime / 1000 + " seconds");
} catch (InterruptedException e) {
//The test harness may have interrupted the test. If so, rethrow the exception
// so that the harness gets it and deals with it.
if (!testGeneratedInterrupt) {
throw e;
}
//reset flag in case hit this code more than once for some reason (just safety)
testGeneratedInterrupt = false;
if (theTestPassed == false) {
throw new RuntimeException(failureMessage);
}
}
}//main
/**
* Test will fail if not passed after this timeout. Default timeout is 300 seconds.
* @param seconds timeout in seconds
*/
public static synchronized void setTimeoutTo(int seconds) {
sleepTime = seconds * 1000;
}
/**
* Set test as passed. Usually shoudn't be called directly.
*/
public static synchronized void pass() {
System.out.println("The test passed.");
System.out.println("The test is over, hit Ctl-C to stop Java VM");
//first check if this is executing in main thread
if (mainThread == Thread.currentThread()) {
//Still in the main thread, so set the flag just for kicks,
// and throw a test passed exception which will be caught
// and end the test.
theTestPassed = true;
throw new TestPassedException();
}
theTestPassed = true;
testGeneratedInterrupt = true;
mainThread.interrupt();
}//pass()
/**
* Fail test generic message.
*/
public static synchronized void fail() {
//test writer didn't specify why test failed, so give generic
fail("it just plain failed! :-)");
}
/**
* Fail test providing specific reason.
* @param whyFailed reason
*/
public static synchronized void fail(String whyFailed) {
System.out.println("The test failed: " + whyFailed);
System.out.println("The test is over, hit Ctl-C to stop Java VM");
//check if this called from main thread
if (mainThread == Thread.currentThread()) {
//If main thread, fail now 'cause not sleeping
throw new RuntimeException(whyFailed);
}
theTestPassed = false;
testGeneratedInterrupt = true;
failureMessage = whyFailed;
mainThread.interrupt();
}//fail()
}// class LWComboBox
class TestPassedException extends RuntimeException {
}