/* * 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; /** *

This class provides basis for AWT Mixing testing. *

It provides all standard test machinery and should be used by * extending and overriding next methods: *

  • {@link OverlappingTestBase#prepareControls()} - setup UI components *
  • {@link OverlappingTestBase#performTest()} - run particular test * Those methods would be run in the loop for each AWT component. *

    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. *

    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. * *

    Due to test machinery limitations all test should be run from their own main() by calling next coe * * public static void main(String args[]) throws InterruptedException { * instance = new YourTestInstance(); * OverlappingTestBase.doMain(args); * } * * * @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. Never 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 *

    Usage of this variable should be marked with CR being the reason. *

    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. *

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

    N.B.: if testEmbeddedFrame == true this method will also add EmbeddedFrame over Canvas * and it should be called after 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 getAWTControls() { java.util.List components = new ArrayList(); 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 defaultShift == true 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 before overlapping happens to ensure test is correct. * @param robot AWT Robot. Usually created by {@link Util#createRobot() } * @param lLoc point to validate to be of {@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. *

    Firstly, verifies point by color pixel check *

    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 will 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 not 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 { }