changeset 1:24c167fc2140

7902086: Add JemmyAWTInput to jemmy/v3
author shurailine
date Wed, 20 Dec 2017 14:16:29 -0800
parents 369b193287fc
children 5c1eb9ef0c6a
files core/JemmyAWTInput/.DS_Store core/JemmyAWTInput/build.xml core/JemmyAWTInput/src/org/jemmy/image/AWTImage.java core/JemmyAWTInput/src/org/jemmy/image/AWTRobotCapturer.java core/JemmyAWTInput/src/org/jemmy/image/AbstractImageComparator.java core/JemmyAWTInput/src/org/jemmy/image/AverageDistanceImageComparator.java core/JemmyAWTInput/src/org/jemmy/image/BufferedImageComparator.java core/JemmyAWTInput/src/org/jemmy/image/ClasspathImageLoader.java core/JemmyAWTInput/src/org/jemmy/image/ColorImageComparator.java core/JemmyAWTInput/src/org/jemmy/image/DiffDialog.form core/JemmyAWTInput/src/org/jemmy/image/DiffDialog.java core/JemmyAWTInput/src/org/jemmy/image/FilesystemImageLoader.java core/JemmyAWTInput/src/org/jemmy/image/ImageFinder.java core/JemmyAWTInput/src/org/jemmy/image/ImageTool.java core/JemmyAWTInput/src/org/jemmy/image/NaturalImageComparator.java core/JemmyAWTInput/src/org/jemmy/image/PNGDecoder.java core/JemmyAWTInput/src/org/jemmy/image/PNGEncoder.java core/JemmyAWTInput/src/org/jemmy/image/PNGImageLoader.java core/JemmyAWTInput/src/org/jemmy/image/PNGImageSaver.java core/JemmyAWTInput/src/org/jemmy/image/ResizeImageComparator.java core/JemmyAWTInput/src/org/jemmy/image/RoughImageComparator.java core/JemmyAWTInput/src/org/jemmy/image/RoughImageFinder.java core/JemmyAWTInput/src/org/jemmy/image/StrictImageComparator.java core/JemmyAWTInput/src/org/jemmy/image/StrictImageFinder.java core/JemmyAWTInput/src/org/jemmy/image/Utils.java core/JemmyAWTInput/src/org/jemmy/image/package.html core/JemmyAWTInput/src/org/jemmy/input/AWTMap.java core/JemmyAWTInput/src/org/jemmy/input/AWTRobotInputFactory.java core/JemmyAWTInput/src/org/jemmy/input/ClassReference.java core/JemmyAWTInput/src/org/jemmy/input/DragImpl.java core/JemmyAWTInput/src/org/jemmy/input/KeyboardImpl.java core/JemmyAWTInput/src/org/jemmy/input/MouseImpl.java core/JemmyAWTInput/src/org/jemmy/input/RobotDriver.java core/JemmyAWTInput/src/org/jemmy/input/RobotExecutor.java core/JemmyAWTInput/src/org/jemmy/input/Version.java core/JemmyAWTInput/src/org/jemmy/input/jemmy.properties core/JemmyAWTInput/src/org/jemmy/operators/AWTScreen.java core/JemmyAWTInput/test/TEST.ROOT core/JemmyAWTInput/test/org/jemmy/image/AreaChart_a.png core/JemmyAWTInput/test/org/jemmy/image/AreaChart_a_res.png core/JemmyAWTInput/test/org/jemmy/image/AverageDistanceImageComparatorTest.java core/JemmyAWTInput/test/org/jemmy/image/CheckBox_a_start.png core/JemmyAWTInput/test/org/jemmy/image/CheckBox_a_start_res.png core/JemmyAWTInput/test/org/jemmy/image/EnvTest.java core/JemmyAWTInput/test/org/jemmy/image/ImageResizerTest.java core/JemmyAWTInput/test/org/jemmy/image/InitTest.java core/JemmyAWTInput/test/org/jemmy/image/NaturalImageComparatorTest.java core/JemmyAWTInput/test/org/jemmy/image/SaveLoadTest.java core/JemmyAWTInput/test/org/jemmy/image/ScreenAreaImageTest.java core/JemmyAWTInput/test/org/jemmy/image/ScreenImageTest.java core/JemmyAWTInput/test/org/jemmy/image/comparator/ComparatorTest.java core/JemmyAWTInput/test/org/jemmy/image/comparator/actual-averagedistance.png core/JemmyAWTInput/test/org/jemmy/image/comparator/actual-hyperlink.png core/JemmyAWTInput/test/org/jemmy/image/comparator/actual-strict.png core/JemmyAWTInput/test/org/jemmy/image/comparator/golden-averagedistance.png core/JemmyAWTInput/test/org/jemmy/image/comparator/golden-hyperlink.png core/JemmyAWTInput/test/org/jemmy/image/comparator/golden-strict.png core/JemmyAWTInput/test/org/jemmy/image/image1.jpg core/JemmyAWTInput/test/org/jemmy/image/image2.jpg core/JemmyAWTInput/test/org/jemmy/image/image3.jpg core/JemmyAWTInput/test/org/jemmy/image/test_image.png core/JemmyAWTInput/test/org/jemmy/image/tool/data/golden/fail.png core/JemmyAWTInput/test/org/jemmy/image/tool/data/golden/mess.png core/JemmyAWTInput/test/org/jemmy/image/tool/data/golden/missed.png core/JemmyAWTInput/test/org/jemmy/image/tool/data/golden/pass.png core/JemmyAWTInput/test/org/jemmy/image/tool/data/results/fail.png core/JemmyAWTInput/test/org/jemmy/image/tool/data/results/mess.png core/JemmyAWTInput/test/org/jemmy/image/tool/data/results/new.png core/JemmyAWTInput/test/org/jemmy/image/tool/data/results/pass.png core/JemmyAWTInput/test/org/jemmy/input/AWTMapTest.java core/JemmyAWTInput/test/org/jemmy/input/RobotDriver2Test.java core/JemmyAWTInput/test/org/jemmy/input/RobotDriverTest.java core/JemmyAWTInput/test/org/jemmy/input/RobotExecutorTest.java core/JemmyAWTInput/test/org/jemmy/input/SmoothMoveTest.java core/JemmyAWTInput/test/org/jemmy/operators/ScreenTest.java
diffstat 75 files changed, 8969 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
Binary file core/JemmyAWTInput/.DS_Store has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/build.xml	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 1997, 2017, 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.
+ -->
+<project name="jemmy" default="compile" basedir=".">
+    <property name="src.dir" location="src"/>
+    <property name="test.dir" location="test"/>
+    <property name="build.dir" location="build"/>
+    <property name="build.classes.dir" location="${build.dir}/classes"/>
+    <property name="build.test.dir" location="${build.dir}/test"/>
+    <property name="dist.jar" location="${build.dir}/JemmyCore.jar"/>
+    <property name="test.list" location="${build.dir}/testlist"/>
+    <property name="test.workdir" location="${build.dir}/test_wd"/>
+    <property name="test.report" location="${build.dir}/test_report"/>
+    <property name="core.jar" location="../JemmyCore/build/JemmyCore.jar"/>
+    <target name="timestamp" unless="buildnumber">
+        <tstamp>
+            <format property="buildnumber" pattern="yyyyMMdd"/>
+        </tstamp>
+    </target>
+    <target name="check-core">
+        <available file="${core.jar}" property="dependencies.are.built"/>
+    </target>
+    <target name="build-dependencies" depends="check-core" unless="dependencies.are.built">
+        <ant dir="../JemmyCore" target="jar"/>
+    </target>
+    <target name="compile" depends="timestamp,build-dependencies">
+        <mkdir dir="${build.classes.dir}"/>
+        <javac srcdir="${src.dir}" classpath="${core.jar}" destdir="${build.classes.dir}" debug="on" includeantruntime="false"/>
+        <copy file="${src.dir}/org/jemmy/input/jemmy.properties" tofile="${build.classes.dir}/org/jemmy/input/jemmy.properties" filtering="on"/>
+        <echo message="build=${buildnumber}" file="${build.classes.dir}/org/jemmy/input/jemmy.properties" append="true"/>
+    </target>
+    <target name="compile-test" depends="compile">
+        <fail message="Please specify jtreg.home" unless="jtreg.home"/>
+        <mkdir dir="${build.test.dir}"/>
+        <javac srcdir="${test.dir}" destdir="${build.test.dir}" debug="on" includeantruntime="false"
+               classpath="${build.classes.dir}:${core.jar}:${jtreg.home}/lib/testng.jar"/>
+    </target>
+    <target name="find-tests" unless="tests">
+        <fileset id="testset" dir="${test.dir}">
+            <include name="**/*Test.java" />
+        </fileset>
+        <pathconvert pathsep="${line.separator}" property="testlist" refid="testset">
+            <globmapper from="${test.dir}/*" to="*"/>
+        </pathconvert>
+        <echo file="${test.list}">${testlist}</echo>
+        <property name="tests" value="@${test.list}"/>
+    </target>
+    <target name="test" depends="compile-test,find-tests">
+        <exec executable="${jtreg.home}/bin/jtreg">
+            <arg value="-cpa:${build.classes.dir}:${core.jar}"/>
+            <arg value="-w:${test.workdir}"/>
+            <arg value="-r:${test.report}"/>
+            <arg value="-conc:1"/>
+            <arg value="-ovm"/>
+            <arg value="-v:default"/>
+            <arg value="-dir:test"/>
+            <arg value="${tests}"/>
+        </exec>
+    </target>
+    <target name="jar" depends="compile">
+        <mkdir dir="${build.dir}"/>
+        <jar jarfile="${dist.jar}" basedir="${build.classes.dir}">
+            <manifest>
+                <attribute name="Main-Class"
+                    value="org.jemmy.input.Version"/>
+            </manifest>
+        </jar>
+    </target>
+    <target name="clean">
+        <delete dir="${build.dir}"/>
+    </target>
+</project>
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/AWTImage.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import org.jemmy.Dimension;
+import org.jemmy.JemmyException;
+import org.jemmy.control.Wrap;
+import org.jemmy.env.Environment;
+import org.jemmy.image.pixel.*;
+
+
+/**
+ *
+ * @author shura
+ */
+public class AWTImage implements Image, WriteableRaster {
+
+    /**
+     *
+     */
+    public static final String OUTPUT = AWTImage.class.getName() + ".OUTPUT";
+    public static final String PNG_FILE = ".png";
+
+    /**
+     * Get the value of imageRoot. The field is used to store images by relative
+     * path within the root. Images should be pointed out by full file names if
+     * the value is null.
+     *
+     * @return the value of imageRoot
+     */
+    public static File getImageRoot() {
+        ImageStore res = Environment.getEnvironment().getProperty(ImageStore.class);
+        if(!(res instanceof PNGFileImageStore)) {
+            throw new IllegalStateException("Unsupported ImageStore: " + res.getClass().getName());
+        }
+        return ((PNGFileImageStore)res).getRoot();
+    }
+
+    /**
+     * Set the value of imageRoot. If null, an image ID should be full path.
+     *
+     * @param imageRoot new value of imageRoot
+     */
+    public static void setImageRoot(File imageRoot) {
+        Environment.getEnvironment().setProperty(ImageStore.class, new PNGFileImageStore(imageRoot));
+    }
+
+    /**
+     * Gets comparator to be used by default.
+     *
+     * @see Wrap#waitImage(org.jemmy.image.Image, java.lang.String,
+     * java.lang.String)
+     * @see ImageComparator
+     * @return default comparator
+     */
+    public static ImageComparator getComparator() {
+        return Environment.getEnvironment().getProperty(ImageComparator.class);
+    }
+
+    /**
+     * Sets comparator to be used by default.
+     *
+     * @see Wrap#waitImage(org.jemmy.image.Image, java.lang.String,
+     * java.lang.String)
+     * @see ImageComparator
+     * @param comparator
+     */
+    public static void setComparator(ImageComparator comparator) {
+        Environment.getEnvironment().setProperty(ImageComparator.class, comparator);
+    }
+
+    static {
+        Environment.getEnvironment().setPropertyIfNotSet(ImageComparator.class,
+                new BufferedImageComparator(Environment.getEnvironment()));
+        Environment.getEnvironment().setPropertyIfNotSet(ImageStore.class, new PNGFileImageStore());
+    }
+
+    private BufferedImage image;
+
+    /**
+     * Creates an instance
+     *
+     * @param img
+     */
+    public AWTImage(BufferedImage img) {
+        this.image = img;
+    }
+
+    public BufferedImage getTheImage() {
+        return image;
+    }
+
+    /**
+     * Compares using current comparator.
+     *
+     * @see AWTImage#getComparator()
+     * @param img
+     * @return diff image.
+     */
+    public Image compareTo(Image img) {
+        return getComparator().compare(this, img);
+    }
+
+    /**
+     * Saves to a filesystem. fileName is expected to be a full path, unless
+     * imageRoot is specified. ".png" extension is added automatically if not
+     * specified.
+     *
+     * @see AWTImage#getImageRoot()
+     * @param fileName full or relative file name
+     */
+    public void save(String fileName) {
+        try {
+            String fullPath = fileName;
+            File imageRoot = getImageRoot();
+            if (imageRoot != null) {
+                fullPath = imageRoot.getAbsolutePath() + File.separator + fileName;
+            }
+            if (!fullPath.toLowerCase().endsWith(PNG_FILE)) {
+                fullPath += PNG_FILE;
+            }
+            new PNGImageSaver().save(image, fullPath);
+            Environment.getEnvironment().getOutput(OUTPUT).println("Image saved to " + fullPath);
+        } catch (IOException ex) {
+            throw new JemmyException("Unable to save image", ex, fileName);
+        }
+    }
+
+    public Dimension getSize() {
+        return new Dimension(image.getWidth(), image.getHeight());
+    }
+
+    public void getColors(int x, int y, double[] colors) {
+        int orig = image.getRGB(x, y);
+        int ivalue;
+        for (Raster.Component c : getSupported()) {
+            switch (c) {
+                case ALPHA:
+                    ivalue = (orig & 0xFF000000) >>> 0x18;
+                    break;
+                case RED:
+                    ivalue = (orig & 0xFF0000) >>> 0x10;
+                    break;
+                case GREEN:
+                    ivalue = (orig & 0xFF00) >>> 0x8;
+                    break;
+                case BLUE:
+                    ivalue = (orig & 0xFF);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown color component" + c);
+            }
+            colors[PixelImageComparator.arrayIndexOf(getSupported(), c)] = (double) ivalue / 0xFF;
+        }
+    }
+
+    public void setColors(int x, int y, double[] colors) {
+        int rgb = 0;
+        double value;
+        int ivalue;
+        for (Raster.Component c : getSupported()) {
+            value = colors[PixelImageComparator.arrayIndexOf(getSupported(), c)];
+            if (value < 0 || value > 1) {
+                throw new IllegalArgumentException("Color component value should be within (0, 1). Gotten: " + value);
+            }
+            ivalue = (int) Math.round(value * 0xFF);
+            if (ivalue > 0xFF) {
+                throw new IllegalStateException("Component value is greater than 0xFF: " + ivalue);
+            }
+            switch (c) {
+                case ALPHA:
+                    rgb |= (ivalue << 0x18);
+                    break;
+                case RED:
+                    rgb |= (ivalue << 0x10);
+                    break;
+                case GREEN:
+                    rgb |= (ivalue << 0x8);
+                    break;
+                case BLUE:
+                    rgb |= ivalue;
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown color component: " + c);
+            }
+        }
+        getTheImage().setRGB(x, y, rgb);
+    }
+
+    public Component[] getSupported() {
+        return new Component[]{Component.RED, Component.BLUE, Component.GREEN, Component.ALPHA};
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/AWTRobotCapturer.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+
+import org.jemmy.Rectangle;
+import org.jemmy.control.Wrap;
+import org.jemmy.input.RobotDriver;
+
+
+/**
+ * Uses java.awt.Robot to capture the images
+ * @author mrkam, shura
+ */
+public class AWTRobotCapturer implements ImageCapturer {
+    static {
+        try {
+            Class.forName(AWTImage.class.getName());
+        } catch (ClassNotFoundException ex) {
+        }
+    }
+
+    public Image capture(Wrap<?> control, Rectangle area) {
+        Rectangle rect = new Rectangle();
+        Rectangle bounds = control.getScreenBounds();
+        rect.x = bounds.x + area.x;
+        rect.y = bounds.y + area.y;
+        rect.width = area.width;
+        rect.height = area.height;
+        return RobotDriver.createScreenCapture(rect);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/AbstractImageComparator.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+
+package org.jemmy.image;
+
+
+import java.awt.image.BufferedImage;
+
+
+/**
+ * Common part of most ImageComparators.
+ *
+ * @author KAM
+ * @deprecated Use classes from org.jemmy.image.pixel package instead.
+ */
+@Deprecated
+public abstract class AbstractImageComparator implements ImageComparator {
+
+    /**
+     * Checks whether images have difference.
+     * @param image1 First image to compare.
+     * @param image2 Second image to compare.
+     * @return true if images have no difference, false otherwise.
+     */
+    public abstract boolean noDifference(BufferedImage image1, BufferedImage image2);
+
+    /**
+     * {@inheritDoc}
+     */
+    public BufferedImage compare(BufferedImage image1, BufferedImage image2) {
+        if (noDifference(image1, image2)) {
+            return null;
+        } else {
+            return ImageTool.subtractImage(image1, image2);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/AverageDistanceImageComparator.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+
+package org.jemmy.image;
+
+
+import java.awt.image.BufferedImage;
+import org.jemmy.image.pixel.AverageDistanceComparator;
+
+
+/**
+ * Compares two images calculating average color distance between pixels and
+ * comparing it to the threshold value. See {@linkplain NaturalImageComparator
+ * NaturalImageComparator} for color comparison details.
+ *
+ * @author KAM
+ */
+public class AverageDistanceImageComparator extends BufferedImageComparator {
+
+    /**
+     * Creates comparator with the default sensitivity value = 0.02
+     * (around 5 in 0-255 color component value).
+     * @see #NaturalImageComparator(double)
+     */
+    public AverageDistanceImageComparator() {
+        this(0.02);
+    }
+
+    /**
+     * Creates comparator with the specified sensitivity value
+     * @param sensitivity Maximum threshold for average 3-D distance between
+     * colors in 3-D sRGB color space for images to be considered equal.
+     * Meaningful values lay between 0 and approx 1.733. 0 means colors should
+     * be equal to pass the comparison, 1.733 (which is more than square root
+     * of 3) means that comparison will be passed even if all the colors are
+     * completely different.
+     */
+    public AverageDistanceImageComparator(double sensitivity) {
+        super(new AverageDistanceComparator(sensitivity));
+    }
+
+    public void setSensitivity(double sensitivity) {
+        ((AverageDistanceComparator)getRasterComparator()).setThreshold(sensitivity);
+    }
+    public double getSensitivity() {
+        return ((AverageDistanceComparator)getRasterComparator()).getThreshold();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/BufferedImageComparator.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import java.awt.image.BufferedImage;
+import org.jemmy.env.Environment;
+import org.jemmy.image.pixel.PixelImageComparator;
+import org.jemmy.image.pixel.Raster;
+import org.jemmy.image.pixel.RasterComparator;
+import org.jemmy.image.pixel.WriteableRaster;
+
+/**
+ *
+ * @author shura
+ */
+public class BufferedImageComparator extends PixelImageComparator {
+
+    public BufferedImageComparator(RasterComparator comparator) {
+        super(comparator);
+    }
+
+    public BufferedImageComparator(Environment env) {
+        super(env);
+    }
+
+    @Override
+    protected Image toImage(Raster image) {
+        if(image instanceof AWTImage) {
+            return (AWTImage)image;
+        } else {
+            throw new IllegalArgumentException("Unrecognized image type" + image.getClass().getName());
+        }
+    }
+
+    @Override
+    protected Raster toRaster(Image image) {
+        if(image instanceof AWTImage) {
+            return (AWTImage)image;
+        } else {
+            throw new IllegalArgumentException("Unrecognized image type" + image.getClass().getName());
+        }
+    }
+
+    @Override
+    protected WriteableRaster createDiffRaster(Raster r1, Raster r2) {
+        AWTImage img2 = (AWTImage) r2;
+        AWTImage img1 = (AWTImage) r1;
+        return new AWTImage(new BufferedImage(
+                Math.max(img1.getSize().width, img2.getSize().width),
+                Math.max(img1.getSize().height, img2.getSize().height),
+                img1.getTheImage().getType()));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/ClasspathImageLoader.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+
+import org.jemmy.env.Environment;
+
+
+/**
+ * ImageLoader implementation which is able to load images through
+ * a given classloader.
+ * @author mrkam, shura
+ */
+public class ClasspathImageLoader implements ImageLoader {
+
+    private String packagePrefix = "";
+    private ClassLoader classLoader = getClassLoader();
+
+    public static final String OUTPUT = AWTImage.class.getName() + ".OUTPUT";
+
+    /**
+     * Get the value of classLoader which is used to load images.
+     *
+     * @return the value of classLoader
+     */
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Image load(String ID) {
+        String fullId = ((packagePrefix != null) ? packagePrefix : "") + ID;
+        Environment.getEnvironment().getOutput(ClasspathImageLoader.OUTPUT).println("Image loaded from " + fullId + " by " + classLoader);
+        return new AWTImage(PNGDecoder.decode(classLoader, fullId));
+    }
+
+    /**
+     * Set the value of classLoader
+     *
+     * @param classLoader new value of classLoader
+     */
+    public void setClassLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    /**
+     * TODO: Add JavaDoc
+     * @param rootPackage
+     */
+    public void setRootPackage(Package rootPackage) {
+        if (rootPackage != null) {
+            this.packagePrefix = rootPackage.getName().replace('.', '/') + "/";
+        } else {
+            this.packagePrefix = null;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/ColorImageComparator.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import java.awt.image.BufferedImage;
+import org.jemmy.image.pixel.PixelEqualityRasterComparator;
+
+/**
+ * Compares two images with color mapping defined by
+ * <code>ColorModel</code> implementation.
+ *
+ * @author Alexandre Iline (alexandre.iline@sun.com)
+ * @deprecated Use classes form org.jemmy.image.pixel instead.
+ */
+@Deprecated
+public class ColorImageComparator implements ImageComparator {
+
+    ColorMap leftMap, rightMap;
+    ImageComparator comparator = null;
+
+    /**
+     * Creates a comparator with a color maps. Object created by this
+     * constructor behaves like
+     * <code>StrictImageComparator</code>. Object created works faster because
+     * it does not create intermediate images for another comparator.
+     *
+     * @param map Map applied to both left and right images during comparision.
+     */
+    public ColorImageComparator(ColorMap map) {
+        this(map, new StrictImageComparator());
+    }
+
+    /**
+     * Creates a comparator with
+     * <code>map</code> color mapping. Actual comparision perfomed by
+     * <code>comparator</code> parameter.
+     *
+     * @param map Map applied to both left and right images during comparision.
+     * @param subComparator comporator to perform a comparision of to images
+     * with mapped colors.
+     */
+    public ColorImageComparator(ColorMap map, ImageComparator subComparator) {
+        this(map, map, subComparator);
+    }
+
+    /**
+     * Creates a comparator with two color maps. Object created by this
+     * constructor behaves like
+     * <code>StrictImageComparator</code>. Object created works faster because
+     * it does not create intermediate images for another comparator.
+     *
+     * @param leftMap Map applied to the left image during comparision.
+     * @param rightMap Map applied to the right image during comparision.
+     */
+    public ColorImageComparator(ColorMap leftMap, ColorMap rightMap) {
+        this(leftMap, rightMap, new BufferedImageComparator(new PixelEqualityRasterComparator(0)));
+    }
+
+    /**
+     * Creates a comparator with two color maps. Actual comparision perfomed by
+     * <code>comparator</code> parameter.
+     *
+     * @param leftMap Map applied to the left image during comparision.
+     * @param rightMap Map applied to the right image during comparision.
+     * @param subComparator comporator to perform a comparision of to images
+     * with mapped colors.
+     */
+    public ColorImageComparator(ColorMap leftMap, ColorMap rightMap, ImageComparator subComparator) {
+        this.leftMap = leftMap;
+        this.rightMap = rightMap;
+        this.comparator = subComparator;
+    }
+
+    /**
+     * Compares images by
+     * <code>ImageComparator</code> passed into constructor, or itself if no
+     * <code>ImageComparator</code> was passed, processing both images by
+     * <code>ColorMap</code> instance before comparision.
+     */
+    @Override
+    public Image compare(Image image1, Image image2) {
+        return (comparator.compare(
+                recolor((AWTImage)image1, leftMap),
+                recolor((AWTImage)image2, rightMap)));
+    }
+
+    private AWTImage recolor(AWTImage isrc, ColorMap map) {
+        BufferedImage src = isrc.getTheImage();
+        BufferedImage result = new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
+        for (int x = 0; x < src.getWidth(); x++) {
+            for (int y = 0; y < src.getWidth(); y++) {
+                result.setRGB(x, y, map.mapColor(src.getRGB(x, y)));
+            }
+        }
+        return new AWTImage(result);
+    }
+
+    protected final boolean compareColors(int rgb1, int rgb2) {
+        return (leftMap.mapColor(rgb1) == rightMap.mapColor(rgb2));
+    }
+
+    public String getID() {
+        return ColorImageComparator.class.getName();
+    }
+
+    /**
+     * Interface to map colors during the comparision.
+     */
+    public static interface ColorMap {
+
+        /**
+         * Maps one color into another.
+         *
+         * @param rgb an original color.
+         * @return a converted color.
+         */
+        public int mapColor(int rgb);
+    }
+
+    /**
+     * Turns
+     * <code>foreground</code> color to white, other - to black.
+     */
+    public static class ForegroundColorMap implements ColorMap {
+
+        int foreground;
+
+        /**
+         * Constructs a ColorImageComparator$ForegroundColorMap object.
+         *
+         * @param foreground Foreground color.
+         */
+        public ForegroundColorMap(int foreground) {
+            this.foreground = foreground;
+        }
+
+        public int mapColor(int rgb) {
+            return ((rgb == foreground) ? 0xffffff : 0);
+        }
+    }
+
+    /**
+     * Turns
+     * <code>background</code> color to black, left others unchanged.
+     */
+    public static class BackgroundColorMap implements ColorMap {
+
+        int background;
+
+        /**
+         * Constructs a ColorImageComparator$BackgroundColorMap object.
+         *
+         * @param background Background color.
+         */
+        public BackgroundColorMap(int background) {
+            this.background = background;
+        }
+
+        public int mapColor(int rgb) {
+            return ((rgb == background) ? 0 : rgb);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/DiffDialog.form	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,257 @@
+<!--
+Copyright (c) 2007, 2017, 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.
+ -->
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
+  <SyntheticProperties>
+    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
+  </SyntheticProperties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Component id="tbSplit" alignment="0" pref="549" max="32767" attributes="0"/>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Component id="tbSplit" alignment="0" pref="461" max="32767" attributes="0"/>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JSplitPane" name="tbSplit">
+      <Properties>
+        <Property name="dividerLocation" type="int" value="200"/>
+        <Property name="orientation" type="int" value="0"/>
+      </Properties>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
+      <SubComponents>
+        <Container class="javax.swing.JSplitPane" name="lrSplit">
+          <Properties>
+            <Property name="dividerLocation" type="int" value="250"/>
+          </Properties>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
+              <JSplitPaneConstraints position="top"/>
+            </Constraint>
+          </Constraints>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
+          <SubComponents>
+            <Container class="javax.swing.JPanel" name="leftPane">
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
+                  <JSplitPaneConstraints position="left"/>
+                </Constraint>
+              </Constraints>
+
+              <Layout>
+                <DimensionLayout dim="0">
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <EmptySpace min="0" pref="250" max="32767" attributes="0"/>
+                  </Group>
+                </DimensionLayout>
+                <DimensionLayout dim="1">
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <EmptySpace min="0" pref="200" max="32767" attributes="0"/>
+                  </Group>
+                </DimensionLayout>
+              </Layout>
+            </Container>
+            <Container class="javax.swing.JPanel" name="rightPane">
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
+                  <JSplitPaneConstraints position="right"/>
+                </Constraint>
+              </Constraints>
+
+              <Layout>
+                <DimensionLayout dim="0">
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <EmptySpace min="0" pref="293" max="32767" attributes="0"/>
+                  </Group>
+                </DimensionLayout>
+                <DimensionLayout dim="1">
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <EmptySpace min="0" pref="200" max="32767" attributes="0"/>
+                  </Group>
+                </DimensionLayout>
+              </Layout>
+            </Container>
+          </SubComponents>
+        </Container>
+        <Container class="javax.swing.JSplitPane" name="dcSplit">
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
+              <JSplitPaneConstraints position="right"/>
+            </Constraint>
+          </Constraints>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
+          <SubComponents>
+            <Container class="javax.swing.JPanel" name="diffPane">
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
+                  <JSplitPaneConstraints position="left"/>
+                </Constraint>
+              </Constraints>
+
+              <Layout>
+                <DimensionLayout dim="0">
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <EmptySpace min="0" pref="100" max="32767" attributes="0"/>
+                  </Group>
+                </DimensionLayout>
+                <DimensionLayout dim="1">
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <EmptySpace min="0" pref="255" max="32767" attributes="0"/>
+                  </Group>
+                </DimensionLayout>
+              </Layout>
+            </Container>
+            <Container class="javax.swing.JPanel" name="controlPane">
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
+                  <JSplitPaneConstraints position="right"/>
+                </Constraint>
+              </Constraints>
+
+              <Layout>
+                <DimensionLayout dim="0">
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Group type="102" attributes="0">
+                          <EmptySpace pref="289" max="32767" attributes="0"/>
+                          <Group type="103" groupAlignment="0" attributes="0">
+                              <Group type="102" alignment="1" attributes="0">
+                                  <Component id="jButton6" min="-2" max="-2" attributes="0"/>
+                                  <EmptySpace max="-2" attributes="0"/>
+                                  <Component id="jButton7" min="-2" max="-2" attributes="0"/>
+                              </Group>
+                              <Group type="102" alignment="1" attributes="0">
+                                  <Component id="jButton1" min="-2" max="-2" attributes="0"/>
+                                  <EmptySpace max="-2" attributes="0"/>
+                                  <Component id="jButton2" min="-2" max="-2" attributes="0"/>
+                              </Group>
+                              <Component id="copyBtn" alignment="1" min="-2" max="-2" attributes="0"/>
+                              <Component id="removeBtn" alignment="1" min="-2" max="-2" attributes="0"/>
+                          </Group>
+                          <EmptySpace max="-2" attributes="0"/>
+                      </Group>
+                  </Group>
+                </DimensionLayout>
+                <DimensionLayout dim="1">
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Group type="102" alignment="0" attributes="0">
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Group type="103" groupAlignment="3" attributes="0">
+                              <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/>
+                              <Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/>
+                          </Group>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="copyBtn" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="removeBtn" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace pref="107" max="32767" attributes="0"/>
+                          <Group type="103" groupAlignment="3" attributes="0">
+                              <Component id="jButton7" alignment="3" min="-2" max="-2" attributes="0"/>
+                              <Component id="jButton6" alignment="3" min="-2" max="-2" attributes="0"/>
+                          </Group>
+                          <EmptySpace max="-2" attributes="0"/>
+                      </Group>
+                  </Group>
+                </DimensionLayout>
+              </Layout>
+              <SubComponents>
+                <Component class="javax.swing.JButton" name="jButton1">
+                  <Properties>
+                    <Property name="mnemonic" type="int" value="43"/>
+                    <Property name="text" type="java.lang.String" value="+"/>
+                  </Properties>
+                  <Events>
+                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/>
+                  </Events>
+                </Component>
+                <Component class="javax.swing.JButton" name="jButton2">
+                  <Properties>
+                    <Property name="mnemonic" type="int" value="45"/>
+                    <Property name="text" type="java.lang.String" value="-"/>
+                  </Properties>
+                  <Events>
+                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/>
+                  </Events>
+                </Component>
+                <Component class="javax.swing.JButton" name="copyBtn">
+                  <Properties>
+                    <Property name="text" type="java.lang.String" value="Copy to golgen"/>
+                    <Property name="enabled" type="boolean" value="false"/>
+                  </Properties>
+                  <Events>
+                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="copyBtnActionPerformed"/>
+                  </Events>
+                </Component>
+                <Component class="javax.swing.JButton" name="removeBtn">
+                  <Properties>
+                    <Property name="text" type="java.lang.String" value="Remove from golden"/>
+                    <Property name="enabled" type="boolean" value="false"/>
+                  </Properties>
+                  <Events>
+                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="removeBtnActionPerformed"/>
+                  </Events>
+                </Component>
+                <Component class="javax.swing.JButton" name="jButton6">
+                  <Properties>
+                    <Property name="text" type="java.lang.String" value="Next"/>
+                  </Properties>
+                  <Events>
+                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton6ActionPerformed"/>
+                  </Events>
+                </Component>
+                <Component class="javax.swing.JButton" name="jButton7">
+                  <Properties>
+                    <Property name="text" type="java.lang.String" value="Exit"/>
+                  </Properties>
+                  <Events>
+                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton7ActionPerformed"/>
+                  </Events>
+                </Component>
+              </SubComponents>
+            </Container>
+          </SubComponents>
+        </Container>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/DiffDialog.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Toolkit;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.image.BufferedImage;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+/**
+ *
+ * @author shura
+ */
+public class DiffDialog extends javax.swing.JDialog {
+
+    private final static StrictImageComparator comparator = new StrictImageComparator();
+    private double scale = 1.0;
+    private int imageWidth, imageHeight, scaledWidth, scaledHeight;
+    private ImagePane left = null, right = null, diff = null;
+    int status = 0;
+
+    /** Creates new form ImageDiff */
+    DiffDialog() {
+        super((JDialog)null, true);
+        initComponents();
+        leftPane.setLayout(new BorderLayout());
+        leftPane.add(new JLabel("Golden"), BorderLayout.NORTH);
+        rightPane.setLayout(new BorderLayout());
+        rightPane.add(new JLabel("Result"), BorderLayout.NORTH);
+        diffPane.setLayout(new BorderLayout());
+        diffPane.add(new JLabel("Diff"), BorderLayout.NORTH);
+        getContentPane().addComponentListener(new ComponentListener() {
+
+            public void componentResized(ComponentEvent e) {
+                lrSplit.setDividerLocation(.5);
+                dcSplit.setDividerLocation(.5);
+                tbSplit.setDividerLocation(.5);
+            }
+
+            public void componentMoved(ComponentEvent e) {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            public void componentShown(ComponentEvent e) {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            public void componentHidden(ComponentEvent e) {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+        });
+        setSize(400, 300);
+
+        // Get the size of the screen
+        Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
+
+        // Determine the new location of the window
+        int w = getSize().width;
+        int h = getSize().height;
+        int x = (dim.width - w) / 2;
+        int y = (dim.height - h) / 2;
+
+        // Move the window
+        setLocation(x, y);
+    }
+
+    void setImages(BufferedImage leftImage, BufferedImage rightImage) {
+        if (leftImage != null && rightImage != null) {
+            copyBtn.setEnabled(true);
+            removeBtn.setEnabled(false);
+            imageWidth = leftImage.getWidth();
+            imageHeight = leftImage.getHeight();
+        } else {
+            if (leftImage == null) {
+                copyBtn.setEnabled(true);
+                removeBtn.setEnabled(false);
+                imageWidth = rightImage.getWidth();
+                imageHeight = rightImage.getHeight();
+            } else if (rightImage == null) {
+                copyBtn.setEnabled(false);
+                removeBtn.setEnabled(true);
+                imageWidth = leftImage.getWidth();
+                imageHeight = leftImage.getHeight();
+            }
+        }
+        if (left == null) {
+            left = new ImagePane(leftImage);
+        } else {
+            left.setImage(leftImage);
+        }
+        leftPane.add(left, BorderLayout.CENTER);
+        if (right == null) {
+            right = new ImagePane(rightImage);
+        } else {
+            right.setImage(rightImage);
+        }
+        rightPane.add(right, BorderLayout.CENTER);
+        if (diff == null) {
+            diff = new ImagePane(subtract(leftImage, rightImage));
+        } else {
+            diff.setImage(subtract(leftImage, rightImage));
+        }
+        diffPane.add(diff, BorderLayout.CENTER);
+        rescaleAll();
+    }
+
+    private BufferedImage subtract(BufferedImage left, BufferedImage right) {
+        if(left != null && right != null) {
+            return ImageTool.subtractImage(left, right);
+        } else {
+            return null;
+        }
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        tbSplit = new javax.swing.JSplitPane();
+        lrSplit = new javax.swing.JSplitPane();
+        leftPane = new javax.swing.JPanel();
+        rightPane = new javax.swing.JPanel();
+        dcSplit = new javax.swing.JSplitPane();
+        diffPane = new javax.swing.JPanel();
+        controlPane = new javax.swing.JPanel();
+        jButton1 = new javax.swing.JButton();
+        jButton2 = new javax.swing.JButton();
+        copyBtn = new javax.swing.JButton();
+        removeBtn = new javax.swing.JButton();
+        jButton6 = new javax.swing.JButton();
+        jButton7 = new javax.swing.JButton();
+
+        tbSplit.setDividerLocation(200);
+        tbSplit.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
+
+        lrSplit.setDividerLocation(250);
+
+        javax.swing.GroupLayout leftPaneLayout = new javax.swing.GroupLayout(leftPane);
+        leftPane.setLayout(leftPaneLayout);
+        leftPaneLayout.setHorizontalGroup(
+            leftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGap(0, 250, Short.MAX_VALUE)
+        );
+        leftPaneLayout.setVerticalGroup(
+            leftPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGap(0, 200, Short.MAX_VALUE)
+        );
+
+        lrSplit.setLeftComponent(leftPane);
+
+        javax.swing.GroupLayout rightPaneLayout = new javax.swing.GroupLayout(rightPane);
+        rightPane.setLayout(rightPaneLayout);
+        rightPaneLayout.setHorizontalGroup(
+            rightPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGap(0, 293, Short.MAX_VALUE)
+        );
+        rightPaneLayout.setVerticalGroup(
+            rightPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGap(0, 200, Short.MAX_VALUE)
+        );
+
+        lrSplit.setRightComponent(rightPane);
+
+        tbSplit.setTopComponent(lrSplit);
+
+        javax.swing.GroupLayout diffPaneLayout = new javax.swing.GroupLayout(diffPane);
+        diffPane.setLayout(diffPaneLayout);
+        diffPaneLayout.setHorizontalGroup(
+            diffPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGap(0, 100, Short.MAX_VALUE)
+        );
+        diffPaneLayout.setVerticalGroup(
+            diffPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGap(0, 255, Short.MAX_VALUE)
+        );
+
+        dcSplit.setLeftComponent(diffPane);
+
+        jButton1.setMnemonic('+');
+        jButton1.setText("+");
+        jButton1.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButton1ActionPerformed(evt);
+            }
+        });
+
+        jButton2.setMnemonic('-');
+        jButton2.setText("-");
+        jButton2.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButton2ActionPerformed(evt);
+            }
+        });
+
+        copyBtn.setText("Copy to golgen");
+        copyBtn.setEnabled(false);
+        copyBtn.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                copyBtnActionPerformed(evt);
+            }
+        });
+
+        removeBtn.setText("Remove from golden");
+        removeBtn.setEnabled(false);
+        removeBtn.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                removeBtnActionPerformed(evt);
+            }
+        });
+
+        jButton6.setText("Next");
+        jButton6.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButton6ActionPerformed(evt);
+            }
+        });
+
+        jButton7.setText("Exit");
+        jButton7.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButton7ActionPerformed(evt);
+            }
+        });
+
+        javax.swing.GroupLayout controlPaneLayout = new javax.swing.GroupLayout(controlPane);
+        controlPane.setLayout(controlPaneLayout);
+        controlPaneLayout.setHorizontalGroup(
+            controlPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(controlPaneLayout.createSequentialGroup()
+                .addContainerGap(289, Short.MAX_VALUE)
+                .addGroup(controlPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, controlPaneLayout.createSequentialGroup()
+                        .addComponent(jButton6)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(jButton7))
+                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, controlPaneLayout.createSequentialGroup()
+                        .addComponent(jButton1)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(jButton2))
+                    .addComponent(copyBtn, javax.swing.GroupLayout.Alignment.TRAILING)
+                    .addComponent(removeBtn, javax.swing.GroupLayout.Alignment.TRAILING))
+                .addContainerGap())
+        );
+        controlPaneLayout.setVerticalGroup(
+            controlPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(controlPaneLayout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(controlPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jButton1)
+                    .addComponent(jButton2))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(copyBtn)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(removeBtn)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 107, Short.MAX_VALUE)
+                .addGroup(controlPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jButton7)
+                    .addComponent(jButton6))
+                .addContainerGap())
+        );
+
+        dcSplit.setRightComponent(controlPane);
+
+        tbSplit.setRightComponent(dcSplit);
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+        getContentPane().setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(tbSplit, javax.swing.GroupLayout.DEFAULT_SIZE, 549, Short.MAX_VALUE)
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(tbSplit, javax.swing.GroupLayout.DEFAULT_SIZE, 461, Short.MAX_VALUE)
+        );
+
+        pack();
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
+        scale *= .9;
+        rescaleAll();
+    }//GEN-LAST:event_jButton2ActionPerformed
+
+    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
+        scale *= 1.1;
+        rescaleAll();
+    }//GEN-LAST:event_jButton1ActionPerformed
+
+    private void rescaleAll() {
+        scaledWidth = (int) (imageWidth * scale);
+        scaledHeight = (int) (imageHeight * scale);
+        left.reScale();
+        right.reScale();
+        diff.reScale();
+        getContentPane().repaint();
+    }
+
+    private void copyBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copyBtnActionPerformed
+        status = -1;
+        setVisible(false);
+    }//GEN-LAST:event_copyBtnActionPerformed
+
+    private void removeBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeBtnActionPerformed
+        status = 1;
+        setVisible(false);
+    }//GEN-LAST:event_removeBtnActionPerformed
+
+    private void jButton6ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton6ActionPerformed
+        setVisible(false);
+    }//GEN-LAST:event_jButton6ActionPerformed
+
+    private void jButton7ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton7ActionPerformed
+        status = -2;
+        setVisible(false);
+    }//GEN-LAST:event_jButton7ActionPerformed
+
+    private class ImagePane extends JPanel {
+
+        BufferedImage img;
+        java.awt.Image scaled;
+
+        public ImagePane(BufferedImage img) {
+            this.img = img;
+        }
+
+        @Override
+        protected void paintComponent(Graphics g) {
+            if (img != null) {
+                g.drawImage(scaled, 0, 0, this);
+            } else {
+                super.paintComponent(g);
+            }
+        }
+
+        void setImage(BufferedImage img) {
+            this.img = img;
+            reScale();
+        }
+
+        void reScale() {
+            if (img != null) {
+                    scaled = img.getScaledInstance(scaledWidth, scaledHeight, java.awt.Image.SCALE_DEFAULT);
+            }
+        }
+    }
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JPanel controlPane;
+    private javax.swing.JButton copyBtn;
+    private javax.swing.JSplitPane dcSplit;
+    private javax.swing.JPanel diffPane;
+    private javax.swing.JButton jButton1;
+    private javax.swing.JButton jButton2;
+    private javax.swing.JButton jButton6;
+    private javax.swing.JButton jButton7;
+    private javax.swing.JPanel leftPane;
+    private javax.swing.JSplitPane lrSplit;
+    private javax.swing.JButton removeBtn;
+    private javax.swing.JPanel rightPane;
+    private javax.swing.JSplitPane tbSplit;
+    // End of variables declaration//GEN-END:variables
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/FilesystemImageLoader.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+
+import java.io.File;
+import org.jemmy.env.Environment;
+
+
+/**
+ * This is an implementation of ImageLoader which loads images from filesystem.
+ * @author mrkam
+ */
+public class FilesystemImageLoader implements ImageLoader {
+
+    private File imageRoot = null;
+
+    public static final String OUTPUT = AWTImage.class.getName() + ".OUTPUT";
+
+    public File getImageRoot() {
+        return imageRoot;
+    }
+
+    public Image load(String ID) {
+        String fullPath = ID + (ID.toLowerCase().endsWith(AWTImage.PNG_FILE) ? "" :
+                AWTImage.PNG_FILE);
+        if (imageRoot != null) {
+            fullPath = imageRoot.getAbsolutePath() + File.separator + ID;
+        }
+        Environment.getEnvironment().getOutput(FilesystemImageLoader.OUTPUT).println("Image loaded from " + fullPath);
+        return new AWTImage(PNGDecoder.decode(fullPath));
+    }
+
+    public void setImageRoot(File imageRoot) {
+        this.imageRoot = imageRoot;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/ImageFinder.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import org.jemmy.Point;
+
+import java.awt.image.BufferedImage;
+
+/**
+ * Interface for all classes performing image lookup.
+ *
+ * @author Alexandre Iline (alexandre.iline@sun.com)
+ */
+public interface ImageFinder {
+
+    /**
+     * Should return location if image lays inside an image represented by this object.
+     * @param image an image to search.
+     * @param index an ordinal image location index. If equal to 1, for example,
+     * second appropriate location will be found.
+     * @return Image location coordinates if image was found, null otherwise.
+     */
+    public Point findImage(BufferedImage image, int index);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/ImageTool.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import javax.imageio.ImageIO;
+
+
+/**
+ * Contains util methods to work with images.
+ *
+ * @author Alexandre Iline (alexandre.iline@sun.com)
+ */
+class ImageTool {
+
+    /**
+     * Increases image.
+     * @param image an image to enlarge.
+     * @param zoom A scale.
+     * @return a result image.
+     */
+    public static BufferedImage enlargeImage(BufferedImage image, int zoom) {
+        int wight = image.getWidth();
+        int height = image.getHeight();
+        BufferedImage result = new BufferedImage(wight * zoom,
+                height * zoom,
+                image.getType());
+        int rgb;
+        for (int x = 0; x < wight; x++) {
+            for (int y = 0; y < height; y++) {
+                rgb = image.getRGB(x, y);
+                for (int i = 0; i < zoom; i++) {
+                    for (int j = 0; j < zoom; j++) {
+                        result.setRGB(x * zoom + i,
+                                y * zoom + j,
+                                rgb);
+                    }
+                }
+            }
+        }
+        return (result);
+    }
+
+    /**
+     * Subtracts second image from first one.
+     * Could be used to save file difference for future analysis.
+     * @param minuend an image to subtract from.
+     * @param deduction an image to subtract.
+     * @return a result image.
+     */
+    public static BufferedImage subtractImage(BufferedImage minuend, BufferedImage deduction) {
+        return (subtractImage(minuend, deduction, 0, 0, null));
+    }
+
+    /**
+     * Subtracts second image from first one.
+     * Could be used to save file difference for future analysis.
+     * @param minuend an image to subtract from.
+     * @param deduction an image to subtract.
+     * @param highlight - a color to highlight the difference. If null,
+     * color difference is shown.
+     * @return a result image.
+     */
+    public static BufferedImage subtractImage(BufferedImage minuend, BufferedImage deduction, Color highlight) {
+        return (subtractImage(minuend, deduction, 0, 0, highlight));
+    }
+
+    /**
+     * Subtracts subimage from image.
+     * Could be used to save file difference for future analysis.
+     * @param minuend an image to subtract from.
+     * @param deduction an image to subtract.
+     * @param relativeX - deduction-in-minuend X coordinate
+     * @param relativeY - deduction-in-minuend Y coordinate
+     * @return a result image.
+     */
+    public static BufferedImage subtractImage(BufferedImage minuend, BufferedImage deduction, int relativeX, int relativeY) {
+        return subtractImage(minuend, deduction, relativeX, relativeY, null);
+    }
+
+    /**
+     * Subtracts subimage from image.
+     * Could be used to save file difference for future analysis.
+     * @param minuend an image to subtract from.
+     * @param deduction an image to subtract.
+     * @param relativeX - deduction-in-minuend X coordinate
+     * @param relativeY - deduction-in-minuend Y coordinate
+     * @param highlight - a color to highlight the difference. If null,
+     * color difference is shown.
+     * @return a result image.
+     */
+    public static BufferedImage subtractImage(BufferedImage minuend, BufferedImage deduction, int relativeX, int relativeY, Color highlight) {
+        int mWidth = minuend.getWidth();
+        int mHeight = minuend.getHeight();
+        int dWidth = deduction.getWidth();
+        int dHeight = deduction.getHeight();
+
+        int maxWidth = (mWidth > relativeX + dWidth) ? mWidth : (relativeX + dWidth);
+        int maxHeight = (mHeight > relativeY + dHeight) ? mHeight : (relativeY + dHeight);
+
+        BufferedImage result = new BufferedImage(maxWidth, maxHeight, BufferedImage.TYPE_INT_RGB);
+        int mColor, dColor;
+        for (int x = 0; x < maxWidth; x++) {
+            for (int y = 0; y < maxHeight; y++) {
+                if (x >= mWidth ||
+                        y >= mHeight) {
+                    mColor = 0;
+                } else {
+                    mColor = minuend.getRGB(x, y);
+                }
+                if (x >= dWidth + relativeX ||
+                        y >= dHeight + relativeY ||
+                        x < relativeX ||
+                        y < relativeY) {
+                    dColor = 0;
+                } else {
+                    dColor = deduction.getRGB(x - relativeX, y - relativeY);
+                }
+                result.setRGB(x, y, (mColor != dColor) ? subtractColor((highlight != null) ? highlight.getRGB() : 0, mColor, dColor) : 0);
+            }
+        }
+        return (result);
+    }
+
+    public static double distance(int rgb1, int rgb2) {
+        float [] buffer1 = new float[3];
+        float [] buffer2 = new float[3];
+        Color c1 = new Color(rgb1);
+        Color c2 = new Color(rgb2);
+        c1.getRGBColorComponents(buffer1);
+        c2.getRGBColorComponents(buffer2);
+        double distSquare = 0;
+        for (int i = 0; i < 3; i++) {
+            distSquare += (buffer1[i] - buffer2[i]) * (buffer1[i] - buffer2[i]);
+        }
+        return Math.sqrt(distSquare);
+    }
+
+    private static int subtractColor(int highlight, int m, int d) {
+        if (highlight == 0) {
+            float scale = (float) (distance(m, d) / Math.sqrt(3));
+            return new Color(scale, scale, scale).getRGB();
+        } else {
+            return highlight;
+        }
+    }
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) throws IOException {
+        if (args.length != 2) {
+            usage();
+            System.exit(1);
+        }
+        compare(new File(args[0]).getAbsoluteFile(), new File(args[1]).getAbsoluteFile());
+    }
+
+    private static void compare(File golden, File results) throws IOException {
+        if (golden.isDirectory()) {
+            File rfl;
+            for (File fl : golden.listFiles()) {
+                compare(fl, new File(results.getAbsolutePath() + File.separator + fl.getName()));
+            }
+            for (File fl : results.listFiles()) {
+                rfl = new File(golden.getAbsolutePath() + File.separator + fl.getName());
+                if (!rfl.exists()) {
+                    compare(rfl, fl);
+                }
+            }
+        } else {
+            DiffDialog dialog = new DiffDialog();
+            dialog.setImages(golden.exists() ? ImageIO.read(golden) : null, results.exists() ? ImageIO.read(results) : null);
+            dialog.setVisible(true);
+            switch (dialog.status) {
+                case -2:
+                    System.exit(0);
+                case -1:
+                    copy(results, golden);
+                    break;
+                case 1:
+                    golden.delete();
+                    break;
+            }
+        }
+    }
+
+    private static void usage() {
+        System.out.println("java -jar JemmyAWTInput.jar <golden image set> <test result image set>");
+    }
+
+    private static void copy(File results, File golden) throws FileNotFoundException, IOException {
+        if (golden.exists()) {
+            golden.delete();
+        }
+        FileInputStream from = new FileInputStream(results);
+        FileOutputStream to = new FileOutputStream(golden);
+        byte[] buffer = new byte[4096];
+        int bytesRead;
+
+        while ((bytesRead = from.read(buffer)) != -1) {
+            to.write(buffer, 0, bytesRead); // write
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/NaturalImageComparator.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import org.jemmy.image.pixel.AverageDistanceComparator;
+import org.jemmy.image.pixel.MaxDistanceComparator;
+
+
+/**
+ * Compares two images naturally
+ * (i.e. ignoring slight difference between pixel colors).
+ *
+ * @author KAM
+ */
+public class NaturalImageComparator extends BufferedImageComparator {
+
+    /**
+     * Creates comparator with the default sensitivity value = 0.02
+     * (around 5 in 0-255 color component value).
+     * @see #NaturalImageComparator(double)
+     */
+    public NaturalImageComparator() {
+        this(0.02);
+    }
+
+    /**
+     * Creates comparator with the specified sensitivity value
+     * @param sensitivity Maximum threshold for 3-D distance between colors
+     * in 3-D sRGB color space for pixels to be considered equal.
+     * Meaningful values are between 0 and approx 1.733. 0 means colors should
+     * be equal to pass the comparison, 1.733 (which is more than square root
+     * of 3) means that comparison will be passed even if the colors are
+     * completely different. You could also use {@linkplain
+     * #findSensitivity(java.awt.image.BufferedImage, java.awt.image.BufferedImage)
+     * findSensitivity()} method to obtain necessary sensitivity value.
+     */
+    public NaturalImageComparator(double sensitivity) {
+        super(new MaxDistanceComparator(sensitivity));
+    }
+    public void setSensitivity(double sensitivity) {
+        ((MaxDistanceComparator)getRasterComparator()).setThreshold(sensitivity);
+    }
+    public double getSensitivity() {
+        return ((MaxDistanceComparator)getRasterComparator()).getThreshold();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/PNGDecoder.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import java.awt.Color;
+
+import java.awt.image.BufferedImage;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.FileInputStream;
+
+import org.jemmy.JemmyException;
+
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+
+/**
+ * Allows to load PNG graphical file.
+ * @author Alexandre Iline
+ */
+public class PNGDecoder extends Object {
+
+    InputStream in;
+
+    /**
+     * Constructs a PNGDecoder object.
+     * @param in input stream to read PNG image from.
+     */
+    public PNGDecoder(InputStream in) {
+        this.in = in;
+    }
+
+    byte read() throws IOException {
+        byte b = (byte)in.read();
+        return(b);
+    }
+
+    int readInt() throws IOException {
+        byte b[] = read(4);
+        return(((b[0]&0xff)<<24) +
+               ((b[1]&0xff)<<16) +
+               ((b[2]&0xff)<<8) +
+               ((b[3]&0xff)));
+    }
+
+    byte[] read(int count) throws IOException {
+        byte[] result = new byte[count];
+        for(int i = 0; i < count; i++) {
+            result[i] = read();
+        }
+        return(result);
+    }
+
+    boolean compare(byte[] b1, byte[] b2) {
+        if(b1.length != b2.length) {
+            return(false);
+        }
+        for(int i = 0; i < b1.length; i++) {
+            if(b1[i] != b2[i]) {
+                return(false);
+            }
+        }
+        return(true);
+    }
+
+    void checkEquality(byte[] b1, byte[] b2) {
+        if(!compare(b1, b2)) {
+            throw(new JemmyException("Format error"));
+        }
+    }
+
+    /**
+     * Decodes image from an input stream passed into constructor.
+     * @return a BufferedImage object
+     * @throws IOException
+     */
+    public BufferedImage decode() throws IOException {
+        return decode(true);
+    }
+
+    /**
+     * Decodes image from an input stream passed into constructor.
+     * @return a BufferedImage object
+     * @param closeStream requests method to close the stream after the image is read
+     * @throws IOException
+     */
+    public BufferedImage decode(boolean closeStream) throws IOException {
+
+        byte[] id = read(12);
+        checkEquality(id, new byte[] {-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13});
+
+        byte[] ihdr = read(4);
+        checkEquality(ihdr, "IHDR".getBytes());
+
+        int width = readInt();
+        int height = readInt();
+
+        BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+
+        byte[] head = read(5);
+        int mode;
+        if(compare(head, new byte[]{1, 0, 0, 0, 0})) {
+            mode = PNGEncoder.BW_MODE;
+        } else if(compare(head, new byte[]{8, 0, 0, 0, 0})) {
+            mode = PNGEncoder.GREYSCALE_MODE;
+        } else if(compare(head, new byte[]{8, 2, 0, 0, 0})) {
+            mode = PNGEncoder.COLOR_MODE;
+        } else {
+            throw(new JemmyException("Format error"));
+        }
+
+        readInt();//!!crc
+
+        int size = readInt();
+
+        byte[] idat = read(4);
+        checkEquality(idat, "IDAT".getBytes());
+
+        byte[] data = read(size);
+
+
+        Inflater inflater = new Inflater();
+        inflater.setInput(data, 0, size);
+
+        int color;
+
+        try {
+            switch (mode) {
+            case PNGEncoder.BW_MODE:
+                {
+                    int bytes = (int)(width / 8);
+                    if((width % 8) != 0) {
+                        bytes++;
+                    }
+                    byte colorset;
+                    byte[] row = new byte[bytes];
+                    for (int y = 0; y < height; y++) {
+                        inflater.inflate(new byte[1]);
+                        inflater.inflate(row);
+                        for (int x = 0; x < bytes; x++) {
+                            colorset = row[x];
+                            for (int sh = 0; sh < 8; sh++) {
+                                if(x * 8 + sh >= width) {
+                                    break;
+                                }
+                                if((colorset & 0x80) == 0x80) {
+                                    result.setRGB(x * 8 + sh, y, Color.white.getRGB());
+                                } else {
+                                    result.setRGB(x * 8 + sh, y, Color.black.getRGB());
+                                }
+                                colorset <<= 1;
+                            }
+                        }
+                    }
+                }
+                break;
+            case PNGEncoder.GREYSCALE_MODE:
+                {
+                    byte[] row = new byte[width];
+                    for (int y = 0; y < height; y++) {
+                        inflater.inflate(new byte[1]);
+                        inflater.inflate(row);
+                        for (int x = 0; x < width; x++) {
+                            color = row[x];
+                            result.setRGB(x, y, (color << 16) + (color << 8) + color);
+                        }
+                    }
+                }
+                break;
+            case PNGEncoder.COLOR_MODE:
+                {
+                    byte[] row = new byte[width * 3];
+                    for (int y = 0; y < height; y++) {
+                        inflater.inflate(new byte[1]);
+                        inflater.inflate(row);
+                        for (int x = 0; x < width; x++) {
+                            result.setRGB(x, y,
+                                          ((row[x * 3 + 0]&0xff) << 16) +
+                                          ((row[x * 3 + 1]&0xff) << 8) +
+                                          ((row[x * 3 + 2]&0xff)));
+                        }
+                    }
+                }
+            }
+        } catch(DataFormatException e) {
+            throw(new JemmyException("ZIP error", e));
+        }
+
+        readInt();//!!crc
+        readInt();//0
+
+        byte[] iend = read(4);
+        checkEquality(iend, "IEND".getBytes());
+
+        readInt();//!!crc
+        if (closeStream) {
+            in.close();
+        }
+
+        return(result);
+    }
+
+    /**
+     * Decodes image from file.
+     * @param fileName a file to read image from
+     * @return a BufferedImage instance.
+     */
+    public static BufferedImage decode(String fileName) {
+        try {
+            return(new PNGDecoder(new FileInputStream(fileName)).decode());
+        } catch(IOException e) {
+            throw(new JemmyException("IOException during image reading", e));
+        }
+    }
+
+    /**
+     * Decodes image from input stream
+     * @param inputStream a file to read image from
+     * @param closeStream requests method to close the stream after the image is read
+     * @return a BufferedImage instance.
+     */
+    public static BufferedImage decode(InputStream inputStream, boolean closeStream) {
+        try {
+            return(new PNGDecoder(inputStream).decode(closeStream));
+        } catch(IOException e) {
+            throw(new JemmyException("IOException during image reading", e));
+        }
+    }
+
+    /**
+     * Decodes image from resource.
+     * @param loader ClassLoader to use to get the resource
+     * @param resource Image resource name
+     * @return a BufferedImage instance.
+     */
+    public static BufferedImage decode(ClassLoader loader, String resource) {
+        try {
+            InputStream resourceStream = loader.getResourceAsStream(resource);
+            if (resourceStream == null) {
+                throw new JemmyException("Resouce '" + resource + "' could not be found!");
+            }
+            return(new PNGDecoder(resourceStream).decode());
+        } catch(IOException e) {
+            throw(new JemmyException("IOException during image reading", e));
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/PNGEncoder.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import java.awt.AWTException;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.Toolkit;
+
+import java.awt.image.BufferedImage;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+import org.jemmy.control.ScreenArea;
+
+/** This class allows to encode BufferedImage into B/W, greyscale or true color PNG
+ * image format with maximum compression.<br>
+ * It also provides complete functionality for capturing full screen, part of
+ * screen or single component, encoding and saving captured image info PNG file.
+ * @author Adam Sotona
+ * @version 1.0 */
+public class PNGEncoder extends Object {
+
+    /** black and white image mode. */
+    public static final byte BW_MODE = 0;
+    /** grey scale image mode. */
+    public static final byte GREYSCALE_MODE = 1;
+    /** full color image mode. */
+    public static final byte COLOR_MODE = 2;
+
+    OutputStream out;
+    CRC32 crc;
+    byte mode;
+
+    /**
+     *
+     * @param file
+     * @throws java.io.FileNotFoundException
+     */
+    public PNGEncoder(File file) throws FileNotFoundException {
+        this(new FileOutputStream(file));
+    }
+    /** public constructor of PNGEncoder class with greyscale mode by default.
+     * @param out output stream for PNG image format to write into
+     */
+    public PNGEncoder(OutputStream out) {
+        this(out, GREYSCALE_MODE);
+    }
+
+    /** public constructor of PNGEncoder class.
+     * @param out output stream for PNG image format to write into
+     * @param mode BW_MODE, GREYSCALE_MODE or COLOR_MODE
+     */
+    public PNGEncoder(OutputStream out, byte mode) {
+        crc=new CRC32();
+        this.out = out;
+        if (mode<0 || mode>2)
+            throw new IllegalArgumentException("Unknown color mode");
+        this.mode = mode;
+    }
+
+    void write(int i) throws IOException {
+        byte b[]={(byte)((i>>24)&0xff),(byte)((i>>16)&0xff),(byte)((i>>8)&0xff),(byte)(i&0xff)};
+        write(b);
+    }
+
+    void write(byte b[]) throws IOException {
+        out.write(b);
+        crc.update(b);
+    }
+
+    /** main encoding method (stays blocked till encoding is finished).
+     * @param image BufferedImage to encode
+     * @throws IOException IOException
+     */
+    public void encode(BufferedImage image) throws IOException {
+        encode(image, true);
+    }
+
+    /** main encoding method (stays blocked till encoding is finished).
+     * @param image BufferedImage to encode
+     * @param closeStream requests method to close the stream after the image is written
+     * @throws IOException IOException
+     */
+    public void encode(BufferedImage image, boolean closeStream) throws IOException {
+        int width = image.getWidth(null);
+        int height = image.getHeight(null);
+        final byte id[] = {-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13};
+        write(id);
+        crc.reset();
+        write("IHDR".getBytes());
+        write(width);
+        write(height);
+        byte head[]=null;
+        switch (mode) {
+            case BW_MODE: head=new byte[]{1, 0, 0, 0, 0}; break;
+            case GREYSCALE_MODE: head=new byte[]{8, 0, 0, 0, 0}; break;
+            case COLOR_MODE: head=new byte[]{8, 2, 0, 0, 0}; break;
+        }
+        write(head);
+        write((int) crc.getValue());
+        ByteArrayOutputStream compressed = new ByteArrayOutputStream(65536);
+        BufferedOutputStream bos = new BufferedOutputStream( new DeflaterOutputStream(compressed, new Deflater(9)));
+        int pixel;
+        int color;
+        int colorset;
+        switch (mode) {
+            case BW_MODE:
+                int rest=width%8;
+                int bytes=width/8;
+                for (int y=0;y<height;y++) {
+                    bos.write(0);
+                    for (int x=0;x<bytes;x++) {
+                        colorset=0;
+                        for (int sh=0; sh<8; sh++) {
+                            pixel=image.getRGB(x*8+sh,y);
+                            color=((pixel >> 16) & 0xff);
+                            color+=((pixel >> 8) & 0xff);
+                            color+=(pixel & 0xff);
+                            colorset<<=1;
+                            if (color>=3*128)
+                                colorset|=1;
+                        }
+                        bos.write((byte)colorset);
+                    }
+                    if (rest>0) {
+                        colorset=0;
+                        for (int sh=0; sh<width%8; sh++) {
+                            pixel=image.getRGB(bytes*8+sh,y);
+                            color=((pixel >> 16) & 0xff);
+                            color+=((pixel >> 8) & 0xff);
+                            color+=(pixel & 0xff);
+                            colorset<<=1;
+                            if (color>=3*128)
+                                colorset|=1;
+                        }
+                        colorset<<=8-rest;
+                        bos.write((byte)colorset);
+                    }
+                }
+                break;
+            case GREYSCALE_MODE:
+                for (int y=0;y<height;y++) {
+                    bos.write(0);
+                    for (int x=0;x<width;x++) {
+                        pixel=image.getRGB(x,y);
+                        color=((pixel >> 16) & 0xff);
+                        color+=((pixel >> 8) & 0xff);
+                        color+=(pixel & 0xff);
+                        bos.write((byte)(color/3));
+                    }
+                }
+                break;
+             case COLOR_MODE:
+                for (int y=0;y<height;y++) {
+                    bos.write(0);
+                    for (int x=0;x<width;x++) {
+                        pixel=image.getRGB(x,y);
+                        bos.write((byte)((pixel >> 16) & 0xff));
+                        bos.write((byte)((pixel >> 8) & 0xff));
+                        bos.write((byte)(pixel & 0xff));
+                    }
+                }
+                break;
+        }
+        bos.close();
+        write(compressed.size());
+        crc.reset();
+        write("IDAT".getBytes());
+        write(compressed.toByteArray());
+        write((int) crc.getValue());
+        write(0);
+        crc.reset();
+        write("IEND".getBytes());
+        write((int) crc.getValue());
+        out.flush();
+        if (closeStream) {
+            out.close();
+        }
+    }
+
+    /** Static method performing screen capture into PNG image format file with given fileName.
+     * @param rect Rectangle of screen to be captured
+     * @param fileName file name for screen capture PNG image file */
+    public static void captureScreen(Rectangle rect, String fileName) {
+        captureScreen(rect, fileName, GREYSCALE_MODE);
+    }
+
+    /** Static method performing screen capture into PNG image format file with given fileName.
+     * @param rect Rectangle of screen to be captured
+     * @param mode image color mode
+     * @param fileName file name for screen capture PNG image file */
+    public static void captureScreen(Rectangle rect, String fileName, byte mode) {
+        try {
+            BufferedImage capture=new Robot().createScreenCapture(rect);
+            BufferedOutputStream file=new BufferedOutputStream(new FileOutputStream(fileName));
+            PNGEncoder encoder=new PNGEncoder(file, mode);
+            encoder.encode(capture);
+        } catch (AWTException awte) {
+            awte.printStackTrace();
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        }
+    }
+
+     /** Static method performing one component screen capture into PNG image format file with given fileName.
+      * @param comp Component to be captured
+      * @param fileName String image target filename */
+    public static void captureScreen(ScreenArea comp, String fileName) {
+        captureScreen(comp, fileName, GREYSCALE_MODE);
+    }
+
+    /** Static method performing one component screen capture into PNG image format file with given fileName.
+     * @param comp Component to be captured
+     * @param fileName String image target filename
+     * @param mode image color mode */
+    public static void captureScreen(ScreenArea comp, String fileName, byte mode) {
+        captureScreen(Utils.convert(comp.getScreenBounds()),
+            fileName, mode);
+    }
+
+
+    /** Static method performing whole screen capture into PNG image format file with given fileName.
+     * @param fileName String image target filename */
+    public static void captureScreen(String fileName) {
+        captureScreen(fileName, GREYSCALE_MODE);
+    }
+
+    /** Static method performing whole screen capture into PNG image format file with given fileName.
+     * @param fileName String image target filename
+     * @param mode image color mode */
+    public static void captureScreen(String fileName, byte mode) {
+        captureScreen(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()), fileName, mode);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/PNGImageLoader.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import java.awt.image.BufferedImage;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * Allowes to process PNF image format.
+ *
+ * @author Alexandre Iline (alexandre.iline@sun.com)
+ */
+class PNGImageLoader {
+
+    /**
+     * Loads an image from a PNG image file.
+     * @param in
+     * @throws IOException
+     */
+    public BufferedImage load(InputStream in) throws IOException {
+        return(new PNGDecoder(in).decode());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/PNGImageSaver.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import java.awt.image.BufferedImage;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+
+/**
+ * Allows to process PNG image format.
+ *
+ * @author Alexandre Iline (alexandre.iline@sun.com)
+ */
+class PNGImageSaver {
+
+    /**
+     * Saves an image into a PNG image file.
+     * @throws IOException
+     */
+    public void save(BufferedImage image, String fileName) throws IOException{
+        File f = new File(fileName);
+        File parent = f.getParentFile();
+        if (parent != null) {
+            parent.mkdirs();
+        }
+        new PNGEncoder(new BufferedOutputStream(new FileOutputStream(f)),
+                       PNGEncoder.COLOR_MODE).encode(image);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/ResizeImageComparator.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import org.jemmy.Dimension;
+import org.jemmy.env.Environment;
+import org.jemmy.env.TestOut;
+import org.jemmy.image.pixel.Raster;
+import org.jemmy.image.pixel.RasterComparator;
+import org.jemmy.image.pixel.ResizeComparator;
+
+/**
+ * Comparator that makes image sizes equal to compare them with other comparator
+ * most of them work only for the images with equal dimensions.
+ *
+ * It is controlled by three parameters: <li>Resize Mode - defines way of
+ * resizing images that are being compared. One of the following constants: <ul> <li>{@linkplain ResizeMode#NO_RESIZE NO_RESIZE},</li> <li>{@linkplain ResizeMode#PROPORTIONAL_RESIZE PROPORTIONAL_RESIZE},</li> <li>{@linkplain ResizeMode#ARBITRARY_RESIZE ARBITRARY_RESIZE}.</li></ul>
+ * Default value is {@linkplain ResizeMode#PROPORTIONAL_RESIZE}.</li> <li>Resize
+ * Hint - defines the way of images scaling that is specified when {@linkplain java.awt.Image#getScaledInstance(int, int, int)
+ * Image.getScaledInstance()} method is invoked. One of the Image.SCALE_XXX is
+ * expected. Default value is
+ * {@linkplain java.awt.Image#SCALE_DEFAULT SCALE_DEFAULT}.</li> <li>Proportion
+ * Distortion Threshold - defines maximum proportion distortion that is used in {@linkplain ResizeMode#PROPORTIONAL_RESIZE
+ * PROPORTIONAL_RESIZE} mode to deal with cases when rounding and other problems
+ * could cause sizes to be not ideally proportional.<p/>
+ * Threshold value is applied in the following way: {@code Math.abs(image.getSize() * scale
+ * - size) / image.getSize() > PROPORTION_DISTORTION_THRESHOLD}, where {@code image.getSize()}
+ * is both image dimensions and {@code size} is target width and height (which
+ * is defined as mininum of sizes of two images) and scale is the scale factor
+ * that scales the particular image to fit the width and height.
+ * <p/>
+ * Default value is 0.02 so as much as 2% of width/height could differ (around 5
+ * in 0-255 color component values).</li>
+ *
+ * @author mrkam
+ */
+public class ResizeImageComparator extends ResizeComparator {
+
+    /**
+     * Indentifies output where resize details are printed. Output is disabled
+     * by default.
+     *
+     * @see Environment#getOutput(java.lang.String)
+     */
+    public static final String OUTPUT = ResizeImageComparator.class.getName()
+            + ".OUTPUT";
+
+    static {
+        Environment.getEnvironment().setOutput(OUTPUT, TestOut.getNullOutput());
+    }
+    ResizeMode resizeMode;
+    int hint;
+    double propDistThreshold;
+
+    /**
+     * Resize Modes
+     *
+     * @see
+     * #ResizeImageComparator(org.jemmy.image.ResizeImageComparator.ResizeMode,
+     * org.jemmy.image.ImageComparator)
+     * @see
+     * #ResizeImageComparator(org.jemmy.image.ResizeImageComparator.ResizeMode,
+     * int, double, org.jemmy.image.ImageComparator)
+     */
+    public static enum ResizeMode {
+
+        /**
+         * Images are never resized. Original images are always compared.
+         */
+        NO_RESIZE,
+        /**
+         * Images are resized only if they have exactly or almost proportional
+         * sizes which is controlled by {@code proportionDistortion} parameter.
+         * If images have different proportions no resize is done and original
+         * images are compared.
+         *
+         * @see
+         * #ResizeImageComparator(org.jemmy.image.ResizeImageComparator.ResizeMode,
+         * int, double, org.jemmy.image.ImageComparator)
+         */
+        PROPORTIONAL_RESIZE,
+        /**
+         * Images are always resized to match both width and height and then
+         * compared.
+         */
+        ARBITRARY_RESIZE
+    }
+
+    /**
+     * Creates ResizeImageComparator with default resize settings: <li>resize
+     * mode: {@linkplain ResizeMode#PROPORTIONAL_RESIZE ResizeMode.PROPORTIONAL_RESIZE}.</li>
+     * <li>proportion distortion threshold: 0.02.</li> <li>resize hint: {@linkplain java.awt.Image#SCALE_DEFAULT Image.SCALE_DEFAULT}.</li>
+     *
+     * @param subComparator comparator to compare images after resize.
+     * @see java.awt.Image#getScaledInstance(int, int, int)
+     * @see ResizeMode
+     * @see ResizeImageComparator
+     */
+    public ResizeImageComparator(ImageComparator subComparator) {
+        super(subComparator, ResizeComparator.Mode.LEFT);
+        this.resizeMode = ResizeMode.PROPORTIONAL_RESIZE;
+        this.hint = java.awt.Image.SCALE_DEFAULT;
+        this.propDistThreshold = 0.02;
+    }
+
+    /**
+     * Creates ResizeImageComparator with the specified resize mode and default
+     * settings for other parameters: <li>proportion distortion threshold:
+     * 0.02.</li> <li>resize hint: {@linkplain java.awt.Image#SCALE_DEFAULT Image.SCALE_DEFAULT}.</li>
+     *
+     * @param resizeMode resize mode for this comparator.
+     * @param subComparator comparator to compare images after resize.
+     * @see ResizeMode
+     * @see ResizeImageComparator
+     */
+    public ResizeImageComparator(ResizeMode resizeMode,
+            ImageComparator subComparator) {
+        this(subComparator);
+        this.resizeMode = resizeMode;
+    }
+
+    /**
+     * Creates ResizeImageComparator with the following settings: <li>resize
+     * mode: {@linkplain ResizeMode#PROPORTIONAL_RESIZE ResizeMode.PROPORTIONAL_RESIZE}.</li>
+     * <li>specified proportion distortion threshold.</li> <li>resize hint: {@linkplain java.awt.Image#SCALE_DEFAULT Image.SCALE_DEFAULT}.</li>
+     *
+     * @param propDistThreshold proportion distortion threshold.
+     * @param subComparator comparator to compare images after resize.
+     * @see ResizeImageComparator
+     */
+    public ResizeImageComparator(double propDistThreshold,
+            ImageComparator subComparator) {
+        this(subComparator);
+        this.propDistThreshold = propDistThreshold;
+    }
+
+    /**
+     * Creates ResizeImageComparator with specified settings.
+     *
+     * @param resizeMode Resize mode.
+     * @param propDistThreshold Proportion distortion threshold.
+     * @param hint Resize hint.
+     * @param subComparator comparator to compare images after resize.
+     * @see ResizeImageComparator
+     * @see ResizeMode
+     */
+    public ResizeImageComparator(ResizeMode resizeMode, double propDistThreshold,
+            int hint, ImageComparator subComparator) {
+        this(subComparator);
+        this.resizeMode = resizeMode;
+        this.hint = hint;
+        this.propDistThreshold = propDistThreshold;
+    }
+
+    @Override
+    public Image resize(Image image, Dimension size) {
+        Dimension isize = getSize(image);
+        double scalex = (double) size.width / isize.width;
+        double scaley = (double) size.height / isize.height;
+        switch (resizeMode) {
+            case NO_RESIZE:
+                return image;
+            case PROPORTIONAL_RESIZE:
+                if (Math.abs(scalex - scaley) > propDistThreshold) {
+                    return null;
+                }
+            case ARBITRARY_RESIZE:
+                BufferedImage res = new BufferedImage(size.width, size.height, ((AWTImage) image).getTheImage().getType());
+                java.awt.Image scaled = ((AWTImage) image).getTheImage().getScaledInstance(
+                        size.width, size.height, hint);
+                Graphics2D g = res.createGraphics();
+                g.drawImage(scaled, 0, 0, null);
+                g.dispose();
+                return new AWTImage(res);
+            default:
+                return null;
+        }
+    }
+    @Override
+    public String getID() {
+        return ResizeImageComparator.class.getName() + "("
+                + getSubComparator().getID() + ")";
+    }
+
+    @Override
+    public Dimension getSize(Image image) {
+        BufferedImage bi = ((AWTImage)image).getTheImage();
+        return new Dimension(bi.getWidth(), bi.getHeight());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/RoughImageComparator.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import org.jemmy.image.pixel.PixelEqualityRasterComparator;
+
+/**
+ * Compares two images roughly (i.e. not all of the pixel colors should match).
+ *
+ * @author Alexandre Iline (alexandre.iline@sun.com), KAM <mrkam@mail.ru>
+ */
+public class RoughImageComparator extends BufferedImageComparator {
+
+    /**
+     * Creates a comparator with
+     * <code>roughness</code> allowed roughness.
+     *
+     * @param roughness Allowed comparision roughness.
+     */
+    public RoughImageComparator(double roughness) {
+        super(new PixelEqualityRasterComparator(roughness));
+    }
+    public void setRoughness(double roughness) {
+        ((PixelEqualityRasterComparator)getRasterComparator()).setThreshold(roughness);
+    }
+    public double getRoughness() {
+        return ((PixelEqualityRasterComparator)getRasterComparator()).getThreshold();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/RoughImageFinder.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import org.jemmy.Point;
+import java.awt.image.BufferedImage;
+
+/**
+ * Performs "rough" image search.
+ *
+ * @author Alexandre Iline (alexandre.iline@sun.com)
+ */
+public class RoughImageFinder implements ImageFinder {
+    double roughness = .0;
+    int bigWidth, bigHeight;
+    int[][] bigPixels;
+
+    /**
+     * Creates an instance allowing to find an image inside the one
+     * passed as parameter with some "roughness".
+     * @param area - Image to search in.
+     * @param roughness - Allowed
+     */
+    public RoughImageFinder(BufferedImage area, double roughness) {
+        this.roughness = roughness;
+        bigWidth  = area.getWidth();
+        bigHeight = area.getHeight();
+        bigPixels = new int[bigWidth][bigHeight];
+        for(int x = 0; x < bigWidth; x++) {
+            for(int y = 0; y < bigHeight; y++) {
+                bigPixels[x][y] = area.getRGB(x, y);
+            }
+        }
+    }
+
+    /**
+     * Performs "rough" search.
+     * @param image an image to search.
+     * @param index an ordinal image location index.
+     * @return Point where number of unmatching pixels less or equal to
+     * <code>image1.getWidth() * image1.getHeight() * roughness<code>
+     */
+    public Point findImage(BufferedImage image, int index) {
+        int smallWidth  = image.getWidth();
+        int smallHeight = image.getHeight();
+        int[][] smallPixels = new int[smallWidth][smallHeight];
+        for(int x = 0; x < smallWidth; x++) {
+            for(int y = 0; y < smallHeight; y++) {
+                smallPixels[x][y] = image.getRGB(x, y);
+            }
+        }
+        double maxRoughPixels = (double)(smallWidth * smallHeight) * roughness;
+        int count = 0;
+        for(int X = 0; X <= bigWidth - smallWidth; X++) {
+            for(int Y = 0; Y <= bigHeight - smallHeight; Y++) {
+                int roughPixels = 0;
+                for(int x = 0; x < smallWidth; x++) {
+                    for(int y = 0; y < smallHeight; y++) {
+                        if(smallPixels[x][y] != bigPixels[X + x][Y + y]) {
+                            roughPixels++;
+                            if(roughPixels > maxRoughPixels) {
+                                break;
+                            }
+                        }
+                    }
+                    if(roughPixels > maxRoughPixels) {
+                        break;
+                    }
+                }
+                if(roughPixels <= maxRoughPixels) {
+                    if(count == index) {
+                        return(new Point(X, Y));
+                    }
+                    count++;
+                }
+            }
+        }
+        return(null);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/StrictImageComparator.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import org.jemmy.image.pixel.PixelEqualityRasterComparator;
+
+/**
+ * Compares two images strictly (i.e. all the pixel colors should match).
+ *
+ * @author Alexandre Iline (alexandre.iline@sun.com), KAM <mrkam@mail.ru>
+ */
+public class StrictImageComparator extends BufferedImageComparator {
+
+    public StrictImageComparator() {
+        super(new PixelEqualityRasterComparator(0));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/StrictImageFinder.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import org.jemmy.Point;
+import java.awt.image.BufferedImage;
+
+/**
+ * Performs "strict" (i.e. based on all pixels matching) image search.
+ *
+ * @author Alexandre Iline (alexandre.iline@sun.com)
+ */
+public class StrictImageFinder implements ImageFinder {
+    int bigWidth, bigHeight;
+    int[][] bigPixels;
+
+    /**
+     * Creates an instance searching subimages insige a parameter image.
+     * @param area - Image to search in.
+     */
+    public StrictImageFinder(BufferedImage area) {
+        bigWidth  = area.getWidth();
+        bigHeight = area.getHeight();
+        bigPixels = new int[bigWidth][bigHeight];
+        for(int x = 0; x < bigWidth; x++) {
+            for(int y = 0; y < bigHeight; y++) {
+                bigPixels[x][y] = area.getRGB(x, y);
+            }
+        }
+    }
+
+    /**
+     * Searchs for an image inside image passed into constructor.
+     * @param image an image to search.
+     * @param index an ordinal image location index. If equal to 1, for example,
+     * second appropriate location will be found.
+     * @return Left-up corner coordinates of image location.
+     */
+    public Point findImage(BufferedImage image, int index) {
+        int smallWidth  = image.getWidth();
+        int smallHeight = image.getHeight();
+        int[][] smallPixels = new int[smallWidth][smallHeight];
+        for(int x = 0; x < smallWidth; x++) {
+            for(int y = 0; y < smallHeight; y++) {
+                smallPixels[x][y] = image.getRGB(x, y);
+            }
+        }
+        boolean good;
+        int count = 0;
+        for(int X = 0; X <= bigWidth - smallWidth; X++) {
+            for(int Y = 0; Y <= bigHeight - smallHeight; Y++) {
+                good = true;
+                for(int x = 0; x < smallWidth; x++) {
+                    for(int y = 0; y < smallHeight; y++) {
+                        if(smallPixels[x][y] != bigPixels[X + x][Y + y]) {
+                            good = false;
+                            break;
+                        }
+                    }
+                    if(!good) {
+                        break;
+                    }
+                }
+                if(good) {
+                    if(count == index) {
+                        return(new Point(X, Y));
+                    }
+                    count++;
+                }
+            }
+        }
+        return(null);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/Utils.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+
+import org.jemmy.Dimension;
+import org.jemmy.Point;
+import org.jemmy.Rectangle;
+
+
+/**
+ *
+ * @author Alexander Kouznetsov <mrkam@mail.ru>
+ */
+public class Utils {
+
+    /**
+     *
+     * @param r
+     * @return java.awt.Rectangle
+     */
+    public static java.awt.Rectangle convert(Rectangle r) {
+        return new java.awt.Rectangle(r.x, r.y, r.width, r.height);
+    }
+
+    /**
+     *
+     * @param r
+     * @return org.jemmy.Rectangle
+     */
+    public static Rectangle convert(java.awt.Rectangle r) {
+        return new Rectangle(r.x, r.y, r.width, r.height);
+    }
+
+    /**
+     *
+     * @param p
+     * @return java.awt.Point
+     */
+    public static java.awt.Point convert(Point p) {
+        return new java.awt.Point(p.x, p.y);
+    }
+
+    /**
+     *
+     * @param p
+     * @return org.jemmy.Point
+     */
+    public static Point convert(java.awt.Point p) {
+        return new Point(p.x, p.y);
+    }
+
+    /**
+     *
+     * @param d
+     * @return java.awt.Dimension
+     */
+    public static java.awt.Dimension convert(Dimension d) {
+        return new java.awt.Dimension(d.width, d.height);
+    }
+
+    /**
+     *
+     * @param d
+     * @return org.jemmy.Dimension
+     */
+    public static Dimension convert(java.awt.Dimension d) {
+        return new Dimension(d.width, d.height);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/image/package.html	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+  <head>
+    <title>Image library</title>
+  </head>
+
+  <body>
+    <h1>Image library</h1>
+
+    Contains classes allowing to compare two images and
+    to find one image inside another.<br><br>
+
+    @since 9 Nov 2002
+
+    <hr>
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/input/AWTMap.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jemmy.JemmyException;
+import org.jemmy.interfaces.Keyboard.KeyboardButton;
+import org.jemmy.interfaces.Keyboard.KeyboardButtons;
+import org.jemmy.interfaces.Keyboard.KeyboardModifiers;
+import org.jemmy.interfaces.Modifier;
+import org.jemmy.interfaces.Mouse.MouseButton;
+import org.jemmy.interfaces.Mouse.MouseButtons;
+import org.jemmy.interfaces.Mouse.MouseModifiers;
+
+
+/**
+ * Converts
+ * @author Alexander Kouznetsov <mrkam@mail.ru>
+ */
+public class AWTMap {
+
+    private static Map<Integer, KeyboardButton> int2key = new HashMap<Integer, KeyboardButton>();
+    private static Map<Integer, Modifier> int2modifier = new HashMap<Integer, Modifier>();
+    private static Map<Integer, MouseButton> int2button = new HashMap<Integer, MouseButton>();
+    private static Map<KeyboardButton, Integer> key2int = new HashMap<KeyboardButton, Integer>();
+    private static Map<Modifier, Integer> modifier2int = new HashMap<Modifier, Integer>();
+    private static Map<MouseButton, Integer> button2int = new HashMap<MouseButton, Integer>();
+
+    static {
+        for (KeyboardButtons button : KeyboardButtons.values()) {
+            String name = button.name();
+            try {
+                int key = KeyEvent.VK_UNDEFINED;
+                if (name.length() == 2 && name.startsWith("D")) {
+                    // digit
+                    key = KeyEvent.class.getDeclaredField("VK_" + name.substring(1)).getInt(null);
+                } else {
+                    key = KeyEvent.class.getDeclaredField("VK_" + name).getInt(null);
+                }
+                int2key.put(key, button);
+                key2int.put(button, key);
+            } catch (NoSuchFieldException ex) {
+                throw new JemmyException("Unable to recognize key", ex, button);
+            } catch (SecurityException ex) {
+                throw new JemmyException("Unable to recognize key", ex, button);
+            } catch (IllegalArgumentException ex) {
+                throw new JemmyException("Unable to recognize key", ex, button);
+            } catch (IllegalAccessException ex) {
+                throw new JemmyException("Unable to recognize key", ex, button);
+            }
+        }
+        for (KeyboardModifiers modifier : KeyboardModifiers.values()) {
+            String name = modifier.name();
+            try {
+                int key = InputEvent.class.getDeclaredField(name).getInt(null);
+                int2modifier.put(key, modifier);
+                modifier2int.put(modifier, key);
+            } catch (NoSuchFieldException ex) {
+                throw new JemmyException("Unable to recognize modifier", ex, modifier);
+            } catch (SecurityException ex) {
+                throw new JemmyException("Unable to recognize modifier", ex, modifier);
+            } catch (IllegalArgumentException ex) {
+                throw new JemmyException("Unable to recognize modifier", ex, modifier);
+            } catch (IllegalAccessException ex) {
+                throw new JemmyException("Unable to recognize modifier", ex, modifier);
+            }
+        }
+        for (MouseModifiers modifier : MouseModifiers.values()) {
+            String name = modifier.name();
+            try {
+                int key = InputEvent.class.getDeclaredField(name).getInt(null);
+                int2modifier.put(key, modifier);
+                modifier2int.put(modifier, key);
+            } catch (NoSuchFieldException ex) {
+                throw new JemmyException("Unable to recognize modifier", ex, modifier);
+            } catch (SecurityException ex) {
+                throw new JemmyException("Unable to recognize modifier", ex, modifier);
+            } catch (IllegalArgumentException ex) {
+                throw new JemmyException("Unable to recognize modifier", ex, modifier);
+            } catch (IllegalAccessException ex) {
+                throw new JemmyException("Unable to recognize modifier", ex, modifier);
+            }
+        }
+        for (MouseButtons button : MouseButtons.values()) {
+            String name = button.name();
+            try {
+                int key = InputEvent.class.getDeclaredField(name + "_MASK").getInt(null);
+                int2button.put(key, button);
+                button2int.put(button, key);
+            } catch (NoSuchFieldException ex) {
+                throw new JemmyException("Unable to recognize button", ex, button);
+            } catch (SecurityException ex) {
+                throw new JemmyException("Unable to recognize button", ex, button);
+            } catch (IllegalArgumentException ex) {
+                throw new JemmyException("Unable to recognize button", ex, button);
+            } catch (IllegalAccessException ex) {
+                throw new JemmyException("Unable to recognize button", ex, button);
+            }
+        }
+    }
+
+    /**
+     * TODO Provide javadoc
+     * @param button
+     * @return One of InputEvent.VK_* constants
+     * @see InputEvent
+     */
+    public int convert(KeyboardButton button) {
+        try {
+            return key2int.get(button);
+        } catch(Exception e) {
+            throw new JemmyException("Unable to recognize key", e, button);
+        }
+    }
+
+    /**
+     * TODO Provide javadoc
+     * @param modifiers
+     * @return
+     */
+    public int convert(Modifier... modifiers) {
+        int result = 0;
+        for (Modifier modifier : modifiers) {
+            try {
+                result |= modifier2int.get(modifier);
+            } catch (Exception e) {
+                throw new JemmyException("Unable to recognize modifier", e, modifier);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * TODO Provide javadoc
+     * @param button
+     * @return
+     */
+    public int convert(MouseButton button) {
+        try {
+            return button2int.get(button);
+        } catch (Exception e) {
+            throw new JemmyException("Unable to recognize mouse button", e, button);
+        }
+    }
+
+    /**
+     * TODO Provide javadoc
+     * @param key
+     * @return
+     */
+    public KeyboardButton convertKeyboardButton(int key) {
+        KeyboardButton res = int2key.get(key);
+        if (res == null) {
+            throw new JemmyException("Unable to recognize key", key);
+        }
+        return res;
+    }
+
+    /**
+     * TODO Provide javadoc
+     * @param modifiers
+     * @return
+     */
+    public Modifier[] convertModifiers(int modifiers) {
+        List<Modifier> result = new ArrayList<Modifier>();
+        for (int key : int2modifier.keySet()) {
+            if ((key & modifiers) != 0) {
+                Modifier res = int2modifier.get(key);
+                if (res == null) {
+                    throw new JemmyException("Unable to recognize modifiers", modifiers);
+                }
+                result.add(res);
+            }
+        }
+        return result.toArray(new Modifier[result.size()]);
+    }
+
+    /**
+     * TODO Provide javadoc
+     * @param button
+     * @return
+     */
+    public MouseButton convertMouseButton(int button) {
+        MouseButton res = int2button.get(button);
+        if (res == null) {
+            throw new JemmyException("Unable to recognize mouse button", button);
+        }
+        return res;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/input/AWTRobotInputFactory.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+import org.jemmy.JemmyException;
+import org.jemmy.control.Wrap;
+import org.jemmy.env.Environment;
+import org.jemmy.interfaces.ControlInterface;
+import org.jemmy.interfaces.ControlInterfaceFactory;
+import org.jemmy.interfaces.Drag;
+import org.jemmy.interfaces.Keyboard;
+import org.jemmy.interfaces.Mouse;
+import org.jemmy.interfaces.TypeControlInterface;
+import org.jemmy.operators.AWTScreen;
+import org.jemmy.operators.Screen;
+
+/**
+ *
+ * @author shura
+ */
+public class AWTRobotInputFactory implements ControlInterfaceFactory {
+
+    /**
+     * Set this Environment property to true or false to run java.awt.Robot in
+     * other or the same JVM
+     */
+    public static final String OTHER_VM_PROPERTY = "awt.robot.othervm";
+
+    /**
+     * Set this Environment property to the name of the host where other JVM runs.
+     * 'localhost' by default
+     */
+    public static final String OTHER_VM_HOST_PROPERTY = "awt.robot.othervm.host";
+
+    /**
+     * Set this Environment property to override the port which is used to
+     * connect to other JVM
+     */
+    public static final String OTHER_VM_PORT_PROPERTY = "awt.robot.othervm.port";
+
+    /**
+     * Set this Environment property to to the maximum time of waiting for the
+     * client to connect to the JVM where Robot is running. It also waits the same
+     * amount of ms for the next connection after the previous terminates.
+     * Default is 15 min.
+     */
+    public static final String OTHER_VM_CONNECTION_TIMEOUT_PROPERTY
+            = "awt.robot.othervm.connection.timeout";
+
+    /**
+     * The name of the timeout that is used by default as the delay time for
+     * java.awt.Robot
+     * @see java.awt.Robot#setAutoDelay(int)
+     */
+    public static final String ROBOT_DELAY_TIMEOUT_NAME = "RobotDriver.DelayTimeout";
+
+    /**
+     * Set this Environment property to the maximum number of pixels between
+     * mouse positions during movement
+     */
+    public static final String ROBOT_MOUSE_SMOOTHNESS_PROPERTY = "awt.robot.mouse.smoothness";
+
+    /**
+     * Specifies whether to run java.awt.Robot in other JVM
+     * @param runInOtherJVM if true then java.awt.Robot will run in other JVM
+     */
+    public static void runInOtherJVM(boolean runInOtherJVM) {
+        RobotExecutor.get().setRunInOtherJVM(runInOtherJVM);
+    }
+
+    /**
+     * Returns runInOtherJVM setting
+     * @return if true then java.awt.Robot is running in other JVM
+     */
+    public static boolean isRunInOtherJVM() {
+        return RobotExecutor.get().isRunInOtherJVM();
+    }
+
+    /**
+     * Specifies mouse movements smoothness
+     * @param mouseSmoothness the maximum number of pixels between
+     * mouse positions during movement
+     * @see #ROBOT_MOUSE_SMOOTHNESS_PROPERTY
+     */
+    public static void setMouseSmoothness(int mouseSmoothness) {
+        if(mouseSmoothness <= 0) {
+            throw new IllegalArgumentException("Mouse smoothness should be greater than zero.");
+        }
+        RobotDriver.setMouseSmoothness(mouseSmoothness);
+    }
+
+    /**
+     * Gets the mouse movements smoothness
+     * @return the maximum number of pixels between
+     * mouse positions during movement
+     * @see #ROBOT_MOUSE_SMOOTHNESS_PROPERTY
+     */
+    public static int getMouseSmoothness() {
+        return RobotDriver.getMouseSmoothness();
+    }
+
+    static {
+        if(Screen.SCREEN == null) {
+            Screen.setSCREEN(new AWTScreen(Environment.getEnvironment()));
+        }
+    }
+
+    public AWTMap getAwtMap() {
+        return RobotExecutor.get().getAWTMap();
+    }
+
+    public void setAwtMap(AWTMap awtMap) {
+        RobotExecutor.get().setAWTMap(awtMap);
+    }
+
+    public <INTERFACE extends ControlInterface> INTERFACE create(Wrap<?> control, Class<INTERFACE> interfaceClass) {
+        if(Mouse.class.isAssignableFrom(interfaceClass)) {
+            return (INTERFACE) new MouseImpl(control);
+        } else if(Keyboard.class.isAssignableFrom(interfaceClass)) {
+            return (INTERFACE) new KeyboardImpl(control);
+        } else if(Drag.class.isAssignableFrom(interfaceClass)) {
+            return (INTERFACE) new DragImpl(control);
+        }
+        throw new JemmyException(AWTRobotInputFactory.class.getName() + " does not support " + interfaceClass.getName());
+    }
+
+    public <TYPE, INTERFACE extends TypeControlInterface<TYPE>> INTERFACE create(Wrap<?> control, Class<INTERFACE> interfaceClass, Class<TYPE> type) {
+        throw new JemmyException(AWTRobotInputFactory.class.getName() + " does not support " + interfaceClass.getName());
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getName() + "[otherVM=" + isRunInOtherJVM() + ", mouseSmoothness=" + getMouseSmoothness() + "]";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/input/ClassReference.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ *
+ * Allows access to classes by reflection.
+ *
+ * @author Alexandre Iline (alexandre.iline@sun.com)
+ */
+public class ClassReference {
+
+    private Class<?> cl;
+    private Object instance;
+
+    /**
+     * Constructor.
+     * @param o Object to work with.
+     */
+    public ClassReference(Object o) {
+        super();
+        instance = o;
+        cl = o.getClass();
+    }
+
+    /**
+     * Contructor.
+     * The object created by this constructor can be used
+     * to access static methods and fields only.
+     *
+     * @param className name of class
+     * @exception ClassNotFoundException
+     */
+    public ClassReference(String className)
+            throws ClassNotFoundException {
+        super();
+        cl = Class.forName(className);
+        instance = null;
+    }
+
+    /**
+     * Executes class's <code>main(java.lang.String[])</code> method
+     * with a zero-length <code>java.lang.String</code> array
+     * as a parameter.
+     *
+     * @exception NoSuchMethodException
+     * @exception InvocationTargetException
+     */
+    public void startApplication()
+            throws InvocationTargetException, NoSuchMethodException {
+        String[] params = new String[0];
+        startApplication(params);
+    }
+
+    /**
+     * Executes class's <code>main(java.lang.String[])</code> method.
+     *
+     * @param params The <code>java.lang.String</code> array to pass
+     * to <code>main(java.lang.String[])</code>.
+     * @exception NoSuchMethodException
+     * @exception InvocationTargetException
+     */
+    public void startApplication(String[] params)
+            throws InvocationTargetException, NoSuchMethodException {
+        String[] real_params;
+        if (params == null) {
+            real_params = new String[0];
+        } else {
+            real_params = params;
+        }
+        String[][] methodParams = {real_params};
+        Class[] classes = {real_params.getClass()};
+        try {
+            invokeMethod("main", methodParams, classes);
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        } catch (IllegalStateException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Locates method by name and parameter types and executes it.
+     *
+     * @param method_name Name of method.
+     * @param params Method parameters.
+     * @param params_classes Method parameters types.
+     * @return the return value from an invocation of the Method.<BR>
+     * If <code>method_name</code> method is void, <code>null</code> is returned.<BR>
+     * If <code>method_name</code> method returns a primitive type, then
+     * return wrapper class instance.
+     * @throws InvocationTargetException when the invoked method throws an exception.
+     * @throws NoSuchMethodException when the method cannot be found.
+     * @throws IllegalAccessException when access to the class or method is lacking.
+     * @throws SecurityException if access to the package or method is denied.
+     */
+    public Object invokeMethod(String method_name, Object[] params, Class<?>[] params_classes)
+            throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
+        if (params == null) {
+            params = new Object[0];
+        }
+        if (params_classes == null) {
+            params_classes = new Class<?>[0];
+        }
+        Method method = cl.getMethod(method_name,
+                params_classes);
+        return (method.invoke(instance, params));
+    }
+
+    /**
+     * Locates constructor by parameter types and creates an instance.
+     *
+     * @param params An array of Method parameters.
+     * @param params_classes An array of Method parameter types.
+     * @return a new class instance.
+     * @throws InvocationTargetException when the invoked constructor throws an exception.
+     * @throws NoSuchMethodException when the constructor cannot be found.
+     * @throws IllegalAccessException when access to the class or constructor is lacking.
+     * @throws InstantiationException when the constructor is for an abstract class.
+     * @throws SecurityException if access to the package or constructor is denied.
+     */
+    public Object newInstance(Object[] params, Class[] params_classes)
+            throws InvocationTargetException, NoSuchMethodException,
+            IllegalAccessException, InstantiationException {
+        if (params == null) {
+            params = new Object[0];
+        }
+        if (params_classes == null) {
+            params_classes = new Class[0];
+        }
+        Constructor constructor = cl.getConstructor(params_classes);
+        return (constructor.newInstance(params));
+    }
+
+    /**
+     * Returns the field value.
+     * @param field_name The name of the field.
+     * @return the field value
+     * @see #setField
+     * @throws NoSuchFieldException when the field cannot be found.
+     * @throws IllegalAccessException when access to the class or constructor is lacking.
+     * @throws SecurityException if access to the package or field is denied.
+     */
+    public Object getField(String field_name)
+            throws NoSuchFieldException, IllegalAccessException {
+        return (cl.getField(field_name).get(instance));
+    }
+
+    /**
+     * Change a field's value.
+     *
+     * @param field_name The name of the field.
+     * @param newValue The fields new value.
+     * @see #getField
+     * @throws NoSuchFieldException when the field cannot be found.
+     * @throws IllegalAccessException when access to the class or constructor is lacking.
+     * @throws SecurityException if access to the package or field is denied.
+     */
+    public void setField(String field_name, Object newValue)
+            throws NoSuchFieldException, IllegalAccessException {
+        cl.getField(field_name).set(instance, newValue);
+    }
+
+    /**
+     * Returns all superclasses.
+     * @return an array of superclasses, starting with the reference class
+     * and ending with <code>java.lang.Object</code>.
+     */
+    public Class[] getClasses() {
+        Class cls = cl;
+        int count = 0;
+        do {
+            count++;
+            cls = cls.getSuperclass();
+        } while (cls != null);
+        Class[] result = new Class[count];
+        cls = cl;
+        for (int i = 0; i < count; i++) {
+            result[i] = cls;
+            cls = cls.getSuperclass();
+        }
+        return (result);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/input/DragImpl.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+
+import java.awt.event.InputEvent;
+import org.jemmy.control.*;
+import org.jemmy.Point;
+import org.jemmy.action.Action;
+import org.jemmy.env.Environment;
+import org.jemmy.env.Timeout;
+import org.jemmy.interfaces.Mouse.MouseButton;
+import org.jemmy.interfaces.Drag;
+import org.jemmy.interfaces.Modifier;
+import org.jemmy.interfaces.Mouse;
+import org.jemmy.interfaces.Mouse.MouseButtons;
+import org.jemmy.interfaces.Showable;
+
+
+/**
+ *
+ * @author shura
+ */
+public class DragImpl implements Drag {
+
+    /**
+     *
+     */
+    public static final int DND_POINTS = 10;
+
+    static {
+        Environment.getEnvironment().setTimeout(BEFORE_DRAG_TIMEOUT);
+        Environment.getEnvironment().setTimeout(BEFORE_DROP_TIMEOUT);
+        Environment.getEnvironment().setTimeout(IN_DRAG_TIMEOUT);
+    }
+
+    private Wrap<?> source;
+
+    /**
+     *
+     * @param source
+     */
+    public DragImpl(Wrap<?> source) {
+        this.source = source;
+    }
+
+    /**
+     *
+     * @param targetPoint
+     */
+    public void dnd(Point targetPoint) {
+        dnd(source, targetPoint);
+    }
+
+    /**
+     *
+     * @param target
+     * @param targetPoint
+     */
+    public void dnd(Wrap target, Point targetPoint) {
+        dnd(source.getClickPoint(), target, targetPoint);
+    }
+
+    /**
+     *
+     * @param point
+     * @param target
+     * @param targetPoint
+     */
+    public void dnd(Point point, Wrap target, Point targetPoint) {
+        dnd(point, target, targetPoint, MouseButtons.BUTTON1);
+    }
+
+    /**
+     *
+     * @param point
+     * @param target
+     * @param targetPoint
+     * @param button
+     */
+    public void dnd(Point point, Wrap target, Point targetPoint, MouseButton button) {
+        dnd(point, target, targetPoint, button, new Modifier[]{});
+    }
+
+    /**
+     *
+     * @param point
+     * @param target
+     * @param targetPoint
+     * @param button
+     * @param modifiers
+     */
+    public void dnd(Point pointParam, final Wrap target, final Point targetPoint, final MouseButton button, final Modifier... modifiers) {
+        final Point point = pointParam == null ? source.getClickPoint() : pointParam;
+        source.getEnvironment().getExecutor().execute(target.getEnvironment(), false, new Action() {
+            public void run(Object... parameters) {
+                if(source.is(Showable.class)) ((Showable)source.as(Showable.class)).shower().show();
+                source.mouse().move(point);
+                source.mouse().press(button, modifiers);
+                source.getEnvironment().getTimeout(BEFORE_DRAG_TIMEOUT.getName()).sleep();
+                Point intermediatePoint = new Point();
+                int xDistance = target.getScreenBounds().x + targetPoint.x - source.getScreenBounds().x - point.x;
+                int yDistance = target.getScreenBounds().y + targetPoint.y - source.getScreenBounds().y - point.y;
+                int startX = point.x + source.getScreenBounds().x;
+                int startY = point.y + source.getScreenBounds().y;
+                int endX = startX + xDistance;
+                int endY = startY + yDistance;
+                for(int i = 0; i < DND_POINTS + 1; i++) {
+                    intermediatePoint.x = startX + xDistance * i / DND_POINTS - source.getScreenBounds().x;
+                    intermediatePoint.y = startY + yDistance * i / DND_POINTS - source.getScreenBounds().y;
+                    source.mouse().move(intermediatePoint);
+                    source.getEnvironment().getTimeout(IN_DRAG_TIMEOUT.getName()).sleep();
+                }
+                source.mouse().move(new Point(endX - source.getScreenBounds().x, endY - source.getScreenBounds().y));
+                //target.mouse().move(targetPoint);
+                source.getEnvironment().getTimeout(BEFORE_DROP_TIMEOUT.getName()).sleep();
+                target.mouse().release(button, modifiers);
+            }
+
+            @Override
+            public String toString() {
+                return "grag'n'drop from " + point + " to " + targetPoint + " of " + target.getClass() + " with mouse button " + button + " with " + modifiers + " modifiers";
+            }
+
+        }, button, modifiers);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/input/KeyboardImpl.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+import org.jemmy.action.Action;
+import org.jemmy.control.Wrap;
+import org.jemmy.interfaces.Keyboard.KeyboardButton;
+import org.jemmy.interfaces.Keyboard.KeyboardModifier;
+import org.jemmy.interfaces.Keyboard;
+import org.jemmy.env.Environment;
+import org.jemmy.env.Timeout;
+import org.jemmy.interfaces.Focusable;
+import org.jemmy.interfaces.Modifier;
+
+/**
+ * KeyDriver
+ *
+ * @author Alexandre Iline(alexandre.iline@sun.com)
+ */
+public class KeyboardImpl implements Keyboard {
+
+    CharBindingMap<KeyboardButton, KeyboardModifier> map;
+    Environment env;
+    Wrap<?> target;
+    RobotDriver robotDriver;
+    private boolean detached;
+    /**
+     * Constructs a KeyRobotDriver object.
+     * @param target
+     */
+    public KeyboardImpl(Wrap<?> target) {
+        //TODO: super(target.getEnvironment().getTimeout(RobotDriver.ROBOT_DELAY_TIMEOUT_NAME));
+        robotDriver = new RobotDriver(target.getEnvironment());
+        this.env = target.getEnvironment();
+        this.map = target.getEnvironment().getBindingMap();
+        this.target = target;
+    }
+
+    static {
+        //TODO: Environment.getEnvironment().setTimeout(new Timeout(RobotDriver.ROBOT_DELAY_TIMEOUT_NAME, 10));
+        Environment.getEnvironment().setTimeout(new Timeout(PUSH.getName(), 100));
+        Environment.getEnvironment().setBindingMap(new DefaultCharBindingMap());
+    }
+
+    private void runAction(Action action) {
+        if(detached) {
+            target.getEnvironment().getExecutor().executeDetached(target.getEnvironment(), false, action);
+        } else {
+            target.getEnvironment().getExecutor().execute(target.getEnvironment(), false, action);
+        }
+    }
+
+    /**
+     *
+     * @return Environment
+     */
+    public Environment getEnvironment() {
+        return env;
+    }
+
+    /**
+     *
+     * @param kbdButton
+     * @param modifiers
+     * @param pushTime
+     */
+    public void pushKey(final KeyboardButton kbdButton, final Modifier modifiers[], final Timeout pushTime) {
+        runAction(new Action() {
+            public void run(Object... parameters) {
+                if(target.is(Focusable.class)) target.as(Focusable.class).focuser().focus();
+                pressKey(kbdButton, modifiers);
+                pushTime.sleep();
+                releaseKey(kbdButton, modifiers);
+            }
+            @Override
+            public String toString() {
+                return "push " + kbdButton + " key with " + modifiers + " modifiers";
+            }
+        });
+    }
+
+    /**
+     *
+     * @param keyChar
+     * @param pushTime
+     */
+    @Override
+    public void typeChar(char keyChar, Timeout pushTime) {
+        pushKey(pushTime, map.getCharKey(keyChar), map.getCharModifiers(keyChar));
+    }
+
+    /**
+     * Press the keyboard key specified by kbdButton preceding with
+     * pressing of modifier buttons specified by modifiers
+     * @param kbdButton one of InputEvent.VK_* constants
+     * @param modifiers combination of InputEvent.*_DOWN_MASK constants
+     * @see java.awt.event.InputEvent
+     */
+    @Override
+    public void pressKey(final KeyboardButton kbdButton, final Modifier... modifiers) {
+        runAction(new Action() {
+            public void run(Object... parameters) {
+                robotDriver.pressKey(kbdButton, modifiers);
+            }
+            @Override
+            public String toString() {
+                return "press " + kbdButton + " key with " + modifiers + " modifiers";
+            }
+        });
+    }
+
+    /**
+     * Release the keyboard key specified by kbdButton and then release
+     * all the modifier keys specified by modifiers
+     * @param kbdButton one of InputEvent.VK_* constants
+     * @param modifiers combination of InputEvent.*_DOWN_MASK constants
+     * @see java.awt.event.InputEvent
+     */
+    @Override
+    public void releaseKey(final KeyboardButton kbdButton, final Modifier... modifiers) {
+        runAction(new Action() {
+            public void run(Object... parameters) {
+                robotDriver.releaseKey(kbdButton, modifiers);
+            }
+            @Override
+            public String toString() {
+                return "press " + kbdButton + " key with " + modifiers + " modifiers";
+            }
+        });
+    }
+
+    /**
+     *
+     * @param kbdButton
+     */
+    @Override
+    public void pressKey(KeyboardButton kbdButton) {
+        pressKey(kbdButton, new Modifier[]{});
+    }
+
+    /**
+     *
+     * @param kbdButton
+     */
+    @Override
+    public void releaseKey(KeyboardButton kbdButton) {
+        releaseKey(kbdButton, new Modifier[]{});
+    }
+
+    /**
+     *
+     * @param kbdButton
+     * @param modifiers
+     */
+    @Override
+    public void pushKey(KeyboardButton kbdButton, Modifier... modifiers) {
+        pushKey(kbdButton, modifiers, getEnvironment().getTimeout(PUSH.getName()));
+    }
+
+    /**
+     *
+     * @param kbdButton
+     */
+    @Override
+    public void pushKey(KeyboardButton kbdButton) {
+        pushKey(kbdButton, new Modifier[]{});
+    }
+
+    /**
+     *
+     * @param keyChar
+     */
+    @Override
+    public void typeChar(char keyChar) {
+        typeChar(keyChar, getEnvironment().getTimeout(PUSH.getName()));
+    }
+
+    @Override
+    public Keyboard detached() {
+        detached = true;
+        return this;
+    }
+
+    @Override
+    public void pushKey(Timeout pushTime, KeyboardButton key, Modifier... modifiers) {
+        pushKey(key, modifiers, pushTime);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/input/MouseImpl.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+import org.jemmy.Rectangle;
+import org.jemmy.Point;
+import java.util.Arrays;
+import org.jemmy.action.Action;
+import org.jemmy.control.Wrap;
+import org.jemmy.env.Environment;
+import org.jemmy.env.Timeout;
+import org.jemmy.interfaces.Modifier;
+import org.jemmy.interfaces.Mouse;
+import static org.jemmy.interfaces.Mouse.CLICK;
+import org.jemmy.interfaces.Showable;
+
+/**
+ *
+ * @author shura
+ */
+public class MouseImpl implements Mouse {
+
+    private Wrap<?> target;
+    private RobotDriver robotDriver;
+    private boolean detached = false;
+
+    static {
+        if (Environment.getEnvironment().getTimeout(CLICK) == null) {
+            Environment.getEnvironment().setTimeout(MouseImpl.CLICK);
+        }
+    }
+
+    /**
+     *
+     * @param target
+     */
+    public MouseImpl(Wrap<?> target) {
+        this.target = target;
+        this.robotDriver = new RobotDriver(new Timeout("", 10));
+    }
+
+    public Mouse detached() {
+        this.detached = true;
+        return this;
+    }
+
+    private void runAction(Action action) {
+        if (detached) {
+            target.getEnvironment().getExecutor().executeDetached(target.getEnvironment(), false, action);
+        } else {
+            target.getEnvironment().getExecutor().execute(target.getEnvironment(), false, action);
+        }
+    }
+
+    /**
+     *
+     */
+    @Override
+    public void press() {
+        press(MouseButtons.BUTTON1);
+    }
+
+    /**
+     *
+     * @param button
+     */
+    @Override
+    public void press(MouseButton button) {
+        press(button, new Modifier[]{});
+    }
+
+     /**
+     *
+     * @param button
+     * @param modifiers
+     */
+    @Override
+    public void press(final MouseButton button, final Modifier... modifiers) {
+        runAction(new Action() {
+
+            public void run(Object... parameters) {
+                robotDriver.pressMouse(button, modifiers);
+            }
+
+            @Override
+            public String toString() {
+                return "pressing mouse button " + button + " with " + modifiers + " modifiers";
+            }
+        });
+    }
+
+    /**
+     *
+     */
+    public void release() {
+        release(MouseButtons.BUTTON1);
+    }
+
+    /**
+     *
+     * @param button
+     */
+    @Override
+    public void release(MouseButton button) {
+        release(button, new Modifier[]{});
+    }
+
+    /**
+     *
+     * @param button
+     * @param modifiers
+     */
+    @Override
+    public void release(final MouseButton button, final Modifier... modifiers) {
+        runAction(new Action() {
+
+            public void run(Object... parameters) {
+                robotDriver.releaseMouse(button, modifiers);
+            }
+
+            @Override
+            public String toString() {
+                return "releasing mouse button " + button + " with " + modifiers + " modifiers";
+            }
+        });
+    }
+
+    /**
+     *
+     */
+    public void move() {
+        move(target.getClickPoint());
+    }
+
+    /**
+     *
+     * @param p
+     */
+    public void move(final Point p) {
+        runAction(new Action() {
+
+            public void run(Object... parameters) {
+                robotDriver.moveMouse(getAbsolute(target, p));
+            }
+
+            @Override
+            public String toString() {
+                return "moving mouse to " + p;
+            }
+        });
+    }
+
+    /**
+     *
+     */
+    public void click() {
+        this.click(1);
+    }
+
+    /**
+     *
+     * @param count
+     */
+    public void click(int count) {
+        this.click(count, null);
+    }
+
+    /**
+     *
+     * @param count
+     * @param p Point to click, if null {@linkplain Wrap#getClickPoint()
+     * Wrap.getClickPoint()} method is invoked to get the point to click.
+     */
+    public void click(int count, Point p) {
+        this.click(count, p, MouseButtons.BUTTON1);
+    }
+
+    /**
+     *
+     * @param count
+     * @param p Point to click, if null {@linkplain Wrap#getClickPoint()
+     * Wrap.getClickPoint()} method is invoked to get the point to click.
+     * @param button
+     */
+    @Override
+    public void click(int count, Point p, MouseButton button) {
+        click(count, p, button, new Modifier[] {});
+    }
+
+    /**
+     *
+     * @param count
+     * @param p Point to click, if null {@linkplain Wrap#getClickPoint()
+     * Wrap.getClickPoint()} method is invoked to get the point to click.
+     * @param button
+     * @param modifiers
+     */
+    @Override
+    public void click(final int count, final Point p, final MouseButton button, final Modifier... modifiers) {
+        runAction(new Action() {
+
+            public void run(Object... parameters) {
+                if (target.is(Showable.class)) {
+                    target.as(Showable.class).shower().show();
+                }
+                robotDriver.clickMouse(getAbsolute(target,
+                        p == null ? target.getClickPoint() : p),
+                        count, button, target.getEnvironment().getTimeout(CLICK), modifiers);
+            }
+
+            @Override
+            public String toString() {
+                return "clicking " + button + " mouse button " + count + " times at " + p + " with " + Arrays.toString(modifiers) + " modifiers";
+            }
+        });
+    }
+
+    static Point getAbsolute(Wrap<?> target, Point p) {
+        Rectangle screenBounds = target.getScreenBounds();
+        return new Point(p.x + screenBounds.x, p.y + screenBounds.y);
+    }
+
+    private void turn(final Point p, final int amount, final Modifier... modifiers) {
+        runAction(new Action() {
+
+            public void run(Object... parameters) {
+                if (target.is(Showable.class)) {
+                    target.as(Showable.class).shower().show();
+                }
+                robotDriver.turnWheel(getAbsolute(target,
+                        p == null ? target.getClickPoint() : p),
+                        amount, modifiers);
+            }
+
+            @Override
+            public String toString() {
+                return "turning wheel to " + amount + " with " + Arrays.toString(modifiers) + " modifiers";
+            }
+        });
+    }
+
+    public void turnWheel(Point point, final int amount, Modifier... modifiers) {
+        turn(point, amount, modifiers);
+    }
+
+    public void turnWheel(Point point, final int amount) {
+        turn(point, amount, new Modifier[]{});
+    }
+
+    public void turnWheel(final int amount) {
+        turn(null, amount, new Modifier[]{});
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/input/RobotDriver.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+
+import org.jemmy.Point;
+import java.awt.event.InputEvent;
+import java.util.HashMap;
+import org.jemmy.Rectangle;
+import org.jemmy.env.Timeout;
+import org.jemmy.env.Environment;
+import org.jemmy.image.Image;
+import static org.jemmy.input.AWTRobotInputFactory.*;
+import org.jemmy.interfaces.Button;
+import org.jemmy.interfaces.Keyboard.KeyboardButton;
+import org.jemmy.interfaces.Keyboard.KeyboardButtons;
+import org.jemmy.interfaces.Keyboard.KeyboardModifiers;
+import org.jemmy.interfaces.Modifier;
+import org.jemmy.interfaces.Mouse.MouseButton;
+import org.jemmy.interfaces.Mouse.MouseButtons;
+import org.jemmy.interfaces.Mouse.MouseModifiers;
+
+
+/**
+ * @author Alexandre Iline(alexandre.iline@sun.com), KAM <mrkam@mail.ru>
+ */
+public class RobotDriver {
+
+    private static boolean haveOldPos = false;
+    private static int smoothness;
+    private static double oldX;
+    private static double oldY;
+
+    static {
+        Environment.getEnvironment().setTimeout(
+                new Timeout(ROBOT_DELAY_TIMEOUT_NAME, 10));
+        Environment.getEnvironment().setPropertyIfNotSet(
+                AWTRobotInputFactory.ROBOT_MOUSE_SMOOTHNESS_PROPERTY,
+                new Integer(Integer.MAX_VALUE).toString());
+        smoothness =  Integer.parseInt(
+                (String)Environment.getEnvironment().getProperty(
+                AWTRobotInputFactory.ROBOT_MOUSE_SMOOTHNESS_PROPERTY));
+    }
+
+    /**
+     * Sets mouse smoothness
+     * @param mouseSmoothness the maximum distance in pixels between
+     * mouse positions during movement
+     * @see #moveMouse(Point)
+     */
+    public static void setMouseSmoothness(int mouseSmoothness) {
+        smoothness = mouseSmoothness;
+    }
+
+    /**
+     * Gets mouse smoothness
+     * @return the maximum distance in pixels between
+     * mouse positions during movement
+     * @see #setMouseSmoothness(int)
+     * @see #moveMouse(Point)
+     */
+    public static int getMouseSmoothness() {
+        return smoothness;
+    }
+
+    /**
+     * Constructs a RobotDriver object.
+     * @param autoDelay Time for <code>Robot.setAutoDelay(long)</code> method.
+     */
+    public RobotDriver(Timeout autoDelay) {
+        RobotExecutor.get().setAutoDelay(autoDelay);
+    }
+
+    /**
+     * Constructs a RobotDriver object.
+     * @param env Environment with ROBOT_DELAY_TIMEOUT_NAME timeout
+     * @see AWTRobotInputFactory#ROBOT_DELAY_TIMEOUT_NAME
+     */
+    public RobotDriver(Environment env) {
+        this(env.getTimeout(ROBOT_DELAY_TIMEOUT_NAME));
+    }
+
+    /**
+     * Capture an image of specified rectangular area of screen
+     * @param screenRect area on screen that will be captured
+     * @return image of specified rectangular area of screen
+     */
+    public static Image createScreenCapture(Rectangle screenRect) {
+        return RobotExecutor.get().createScreenCapture(screenRect);
+    }
+
+    /**
+     * Presses mouse button specified by mouseButton preceding pressing of
+     * modifier keys or buttons specified by modifiers
+     * @param mouseButton One of MouseEvent.BUTTON*_MASK
+     * @param modifiers Combination of InputEvent.*_DOWN_MASK
+     * @see java.awt.event.InputEvent
+     * @see java.awt.event.MouseEvent
+     */
+    public void pressMouse(MouseButton mouseButton, Modifier... modifiers) {
+        pressModifiers(modifiers);
+        makeAnOperation("mousePress",
+                new Object[]{mouseButton},
+                new Class[]{MouseButton.class});
+    }
+
+    /**
+     * Releases mouse button specified by mouseButton then releasing
+     * modifier keys or buttons specified by modifiers
+     * @param mouseButton One of MouseEvent.BUTTON*_MASK
+     * @param modifiers Combination of InputEvent.*_DOWN_MASK
+     * @see java.awt.event.InputEvent
+     * @see java.awt.event.MouseEvent
+     */
+    public void releaseMouse(MouseButton mouseButton, Modifier... modifiers) {
+        makeAnOperation("mouseRelease",
+                new Object[]{mouseButton},
+                new Class[]{MouseButton.class});
+        releaseModifiers(modifiers);
+    }
+
+    /**
+     * Moves mouse to the specified mouse. When previous mouse location is
+     * remembered mouse moved smoothly between the points according to
+     * mouse smoothness parameter. Otherwise it jumps to the specified point
+     * @param point Position on the screen where to move mouse
+     * @see #setMouseSmoothness(int)
+     * @see #getMouseSmoothness()
+     */
+    public void moveMouse(Point point) {
+        double targetX = point.x;
+        double targetY = point.y;
+        if (haveOldPos && (oldX != targetX || oldY != targetY)) {
+            double currX = oldX;
+            double currY = oldY;
+            double hyp = Math.sqrt((targetX - currX) * (targetX - currX) +
+                    (targetY - currY) * (targetY - currY));
+            double steps = Math.ceil(hyp / Math.min(hyp, smoothness));
+            double vx = (targetX - currX) / steps;
+            double vy = (targetY - currY) / steps;
+            assert (long)vx * vx + (long)vy * vy <= (long)smoothness * smoothness;
+            while (Math.round(currX) != Math.round(targetX) ||
+                    Math.round(currY) != Math.round(targetY)) {
+                currX += vx;
+                currY += vy;
+                makeAnOperation("mouseMove", new Object[]{
+                            new Integer((int) Math.round(currX)),
+                            new Integer((int) Math.round(currY))},
+                        new Class[]{Integer.TYPE, Integer.TYPE});
+            }
+        } else {
+            makeAnOperation("mouseMove",
+                    new Object[]{new Integer(point.x), new Integer(point.y)},
+                    new Class[]{Integer.TYPE, Integer.TYPE});
+        }
+        haveOldPos = true;
+        oldX = targetX;
+        oldY = targetY;
+    }
+
+    /**
+     * Clicks the mouse button specified by mouseButton at the specified point
+     * specified number of times preceding it by pressing the modifiers key or
+     * buttons and ending by releasing them. The last click is as long as
+     * mouseClick timeout
+     * @param point Screen location where to click mouse
+     * @param clickCount Number of clicks
+     * @param mouseButton One of MouseEvent.BUTTON*_MASK
+     * @param modifiers Combination of InputEvent.*_DOWN_MASK
+     * @param mouseClick Timeout of the last click
+     * @see java.awt.event.InputEvent
+     * @see java.awt.event.MouseEvent
+     */
+    public void clickMouse(Point point, int clickCount, MouseButton mouseButton, Timeout mouseClick, Modifier... modifiers) {
+        pressModifiers(modifiers);
+        moveMouse(point);
+        makeAnOperation("mousePress", new Object[]{mouseButton}, new Class[]{MouseButton.class});
+        for (int i = 1; i < clickCount; i++) {
+            makeAnOperation("mouseRelease", new Object[]{mouseButton}, new Class[]{MouseButton.class});
+            makeAnOperation("mousePress", new Object[]{mouseButton}, new Class[]{MouseButton.class});
+        }
+        mouseClick.sleep();
+        makeAnOperation("mouseRelease", new Object[]{mouseButton}, new Class[]{MouseButton.class});
+        releaseModifiers(modifiers);
+    }
+
+    /**
+     * @deprecated Implementation doesn't seem to be correct as it ignores mouseButton and modifiers
+     * @param point
+     * @param mouseButton One of MouseEvent.BUTTON*_MASK
+     * @param modifiers
+     */
+    public void dragMouse(Point point, int mouseButton, int modifiers) {
+        moveMouse(point);
+    }
+
+    /**
+     * Performs drag and drop from startPoint to endPoint using specified
+     * mouseButton and modifiers to perform it.
+     * @param startPoint Screen coordinates of drag start point
+     * @param endPoint Screen coordinates of drag end point
+     * @param mouseButton One of MouseEvent.BUTTON*_MASK
+     * @param modifiers Combination of InputEvent.*_DOWN_MASK
+     * @param before Timeout between pressing mouse at the startPoint and
+     * mouse move
+     * @param after Timeout between mouse move to the endPoint and mouse
+     * release
+     */
+    public void dragNDrop(Point startPoint, Point endPoint, MouseButton mouseButton, Modifier modifiers[], Timeout before, Timeout after) {
+        moveMouse(startPoint);
+        pressMouse(mouseButton, modifiers);
+        before.sleep();
+        moveMouse(endPoint);
+        after.sleep();
+        releaseMouse(mouseButton, modifiers);
+    }
+
+    /**
+     * Presses a key.
+     * @param kbdButton Key code (<code>KeyEventVK_*</code> field.
+     * @param modifiers a combination of <code>InputEvent.*_MASK</code> fields.
+     */
+    public void pressKey(KeyboardButton kbdButton, Modifier... modifiers) {
+        pressModifiers(modifiers);
+        makeAnOperation("keyPress",
+                new Object[]{kbdButton},
+                new Class[]{KeyboardButton.class});
+    }
+
+    /**
+     * Releases a key.
+     * @param kbdButton Key code (<code>KeyEventVK_*</code> field.
+     * @param modifiers a combination of <code>InputEvent.*_MASK</code> fields.
+     */
+    public void releaseKey(KeyboardButton kbdButton, Modifier... modifiers) {
+        makeAnOperation("keyRelease",
+                new Object[]{kbdButton},
+                new Class[]{KeyboardButton.class});
+        releaseModifiers(modifiers);
+    }
+
+    /**
+     * Turns the wheel.
+     * @param p
+     * @param amount Either positive or negative
+     * @param modifiers
+     */
+    public void turnWheel(Point p, int amount, Modifier... modifiers) {
+        pressModifiers(modifiers);
+        moveMouse(p);
+        java.awt.Robot r = null;
+        makeAnOperation("mouseWheel",
+                new Object[]{amount},
+                new Class[]{Integer.TYPE});
+        releaseModifiers(modifiers);
+    }
+
+    /**
+     * Performs a single operation.
+     * @param method a name of <code>java.awt.Robot</code> method.
+     * @param params method parameters
+     * @param paramClasses method parameters classes
+     */
+    public void makeAnOperation(final String method, final Object[] params, final Class[] paramClasses) {
+        RobotExecutor.get().makeAnOperation(method, params, paramClasses);
+    }
+
+    final static int SHIFT_MASK = InputEvent.SHIFT_DOWN_MASK | InputEvent.SHIFT_MASK;
+    final static int ALT_GRAPH_MASK = InputEvent.ALT_GRAPH_DOWN_MASK | InputEvent.ALT_GRAPH_MASK;
+    final static int ALT_MASK = InputEvent.ALT_DOWN_MASK | InputEvent.ALT_MASK;
+    final static int META_MASK = InputEvent.META_DOWN_MASK | InputEvent.META_MASK;
+    final static int CTRL_MASK = InputEvent.CTRL_DOWN_MASK | InputEvent.CTRL_MASK;
+
+    /**
+     * Presses modifiers keys by robot.
+     * @param modifiers a combination of <code>InputEvent.*_MASK</code> fields.
+     */
+    protected void pressModifiers(Modifier... modifiers) {
+        for (Modifier modifier : modifiers) { // TODO: ALT_GRAPH_MASK?
+            if (modifier == KeyboardModifiers.ALT_DOWN_MASK) {
+                pressKey(KeyboardButtons.ALT);
+            } else if (modifier == KeyboardModifiers.CTRL_DOWN_MASK) {
+                pressKey(KeyboardButtons.CONTROL);
+            } else if (modifier == KeyboardModifiers.META_DOWN_MASK) {
+                pressKey(KeyboardButtons.META);
+            } else if (modifier == KeyboardModifiers.SHIFT_DOWN_MASK) {
+                pressKey(KeyboardButtons.SHIFT);
+            }
+        }
+    }
+
+    /**
+     * Releases modifiers keys by robot.
+     * @param modifiers a combination of <code>InputEvent.*_MASK</code> fields.
+     */
+    protected void releaseModifiers(Modifier... modifiers) {
+        for (Modifier modifier : modifiers) { // TODO: ALT_GRAPH_MASK?
+            if (modifier == KeyboardModifiers.ALT_DOWN_MASK) {
+                releaseKey(KeyboardButtons.ALT);
+            } else if (modifier == KeyboardModifiers.CTRL_DOWN_MASK) {
+                releaseKey(KeyboardButtons.CONTROL);
+            } else if (modifier == KeyboardModifiers.META_DOWN_MASK) {
+                releaseKey(KeyboardButtons.META);
+            } else if (modifier == KeyboardModifiers.SHIFT_DOWN_MASK) {
+                releaseKey(KeyboardButtons.SHIFT);
+            }
+        }
+    }
+    /**
+     * If java.awt.Robot is running in other JVM, it shutdowns that JVM
+     * @see AWTRobotInputFactory#runInOtherJVM(boolean)
+     */
+    public static void exit() {
+        RobotExecutor.get().exit();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/input/RobotExecutor.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,601 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+
+import java.awt.EventQueue;
+import java.awt.image.BufferedImage;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OptionalDataException;
+import java.io.PrintWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.jemmy.JemmyException;
+import org.jemmy.Rectangle;
+import org.jemmy.env.Environment;
+import org.jemmy.env.Timeout;
+import org.jemmy.image.AWTImage;
+import org.jemmy.image.Image;
+import org.jemmy.image.PNGDecoder;
+import org.jemmy.image.PNGEncoder;
+import org.jemmy.timing.State;
+import org.jemmy.timing.Waiter;
+import org.jemmy.interfaces.Keyboard.KeyboardButton;
+import org.jemmy.interfaces.Mouse.MouseButton;
+
+/**
+ *
+ * @author КАМ
+ */
+class RobotExecutor {
+
+    private static RobotExecutor instance;
+    private AWTMap awtMap = null;
+
+    public static RobotExecutor get() {
+        if (instance == null) {
+            instance = new RobotExecutor();
+        }
+        return instance;
+    }
+
+    /**
+     * A reference to the robot instance.
+     */
+    protected ClassReference robotReference = null;
+    protected Timeout autoDelay;
+    private boolean inited = false;
+    private boolean runInOtherJVM = false;
+    private boolean ready = false;
+    private boolean connectionEstablished = false;
+    private ObjectOutputStream outputStream;
+    private ObjectInputStream inputStream;
+    private Socket socket;
+    private int connectionPort;
+    private String connectionHost;
+    public static final int CONNECTION_TIMEOUT = Integer.parseInt(
+            (String)Environment.getEnvironment().getProperty(
+            AWTRobotInputFactory.OTHER_VM_CONNECTION_TIMEOUT_PROPERTY,
+            Integer.toString(60000 * 15))); // 15 min
+
+    public RobotExecutor() {
+    }
+
+    void setAWTMap(AWTMap awtMap) {
+        this.awtMap = awtMap;
+    }
+
+    AWTMap getAWTMap() {
+        if (awtMap == null) {
+            awtMap = new AWTMap();
+        }
+        return awtMap;
+    }
+
+    private void ensureInited() {
+        if (!inited) {
+             runInOtherJVM = Boolean.parseBoolean((String)Environment.getEnvironment()
+                     .getProperty(AWTRobotInputFactory.OTHER_VM_PROPERTY,
+                     Boolean.toString(runInOtherJVM)));
+             inited = true;
+        }
+    }
+
+    public Image createScreenCapture(Rectangle screenRect) {
+         Object result = makeAnOperation("createScreenCapture", new Object[] {
+            new java.awt.Rectangle(screenRect.x, screenRect.y, screenRect.width,
+                    screenRect.height) },
+            new Class[] { java.awt.Rectangle.class });
+         if (result.getClass().isAssignableFrom(BufferedImage.class)) {
+             return new AWTImage(BufferedImage.class.cast(result));
+         } else {
+             throw new JemmyException("Screen capture (" + result
+                     + ") is not a BufferedImage");
+         }
+    }
+
+    public Object makeAnOperation(String method, Object[] params, Class[] paramClasses) {
+        ensureInited();
+        if (runInOtherJVM) {
+            return makeAnOperationRemotely(method, params, paramClasses);
+        } else {
+            return makeAnOperationLocally(method, params, paramClasses);
+        }
+    }
+
+    public void exit() {
+        ensureInited();
+        if (runInOtherJVM) {
+            ensureConnection();
+            try {
+                outputStream.writeObject("exit");
+                connectionEstablished = false;
+                deleteProperties();
+            } catch (IOException ex) {
+                throw new JemmyException("Failed to invoke exit", ex);
+            }
+        }
+    }
+
+    private Object makeAnOperationLocally(String method, Object[] params, Class[] paramClasses) {
+        if (robotReference == null) {
+            initRobot();
+        }
+        try {
+            convert(method, params, paramClasses);
+            Object result = robotReference.invokeMethod(method, params, paramClasses);
+            synchronizeRobot();
+            return result;
+        } catch (InvocationTargetException e) {
+            throw (new JemmyException("Exception during java.awt.Robot accessing", e));
+        } catch (IllegalStateException e) {
+            throw (new JemmyException("Exception during java.awt.Robot accessing", e));
+        } catch (NoSuchMethodException e) {
+            throw (new JemmyException("Exception during java.awt.Robot accessing", e));
+        } catch (IllegalAccessException e) {
+            throw (new JemmyException("Exception during java.awt.Robot accessing", e));
+        }
+    }
+
+    private int convert(Object obj) {
+        if (MouseButton.class.isAssignableFrom(obj.getClass())) {
+            return awtMap.convert((MouseButton)obj);
+        } else if (KeyboardButton.class.isAssignableFrom(obj.getClass())) {
+            return awtMap.convert((KeyboardButton)obj);
+        } else {
+            throw new JemmyException("Unable to recognize object", obj);
+        }
+    }
+
+    private static final Set<String> convertables = new HashSet<String>(Arrays.asList(new String[] {"mousePress", "mouseRelease", "keyPress", "keyRelease"}));
+
+    private void convert(String method, Object[] params, Class[] paramClasses) {
+        if (convertables.contains(method))
+            for (int i = 0; i < params.length; i++) {
+            params[i] = new Integer(convert(params[i]));
+            paramClasses[i] = Integer.TYPE;
+        }
+    }
+
+    public static void main(String[] args) {
+        System.setProperty("apple.awt.UIElement", "true");
+        if (args.length != 0 && args.length != 1) {
+            System.err.println("Usage: java ... [-D" +
+                    Environment.JEMMY_PROPERTIES_FILE_PROPERTY + "=" +
+                    "<.jemmy.properties full path>]" +
+                    " RobotExecutor [connectionPort]");
+            System.exit(-1);
+        }
+        if (args.length == 1) {
+            Environment.getEnvironment().setProperty(
+                    AWTRobotInputFactory.OTHER_VM_PORT_PROPERTY, args[0]);
+        }
+        RobotExecutor re = new RobotExecutor();
+        try {
+            re.server();
+        } catch (Exception ex) {
+            ex.printStackTrace(System.err);
+            System.err.flush();
+            System.exit(-1);
+        }
+    }
+
+    private File props;
+
+    private void deleteProperties() {
+        if (props != null) {
+            props.delete();
+            props = null;
+        }
+    }
+
+    private void prepareProperties() {
+        deleteProperties();
+        try {
+            props = File.createTempFile(".jemmy.othervm.", ".properties");
+            props.deleteOnExit();
+            PrintWriter fw = new PrintWriter(props);
+            for(Field f : AWTRobotInputFactory.class.getDeclaredFields()) {
+                if ((f.getModifiers() & Modifier.FINAL) != 0 &&
+                        (f.getModifiers() & Modifier.STATIC) != 0 &&
+                        f.getType().equals(String.class) &&
+                        f.getName().startsWith("OTHER_VM_") &&
+                        Environment.getEnvironment().getProperty((String)f.get(null)) != null) {
+                    fw.println(f.get(null) + "=" + Environment.getEnvironment().getProperty((String)f.get(null)));
+                }
+            }
+            fw.close();
+        } catch (IllegalArgumentException ex) {
+            throw new JemmyException("Failed to create temporary properties file: " + props.getAbsolutePath(), ex);
+        } catch (IllegalAccessException ex) {
+            throw new JemmyException("Failed to create temporary properties file: " + props.getAbsolutePath(), ex);
+        } catch (IOException ex) {
+            throw new JemmyException("Failed to create temporary properties file: " + props.getAbsolutePath(), ex);
+        }
+
+    }
+
+    private void startServer() {
+        try {
+            prepareProperties();
+            ProcessBuilder pb = new ProcessBuilder("java",
+                    //"-Xrunjdwp:transport=dt_socket,suspend=y,server=y,address=8000",
+                    "-cp", System.getProperty("java.class.path"),
+                    "-D" + Environment.JEMMY_PROPERTIES_FILE_PROPERTY +
+                    "=" + props.getAbsolutePath(),
+                    RobotExecutor.class.getName(),
+                    Integer.toString(connectionPort));
+            // TODO: Improve output
+//            System.out.println("Starting server");
+//            System.out.println("Command: " + pb.command());
+//            System.out.flush();
+            pb.redirectErrorStream(true);
+            final Process p = pb.start();
+            new Thread() {
+
+                @Override
+                public void run() {
+                    BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
+                    while (true) {
+                        try {
+                            String line = br.readLine();
+                            if (line == null) {
+                                break;
+                            }
+                            System.out.println("SERVER: " + line);
+                        } catch (IOException ex) {
+                            throw new JemmyException("Exception during other JVM output processing", ex);
+                        }
+                    }
+                }
+            }.start();
+        } catch (IOException ex) {
+            throw new JemmyException("Failed to start other JVM", ex);
+        }
+    }
+
+    public void ensureConnection() {
+        ensureInited();
+        if (runInOtherJVM && !connectionEstablished) {
+            initClientConnection();
+        }
+    }
+
+    private void initClientConnection() {
+        connectionHost = (String)Environment.getEnvironment().getProperty(
+             AWTRobotInputFactory.OTHER_VM_HOST_PROPERTY, "localhost");
+        connectionPort = Integer.parseInt((String)Environment.getEnvironment()
+             .getProperty(AWTRobotInputFactory.OTHER_VM_PORT_PROPERTY,
+             "53669"));
+        try {
+            try {
+                socket = new Socket(connectionHost, connectionPort);
+            } catch (IOException ex) {
+                if ("localhost".equalsIgnoreCase(connectionHost)
+                        || "127.0.0.1".equals(connectionHost)) {
+                    // TODO Improve check for localhost
+                    startServer();
+                    Environment.getEnvironment().getTimeout("");
+                    Timeout waitTime = new Timeout("connection wait time", 5 * 60000);
+                    socket = new Waiter(waitTime).ensureState(new State<Socket>() {
+                        Exception ex;
+                        public Socket reached() {
+                            Socket socket = null;
+                            try {
+                                socket = new Socket(connectionHost, connectionPort);
+                            } catch (UnknownHostException ex1) {
+                                ex = ex1;
+                            } catch (Exception ex1) {
+                                ex = ex1;
+                            }
+                            return socket;
+                        }
+
+                        @Override
+                        public String toString() {
+                            if (ex != null) {
+                                // TODO: Provide better mechanics for exception handling
+                                Logger.getLogger(RobotExecutor.class.getName())
+                                        .log(Level.INFO, null, ex);
+                            }
+                            return "Waiting for connection to be established " +
+                                    "with other JVM (" + connectionHost
+                                    + ":" + connectionPort + ", exception: " + ex + ")";
+                        }
+                    });
+                } else {
+                    throw new JemmyException("Failed to establish socket " +
+                            "connection with other JVM (" + connectionHost
+                            + ":" + connectionPort + ")", ex);
+                }
+            }
+            outputStream = new ObjectOutputStream(socket.getOutputStream());
+            inputStream = new ObjectInputStream(socket.getInputStream());
+
+            connectionEstablished = true;
+            ready = true;
+
+            System.out.println("Connection established!");
+            setAutoDelay(autoDelay);
+        } catch (IOException ex) {
+            throw new JemmyException("Failed to establish socket connection " +
+                    "with other JVM (" + connectionHost + ":" + connectionPort
+                    + ")", ex);
+        }
+    }
+
+    public synchronized Object getProperty(String name) {
+        ensureConnection();
+        try {
+            outputStream.writeObject("getProperty");
+            outputStream.writeObject(name);
+            Object result = inputStream.readObject();
+            String response = (String)(inputStream.readObject());
+            if (!"OK".equals(response)) {
+                throw new JemmyException("Remote operation didn't succeed");
+            }
+            return result;
+        } catch (ClassNotFoundException ex) {
+            throw new JemmyException("Socket communication with other JVM failed", ex);
+        } catch (OptionalDataException ex) {
+            throw new JemmyException("Socket communication with other JVM " +
+                    "failed: OptionalDataException eof = " + ex.eof + ", " +
+                    "length = " + ex.length, ex);
+        } catch (IOException ex) {
+            throw new JemmyException("Socket communication with other JVM failed", ex);
+        }
+    }
+
+    private synchronized Object makeAnOperationRemotely(String method, Object[] params, Class[] paramClasses) {
+        ensureConnection();
+        try {
+            outputStream.writeObject("makeAnOperation");
+            outputStream.writeObject(method);
+            outputStream.writeObject(params);
+            outputStream.writeObject(paramClasses);
+            Object result;
+            String response = (String)(inputStream.readObject());
+            if ("image".equals(response)) {
+                result = PNGDecoder.decode(inputStream, false);
+            } else {
+                if (!"OK".equals(response)) {
+                    throw new JemmyException("Remote operation didn't succeed");
+                }
+                result = inputStream.readObject();
+            }
+            return result;
+        } catch (ClassNotFoundException ex) {
+            throw new JemmyException("Socket communication with other JVM failed", ex);
+        } catch (OptionalDataException ex) {
+            throw new JemmyException("Socket communication with other JVM " +
+                    "failed: OptionalDataException eof = " + ex.eof + ", " +
+                    "length = " + ex.length, ex);
+        } catch (IOException ex) {
+            throw new JemmyException("Socket communication with other JVM failed", ex);
+        }
+    }
+
+    private void server() {
+        System.out.println("Robot ready!");
+        System.out.flush();
+        ServerSocket sc;
+        connectionPort = Integer.parseInt((String)Environment.getEnvironment()
+             .getProperty(AWTRobotInputFactory.OTHER_VM_PORT_PROPERTY,
+             "53669"));
+        while(true) {
+            Thread watchdog = new Thread("RobotExecutor.server watchdog") {
+
+                @Override
+                public void run() {
+                    try {
+                        Thread.sleep(CONNECTION_TIMEOUT);
+                        System.out.println("Exiting server as there is no " +
+                                "connection for " + CONNECTION_TIMEOUT / 60000.0
+                                + " minutes");
+                        System.out.flush();
+                        System.exit(0);
+                    } catch (InterruptedException ex) {
+                        // Ignoring exception as it is okay
+                    }
+                }
+
+            };
+            watchdog.start();
+            System.out.println("Waiting for incoming connection for up to "
+                    + CONNECTION_TIMEOUT / 60000.0 + " minutes");
+            try {
+                sc = new ServerSocket(connectionPort);
+                socket = sc.accept();
+                watchdog.interrupt();
+            } catch (IOException ex) {
+                throw new JemmyException("Can't establish connection with client", ex);
+            }
+            System.out.println("Connection established!");
+            try {
+                inputStream = new ObjectInputStream(socket.getInputStream());
+                outputStream = new ObjectOutputStream(socket.getOutputStream());
+                while(true) {
+                    String command = (String)inputStream.readObject();
+                    if ("exit".equals(command)) {
+                        System.exit(0);
+                    }
+                    if ("getProperty".equals(command)) {
+                        String property = (String)inputStream.readObject();
+                        outputStream.writeObject(Environment.getEnvironment().getProperty(property));
+                        outputStream.writeObject("OK");
+                    }
+                    if ("makeAnOperation".equals(command)) {
+                        String method = (String)inputStream.readObject();
+                        Object[] params = (Object[])inputStream.readObject();
+                        Class[] paramClasses = (Class[])inputStream.readObject();
+                        Object result = makeAnOperationLocally(method, params,
+                                paramClasses);
+                        if (result instanceof BufferedImage) {
+                            outputStream.writeObject("image");
+                            BufferedImage image = BufferedImage.class.cast(result);
+                            new PNGEncoder(outputStream, PNGEncoder.COLOR_MODE)
+                                    .encode(image, false);
+                        } else {
+                            outputStream.writeObject("OK");
+                            outputStream.writeObject(result);
+                        }
+                    }
+                }
+            } catch (ClassNotFoundException ex) {
+                throw new JemmyException("Socket communication with other " +
+                        "JVM failed", ex);
+            } catch (IOException ex) {
+                Logger.getLogger(RobotExecutor.class.getName())
+                        .log(Level.SEVERE, null, ex);
+            } finally {
+                if (socket != null) {
+                    try {
+                        socket.close();
+                    } catch (IOException ex) {
+                        Logger.getLogger(RobotExecutor.class.getName()).log(
+                                Level.SEVERE, "Exception during socket closing", ex);
+                    }
+                }
+                if (sc != null) {
+                    try {
+                        sc.close();
+                    } catch (IOException ex) {
+                        Logger.getLogger(RobotExecutor.class.getName()).log(
+                                Level.SEVERE, "Exception during server socket " +
+                                "closing", ex);
+                    }
+                }
+            }
+        }
+    }
+
+    private void initRobot() {
+        // need to init Robot in dispatch thread because it hangs on Linux
+        // (see http://www.netbeans.org/issues/show_bug.cgi?id=37476)
+        if (EventQueue.isDispatchThread()) {
+            doInitRobot();
+        } else {
+            try {
+                EventQueue.invokeAndWait(new Runnable() {
+
+                    public void run() {
+                        doInitRobot();
+                    }
+                });
+            } catch (InterruptedException ex) {
+                throw new JemmyException("Failed to initialize robot", ex);
+            } catch (InvocationTargetException ex) {
+                throw new JemmyException("Failed to initialize robot", ex);
+            }
+        }
+    }
+
+    private void doInitRobot() {
+        try {
+            ClassReference robotClassReverence = new ClassReference("java.awt.Robot");
+            robotReference = new ClassReference(robotClassReverence.newInstance(null, null));
+            if (awtMap == null) {
+                awtMap = new AWTMap();
+            }
+            setAutoDelay(autoDelay);
+            ready = true;
+        } catch (InvocationTargetException e) {
+            throw (new JemmyException("Exception during java.awt.Robot accessing", e));
+        } catch (IllegalStateException e) {
+            throw (new JemmyException("Exception during java.awt.Robot accessing", e));
+        } catch (NoSuchMethodException e) {
+            throw (new JemmyException("Exception during java.awt.Robot accessing", e));
+        } catch (IllegalAccessException e) {
+            throw (new JemmyException("Exception during java.awt.Robot accessing", e));
+        } catch (ClassNotFoundException e) {
+            throw (new JemmyException("Exception during java.awt.Robot accessing", e));
+        } catch (InstantiationException e) {
+            throw (new JemmyException("Exception during java.awt.Robot accessing", e));
+        }
+    }
+
+    /**
+     * Calls <code>java.awt.Robot.waitForIdle()</code> method.
+     */
+    protected void synchronizeRobot() {
+        ensureInited();
+        if (!runInOtherJVM) {
+            // TODO: It looks like this method is rudimentary
+            if (!EventQueue.isDispatchThread()) {
+                if (robotReference == null) {
+                    initRobot();
+                }
+                try {
+                    robotReference.invokeMethod("waitForIdle", null, null);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    public void setAutoDelay(Timeout autoDelay) {
+        this.autoDelay = autoDelay;
+        if (ready) {
+            makeAnOperation("setAutoDelay", new Object[]{new Integer((int) ((autoDelay != null) ? autoDelay.getValue() : 0))}, new Class[]{Integer.TYPE});
+        }
+    }
+
+    public boolean isRunInOtherJVM() {
+        ensureInited();
+        return runInOtherJVM;
+    }
+
+    public void setRunInOtherJVM(boolean runInOtherJVM) {
+        if (inited && this.runInOtherJVM && this.connectionEstablished && !runInOtherJVM) {
+            shutdownConnection();
+        }
+        this.runInOtherJVM = runInOtherJVM;
+        inited = true;
+        ready = false;
+    }
+
+    private void shutdownConnection() {
+        try {
+            outputStream.writeObject("exit");
+            socket.close();
+            connectionEstablished = false;
+        } catch (IOException ex) {
+            throw new JemmyException("Failed to shutdown connection", ex);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/input/Version.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+/**
+ *
+ * @author shura
+ */
+public class Version extends org.jemmy.Version {
+
+    public static final Version VERSION = new Version();
+
+    /**
+     *
+     */
+    public Version() {
+        super(Version.class.getPackage().getName());
+    }
+    /**
+     *
+     * @param args
+     */
+    public static void main(String[] args) {
+        System.out.println("JemmyAWTInput version: " + VERSION.getVersion());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/input/jemmy.properties	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,3 @@
+version.major=0
+version.minor=9
+version.mini=5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/src/org/jemmy/operators/AWTScreen.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.operators;
+
+
+import java.awt.Toolkit;
+import org.jemmy.Rectangle;
+import org.jemmy.env.Environment;
+
+/**
+ *
+ * @author shura
+ */
+public class AWTScreen extends Screen {
+
+    /**
+     *
+     */
+    public AWTScreen() {
+        this(Environment.getEnvironment());
+    }
+
+    /**
+     *
+     * @param env
+     */
+    public AWTScreen(Environment env) {
+        super(env);
+    }
+
+    @Override
+    public Rectangle getScreenBounds() {
+        java.awt.Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
+        return new Rectangle(0, 0, size.width, size.height);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/TEST.ROOT	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,2 @@
+TestNG.dirs = .
+
Binary file core/JemmyAWTInput/test/org/jemmy/image/AreaChart_a.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/AreaChart_a_res.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/image/AverageDistanceImageComparatorTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import javax.imageio.ImageIO;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.AssertJUnit.assertEquals;
+
+
+/**
+ *
+ * @author KAM
+ */
+public class AverageDistanceImageComparatorTest {
+
+    public AverageDistanceImageComparatorTest() {
+    }
+
+    static final int NUMBER_OF_IMAGES = 3;
+    static BufferedImage[] images;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        images = new BufferedImage[NUMBER_OF_IMAGES];
+        for(int i = 0; i < NUMBER_OF_IMAGES; i++) {
+            images[i] = ImageIO.read(AverageDistanceImageComparatorTest.class
+                    .getResource("image" + (i + 1) + ".jpg"));
+        }
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @BeforeMethod
+    public void setUp() {
+    }
+
+    @AfterMethod
+    public void tearDown() {
+    }
+
+    /**
+     * Test of compare method, of class AverageDistanceImageComparator.
+     */
+    @Test
+    public void testCompare1() {
+        System.out.println("compare1");
+        Graphics2D g;
+
+        BufferedImage image1 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image1.createGraphics();
+        g.setColor(new Color(0.5f, 0.5f, 0.5f));
+        g.fillRect(0, 0, 10, 10);
+        g.dispose();
+
+        BufferedImage image2 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image2.createGraphics();
+        g.setColor(new Color(0.52f, 0.5f, 0.5f));
+        g.fillRect(0, 0, 10, 10);
+        g.dispose();
+
+        AverageDistanceImageComparator instance = new AverageDistanceImageComparator();
+        assertNull(instance.compare(new AWTImage(image1), new AWTImage(image2)));
+    }
+
+    /**
+     * Test of compare method, of class AverageDistanceImageComparator.
+     */
+    @Test
+    public void testCompare2() {
+        System.out.println("compare2");
+        Graphics2D g;
+
+        BufferedImage image1 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image1.createGraphics();
+        g.setColor(new Color(0.5f, 0.5f, 0.5f));
+        g.fillRect(0, 0, 10, 10);
+        g.dispose();
+
+        BufferedImage image3 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image3.createGraphics();
+        g.setColor(new Color(0.51f, 0.51f, 0.51f));
+        g.fillRect(0, 0, 10, 10);
+        g.dispose();
+
+        AverageDistanceImageComparator instance = new AverageDistanceImageComparator();
+        assertNull(instance.compare(new AWTImage(image1), new AWTImage(image3)));
+    }
+
+    /**
+     * Test of compare method, of class AverageDistanceImageComparator.
+     */
+    @Test
+    public void testCompare3Pos() {
+        System.out.println("compare3Pos");
+        Graphics2D g;
+
+        BufferedImage image1 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image1.createGraphics();
+        g.setColor(new Color(0.5f, 0.5f, 0.5f));
+        g.fillRect(0, 0, 10, 10);
+        g.dispose();
+
+        BufferedImage image3 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image3.createGraphics();
+        g.setColor(new Color(0.5f, 0.5f, 0.5f));
+        g.fillRect(0, 0, 10, 10);
+        g.setColor(new Color(0.6f, 0.6f, 0.6f));
+        g.fillRect(3, 3, 3, 3);
+        g.dispose();
+
+        AverageDistanceImageComparator instance = new AverageDistanceImageComparator();
+        assertNull(instance.compare(new AWTImage(image1), new AWTImage(image3)));
+    }
+
+    /**
+     * Test of compare method, of class AverageDistanceImageComparator.
+     */
+    @Test
+    public void testCompare3Neg() {
+        System.out.println("compare3Neg");
+        Graphics2D g;
+
+        BufferedImage image1 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image1.createGraphics();
+        g.setColor(new Color(0.5f, 0.5f, 0.5f));
+        g.fillRect(0, 0, 10, 10);
+        g.dispose();
+
+        BufferedImage image3 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image3.createGraphics();
+        g.setColor(new Color(0.5f, 0.5f, 0.5f));
+        g.fillRect(0, 0, 10, 10);
+        g.setColor(new Color(0.6f, 0.6f, 0.6f));
+        g.fillRect(3, 3, 3, 4);
+        g.dispose();
+
+        AverageDistanceImageComparator instance = new AverageDistanceImageComparator();
+        assertNotNull(instance.compare(new AWTImage(image1), new AWTImage(image3)));
+    }
+
+    /**
+     * Test of compare method, of class AverageDistanceImageComparator.
+     */
+    @Test
+    public void testCompare4() {
+        System.out.println("compare4");
+        Graphics2D g;
+
+        BufferedImage image1 = new BufferedImage(10, 11, BufferedImage.TYPE_INT_RGB);
+        BufferedImage image3 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+
+        AverageDistanceImageComparator instance = new AverageDistanceImageComparator();
+        assertNotNull(instance.compare(new AWTImage(image1), new AWTImage(image3)));
+    }
+
+    /**
+     * Test of compare method, of class AverageDistanceImageComparator.
+     */
+    @Test
+    public void testCompare5() {
+        System.out.println("compare5");
+        Graphics2D g;
+
+        BufferedImage image1 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        BufferedImage image3 = new BufferedImage(11, 10, BufferedImage.TYPE_INT_RGB);
+
+        AverageDistanceImageComparator instance = new AverageDistanceImageComparator();
+        assertNotNull(instance.compare(new AWTImage(image1), new AWTImage(image3)));
+    }
+
+    /**
+     * Test of compare method, of class AverageDistanceImageComparator.
+     */
+    @Test
+    public void testCompare6() {
+        System.out.println("compare6");
+        Graphics2D g;
+
+        BufferedImage image1 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image1.createGraphics();
+        g.setColor(new Color(0, 0, 0));
+        g.fillRect(0, 0, 10, 10);
+        g.dispose();
+
+        BufferedImage image3 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image3.createGraphics();
+        g.setColor(new Color(255, 255, 255));
+        g.fillRect(0, 0, 10, 10);
+        g.dispose();
+
+        AverageDistanceImageComparator instance = new AverageDistanceImageComparator();
+        assertNotNull(instance.compare(new AWTImage(image1), new AWTImage(image3)));
+    }
+
+    /**
+     * Test of compare method, of class AverageDistanceImageComparator.
+     */
+    @Test
+    public void testCompare7() {
+        System.out.println("compare7");
+        Graphics2D g;
+
+        BufferedImage image1 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image1.createGraphics();
+        g.setColor(new Color(0.51f, 0.51f, 0.51f));
+        g.fillRect(0, 0, 10, 5);
+        g.setColor(new Color(0.49f, 0.49f, 0.49f));
+        g.fillRect(0, 5, 10, 5);
+        g.dispose();
+
+        BufferedImage image3 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image3.createGraphics();
+        g.setColor(new Color(0.49f, 0.49f, 0.49f));
+        g.fillRect(0, 5, 10, 5);
+        g.setColor(new Color(0.51f, 0.51f, 0.51f));
+        g.fillRect(0, 0, 10, 5);
+        g.dispose();
+
+        AverageDistanceImageComparator instance = new AverageDistanceImageComparator();
+        assertNull(instance.compare(new AWTImage(image1), new AWTImage(image3)));
+    }
+
+    /**
+     * Test of compare method, of class AverageDistanceImageComparator.
+     * @throws IOException
+     */
+    @Test
+    public void testCompare() throws IOException {
+        System.out.println("compare");
+        boolean[][][] expected = {
+            // NO_RESIZE
+            {
+                { true, false, false },
+                { false, true, false },
+                { false, false, true }
+            },
+            // PROPORTIONAL_RESIZE
+            {
+                { true, true, false },
+                { true, true, false },
+                { false, false, true }
+            },
+            // ARBITRARY_RESIZE
+            {
+                { true, true, true },
+                { true, true, true },
+                { true, true, true }
+            }
+        };
+        for(int i = 0; i < NUMBER_OF_IMAGES; i++) {
+            BufferedImage image1 = images[i];
+            for(int j = i; j < NUMBER_OF_IMAGES; j++) {
+                System.out.println("Comparing " + i + " to " + j);
+                BufferedImage image2 = images[j];
+                System.out.println("\nimage " + i + " " + image1.getWidth() +
+                        "x" + image1.getHeight());
+                System.out.println("image " + j + " " + image2.getWidth() + "x"
+                        + image2.getHeight());
+                for(ResizeImageComparator.ResizeMode resizeMode :
+                        ResizeImageComparator.ResizeMode.values()) {
+
+                    System.out.println("\n " + resizeMode);
+                    AWTImage.setComparator(new ResizeImageComparator(resizeMode,
+                            new AverageDistanceImageComparator(0.024)));
+                    Image awtImage1 = new AWTImage(image1);
+                    Image awtImage2 = new AWTImage(image2);
+                    boolean expResult = expected[resizeMode.ordinal()][i][j];
+                    Image diff = awtImage1.compareTo(awtImage2);
+                    boolean result = diff == null;
+                    if (diff != null) {
+                        diff.save("diff" + i + j + resizeMode + ".png");
+                    }
+                    assertEquals("Failed comparison for image " + i + " with " +
+                            "image " + j + ", resizeMode = " + resizeMode,
+                            expResult, result);
+                }
+            }
+        }
+    }
+
+}
\ No newline at end of file
Binary file core/JemmyAWTInput/test/org/jemmy/image/CheckBox_a_start.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/CheckBox_a_start_res.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/image/EnvTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import org.jemmy.env.Environment;
+import org.jemmy.image.pixel.PixelEqualityRasterComparator;
+import org.jemmy.image.pixel.RasterComparator;
+import org.testng.annotations.Test;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+/**
+ *
+ * @author shura
+ */
+public class EnvTest {
+
+    public EnvTest() {
+    }
+
+    @Test
+    public void testRaster() {
+        Environment.getEnvironment().setProperty(RasterComparator.class, new PixelEqualityRasterComparator(1));
+        Environment.getEnvironment().setProperty(ImageCapturer.class, new AWTRobotCapturer());
+        ImageComparator comp = Environment.getEnvironment().getProperty(ImageComparator.class);
+        assertTrue(comp instanceof BufferedImageComparator);
+        assertTrue(((BufferedImageComparator)comp).getRasterComparator() instanceof PixelEqualityRasterComparator);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/image/ImageResizerTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import java.awt.image.BufferedImage;
+import org.jemmy.image.ResizeImageComparator.ResizeMode;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertNull;
+import static org.testng.AssertJUnit.assertEquals;
+
+/**
+ *
+ * @author mrkam
+ */
+public class ImageResizerTest {
+
+    public ImageResizerTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @BeforeMethod
+    public void setUp() {
+    }
+
+    @AfterMethod
+    public void tearDown() {
+    }
+
+    /**
+     * Test of default value of ResizeMode, of class ImageResizer.
+     */
+    @Test
+    public void testDefaultResizeMode() {
+        System.out.println("testDefaultResizeMode");
+        ResizeMode expResult = ResizeMode.PROPORTIONAL_RESIZE;
+        ResizeMode result = new ResizeImageComparator(null).resizeMode;
+        assertEquals(expResult, result);
+    }
+
+    /**
+     * Test of setDefaultResizeMode method, of class ImageResizer.
+     */
+    @Test
+    public void testProportionalResizeMode() {
+        System.out.println("testProportionalResizeMode");
+
+        new ResizeImageComparator(new ImageComparator() {
+
+            public Image compare(Image image1,
+                    Image image2) {
+                assertEquals(50, ((AWTImage) image2).getSize().width);
+                assertEquals(50, ((AWTImage) image2).getSize().height);
+                return null;
+            }
+
+            public String getID() {
+                return "test";
+            }
+        }).compare(new AWTImage(new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB)),
+                new AWTImage(new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB)));
+    }
+
+    /**
+     * Test of getResized method, of class ImageResizer.
+     */
+    @Test
+    public void testGetResizedNoResize() {
+        System.out.println("getResizedNoResize");
+        final AWTImage image1 =
+                new AWTImage(new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB));
+        final AWTImage image2 =
+                new AWTImage(new BufferedImage(15, 15, BufferedImage.TYPE_INT_RGB));
+
+        new ResizeImageComparator(ResizeMode.NO_RESIZE, new ImageComparator() {
+
+            public Image compare(Image im1,
+                    Image im2) {
+                assertEquals(image1, im1);
+                assertEquals(image2, im2);
+                return null;
+            }
+
+            public String getID() {
+                return "test";
+            }
+        }).compare(image1, image2);
+    }
+
+    /**
+     * Test of getResized method, of class ImageResizer.
+     */
+    @Test
+    public void testGetResizedProportional1() {
+        System.out.println("getResizedProportional1");
+        BufferedImage image1 =
+                new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        BufferedImage image2 =
+                new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        assertNull(new ResizeImageComparator(ResizeMode.PROPORTIONAL_RESIZE,
+                new StrictImageComparator()).compare(new AWTImage(image1), new AWTImage(image2)));
+    }
+
+    /**
+     * Test of getResized method, of class ImageResizer.
+     */
+    @Test
+    public void testGetResizedProportional2() {
+        System.out.println("getResizedProportional2");
+        BufferedImage image1 =
+                new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        BufferedImage image2 =
+                new BufferedImage(15, 15, BufferedImage.TYPE_INT_RGB);
+        assertNull(new ResizeImageComparator(ResizeMode.PROPORTIONAL_RESIZE,
+                new StrictImageComparator()).compare(new AWTImage(image1), new AWTImage(image2)));
+    }
+
+    /**
+     * Test of getResized method, of class ImageResizer.
+     */
+    @Test
+    public void testGetResizedProportional3() {
+        System.out.println("getResizedProportional3");
+        final AWTImage image1 =
+                new AWTImage(new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB));
+        final AWTImage image2 =
+                new AWTImage(new BufferedImage(10, 15, BufferedImage.TYPE_INT_RGB));
+        new ResizeImageComparator(ResizeMode.PROPORTIONAL_RESIZE,
+                new ImageComparator() {
+
+                    public Image compare(Image im1,
+                            Image im2) {
+                        assertEquals(image1, im1);
+                        assertEquals(image2, im2);
+                        return null;
+                    }
+
+                    public String getID() {
+                        return "test";
+                    }
+                }).compare(image1, image2);
+    }
+
+    /**
+     * Test of getResized method, of class ImageResizer.
+     */
+    @Test
+    public void testGetResizedArbitrary1() {
+        System.out.println("getResizedArbitrary1");
+        BufferedImage image1 =
+                new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        BufferedImage image2 =
+                new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        assertNull(new ResizeImageComparator(ResizeMode.ARBITRARY_RESIZE,
+                new StrictImageComparator()).compare(new AWTImage(image1), new AWTImage(image2)));
+    }
+
+    /**
+     * Test of getResized method, of class ImageResizer.
+     */
+    @Test
+    public void testGetResizedArbitrary2() {
+        System.out.println("getResizedArbitrary2");
+        BufferedImage image1 =
+                new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        BufferedImage image2 =
+                new BufferedImage(15, 15, BufferedImage.TYPE_INT_RGB);
+        assertNull(new ResizeImageComparator(ResizeMode.ARBITRARY_RESIZE,
+                new StrictImageComparator()).compare(new AWTImage(image1), new AWTImage(image2)));
+    }
+
+    /**
+     * Test of getResized method, of class ImageResizer.
+     */
+    @Test
+    public void testGetResizedArbitrary3() {
+        System.out.println("getResizedArbitrary3");
+        BufferedImage image1 =
+                new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        BufferedImage image2 =
+                new BufferedImage(10, 15, BufferedImage.TYPE_INT_RGB);
+        assertNull(new ResizeImageComparator(ResizeMode.ARBITRARY_RESIZE,
+                new StrictImageComparator()).compare(new AWTImage(image1), new AWTImage(image2)));
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/image/InitTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import java.io.IOException;
+import org.jemmy.env.Environment;
+import org.jemmy.image.pixel.MaxDistanceComparator;
+import org.jemmy.image.pixel.PixelEqualityRasterComparator;
+import org.jemmy.operators.AWTScreen;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ *
+ * @author shura
+ */
+public class InitTest {
+
+    public InitTest() {
+    }
+
+    @BeforeClass
+    public static void setup() throws InterruptedException, IOException {
+    }
+
+    @Test
+    public void testImage() {
+        Environment.getEnvironment().setProperty(ImageCapturer.class, new AWTRobotCapturer());
+        Image i = new AWTScreen().getScreenImage();
+        assertTrue(i instanceof AWTImage);
+        assertNull(i.compareTo(i));
+    }
+
+    @Test
+    public void testComparator() {
+        Environment.getEnvironment().setProperty(ImageCapturer.class, new AWTRobotCapturer());
+        ImageComparator comp = Environment.getEnvironment().getProperty(ImageComparator.class);
+        System.out.println("comp = " + comp);
+        assertTrue(comp instanceof BufferedImageComparator);
+        assertTrue(((BufferedImageComparator)comp).getRasterComparator() instanceof PixelEqualityRasterComparator);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/image/NaturalImageComparatorTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.IOException;
+import javax.imageio.ImageIO;
+
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.fail;
+import static org.testng.AssertJUnit.assertEquals;
+
+/**
+ *
+ * @author KAM
+ */
+public class NaturalImageComparatorTest {
+
+    public NaturalImageComparatorTest() {
+    }
+    static final int NUMBER_OF_IMAGES = 3;
+    static BufferedImage[] images;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        images = new BufferedImage[NUMBER_OF_IMAGES];
+        for (int i = 0; i < NUMBER_OF_IMAGES; i++) {
+            images[i] = ImageIO.read(NaturalImageComparatorTest.class.getResource("image" + (i + 1) + ".jpg"));
+        }
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @BeforeMethod
+    public void setUp() {
+    }
+
+    @AfterMethod
+    public void tearDown() {
+    }
+
+    /**
+     * Test of compare method, of class NaturalImageComparator.
+     */
+    @Test
+    public void testCompare1() {
+        System.out.println("compare1");
+        Graphics2D g;
+
+        BufferedImage image1 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image1.createGraphics();
+        g.setColor(new Color(0.5f, 0.5f, 0.5f));
+        g.fillRect(0, 0, 10, 10);
+        g.dispose();
+
+        BufferedImage image2 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image2.createGraphics();
+        g.setColor(new Color(0.52f, 0.5f, 0.5f));
+        g.fillRect(0, 0, 10, 10);
+        g.dispose();
+
+        NaturalImageComparator instance = new NaturalImageComparator();
+        assertNull(instance.compare(new AWTImage(image1), new AWTImage(image2)));
+    }
+
+    /**
+     * Test of compare method, of class NaturalImageComparator.
+     */
+    @Test
+    public void testCompare2() {
+        System.out.println("compare2");
+        Graphics2D g;
+
+        BufferedImage image1 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image1.createGraphics();
+        g.setColor(new Color(0.5f, 0.5f, 0.5f));
+        g.fillRect(0, 0, 10, 10);
+        g.dispose();
+
+        BufferedImage image3 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image3.createGraphics();
+        g.setColor(new Color(0.51f, 0.51f, 0.51f));
+        g.fillRect(0, 0, 10, 10);
+        g.dispose();
+
+        NaturalImageComparator instance = new NaturalImageComparator();
+        assertNull(instance.compare(new AWTImage(image1), new AWTImage(image3)));
+    }
+
+    /**
+     * Test of compare method, of class NaturalImageComparator.
+     */
+    @Test
+    public void testCompare3() {
+        System.out.println("compare3");
+        Graphics2D g;
+
+        BufferedImage image1 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image1.createGraphics();
+        g.setColor(new Color(0.5f, 0.5f, 0.5f));
+        g.fillRect(0, 0, 10, 10);
+        g.dispose();
+
+        BufferedImage image3 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        g = image3.createGraphics();
+        g.setColor(new Color(0.5f, 0.5f, 0.5f));
+        g.fillRect(0, 0, 10, 10);
+        g.setColor(new Color(0.53f, 0.5f, 0.5f));
+        g.fillRect(3, 3, 1, 1);
+        g.dispose();
+
+        NaturalImageComparator instance = new NaturalImageComparator();
+        assertNotNull(instance.compare(new AWTImage(image1), new AWTImage(image3)));
+    }
+
+    /**
+     * Test of compare method, of class NaturalImageComparator.
+     */
+    @Test
+    public void testCompare4() {
+        System.out.println("compare4");
+        Graphics2D g;
+
+        BufferedImage image1 = new BufferedImage(10, 11, BufferedImage.TYPE_INT_RGB);
+        BufferedImage image3 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+
+        NaturalImageComparator instance = new NaturalImageComparator();
+        assertNotNull(instance.compare(new AWTImage(image1), new AWTImage(image3)));
+    }
+
+    /**
+     * Test of compare method, of class NaturalImageComparator.
+     */
+    @Test
+    public void testCompare5() {
+        System.out.println("compare5");
+        Graphics2D g;
+
+        BufferedImage image1 = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
+        BufferedImage image3 = new BufferedImage(11, 10, BufferedImage.TYPE_INT_RGB);
+
+        NaturalImageComparator instance = new NaturalImageComparator();
+        assertNotNull(instance.compare(new AWTImage(image1), new AWTImage(image3)));
+    }
+
+    /**
+     * Test of compare method, of class NaturalImageComparator.
+     *
+     * @throws IOException
+     */
+    @Test
+    public void testCompare() throws IOException {
+        System.out.println("compare");
+        boolean[][][] expected = {
+            // NO_RESIZE
+            {
+                {true, false, false},
+                {false, true, false},
+                {false, false, true}
+            },
+            // PROPORTIONAL_RESIZE
+            {
+                {true, true, false},
+                {true, true, false},
+                {false, false, true}
+            },
+            // ARBITRARY_RESIZE
+            {
+                {true, true, true},
+                {true, true, true},
+                {true, true, true}
+            }
+        };
+        for (int i = 0; i < NUMBER_OF_IMAGES; i++) {
+            BufferedImage image1 = images[i];
+            for (int j = i; j < NUMBER_OF_IMAGES; j++) {
+                BufferedImage image2 = images[j];
+                System.out.println("\nimage " + i + " " + image1.getWidth()
+                        + "x" + image1.getHeight());
+                System.out.println("image " + j + " " + image2.getWidth() + "x"
+                        + image2.getHeight());
+                for (ResizeImageComparator.ResizeMode resizeMode :
+                        ResizeImageComparator.ResizeMode.values()) {
+
+                    System.out.println("\n " + resizeMode);
+                    AWTImage.setComparator(new ResizeImageComparator(resizeMode,
+                            new NaturalImageComparator(1.1)));
+                    Image awtImage1 = new AWTImage(image1);
+                    Image awtImage2 = new AWTImage(image2);
+                    boolean expResult = expected[resizeMode.ordinal()][i][j];
+                    Image diff = awtImage1.compareTo(awtImage2);
+                    boolean result = diff == null;
+                    if (diff != null) {
+                        diff.save("diff" + i + j + resizeMode + ".png");
+                    }
+                    assertEquals("Failed comparison for image " + i + " with "
+                            + "image " + j + ", resizeMode = " + resizeMode,
+                            expResult, result);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testFXDiff() {
+        System.out.println("fxDiff");
+        AWTImage.setComparator(new NaturalImageComparator());
+        ClasspathImageLoader cil = new ClasspathImageLoader();
+        cil.setClassLoader(this.getClass().getClassLoader());
+        cil.setRootPackage(this.getClass().getPackage());
+        Image im1 = cil.load("AreaChart_a.png");
+        Image im2 = cil.load("AreaChart_a_res.png");
+        Image diff = im1.compareTo(im2);
+        if (diff != null) {
+            diff.save("testFXDiff_1to2.png");
+            checkDiff(diff);
+        }
+        assertNotNull("Images has to be different", diff);
+
+        diff = im2.compareTo(im1);
+        if (diff != null) {
+            diff.save("testFXDiff_2to1.png");
+            checkDiff(diff);
+        }
+        assertNotNull("Images has to be different", diff);
+    }
+
+    /**
+     * http://javafx-jira.kenai.com/browse/JMY-202
+     * Jemmy produces completely black diffs in some cases
+     */
+    @Test
+    public void testFXDiff2() {
+        System.out.println("fxDiff2");
+        AWTImage.setComparator(new NaturalImageComparator());
+        ClasspathImageLoader cil = new ClasspathImageLoader();
+        cil.setClassLoader(this.getClass().getClassLoader());
+        cil.setRootPackage(this.getClass().getPackage());
+
+        BufferedImage img1 = ((AWTImage) cil.load("AreaChart_a.png")).getTheImage();
+        BufferedImage img3 = new BufferedImage(img1.getWidth(), img1.getHeight(), 3);
+        for (int x = 0; x < img1.getWidth(); x++) {
+            for (int y = 0; y < img1.getHeight(); y++) {
+                img3.setRGB(x, y, img1.getRGB(x, y));
+            }
+        }
+        Image im1 = new AWTImage(img3);
+        Image im2 = cil.load("AreaChart_a_res.png");
+        Image diff = im1.compareTo(im2);
+        if (diff != null) {
+            diff.save("testFXDiff2_1to2.png");
+            checkDiff(diff);
+        }
+        assertNotNull("Images has to be different", diff);
+
+        diff = im2.compareTo(im1);
+        if (diff != null) {
+            diff.save("testFXDiff2_2to1.png");
+            checkDiff(diff);
+        }
+        assertNotNull("Images has to be different", diff);
+    }
+
+    private void checkDiff(Image diff) {
+        BufferedImage im = ((AWTImage) diff).getTheImage();
+        int[] data = ((DataBufferInt) im.getRaster().getDataBuffer()).getData();
+        for (int d : data) {
+            if ((d & 0xffffff) != 0) {
+                System.out.println("d = " + Integer.toBinaryString(d));
+                return;
+            }
+            if (d != 0) {
+                System.out.println("d = " + Integer.toBinaryString(d));
+            }
+        }
+        fail("Diff is completely black");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/image/SaveLoadTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import java.io.*;
+import org.jemmy.Rectangle;
+import org.jemmy.control.Wrap;
+import org.jemmy.env.Environment;
+import org.jemmy.image.pixel.PNGSaver;
+import org.jemmy.operators.ScreenRectangle;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertNull;
+
+/**
+ *
+ * @author shura
+ */
+public class SaveLoadTest {
+
+    public SaveLoadTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @BeforeClass
+    public void setUp() {
+    }
+
+    @AfterClass
+    public void tearDown() {
+    }
+
+    @Test
+    public void hello() throws FileNotFoundException, IOException {
+        Wrap<?> wrap = new ScreenRectangle(Environment.getEnvironment(), new Rectangle(0, 0, 220, 220));
+        AWTImage img = (AWTImage) new AWTRobotCapturer().capture(wrap, new Rectangle(0, 0, 200, 200));
+        File imgFile = new File(System.getProperty("user.dir") + File.separator + "out.png");
+        new PNGSaver(new FileOutputStream(imgFile), PNGSaver.COLOR_MODE).encode(img);
+        AWTImage loaded = new AWTImage(new PNGImageLoader().load(new FileInputStream(imgFile)));
+        new PNGSaver(new FileOutputStream(new File(System.getProperty("user.dir") + File.separator + "loaded.png")), PNGSaver.COLOR_MODE).encode(loaded);
+        AWTImage diff = (AWTImage) img.compareTo(loaded);
+        if(diff != null) {
+            new PNGSaver(new FileOutputStream(new File(System.getProperty("user.dir") + File.separator + "diff.png")), PNGSaver.COLOR_MODE).encode(diff);
+        }
+        assertNull(diff);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/image/ScreenAreaImageTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.jemmy.Rectangle;
+import org.jemmy.TimeoutExpiredException;
+import org.jemmy.control.Wrap;
+import org.jemmy.env.Environment;
+import org.jemmy.operators.AWTScreen;
+import org.jemmy.operators.Screen;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.fail;
+
+/**
+ *
+ * @author shura
+ */
+public class ScreenAreaImageTest {
+
+    public ScreenAreaImageTest() {
+    }
+
+    static ImageLoader loader;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        Screen.setSCREEN(new AWTScreen());
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+    File tmpDump = null;
+    File tmpDumpPart = null;
+    Wrap<Object> area;
+    Frame frm;
+    Rectangle part = new Rectangle(10, 10, 80, 80);
+
+    @BeforeMethod
+    public void setUp() throws IOException, InterruptedException {
+        Environment.getEnvironment().setImageCapturer(new AWTRobotCapturer());
+        loader = new FilesystemImageLoader();
+        frm = new Frame("some frame");
+        frm.setVisible(true);
+        frm.setSize(100, 100);
+        frm.setLocation(100, 100);
+        area = new Wrap<Object>(Environment.getEnvironment(), "screen area") {
+
+            @Override
+            public Rectangle getScreenBounds() {
+                return new Rectangle(100, 100, 100, 100);
+            }
+        };
+        // Added timeout to prevent failures due to Windows Vista Visual Effects
+        Thread.sleep(1000);
+        tmpDump = File.createTempFile("screen", ".png");
+        tmpDumpPart = File.createTempFile("screenPart", ".png");
+        area.getScreenImage().save(tmpDump.getAbsolutePath());
+        area.getScreenImage(part).save(tmpDumpPart.getAbsolutePath());
+        try {
+            Thread.sleep(2000);
+        } catch (InterruptedException ex) {
+            Logger.getLogger(ScreenImageTest.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    @AfterMethod
+    public void tearDown() {
+        frm.setVisible(false);
+    }
+
+    @Test
+    public void compareFull() {
+        try {
+            area.waitImage(loader.load(tmpDump.getAbsolutePath()), null, null);
+        } catch(TimeoutExpiredException e) {
+            fail(e.getLocalizedMessage());
+            throw e;
+        }
+    }
+
+    @Test
+    public void comparePart() {
+        try {
+            area.waitImage(loader.load(tmpDumpPart.getAbsolutePath()), part, null, null);
+        } catch(TimeoutExpiredException e) {
+            fail(e.getLocalizedMessage());
+            throw e;
+        }
+    }
+
+    @Test
+    public void compareNegative() throws InterruptedException, InvocationTargetException {
+        EventQueue.invokeAndWait(new Runnable() {
+
+            public void run() {
+                frm.setVisible(false);
+            }
+        });
+        Thread.sleep(100);
+        try {
+            area.waitImage(loader.load(tmpDump.getAbsolutePath()), null, null);
+            // TODO: Test unstable. Sometimes passes
+            fail();
+        } catch(TimeoutExpiredException e) {
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/image/ScreenImageTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image;
+
+
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import org.jemmy.TimeoutExpiredException;
+import org.jemmy.control.Wrap;
+import org.jemmy.env.Environment;
+import org.jemmy.env.Timeout;
+import org.jemmy.operators.AWTScreen;
+import org.jemmy.operators.Screen;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+import static org.testng.AssertJUnit.assertEquals;
+
+/**
+ *
+ * @author shura
+ */
+public class ScreenImageTest {
+    public static final String GOLDEN = "golden.png";
+
+    public ScreenImageTest() {
+    }
+
+    static FilesystemImageLoader loader;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        Environment.getEnvironment().setImageCapturer(new AWTRobotCapturer());
+        File workdir = new File(System.getProperty("user.dir") + File.separator +
+                "build" + File.separator +
+                "test" + File.separator +
+                "results");
+        workdir.mkdirs();
+        loader = new FilesystemImageLoader();
+        loader.setImageRoot(workdir);
+        AWTImage.setImageRoot(workdir);
+        Screen.setSCREEN(new AWTScreen());
+        Screen.SCREEN.getEnvironment().setTimeout(new Timeout(Wrap.WAIT_STATE_TIMEOUT.getName(), 10000));
+        System.out.println("Saving data to " + AWTImage.getImageRoot().getAbsolutePath());
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @BeforeMethod
+    public void setUp() throws IOException {
+        Screen.SCREEN.getScreenImage().save(GOLDEN);
+    }
+
+    @AfterMethod
+    public void tearDown() {
+    }
+
+    /**
+     * This test usually fails because it compares the whole screen where
+     * changes usually happen between screenshots.
+     */
+    @Test
+    public void compareFull() {
+        try {
+            Screen.SCREEN.waitImage(loader.load(GOLDEN), "positive.png", "positive_diff.png");
+        } catch(TimeoutExpiredException e) {
+            e.printStackTrace();
+            fail("compareFull failed, see positive.png and positive_diff.png for details");
+        }
+    }
+
+    @Test
+    public void compareNegative() throws InterruptedException, InvocationTargetException {
+        final Frame frm = new Frame("some frame");
+        EventQueue.invokeAndWait(new Runnable() {
+
+            public void run() {
+                frm.setSize(100, 100);
+                frm.setVisible(true);
+            }
+        });
+        //note - if you will be running test from netbeans you would also get a difference
+        //from JUnit execution progress. Which is fine :)
+        try {
+            Screen.SCREEN.waitImage(loader.load(GOLDEN), "negative.png", "negative_diff.png");
+            fail("compareFull failed, see negative.png and negative_diff.png for details");
+        } catch(TimeoutExpiredException e) {
+        }
+        frm.setVisible(false);
+        assertTrue(new File(AWTImage.getImageRoot(), "negative_diff.png").exists());
+        AWTImage res = (AWTImage) loader.load("negative.png");
+        AWTImage diff = (AWTImage) loader.load("negative_diff.png");
+        assertEquals(res.getTheImage().getWidth(), diff.getTheImage().getWidth());
+        assertEquals(res.getTheImage().getHeight(), diff.getTheImage().getHeight());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/image/comparator/ComparatorTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.image.comparator;
+
+import java.awt.Color;
+import java.io.File;
+import java.awt.image.BufferedImage;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import org.jemmy.image.*;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+
+/**
+ *
+ * @author shura
+ */
+public class ComparatorTest {
+
+    public ComparatorTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @BeforeMethod
+    public void setUp() {
+    }
+
+    @AfterMethod
+    public void tearDown() {
+    }
+
+    // TODO add test methods here.
+    // The methods must be annotated with annotation @Test. For example:
+    //
+    @Test
+    public void average() {
+        BufferedImage golden =
+                PNGDecoder.decode(getClass().getClassLoader().
+                getResourceAsStream("org/jemmy/image/comparator/golden-averagedistance.png"), true);
+        BufferedImage actual =
+                PNGDecoder.decode(getClass().getClassLoader().
+                getResourceAsStream("org/jemmy/image/comparator/actual-averagedistance.png"), true);
+        assertNotNull(new AverageDistanceImageComparator(.001).compare(new AWTImage(golden), new AWTImage(actual)));
+    }
+
+    @Test
+    public void strict() {
+        BufferedImage golden =
+                PNGDecoder.decode(getClass().getClassLoader().
+                getResourceAsStream("org/jemmy/image/comparator/golden-strict.png"), true);
+        BufferedImage actual =
+                PNGDecoder.decode(getClass().getClassLoader().
+                getResourceAsStream("org/jemmy/image/comparator/actual-strict.png"), true);
+        assertNotNull(new StrictImageComparator().compare(new AWTImage(golden), new AWTImage(actual)));
+    }
+
+    @Test
+    public void strict_hyperlink() throws FileNotFoundException, IOException {
+        BufferedImage golden =
+                PNGDecoder.decode(getClass().getClassLoader().
+                getResourceAsStream("org/jemmy/image/comparator/golden-hyperlink.png"), true);
+        BufferedImage actual =
+                PNGDecoder.decode(getClass().getClassLoader().
+                getResourceAsStream("org/jemmy/image/comparator/actual-hyperlink.png"), true);
+        BufferedImage diff = ((AWTImage)new StrictImageComparator().compare(new AWTImage(golden), new AWTImage(actual))).getTheImage();
+        //assertNull(diff);
+        //barbashov sucks!
+        //he submits similar images
+        new PNGEncoder(new File("/tmp/aaa.png")).encode(diff);
+        assertNotNull(diff);
+        for (int x = 0; x < diff.getWidth(); x++) {
+            for (int y = 0; y < diff.getHeight(); y++) {
+                /*
+                if(new Color(golden.getRGB(x, y)).getAlpha() != 255) {
+                    System.out.println("Haha!");
+                }
+                if(new Color(actual.getRGB(x, y)).getAlpha() != 255) {
+                    System.out.println("Haha!");
+                }
+                 *
+                 */
+                Color color = new Color(diff.getRGB(x, y));
+                if (color.getRed() != 0 || color.getGreen() != 0 || color.getBlue() != 0) {
+                    return;
+                }
+            }
+        }
+        fail("There got to be non black pixels.");
+    }
+}
Binary file core/JemmyAWTInput/test/org/jemmy/image/comparator/actual-averagedistance.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/comparator/actual-hyperlink.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/comparator/actual-strict.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/comparator/golden-averagedistance.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/comparator/golden-hyperlink.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/comparator/golden-strict.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/image1.jpg has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/image2.jpg has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/image3.jpg has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/test_image.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/tool/data/golden/fail.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/tool/data/golden/mess.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/tool/data/golden/missed.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/tool/data/golden/pass.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/tool/data/results/fail.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/tool/data/results/mess.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/tool/data/results/new.png has changed
Binary file core/JemmyAWTInput/test/org/jemmy/image/tool/data/results/pass.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/input/AWTMapTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+
+import java.awt.event.MouseEvent;
+import java.util.Arrays;
+import java.util.HashSet;
+import org.jemmy.JemmyException;
+import org.jemmy.interfaces.Keyboard.KeyboardButton;
+import org.jemmy.interfaces.Modifier;
+import org.jemmy.interfaces.Mouse.MouseButton;
+import static org.jemmy.interfaces.Keyboard.KeyboardButtons.*;
+import org.jemmy.interfaces.Keyboard.KeyboardModifiers;
+import org.jemmy.interfaces.Mouse.MouseButtons;
+import org.jemmy.interfaces.Mouse.MouseModifiers;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static java.awt.event.KeyEvent.*;
+import static org.testng.Assert.fail;
+import static org.testng.AssertJUnit.assertEquals;
+
+
+/**
+ *
+ * @author Alexander Kouznetsov <mrkam@mail.ru>
+ */
+public class AWTMapTest {
+
+    public AWTMapTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @BeforeMethod
+    public void setUp() {
+    }
+
+    @AfterMethod
+    public void tearDown() {
+    }
+
+    private final KeyboardButton [] jemmyKeyboardButtons = new KeyboardButton [] { A, ADD, D5, F5, NUMPAD5, OPEN_BRACKET };
+    private final int [] awtKeyboardButtons = new int [] { VK_A, VK_ADD, VK_5, VK_F5, VK_NUMPAD5, VK_OPEN_BRACKET };
+    /**
+     * Test of convert method, of class AWTMap.
+     */
+    @Test
+    public void testConvert_KeyboardKeyboardButton() {
+        System.out.println("convert");
+        for(int i = 0; i < jemmyKeyboardButtons.length; i++) {
+            int result = new AWTMap().convert(jemmyKeyboardButtons[i]);
+            assertEquals("Failed check for " + jemmyKeyboardButtons[i], awtKeyboardButtons[i], result);
+        }
+    }
+
+    private final Modifier[][] jemmyModifierCombinations = new Modifier [][] {
+            { KeyboardModifiers.SHIFT_DOWN_MASK },
+            { KeyboardModifiers.CTRL_DOWN_MASK, KeyboardModifiers.SHIFT_DOWN_MASK },
+            { KeyboardModifiers.CTRL_DOWN_MASK, KeyboardModifiers.ALT_DOWN_MASK, KeyboardModifiers.SHIFT_DOWN_MASK },
+            { MouseModifiers.BUTTON1_DOWN_MASK },
+            { MouseModifiers.BUTTON1_DOWN_MASK, KeyboardModifiers.SHIFT_DOWN_MASK },
+    };
+    private final int[] awtModifierCombinations = new int [] {
+            SHIFT_DOWN_MASK,
+            CTRL_DOWN_MASK | SHIFT_DOWN_MASK,
+            CTRL_DOWN_MASK | ALT_DOWN_MASK | SHIFT_DOWN_MASK,
+            BUTTON1_DOWN_MASK,
+            BUTTON1_DOWN_MASK | SHIFT_DOWN_MASK
+    };
+
+    /**
+     * Test of convert method, of class AWTMap.
+     */
+    @Test
+    public void testConvert_ModifierArr() {
+        System.out.println("convert");
+        for(int i = 0; i < jemmyModifierCombinations.length; i++) {
+            Modifier[] modifiers = jemmyModifierCombinations[i];
+            int expResult = awtModifierCombinations[i];
+            int result = new AWTMap().convert(modifiers);
+            assertEquals("Failed check for " + Arrays.toString(modifiers), expResult, result);
+        }
+    }
+
+    private final MouseButton [] jemmyMouseButtons = new MouseButton [] { MouseButtons.BUTTON1 };
+    private final int [] awtMouseButtons = new int [] { BUTTON1_MASK };
+
+    /**
+     * Test of convert method, of class AWTMap.
+     */
+    @Test
+    public void testConvert_MouseMouseButton() {
+        System.out.println("convert");
+        for(int i = 0; i < jemmyMouseButtons.length; i++) {
+            MouseButton button = jemmyMouseButtons[i];
+            int expResult = awtMouseButtons[i];
+            int result = new AWTMap().convert(button);
+            assertEquals("Failed check for " + button, expResult, result);
+        }
+    }
+
+    /**
+     * Test of convertKeyboardButton method, of class AWTMap.
+     */
+    @Test
+    public void testConvertKeyboardButton() {
+        System.out.println("convertKeyboardButton");
+        for (int i = 0; i < awtKeyboardButtons.length; i++) {
+            int key = awtKeyboardButtons[i];
+            KeyboardButton expResult = jemmyKeyboardButtons[i];
+            KeyboardButton result = new AWTMap().convertKeyboardButton(key);
+            assertEquals("Failed check for " + expResult, expResult, result);
+        }
+    }
+
+    /**
+     * Test of convertModifiers method, of class AWTMap.
+     */
+    @Test
+    public void testConvertModifiers() {
+        System.out.println("convertModifiers");
+        for (int i = 0; i < awtModifierCombinations.length; i ++) {
+            int modifiers = awtModifierCombinations[i];
+            Modifier[] expResult = jemmyModifierCombinations[i];
+            Modifier[] result = new AWTMap().convertModifiers(modifiers);
+            assertEquals("Failed check with " + Arrays.toString(expResult), new HashSet<Modifier>(Arrays.asList(expResult)), new HashSet<Modifier>(Arrays.asList(result)));
+        }
+    }
+
+    /**
+     * Test of convertMouseButton method, of class AWTMap.
+     */
+    @Test
+    public void testConvertMouseButton() {
+        System.out.println("convertMouseButton");
+        for (int i = 0; i < awtMouseButtons.length; i++) {
+            int button = awtMouseButtons[i];
+            MouseButton expResult = jemmyMouseButtons[i];
+            MouseButton result = new AWTMap().convertMouseButton(button);
+            assertEquals("Check failed with " + expResult, expResult, result);
+        }
+    }
+
+    @Test
+    public void testGetException() {
+        try {
+            new AWTMap().convert(new KeyboardButton() {});
+            fail("No JemmyException");
+        } catch(JemmyException e) {
+        } catch(Exception e) {
+            fail("Not a JemmyException");
+        }
+        try {
+            new AWTMap().convert(new MouseButton() {});
+            fail("No JemmyException");
+        } catch(JemmyException e) {
+        } catch(Exception e) {
+            fail("Not a JemmyException");
+        }
+        try {
+            new AWTMap().convert(new Modifier() {}, new Modifier() {});
+            fail("No JemmyException");
+        } catch(JemmyException e) {
+        } catch(Exception e) {
+            fail("Not a JemmyException");
+        }
+        try {
+            new AWTMap().convertKeyboardButton(-1);
+            fail("No JemmyException");
+        } catch(JemmyException e) {
+        } catch(Exception e) {
+            fail("Not a JemmyException");
+        }
+        try {
+            new AWTMap().convertMouseButton(-1);
+            fail("No JemmyException");
+        } catch(JemmyException e) {
+        } catch(Exception e) {
+            fail("Not a JemmyException");
+        }
+    }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/input/RobotDriver2Test.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,626 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+
+import java.awt.AWTException;
+import java.awt.BorderLayout;
+import java.awt.Button;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.Robot;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Queue;
+import java.util.Random;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import org.jemmy.Point;
+import org.jemmy.control.Wrap;
+import org.jemmy.env.Environment;
+import org.jemmy.env.Timeout;
+import org.jemmy.image.AWTImage;
+import org.jemmy.interfaces.Keyboard.KeyboardButton;
+import org.jemmy.interfaces.Modifier;
+import org.jemmy.interfaces.Mouse.MouseButton;
+import org.jemmy.interfaces.Mouse.MouseButtons;
+import org.jemmy.timing.State;
+import org.jemmy.timing.Waiter;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+
+/**
+ *
+ * @author Alexander Kouznetsov <mrkam@mail.ru>
+ */
+public class RobotDriver2Test {
+
+    final static Timeout TIMEOUT = new Timeout("Wait for state to be reached", 10000);
+    final static Timeout DELTA_TIMEOUT = new Timeout("Delta timeout of wait for state to be reached", 1000);
+
+    public RobotDriver2Test() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        File workdir = new File(System.getProperty("user.dir") + File.separator +
+                "build" + File.separator +
+                "test" + File.separator +
+                "results");
+        workdir.mkdirs();
+        AWTImage.setImageRoot(workdir);
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+        RobotDriver.exit();
+    }
+
+    Frame frm;
+    Button btn;
+    Wrap<Object> area;
+    RobotDriver instance;
+    Robot rb;
+    Queue<InputEvent> queue = new ConcurrentLinkedQueue<InputEvent>();
+    static Random r = new Random();
+
+    @BeforeMethod
+    public void setUp() throws InterruptedException, AWTException, InvocationTargetException {
+        EventQueue.invokeAndWait(new Runnable() {
+
+            public void run() {
+                frm = new Frame("some frame");
+                frm.setSize(100, 100);
+                frm.setLocation(100, 100);
+                btn = new Button("some button");
+                MouseAdapter m = new MouseAdapter() {
+
+                    @Override
+                    public void mousePressed(MouseEvent e) {
+                        System.out.println("mousePressed event triggered: " + e);
+                        System.out.flush();
+                        queue.add(e);
+                    }
+
+                    @Override
+                    public void mouseReleased(MouseEvent e) {
+                        System.out.println("mouseReleased event triggered: " + e);
+                        System.out.flush();
+                        queue.add(e);
+                    }
+
+                    @Override
+                    public void mouseMoved(MouseEvent e) {
+                        System.out.println("mouseMoved event triggered: " + e);
+                        System.out.flush();
+                        queue.add(e);
+                    }
+
+                    @Override
+                    public void mouseClicked(MouseEvent e) {
+                        System.out.println("mouseClicked event triggered: " + e);
+                        System.out.flush();
+                        queue.add(e);
+                    }
+
+                    @Override
+                    public void mouseDragged(MouseEvent e) {
+                        System.out.println("mouseDragged event triggered: " + e);
+                        System.out.flush();
+                        queue.add(e);
+                    }
+
+                };
+                btn.addMouseListener(m);
+                btn.addMouseMotionListener(m);
+                btn.addKeyListener(new KeyAdapter() {
+
+                    @Override
+                    public void keyPressed(KeyEvent e) {
+                        System.out.println("keyPressed event triggered: " + e);
+                        System.out.flush();
+                        queue.add(e);
+                    }
+
+                    @Override
+                    public void keyReleased(KeyEvent e) {
+                        System.out.println("keyReleased event triggered: " + e);
+                        System.out.flush();
+                        queue.add(e);
+                    }
+
+                });
+                frm.add(btn, BorderLayout.SOUTH);
+                frm.doLayout();
+                instance = new RobotDriver(Environment.getEnvironment());
+                frm.setVisible(true);
+                btn.requestFocusInWindow();
+            }
+        });
+
+        rb = new Robot();
+        rb.waitForIdle();
+
+        RobotExecutor.get().setRunInOtherJVM(true);
+
+    }
+
+    @AfterMethod
+    public void tearDown() throws InterruptedException, InvocationTargetException {
+        EventQueue.invokeAndWait(new Runnable() {
+
+            public void run() {
+                frm.setVisible(false);
+            }
+        });
+    }
+
+//    /**
+//     * Test of createScreenCapture method, of class RobotDriver.
+//     */
+//    @Test
+//    public void testCreateScreenCaptureLocally() throws AWTException, InterruptedException {
+//        System.out.println("testCreateScreenCaptureLocally");
+//        Thread.sleep(3000);
+//        Rectangle screenRect = new Rectangle(100, 100, 100, 100);
+//        RobotExecutor.get().setRunInOtherJVM(false);
+//        Image expResult = new AWTImage(new Robot().createScreenCapture(new java.awt.Rectangle(100, 100, 100, 100)));
+//        Image result = RobotDriver.createScreenCapture(screenRect);
+//        Image diff = expResult.compareTo(result);
+//        if (diff != null) {
+//            diff.save("testCreateScreenCaptureLocally.png");
+//            fail();
+//        }
+//    }
+//
+//    /**
+//     * Test of createScreenCapture method, of class RobotDriver.
+//     */
+//    @Test
+//    public void testCreateScreenCaptureRemotely() throws AWTException, InterruptedException {
+//        System.out.println("testCreateScreenCaptureRemotely");
+//        Thread.sleep(3000);
+//        Rectangle screenRect = new Rectangle(100, 100, 100, 100);
+//        RobotExecutor.get().setRunInOtherJVM(true);
+//        Image expResult = new AWTImage(new Robot().createScreenCapture(new java.awt.Rectangle(100, 100, 100, 100)));
+//        Image result = RobotDriver.createScreenCapture(screenRect);
+//        RobotDriver.createScreenCapture(screenRect);
+//        Image diff = expResult.compareTo(result);
+//        if (diff != null) {
+//            diff.save("testCreateScreenCaptureRemotely.png");
+//            fail();
+//        }
+//    }
+//
+//    /**
+//     * Test of createScreenCapture method, of class RobotDriver.
+//     */
+//    @Test
+//    public void testCreateScreenCaptureRemotely2() throws AWTException, InterruptedException {
+//        System.out.println("testCreateScreenCaptureRemotely2");
+//        Thread.sleep(3000);
+//        Rectangle screenRect = new Rectangle(100, 100, 100, 100);
+//        RobotExecutor.get().setRunInOtherJVM(true);
+//        Image expResult = new AWTImage(new Robot().createScreenCapture(new java.awt.Rectangle(100, 100, 100, 100)));
+//        Image result = RobotDriver.createScreenCapture(screenRect);
+//        Image diff = expResult.compareTo(result);
+//        if (diff != null) {
+//            diff.save("testCreateScreenCaptureRemotely2.png");
+//            fail();
+//        }
+//    }
+
+    /**
+     * Test of all RobotDriver methods invoked multiple times
+     * @throws InterruptedException
+     */
+//    @Test
+    public void testAll() throws InterruptedException {
+        for(int i = 0; i < 10; i++) {
+            testPressMouse();
+        }
+    }
+
+    public void test() throws InterruptedException {
+//        testPressMouse();
+        testPressKey();
+    }
+
+    @Test
+    public void test0() throws InterruptedException {
+        test();
+    }
+
+    @Test
+    public void test1() throws InterruptedException {
+        test();
+    }
+
+    @Test
+    public void test2() throws InterruptedException {
+        test();
+    }
+
+    @Test
+    public void test3() throws InterruptedException {
+        test();
+    }
+
+    @Test
+    public void test4() throws InterruptedException {
+        test();
+    }
+
+    @Test
+    public void test5() throws InterruptedException {
+        test();
+    }
+
+    @Test
+    public void test6() throws InterruptedException {
+        test();
+    }
+
+    @Test
+    public void test7() throws InterruptedException {
+        test();
+    }
+
+    @Test
+    public void test8() throws InterruptedException {
+        test();
+    }
+
+    @Test
+    public void test9() throws InterruptedException {
+        test();
+    }
+
+    protected static final int[] MODIFIERS = new int[] {
+            InputEvent.SHIFT_DOWN_MASK,
+            InputEvent.CTRL_DOWN_MASK,
+            InputEvent.ALT_DOWN_MASK,
+            InputEvent.SHIFT_MASK,
+            InputEvent.CTRL_MASK,
+            InputEvent.ALT_MASK,
+//            MouseEvent.ALT_GRAPH_DOWN_MASK,
+//            MouseEvent.META_DOWN_MASK
+        };
+
+    public static int getModifiers() {
+        int modifiersMask = r.nextInt(1 << MODIFIERS.length/2);
+        int m = 0;
+        System.out.print("Modifiers:");
+        for (int i = 0; i < MODIFIERS.length/2; i++) {
+            if ((modifiersMask & (1 << i)) != 0 ) {
+                m |= MODIFIERS[i];
+                System.out.print(" " + i);
+            }
+        }
+        System.out.println("");
+        return m;
+    }
+
+    protected static HashMap<Integer, Integer> normalizeMap =  new HashMap<Integer, Integer>();
+    static {
+        normalizeMap.put(InputEvent.SHIFT_MASK,InputEvent.SHIFT_DOWN_MASK);
+        normalizeMap.put(InputEvent.CTRL_MASK,InputEvent.CTRL_DOWN_MASK);
+        normalizeMap.put(InputEvent.ALT_MASK,InputEvent.ALT_DOWN_MASK);
+        normalizeMap.put(InputEvent.META_MASK,InputEvent.META_DOWN_MASK);
+    }
+
+    protected static HashSet<Integer> normalize(HashSet<Integer> modifiers) {
+        HashSet<Integer> normalized = new HashSet<Integer>();
+        for (Integer mod : modifiers) {
+            Integer n = normalizeMap.get(mod);
+            if (n != null) {
+                normalized.add(n);
+            } else {
+                normalized.add(mod);
+            }
+        }
+        return normalized;
+    }
+
+    protected static HashSet<Integer> modifiers2Set(int mods) {
+        HashSet<Integer> set = new HashSet<Integer>();
+        for (int i = 0; i < MODIFIERS.length; i++) {
+            if ((mods & MODIFIERS[i]) > 0) {
+                set.add(MODIFIERS[i]);
+            }
+        }
+        return set;
+   }
+
+    protected static boolean compareModifiers(int m1, int m2) {
+        return normalize(modifiers2Set(m1)).equals(normalize(modifiers2Set(m2)));
+    }
+    /**
+     * Test of pressMouse method, of class RobotDriver.
+     */
+    public void testPressMouse() throws InterruptedException {
+        System.out.println("pressMouse");
+        Thread.sleep(3000);
+        final int[] MOUSE_BUTTONS_1 = new int[] {
+            MouseEvent.BUTTON1_MASK,
+            MouseEvent.BUTTON2_MASK,
+            MouseEvent.BUTTON3_MASK
+        };
+        final int[] MOUSE_BUTTONS_2 = new int[] {
+            MouseEvent.BUTTON1,
+            MouseEvent.BUTTON2,
+            MouseEvent.BUTTON3
+        };
+        int bIndex = r.nextInt(MOUSE_BUTTONS_1.length);
+        final int mouseButton1 = MOUSE_BUTTONS_1[bIndex];
+        final int mouseButton2 = MOUSE_BUTTONS_2[bIndex];
+        System.out.print("Button: " + bIndex + " Modifier:");
+        final int modifiers = getModifiers();
+        queue.clear();
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        System.out.println("Pressing mouse");
+        instance.moveMouse(new Point(locationOnScreen.x + btn.getWidth() / 2, locationOnScreen.y + btn.getHeight() / 2));
+        AWTMap map = new AWTMap();
+        MouseButton button = map.convertMouseButton(mouseButton1);
+        Modifier[] converted_modifiers = map.convertModifiers(modifiers);
+        instance.pressMouse(button, converted_modifiers);
+        instance.releaseMouse(button, converted_modifiers);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                while(true) {
+                    InputEvent e = queue.poll();
+                    if (e != null) {
+                        if (e instanceof MouseEvent) {
+                            MouseEvent me = (MouseEvent) e;
+                            if (me.getID() == MouseEvent.MOUSE_PRESSED && me.getButton() == mouseButton2 && (compareModifiers(me.getModifiers(), modifiers))) {
+                                return true;
+                            }
+                            if (me.getID() == MouseEvent.MOUSE_PRESSED) {
+                                System.out.println("Wrong combination of button and modifiers triggered:");
+                                System.out.println("me.getModifiers() = " + Integer.toString(me.getModifiers(), 2)  + ", modifiers = " + Integer.toString(modifiers, 2));
+                                System.out.println("expected: " + new MouseEvent(
+                                        me.getComponent(), MouseEvent.MOUSE_PRESSED,
+                                        me.getWhen(), modifiers, me.getX(), me.getY(), me.getClickCount(), me.isPopupTrigger(), mouseButton2));
+                                System.out.println("     got: " + me);
+                            }
+                        }
+                    } else {
+                        break;
+                    }
+                }
+                return null;
+            }
+
+        });
+
+        System.out.println("PASSED");
+
+    }
+
+//    /**
+//     * Test of releaseMouse method, of class RobotDriver.
+//     */
+//    @Test
+//    public void testReleaseMouse() throws InterruptedException {
+//        System.out.println("releaseMouse");
+//        Thread.sleep(3000);
+//        int mouseButton = MouseEvent.BUTTON2_MASK;
+//        int modifiers = MouseEvent.CTRL_DOWN_MASK;
+//        mouseReleased = false;
+//        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+//        instance.moveMouse(new Point(locationOnScreen.x + btn.getWidth() / 2, locationOnScreen.y + btn.getHeight() / 2));
+//        System.out.println("Pressing mouse");
+//        instance.pressMouse(mouseButton, modifiers);
+//        System.out.println("Releasing mouse");
+//        instance.releaseMouse(mouseButton, modifiers);
+//
+//        rb.waitForIdle();
+//        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+//
+//            public Boolean reached() {
+//                return mouseReleased ? true: null;
+//            }
+//
+//        });
+//        assertTrue(mouseReleased);
+//    }
+//
+//    /**
+//     * Test of moveMouse method, of class RobotDriver.
+//     */
+//    @Test
+//    public void testMoveMouse() throws InterruptedException {
+//        System.out.println("moveMouse");
+//        Thread.sleep(3000);
+//        mouseMoved = false;
+//        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+//        System.out.println("Moving mouse");
+//        Point startPoint = new Point(locationOnScreen.x, locationOnScreen.y);
+//        Point endPoint = new Point(locationOnScreen.x + btn.getWidth(), locationOnScreen.y + btn.getHeight());
+//        double steps = 5; //Math.max(btn.getWidth(), btn.getHeight());
+//        double dx = (endPoint.x - startPoint.x) / steps;
+//        double dy = (endPoint.y - startPoint.y) / steps;
+//        for(int i = 0; i < steps; i++) {
+//            Point point = new Point(startPoint.x + dx * i, startPoint.y + dy * i);
+//            instance.moveMouse(point);
+//            Thread.sleep(100);
+//        }
+//
+//        rb.waitForIdle();
+//        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+//
+//            public Boolean reached() {
+//                return mouseMoved ? true: null;
+//            }
+//
+//        });
+//        assertTrue(mouseMoved);
+//    }
+//
+//    /**
+//     * Test of clickMouse method, of class RobotDriver.
+//     */
+//    @Test
+//    public void testClickMouse() throws InterruptedException {
+//        System.out.println("clickMouse");
+//        Thread.sleep(3000);
+//        mouseClicked = false;
+//        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+//        Point point = new Point(locationOnScreen.x + btn.getWidth() / 2, locationOnScreen.y + btn.getHeight() / 2);
+//        int clickCount = 1;
+//        int mouseButton = MouseEvent.BUTTON3_MASK;
+//        int modifiers = InputEvent.ALT_DOWN_MASK;
+//        Timeout mouseClick = new Timeout("mouseClick", 100);
+//        System.out.println("Clicking mouse");
+//        instance.clickMouse(point, clickCount, mouseButton, modifiers, mouseClick);
+//
+//        rb.waitForIdle();
+//        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+//
+//            public Boolean reached() {
+//                return mouseClicked ? true: null;
+//            }
+//
+//        });
+//        assertTrue(mouseClicked);
+//    }
+//
+//    /**
+//     * Test of dragNDrop method, of class RobotDriver.
+//     */
+//    @Test
+//    public void testDragNDrop() throws InterruptedException {
+//        System.out.println("dragNDrop");
+//        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+//        Point startPoint = new Point(locationOnScreen.x + btn.getWidth() / 2, locationOnScreen.y + btn.getHeight() / 2);
+//        Point endPoint = new Point(frm.getLocationOnScreen().x + frm.getWidth() / 2, frm.getLocationOnScreen().y + frm.getHeight() / 2);
+//        int mouseButton = MouseEvent.BUTTON2_MASK;
+//        int modifiers = 0;
+//        Timeout before = new Timeout("before", 500);
+//        Timeout after = new Timeout("after", 500);
+//        mouseDragged = false;
+//        instance.dragNDrop(startPoint, endPoint, mouseButton, modifiers, before, after);
+//
+//        rb.waitForIdle();
+//        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+//
+//            public Boolean reached() {
+//                return mouseDragged ? true: null;
+//            }
+//
+//        });
+//        assertTrue(mouseDragged);
+//    }
+
+    /**
+     * Test of pressKey method, of class RobotDriver.
+     */
+    public void testPressKey() throws InterruptedException {
+        System.out.println("pressKey");
+        Thread.sleep(3000);
+
+        final int keyCode = KeyEvent.VK_A;
+        final int modifiers = getModifiers();
+        AWTMap map = new AWTMap();
+        KeyboardButton button = map.convertKeyboardButton(keyCode);
+        Modifier[] converted_modifiers = map.convertModifiers(modifiers);
+
+        queue.clear();
+
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        instance.clickMouse(new Point(locationOnScreen.x + btn.getWidth() / 2, locationOnScreen.y + btn.getHeight() / 2), 1,
+                MouseButtons.BUTTON1, DELTA_TIMEOUT);
+
+        rb.waitForIdle();
+
+        instance.pressKey(button, converted_modifiers);
+        instance.releaseKey(button, converted_modifiers);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                while(true) {
+                    InputEvent e = queue.poll();
+                    if (e != null) {
+                        if (e instanceof KeyEvent) {
+                            KeyEvent ke = (KeyEvent) e;
+                            if (ke.getID() == KeyEvent.KEY_PRESSED && ke.getKeyCode() == keyCode && compareModifiers(ke.getModifiers(), modifiers)) {
+                                return true;
+                            }
+                            if (ke.getID() == KeyEvent.KEY_PRESSED) {
+                                System.out.println("Wrong combination of button and modifiers triggered:");
+                                System.out.println("ke.getModifiers() = " + Integer.toString(ke.getModifiers(), 2)  + ", modifiers = " + Integer.toString(modifiers, 2));
+                                System.out.println("expected: " + new KeyEvent(ke.getComponent(), KeyEvent.KEY_PRESSED, ke.getWhen(), modifiers, keyCode, KeyEvent.CHAR_UNDEFINED));
+                                System.out.println("     got: " + ke);
+                            }
+                        }
+                    } else {
+                        break;
+                    }
+                }
+                return null;
+            }
+
+        });
+
+        System.out.println("PASSED");
+    }
+
+//    /**
+//     * Test of releaseKey method, of class RobotDriver.
+//     */
+//    @Test
+//    public void testReleaseKey() throws InterruptedException {
+//        System.out.println("releaseKey");
+//        int keyCode = KeyEvent.VK_B;
+//        int modifiers = 0;
+//        keyReleased = false;
+//        instance.pressKey(keyCode, modifiers);
+//        instance.releaseKey(keyCode, modifiers);
+//
+//        rb.waitForIdle();
+//        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+//
+//            public Boolean reached() {
+//                return keyReleased ? true: null;
+//            }
+//
+//        });
+//        assertTrue(keyReleased);
+//    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/input/RobotDriverTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+
+import java.awt.AWTException;
+import java.awt.BorderLayout;
+import java.awt.Button;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.Robot;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import org.jemmy.Point;
+import org.jemmy.control.Wrap;
+import org.jemmy.env.Environment;
+import org.jemmy.env.Timeout;
+import org.jemmy.image.AWTImage;
+import org.jemmy.interfaces.Keyboard;
+import org.jemmy.interfaces.Keyboard.KeyboardButton;
+import org.jemmy.interfaces.Keyboard.KeyboardButtons;
+import org.jemmy.interfaces.Keyboard.KeyboardModifiers;
+import org.jemmy.interfaces.Modifier;
+import org.jemmy.interfaces.Mouse;
+import org.jemmy.interfaces.Mouse.MouseButton;
+import org.jemmy.interfaces.Mouse.MouseButtons;
+import org.jemmy.timing.State;
+import org.jemmy.timing.Waiter;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+
+/**
+ *
+ * @author Alexander Kouznetsov <mrkam@mail.ru>
+ */
+public class RobotDriverTest {
+
+    final static Timeout TIMEOUT = new Timeout("Wait for state to be reached", 10000);
+    final static Timeout DELTA_TIMEOUT = new Timeout("Delta timeout of wait for state to be reached", 1000);
+
+    public RobotDriverTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        File workdir = new File(System.getProperty("user.dir") + File.separator +
+                "build" + File.separator +
+                "test" + File.separator +
+                "results");
+        workdir.mkdirs();
+        AWTImage.setImageRoot(workdir);
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+        RobotDriver.exit();
+    }
+
+    Frame frm;
+    Button btn;
+    Wrap<Object> area;
+    private volatile boolean mousePressed;
+    private volatile boolean mouseReleased;
+    private volatile boolean mouseMoved;
+    private volatile boolean mouseClicked;
+    private volatile boolean mouseDragged;
+    private volatile boolean keyPressed;
+    private volatile boolean keyReleased;
+    RobotDriver instance;
+    Robot rb;
+
+    @BeforeMethod
+    public void setUp() throws InterruptedException, AWTException, InvocationTargetException {
+        EventQueue.invokeAndWait(new Runnable() {
+
+            public void run() {
+                frm = new Frame("some frame");
+                frm.setSize(100, 100);
+                frm.setLocation(100, 100);
+                btn = new Button("some button");
+                MouseAdapter m = new MouseAdapter() {
+
+                    @Override
+                    public void mousePressed(MouseEvent e) {
+                        System.out.println("mousePressed event triggered: " + e);
+                        System.out.flush();
+                        if ((e.getButton() & MouseEvent.BUTTON1) != 0 && e.isShiftDown()) {
+                            mousePressed = true;
+                        }
+                    }
+
+                    @Override
+                    public void mouseReleased(MouseEvent e) {
+                        System.out.println("mouseReleased event triggered: " + e);
+                        System.out.flush();
+                        if ((e.getButton() & MouseEvent.BUTTON2) != 0 && e.isControlDown()) {
+                            mouseReleased = true;
+                        }
+                    }
+
+                    @Override
+                    public void mouseMoved(MouseEvent e) {
+                        System.out.println("mouseMoved event triggered: " + e);
+                        System.out.flush();
+                        mouseMoved = true;
+                    }
+
+                    @Override
+                    public void mouseClicked(MouseEvent e) {
+                        System.out.println("mouseClicked event triggered: " + e);
+                        System.out.flush();
+                        if ((e.getButton() & MouseEvent.BUTTON3) != 0 && e.isAltDown()) {
+                            mouseClicked = true;
+                        }
+                    }
+
+                    @Override
+                    public void mouseDragged(MouseEvent e) {
+                        System.out.println("mouseDragged event triggered: " + e);
+                        System.out.flush();
+                        if ((e.getModifiers() & MouseEvent.BUTTON2_MASK) != 0) {
+                            mouseDragged = true;
+                        }
+                    }
+
+                };
+                btn.addMouseListener(m);
+                btn.addMouseMotionListener(m);
+                btn.addKeyListener(new KeyAdapter() {
+
+                    @Override
+                    public void keyPressed(KeyEvent e) {
+                        System.out.println("keyPressed event triggered: " + e);
+                        System.out.flush();
+                        if (e.getKeyCode() == KeyEvent.VK_A && e.isShiftDown()) {
+                            keyPressed = true;
+                        }
+                    }
+
+                    @Override
+                    public void keyReleased(KeyEvent e) {
+                        System.out.println("keyReleased event triggered: " + e);
+                        System.out.flush();
+                        if (e.getKeyCode() == KeyEvent.VK_B) {
+                            keyReleased = true;
+                        }
+                    }
+
+                });
+                frm.add(btn, BorderLayout.SOUTH);
+                frm.doLayout();
+                instance = new RobotDriver(Environment.getEnvironment());
+                frm.setVisible(true);
+                btn.requestFocusInWindow();
+            }
+        });
+
+        rb = new Robot();
+        rb.waitForIdle();
+
+        RobotExecutor.get().setRunInOtherJVM(true);
+    }
+
+    @AfterMethod
+    public void tearDown() throws InterruptedException, InvocationTargetException {
+        EventQueue.invokeAndWait(new Runnable() {
+
+            public void run() {
+                frm.setVisible(false);
+            }
+        });
+    }
+
+//    /**
+//     * Test of createScreenCapture method, of class RobotDriver.
+//     */
+//    @Test
+//    public void testCreateScreenCaptureLocally() throws AWTException, InterruptedException {
+//        System.out.println("testCreateScreenCaptureLocally");
+//        Thread.sleep(3000);
+//        Rectangle screenRect = new Rectangle(100, 100, 100, 100);
+//        RobotExecutor.get().setRunInOtherJVM(false);
+//        Image expResult = new AWTImage(new Robot().createScreenCapture(new java.awt.Rectangle(100, 100, 100, 100)));
+//        Image result = RobotDriver.createScreenCapture(screenRect);
+//        Image diff = expResult.compareTo(result);
+//        if (diff != null) {
+//            diff.save("testCreateScreenCaptureLocally.png");
+//            fail();
+//        }
+//    }
+//
+//    /**
+//     * Test of createScreenCapture method, of class RobotDriver.
+//     */
+//    @Test
+//    public void testCreateScreenCaptureRemotely() throws AWTException, InterruptedException {
+//        System.out.println("testCreateScreenCaptureRemotely");
+//        Thread.sleep(3000);
+//        Rectangle screenRect = new Rectangle(100, 100, 100, 100);
+//        RobotExecutor.get().setRunInOtherJVM(true);
+//        Image expResult = new AWTImage(new Robot().createScreenCapture(new java.awt.Rectangle(100, 100, 100, 100)));
+//        Image result = RobotDriver.createScreenCapture(screenRect);
+//        RobotDriver.createScreenCapture(screenRect);
+//        Image diff = expResult.compareTo(result);
+//        if (diff != null) {
+//            diff.save("testCreateScreenCaptureRemotely.png");
+//            fail();
+//        }
+//    }
+//
+//    /**
+//     * Test of createScreenCapture method, of class RobotDriver.
+//     */
+//    @Test
+//    public void testCreateScreenCaptureRemotely2() throws AWTException, InterruptedException {
+//        System.out.println("testCreateScreenCaptureRemotely2");
+//        Thread.sleep(3000);
+//        Rectangle screenRect = new Rectangle(100, 100, 100, 100);
+//        RobotExecutor.get().setRunInOtherJVM(true);
+//        Image expResult = new AWTImage(new Robot().createScreenCapture(new java.awt.Rectangle(100, 100, 100, 100)));
+//        Image result = RobotDriver.createScreenCapture(screenRect);
+//        Image diff = expResult.compareTo(result);
+//        if (diff != null) {
+//            diff.save("testCreateScreenCaptureRemotely2.png");
+//            fail();
+//        }
+//    }
+
+    /**
+     * Test of pressMouse method, of class RobotDriver.
+     */
+    @Test
+    public void testPressMouse() throws InterruptedException {
+        System.out.println("pressMouse");
+        Thread.sleep(3000);
+//        new Thread() {
+//
+//            @Override
+//            public void run() {
+                MouseButton mouseButton = MouseButtons.BUTTON1;
+                Modifier modifiers[] = new Modifier[] {KeyboardModifiers.SHIFT_DOWN_MASK};
+                mousePressed = false;
+                java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+                System.out.println("Pressing mouse");
+                instance.moveMouse(new Point(locationOnScreen.x + btn.getWidth() / 2, locationOnScreen.y + btn.getHeight() / 2));
+                instance.pressMouse(mouseButton, modifiers);
+                instance.releaseMouse(mouseButton, modifiers);
+
+                rb.waitForIdle();
+                new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+                    public Boolean reached() {
+                        return mousePressed ? true: null;
+                    }
+
+                });
+
+//            }
+//
+//        }.start();
+    }
+
+    /**
+     * Test of releaseMouse method, of class RobotDriver.
+     */
+    @Test
+    public void testReleaseMouse() throws InterruptedException {
+        System.out.println("releaseMouse");
+        Thread.sleep(3000);
+        MouseButton mouseButton = MouseButtons.BUTTON2;
+        Modifier modifiers[] = new Modifier[] {KeyboardModifiers.CTRL_DOWN_MASK};
+        mouseReleased = false;
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        instance.moveMouse(new Point(locationOnScreen.x + btn.getWidth() / 2, locationOnScreen.y + btn.getHeight() / 2));
+        System.out.println("Pressing mouse");
+        instance.pressMouse(mouseButton, modifiers);
+        System.out.println("Releasing mouse");
+        instance.releaseMouse(mouseButton, modifiers);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return mouseReleased ? true: null;
+            }
+
+        });
+        assertTrue(mouseReleased);
+    }
+
+    /**
+     * Test of moveMouse method, of class RobotDriver.
+     */
+    @Test
+    public void testMoveMouse() throws InterruptedException {
+        System.out.println("moveMouse");
+        Thread.sleep(3000);
+        mouseMoved = false;
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        System.out.println("Moving mouse");
+        Point startPoint = new Point(locationOnScreen.x, locationOnScreen.y);
+        Point endPoint = new Point(locationOnScreen.x + btn.getWidth(), locationOnScreen.y + btn.getHeight());
+        double steps = 5; //Math.max(btn.getWidth(), btn.getHeight());
+        double dx = (endPoint.x - startPoint.x) / steps;
+        double dy = (endPoint.y - startPoint.y) / steps;
+        for(int i = 0; i < steps; i++) {
+            Point point = new Point(startPoint.x + dx * i, startPoint.y + dy * i);
+            instance.moveMouse(point);
+            Thread.sleep(100);
+        }
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return mouseMoved ? true: null;
+            }
+
+        });
+        assertTrue(mouseMoved);
+    }
+
+    /**
+     * Test of moveMouse method with smoothness set Integer.MAX_VALUE
+     * of class RobotDriver.
+     */
+    @Test
+    public void testMoveNonSmoothMouse() throws InterruptedException {
+        System.out.println("testMoveNonSmoothMouse");
+        Thread.sleep(3000);
+        mouseMoved = false;
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        System.out.println("Moving mouse");
+        Point startPoint = new Point(locationOnScreen.x - 10, locationOnScreen.y - 10);
+        Point endPoint = new Point(locationOnScreen.x + btn.getWidth() + 10, locationOnScreen.y + btn.getHeight() + 10);
+        instance.moveMouse(startPoint);
+        Thread.sleep(100);
+        instance.moveMouse(endPoint);
+        Thread.sleep(2000);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return !mouseMoved ? true : null;
+            }
+
+        });
+        assertFalse(mouseMoved);
+    }
+
+    /**
+     * Test of clickMouse method, of class RobotDriver.
+     */
+    @Test
+    public void testClickMouse() throws InterruptedException {
+        System.out.println("clickMouse");
+        Thread.sleep(3000);
+        mouseClicked = false;
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        Point point = new Point(locationOnScreen.x + btn.getWidth() / 2, locationOnScreen.y + btn.getHeight() / 2);
+        int clickCount = 1;
+        MouseButton mouseButton = MouseButtons.BUTTON3;
+        Modifier modifiers[] = new Modifier[] {KeyboardModifiers.ALT_DOWN_MASK};
+        Timeout mouseClick = new Timeout("mouseClick", 100);
+        System.out.println("Clicking mouse");
+        instance.clickMouse(point, clickCount, mouseButton, mouseClick, modifiers);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return mouseClicked ? true: null;
+            }
+
+        });
+        assertTrue(mouseClicked);
+    }
+
+    /**
+     * Test of dragNDrop method, of class RobotDriver.
+     */
+    @Test
+    public void testDragNDrop() throws InterruptedException {
+        System.out.println("dragNDrop");
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        Point startPoint = new Point(locationOnScreen.x + btn.getWidth() / 2, locationOnScreen.y + btn.getHeight() / 2);
+        Point endPoint = new Point(frm.getLocationOnScreen().x + frm.getWidth() / 2, frm.getLocationOnScreen().y + frm.getHeight() / 2);
+        MouseButton mouseButton = MouseButtons.BUTTON2;
+        Modifier modifiers[] = new Modifier[] {};
+        Timeout before = new Timeout("before", 500);
+        Timeout after = new Timeout("after", 500);
+        mouseDragged = false;
+        instance.dragNDrop(startPoint, endPoint, mouseButton, modifiers, before, after);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return mouseDragged ? true: null;
+            }
+
+        });
+        assertTrue(mouseDragged);
+    }
+
+    /**
+     * Test of pressKey method, of class RobotDriver.
+     */
+    @Test
+    public void testPressKey() throws InterruptedException {
+        System.out.println("pressKey");
+        KeyboardButton kbdButton = KeyboardButtons.A;
+        Modifier modifiers[] = new Modifier[] {KeyboardModifiers.SHIFT_DOWN_MASK};
+        keyPressed = false;
+        instance.pressKey(kbdButton, modifiers);
+        instance.releaseKey(kbdButton, modifiers);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return keyPressed ? true: null;
+            }
+
+        });
+        assertTrue(keyPressed);
+    }
+
+    /**
+     * Test of releaseKey method, of class RobotDriver.
+     */
+    @Test
+    public void testReleaseKey() throws InterruptedException {
+        System.out.println("releaseKey");
+        KeyboardButton kbdButton = KeyboardButtons.B;
+        Modifier modifiers[] = new Modifier[] {};
+        keyReleased = false;
+        instance.pressKey(kbdButton, modifiers);
+        instance.releaseKey(kbdButton, modifiers);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return keyReleased ? true: null;
+            }
+
+        });
+        assertTrue(keyReleased);
+    }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/input/RobotExecutorTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.jemmy.JemmyException;
+import org.jemmy.Rectangle;
+import org.jemmy.env.Environment;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.fail;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+
+/**
+ * TODO: this test is unstable
+ * @author Alexander Kouznetsov <mrkam@mail.ru>
+ */
+public class RobotExecutorTest {
+
+//    public RobotExecutorTest() {
+//    }
+//
+//    static File props;
+//
+//    @BeforeClass
+//    public static void setUpClass() throws Exception {
+//        props = File.createTempFile("jemmy", "properties");
+//        final FileWriter fileWriter = new FileWriter(props);
+//        fileWriter.write(AWTRobotInputFactory.OTHER_VM_CONNECTION_TIMEOUT_PROPERTY + "=" + CONNECTION_TIMEOUT);
+//        fileWriter.flush();
+//    }
+//
+//    @AfterClass
+//    public static void tearDownClass() throws Exception {
+//        props.delete();
+//    }
+//
+//    @BeforeMethod
+//    public void setUp() {
+//    }
+//
+//    @AfterMethod
+//    public void tearDown() {
+//    }
+
+//    /**
+//     * Test of get method, of class RobotExecutor.
+//     */
+//    @Test
+//    public void testGet() {
+//        System.out.println("get");
+//        RobotExecutor expResult = null;
+//        RobotExecutor result = RobotExecutor.get();
+//        assertEquals(expResult, result);
+//        // TODO review the generated test code and remove the default call to fail.
+//        fail("The test case is a prototype.");
+//    }
+//
+//    /**
+//     * Test of createScreenCapture method, of class RobotExecutor.
+//     */
+//    @Test
+//    public void testCreateScreenCapture() {
+//        System.out.println("createScreenCapture");
+//        Rectangle screenRect = null;
+//        RobotExecutor instance = new RobotExecutor();
+//        Image expResult = null;
+//        Image result = instance.createScreenCapture(screenRect);
+//        assertEquals(expResult, result);
+//        // TODO review the generated test code and remove the default call to fail.
+//        fail("The test case is a prototype.");
+//    }
+//
+//    /**
+//     * Test of makeAnOperation method, of class RobotExecutor.
+//     */
+//    @Test
+//    public void testMakeAnOperation() {
+//        System.out.println("makeAnOperation");
+//        String method = "";
+//        Object[] params = null;
+//        Class[] paramClasses = null;
+//        RobotExecutor instance = new RobotExecutor();
+//        Object expResult = null;
+//        Object result = instance.makeAnOperation(method, params, paramClasses);
+//        assertEquals(expResult, result);
+//        // TODO review the generated test code and remove the default call to fail.
+//        fail("The test case is a prototype.");
+//    }
+//
+//    final static int CONNECTION_TIMEOUT = 30000;
+//    final static int DESTROY_TIMEOUT = 8000;
+//
+//    /**
+//     *
+//     * @throws IOException
+//     */
+//    @Test
+//    public void testOtherVMConnectionTimout() throws IOException, InterruptedException {
+//        System.out.println("testOtherVMConnectionTimout");
+//        String ROBOT_TIMEOUT = "123000";
+//        Object prevValue = Environment.getEnvironment().setProperty(AWTRobotInputFactory.OTHER_VM_CONNECTION_TIMEOUT_PROPERTY, ROBOT_TIMEOUT);
+//        RobotExecutor re = RobotExecutor.get();
+//        re.setRunInOtherJVM(true);
+//        String timeout = (String)re.getProperty(AWTRobotInputFactory.OTHER_VM_CONNECTION_TIMEOUT_PROPERTY);
+//        re.exit();
+//        Thread.sleep(DESTROY_TIMEOUT);
+//        Environment.getEnvironment().setProperty(AWTRobotInputFactory.OTHER_VM_CONNECTION_TIMEOUT_PROPERTY, prevValue);
+//        assertEquals(ROBOT_TIMEOUT, timeout);
+//    }
+//
+//    /**
+//     *
+//     * @throws IOException
+//     */
+//    @Test
+//    public void testOtherVMConnectionPort() throws IOException, InterruptedException {
+//        System.out.println("testOtherVMJemmyProperties");
+//        String PORT = "12300";
+//        Object prevValue = Environment.getEnvironment().setProperty(AWTRobotInputFactory.OTHER_VM_PORT_PROPERTY, PORT);
+//        RobotExecutor re = RobotExecutor.get();
+//        re.setRunInOtherJVM(true);
+//        String port = (String)re.getProperty(AWTRobotInputFactory.OTHER_VM_PORT_PROPERTY);
+//        re.exit();
+//        Thread.sleep(DESTROY_TIMEOUT);
+//        Environment.getEnvironment().setProperty(AWTRobotInputFactory.OTHER_VM_PORT_PROPERTY, prevValue);
+//        assertEquals(PORT, port);
+//    }
+//
+//    /**
+//     * Test of exit method, of class RobotExecutor.
+//     */
+//    @Test
+//    public void testExit() {
+//        System.out.println("exit");
+//        Process pp = null;
+//        try {
+//            ProcessBuilder pb = new ProcessBuilder("java", "-cp", System.getProperty("java.class.path"), "-D" + Environment.JEMMY_PROPERTIES_FILE_PROPERTY + "=" + props.getCanonicalPath(), RobotExecutor.class.getName());
+//            pb.redirectErrorStream(true);
+//            final Process p = pb.start();
+//            pp = p;
+//            new Thread() {
+//
+//                @Override
+//                public void run() {
+//                    BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
+//                    while (true) {
+//                        try {
+//                            String line = br.readLine();
+//                            if (line == null) {
+//                                break;
+//                            }
+//                            System.out.println("SERVER: " + line);
+//                        } catch (IOException ex) {
+//                            throw new JemmyException("Exception during other JVM output processing", ex);
+//                        }
+//                    }
+//                }
+//            }.start();
+//            RobotExecutor re = RobotExecutor.get();
+//            re.setRunInOtherJVM(true);
+//            re.exit();
+//            final boolean [] result = new boolean[] { false };
+//            synchronized (result) {
+//                new Thread() {
+//
+//                    @Override
+//                    public void run() {
+//                        try {
+//                            p.waitFor();
+//                            synchronized (result) {
+//                                result[0] = true;
+//                                result.notify();
+//                            }
+//                        } catch (InterruptedException ex) {
+//                            Logger.getLogger(RobotExecutorTest.class.getName()).log(Level.SEVERE, null, ex);
+//                            synchronized (result) {
+//                                result[0] = false;
+//                                result.notify();
+//                            }
+//                        }
+//                    }
+//
+//                }.start();
+//                try {
+//                    result.wait(DESTROY_TIMEOUT * 2);
+//                } catch (InterruptedException ex) {
+//                    Logger.getLogger(RobotExecutorTest.class.getName()).log(Level.SEVERE, null, ex);
+//                }
+//                assertTrue("Server process doesn't finish", result[0]);
+//            }
+//        } catch (IOException ex) {
+//            throw new JemmyException("Failed to start other JVM", ex);
+//        } finally {
+//            if (pp != null) {
+//                pp.destroy();
+//                try {
+//                    Thread.sleep(DESTROY_TIMEOUT);
+//                } catch (InterruptedException ex) {
+//                    Logger.getLogger(RobotExecutorTest.class.getName()).log(Level.SEVERE, null, ex);
+//                }
+//            }
+//        }
+//    }
+//
+//    /**
+//     * Test of main method, of class RobotExecutor.
+//     */
+//    @Test
+//    public void testMain() throws InterruptedException {
+//        System.out.println("main");
+//        try {
+//            final boolean [] result = new boolean[] { false };
+//            long start = System.currentTimeMillis();
+//            ProcessBuilder pb = new ProcessBuilder("java", "-cp", System.getProperty("java.class.path"), "-D" + Environment.JEMMY_PROPERTIES_FILE_PROPERTY + "=" + props.getCanonicalPath(), RobotExecutor.class.getName());
+//            pb.redirectErrorStream(true);
+//            final Process p = pb.start();
+//            synchronized(result) {
+//                new Thread() {
+//
+//                    @Override
+//                    public void run() {
+//                        BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
+//                        while (true) {
+//                            try {
+//                                String line = br.readLine();
+//                                if (line == null) {
+//                                    break;
+//                                }
+//                                System.out.println("SERVER: " + line);
+//                                if (line.startsWith("Exiting server as there is no connection for")) {
+//                                    synchronized (result) {
+//                                        result[0] = true;
+//                                        result.notify();
+//                                    }
+//                                }
+//                            } catch (IOException ex) {
+//                                throw new JemmyException("Exception during other JVM output processing", ex);
+//                            }
+//                        }
+//                    }
+//                }.start();
+//                result.wait((int)(CONNECTION_TIMEOUT * 1.1));
+//                long end = System.currentTimeMillis();
+//                long time = end - start;
+//                p.destroy();
+//                try {
+//                    Thread.sleep(DESTROY_TIMEOUT);
+//                } catch (InterruptedException interruptedException) {
+//                    Logger.getLogger(RobotExecutorTest.class.getName()).log(Level.SEVERE, null, interruptedException);
+//                }
+//                if (Math.abs(time - CONNECTION_TIMEOUT) > CONNECTION_TIMEOUT * 0.3) {
+//                    fail("Application finished with time (" + time + ") more than 30% different from timeout (" + CONNECTION_TIMEOUT + ")");
+//                }
+//            }
+//        } catch (IOException ex) {
+//            throw new JemmyException("Failed to start other JVM", ex);
+//        }
+//    }
+//
+//    /**
+//     * Test of main method, of class RobotExecutor.
+//     */
+//    @Test
+//    public void testConnectToAlreadyRunningServer() throws InterruptedException {
+//        System.out.println("ConnectToAlreadyRunningServer");
+//        Process pp = null;
+//        try {
+//            ProcessBuilder pb = new ProcessBuilder("java", "-cp", System.getProperty("java.class.path"), "-D" + Environment.JEMMY_PROPERTIES_FILE_PROPERTY + "=" + props.getCanonicalPath(), RobotExecutor.class.getName());
+//            pb.redirectErrorStream(true);
+//            final Process p = pb.start();
+//            pp = p;
+//            new Thread() {
+//
+//                @Override
+//                public void run() {
+//                    BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
+//                    while (true) {
+//                        try {
+//                            String line = br.readLine();
+//                            if (line == null) {
+//                                break;
+//                            }
+//                            System.out.println("SERVER: " + line);
+//                        } catch (IOException ex) {
+//                            throw new JemmyException("Exception during other JVM output processing", ex);
+//                        }
+//                    }
+//                }
+//            }.start();
+//            RobotExecutor re = RobotExecutor.get();
+//            re.setRunInOtherJVM(true);
+//            re.createScreenCapture(new Rectangle(0, 0, 10, 10));
+//            re.exit();
+//        } catch (IOException ex) {
+//            throw new JemmyException("Failed to start other JVM", ex);
+//        } finally {
+//            if (pp != null) {
+//                pp.destroy();
+//                Thread.sleep(DESTROY_TIMEOUT);
+//            }
+//        }
+//    }
+//
+//    /**
+//     * Test of synchronizeRobot method, of class RobotExecutor.
+//     */
+//    @Test
+//    public void testSynchronizeRobot() {
+//        System.out.println("synchronizeRobot");
+//        RobotExecutor instance = new RobotExecutor();
+//        instance.synchronizeRobot();
+//        // TODO review the generated test code and remove the default call to fail.
+//        fail("The test case is a prototype.");
+//    }
+//
+//    /**
+//     * Test of setAutoDelay method, of class RobotExecutor.
+//     */
+//    @Test
+//    public void testSetAutoDelay() {
+//        System.out.println("setAutoDelay");
+//        Timeout autoDelay = null;
+//        RobotExecutor instance = new RobotExecutor();
+//        instance.setAutoDelay(autoDelay);
+//        // TODO review the generated test code and remove the default call to fail.
+//        fail("The test case is a prototype.");
+//    }
+//
+//    /**
+//     * Test of isRunInOtherJVM method, of class RobotExecutor.
+//     */
+//    @Test
+//    public void testIsRunInSeparateJVM() {
+//        System.out.println("isRunInOtherJVM");
+//        RobotExecutor instance = new RobotExecutor();
+//        boolean expResult = false;
+//        boolean result = instance.isRunInOtherJVM();
+//        assertEquals(expResult, result);
+//        // TODO review the generated test code and remove the default call to fail.
+//        fail("The test case is a prototype.");
+//    }
+//
+//    /**
+//     * Test of setRunInOtherJVM method, of class RobotExecutor.
+//     */
+//    @Test
+//    public void testSetRunInSeparateJVM() {
+//        System.out.println("setRunInOtherJVM");
+//        boolean runInSeparateJVM = false;
+//        RobotExecutor instance = new RobotExecutor();
+//        instance.setRunInOtherJVM(runInSeparateJVM);
+//        // TODO review the generated test code and remove the default call to fail.
+//        fail("The test case is a prototype.");
+//    }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/input/SmoothMoveTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+package org.jemmy.input;
+
+
+import java.awt.AWTException;
+import java.awt.BorderLayout;
+import java.awt.Button;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.Robot;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import org.jemmy.Point;
+import org.jemmy.control.Wrap;
+import org.jemmy.env.Environment;
+import org.jemmy.env.Timeout;
+import org.jemmy.image.AWTImage;
+import org.jemmy.timing.State;
+import org.jemmy.timing.Waiter;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+
+
+/**
+ *
+ * @author Alexander Kouznetsov <mrkam@mail.ru>
+ */
+public class SmoothMoveTest {
+
+    final static Timeout TIMEOUT = new Timeout("Wait for state to be reached", 10000);
+    final static Timeout DELTA_TIMEOUT = new Timeout("Delta timeout of wait for state to be reached", 1000);
+
+    public SmoothMoveTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        File workdir = new File(System.getProperty("user.dir") + File.separator +
+                "build" + File.separator +
+                "test" + File.separator +
+                "results");
+        workdir.mkdirs();
+        AWTImage.setImageRoot(workdir);
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+        RobotDriver.exit();
+    }
+
+    Frame frm;
+    Button btn;
+    Wrap<Object> area;
+    private volatile boolean mousePressed;
+    private volatile boolean mouseReleased;
+    private volatile boolean mouseMoved;
+    private volatile boolean mouseClicked;
+    private volatile boolean mouseDragged;
+    private volatile boolean keyPressed;
+    private volatile boolean keyReleased;
+    RobotDriver instance;
+    Robot rb;
+
+    @BeforeMethod
+    public void setUp() throws InterruptedException, AWTException, InvocationTargetException {
+
+        Environment.getEnvironment().setProperty(AWTRobotInputFactory.ROBOT_MOUSE_SMOOTHNESS_PROPERTY, "5");
+
+        EventQueue.invokeAndWait(new Runnable() {
+
+            public void run() {
+                frm = new Frame("some frame");
+                frm.setSize(100, 100);
+                frm.setLocation(100, 100);
+                btn = new Button("some button");
+                MouseAdapter m = new MouseAdapter() {
+
+                    @Override
+                    public void mousePressed(MouseEvent e) {
+                        System.out.println("mousePressed event triggered: " + e);
+                        System.out.flush();
+                        if ((e.getButton() & MouseEvent.BUTTON1) != 0 && e.isShiftDown()) {
+                            mousePressed = true;
+                        }
+                    }
+
+                    @Override
+                    public void mouseReleased(MouseEvent e) {
+                        System.out.println("mouseReleased event triggered: " + e);
+                        System.out.flush();
+                        if ((e.getButton() & MouseEvent.BUTTON2) != 0 && e.isControlDown()) {
+                            mouseReleased = true;
+                        }
+                    }
+
+                    @Override
+                    public void mouseMoved(MouseEvent e) {
+                        System.out.println("mouseMoved event triggered: " + e);
+                        System.out.flush();
+                        mouseMoved = true;
+                    }
+
+                    @Override
+                    public void mouseClicked(MouseEvent e) {
+                        System.out.println("mouseClicked event triggered: " + e);
+                        System.out.flush();
+                        if ((e.getButton() & MouseEvent.BUTTON3) != 0 && e.isAltDown()) {
+                            mouseClicked = true;
+                        }
+                    }
+
+                    @Override
+                    public void mouseDragged(MouseEvent e) {
+                        System.out.println("mouseDragged event triggered: " + e);
+                        System.out.flush();
+                        if ((e.getModifiers() & MouseEvent.BUTTON2_MASK) != 0) {
+                            mouseDragged = true;
+                        }
+                    }
+
+                };
+                btn.addMouseListener(m);
+                btn.addMouseMotionListener(m);
+                btn.addKeyListener(new KeyAdapter() {
+
+                    @Override
+                    public void keyPressed(KeyEvent e) {
+                        System.out.println("keyPressed event triggered: " + e);
+                        System.out.flush();
+                        if (e.getKeyCode() == KeyEvent.VK_A && e.isShiftDown()) {
+                            keyPressed = true;
+                        }
+                    }
+
+                    @Override
+                    public void keyReleased(KeyEvent e) {
+                        System.out.println("keyReleased event triggered: " + e);
+                        System.out.flush();
+                        if (e.getKeyCode() == KeyEvent.VK_B) {
+                            keyReleased = true;
+                        }
+                    }
+
+                });
+                frm.add(btn, BorderLayout.SOUTH);
+                frm.doLayout();
+                instance = new RobotDriver(Environment.getEnvironment());
+                frm.setVisible(true);
+                btn.requestFocusInWindow();
+            }
+        });
+
+        rb = new Robot();
+        rb.waitForIdle();
+
+        RobotExecutor.get().setRunInOtherJVM(false);
+    }
+
+    @AfterMethod
+    public void tearDown() throws InterruptedException, InvocationTargetException {
+        EventQueue.invokeAndWait(new Runnable() {
+
+            public void run() {
+                frm.setVisible(false);
+            }
+        });
+    }
+
+    /**
+     * Test of moveMouse method in right-down direction of class RobotDriver.
+     */
+    @Test
+    public void testMoveSmoothMousePP() throws InterruptedException {
+        System.out.println("testMoveSmoothMousePP");
+        Thread.sleep(3000);
+        mouseMoved = false;
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        System.out.println("Moving mouse");
+        Point startPoint = new Point(locationOnScreen.x - 10, locationOnScreen.y - 10);
+        Point endPoint = new Point(locationOnScreen.x + btn.getWidth() + 10, locationOnScreen.y + btn.getHeight() + 10);
+        instance.moveMouse(startPoint);
+        Thread.sleep(100);
+        instance.moveMouse(endPoint);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return mouseMoved ? true : null;
+            }
+
+        });
+        assertTrue(mouseMoved);
+    }
+
+    /**
+     * Test of moveMouse method in left-down direction of class RobotDriver.
+     */
+    @Test
+    public void testMoveSmoothMouseNP() throws InterruptedException {
+        System.out.println("testMoveSmoothMouseNP");
+        Thread.sleep(3000);
+        mouseMoved = false;
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        System.out.println("Moving mouse");
+        Point startPoint = new Point(locationOnScreen.x + btn.getWidth() + 10, locationOnScreen.y - 10);
+        Point endPoint = new Point(locationOnScreen.x - 10, locationOnScreen.y + btn.getHeight() + 10);
+        instance.moveMouse(startPoint);
+        Thread.sleep(100);
+        instance.moveMouse(endPoint);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return mouseMoved ? true : null;
+            }
+
+        });
+        assertTrue(mouseMoved);
+    }
+
+    /**
+     * Test of moveMouse method in left-up direction of class RobotDriver.
+     */
+    @Test
+    public void testMoveSmoothMouseNN() throws InterruptedException {
+        System.out.println("testMoveSmoothMouseNN");
+        Thread.sleep(3000);
+        mouseMoved = false;
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        System.out.println("Moving mouse");
+        Point startPoint = new Point(locationOnScreen.x + btn.getWidth() + 10, locationOnScreen.y + btn.getHeight() + 10);
+        Point endPoint = new Point(locationOnScreen.x - 10, locationOnScreen.y - 10);
+        instance.moveMouse(startPoint);
+        Thread.sleep(100);
+        instance.moveMouse(endPoint);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return mouseMoved ? true : null;
+            }
+
+        });
+        assertTrue(mouseMoved);
+    }
+
+    /**
+     * Test of moveMouse method in right-up direction of class RobotDriver.
+     */
+    @Test
+    public void testMoveSmoothMousePN() throws InterruptedException {
+        System.out.println("testMoveSmoothMousePN");
+        Thread.sleep(3000);
+        mouseMoved = false;
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        System.out.println("Moving mouse");
+        Point startPoint = new Point(locationOnScreen.x - 10, locationOnScreen.y + btn.getHeight() + 10);
+        Point endPoint = new Point(locationOnScreen.x + btn.getWidth() + 10, locationOnScreen.y - 10);
+        instance.moveMouse(startPoint);
+        Thread.sleep(100);
+        instance.moveMouse(endPoint);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return mouseMoved ? true : null;
+            }
+
+        });
+        assertTrue(mouseMoved);
+    }
+
+    /**
+     * Test of moveMouse method along X axis of class RobotDriver.
+     */
+    @Test
+    public void testMoveSmoothMouseX() throws InterruptedException {
+        System.out.println("testMoveSmoothMouseX");
+        Thread.sleep(3000);
+        mouseMoved = false;
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        System.out.println("Moving mouse");
+        Point startPoint = new Point(locationOnScreen.x - 10,
+                locationOnScreen.y + btn.getHeight() / 2);
+        Point endPoint = new Point(locationOnScreen.x + btn.getWidth() + 10,
+                locationOnScreen.y + btn.getHeight() / 2);
+        instance.moveMouse(startPoint);
+        Thread.sleep(100);
+        instance.moveMouse(endPoint);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return mouseMoved ? true : null;
+            }
+
+        });
+        assertTrue(mouseMoved);
+    }
+
+    /**
+     * Test of moveMouse method along Y axis of class RobotDriver.
+     */
+    @Test
+    public void testMoveSmoothMouseY() throws InterruptedException {
+        System.out.println("testMoveSmoothMouseY");
+        Thread.sleep(3000);
+        mouseMoved = false;
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        System.out.println("Moving mouse");
+        Point startPoint = new Point(locationOnScreen.x + btn.getWidth() / 2,
+                locationOnScreen.y - 10);
+        Point endPoint = new Point(locationOnScreen.x + btn.getWidth() / 2,
+                locationOnScreen.y + btn.getHeight() + 10);
+        instance.moveMouse(startPoint);
+        Thread.sleep(100);
+        instance.moveMouse(endPoint);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return mouseMoved ? true : null;
+            }
+
+        });
+        assertTrue(mouseMoved);
+    }
+
+    /**
+     * Test of moveMouse method with smooth set to false, of class RobotDriver.
+     */
+    @Test
+    public void testMoveSmoothMouseDifferentDrivers() throws InterruptedException {
+        System.out.println("testMoveSmoothMouseDifferentDrivers");
+        Thread.sleep(3000);
+        mouseMoved = false;
+        java.awt.Point locationOnScreen = btn.getLocationOnScreen();
+        System.out.println("Moving mouse");
+        Point startPoint = new Point(locationOnScreen.x + btn.getWidth() + 10, locationOnScreen.y - 10);
+        Point endPoint = new Point(locationOnScreen.x - 10, locationOnScreen.y + btn.getHeight() + 10);
+        instance.moveMouse(startPoint);
+        Thread.sleep(100);
+        instance = new RobotDriver(Environment.getEnvironment());
+        instance.moveMouse(endPoint);
+
+        rb.waitForIdle();
+        new Waiter(TIMEOUT, DELTA_TIMEOUT).ensureState(new State<Boolean>(){
+
+            public Boolean reached() {
+                return mouseMoved ? true : null;
+            }
+
+        });
+        assertTrue(mouseMoved);
+    }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/JemmyAWTInput/test/org/jemmy/operators/ScreenTest.java	Wed Dec 20 14:16:29 2017 -0800
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2007, 2017, 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.
+ */
+
+package org.jemmy.operators;
+
+import org.jemmy.Point;
+import org.jemmy.env.*;
+import org.jemmy.Rectangle;
+import org.jemmy.control.Wrap;
+import org.jemmy.input.AWTRobotInputFactory;
+import org.jemmy.operators.Screen;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author shura
+ */
+public class ScreenTest {
+
+    public ScreenTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        Environment.getEnvironment().setInputFactory(new AWTRobotInputFactory());
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @BeforeMethod
+    public void setUp() {
+    }
+
+    @AfterMethod
+    public void tearDown() {
+    }
+
+    // TODO add test methods here.
+    // The methods must be annotated with annotation @Test. For example:
+    //
+    @Test
+    public void testMouseMove() {
+        Wrap<?> screen = new AWTScreen(Environment.getEnvironment());
+        screen.mouse().move();
+        screen.mouse().move(new Point(0, 0));
+        screen.mouse().move(new Point(screen.getScreenBounds().width, screen.getScreenBounds().height));
+    }
+
+}
\ No newline at end of file