1 /*
   2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package robottest;
  26 
  27 import java.io.File;
  28 import java.io.FileInputStream;
  29 import java.io.FileOutputStream;
  30 import java.nio.Buffer;
  31 import java.nio.IntBuffer;
  32 import javafx.animation.AnimationTimer;
  33 import javafx.collections.FXCollections;
  34 import javafx.collections.ObservableList;
  35 import javafx.event.ActionEvent;
  36 import javafx.event.EventHandler;
  37 import javafx.geometry.BoundingBox;
  38 import javafx.geometry.Bounds;
  39 import javafx.scene.Group;
  40 import javafx.scene.Scene;
  41 import javafx.scene.control.Button;
  42 import javafx.scene.control.Label;
  43 import javafx.scene.control.ListView;
  44 import javafx.scene.control.TextField;
  45 import javafx.scene.input.KeyCode;
  46 import javafx.scene.input.MouseButton;
  47 import javafx.scene.image.Image;
  48 import javafx.scene.image.ImageView;
  49 import javafx.scene.image.PixelReader;
  50 import javafx.scene.image.WritableImage;
  51 import javafx.scene.image.WritablePixelFormat;
  52 import javafx.scene.layout.GridPane;
  53 import javafx.scene.layout.Pane;
  54 import javafx.scene.layout.VBox;
  55 import javafx.scene.paint.Color;
  56 import javafx.scene.robot.Robot;
  57 import javafx.scene.shape.Rectangle;
  58 import javafx.stage.Popup;
  59 import javafx.stage.Stage;
  60 
  61 public class RobotBuilder {
  62     //Variable used by "RobotTest" section
  63     private final Rectangle rec1 = new Rectangle(50, 50, 40, 160);
  64     private Popup screenShot;
  65 
  66     private static RobotBuilder instance;
  67 
  68 //    protected TestRobot {}
  69 
  70     public static RobotBuilder getInstance() {
  71         if (instance==null)
  72                  instance = new RobotBuilder();
  73         return instance;
  74     }
  75 
  76     /**
  77      * The method updates globalScene with robot tests
  78      * @param globalScene the global Scene
  79      * @param mainBox the Box to insert into
  80      * @param robotStage the Robot Stage
  81      */
  82     void robotTest(final Scene globalScene, final VBox mainBox,
  83                    final Stage robotStage) {
  84         Label l = new Label("Robot features Demo");
  85         Group lGroup = new Group(l);
  86         lGroup.setLayoutX(400);
  87         lGroup.setLayoutY(10);
  88 
  89         //Rectangle's coordinates
  90         final int recX = 50;
  91         final int recY = 50;
  92 
  93         Group allGroup = new Group();
  94         rec1.setFill(Color.RED);
  95         Rectangle rec2 = new Rectangle(recX + 40, recY, 40, 160);
  96         rec2.setFill(Color.BLUE);
  97         Rectangle rec3 = new Rectangle(recX + 80, recY, 40, 160);
  98         rec3.setFill(Color.YELLOW);
  99         Rectangle rec4 = new Rectangle(recX + 120, recY, 40, 160);
 100         rec4.setFill(Color.GREEN);
 101 
 102         GridPane grid = new GridPane();
 103         grid.setVgap(50);
 104         grid.setHgap(20);
 105         grid.setLayoutX(recX + 300);
 106         grid.setLayoutY(recY + 50);
 107 
 108         final TextField result1 = new TextField("Result");
 109         result1.setEditable(false);
 110         Button screenTestBtn = new Button("Robot Get Screen Capture Test");
 111         screenTestBtn.setOnAction(new EventHandler<ActionEvent>() {
 112             @Override public void handle(ActionEvent e) {
 113                screenShot = robotScreenTest(result1, robotStage);
 114             }
 115         });
 116 
 117         grid.setConstraints(screenTestBtn, 0, 0);
 118         grid.getChildren().add(screenTestBtn);
 119         grid.setConstraints(result1, 1, 0);
 120         grid.getChildren().add(result1);
 121 
 122         final TextField result2 = new TextField("Result");
 123         result2.setEditable(false);
 124         Button pixelTestBtn = new Button("Robot Get Pixel Color Test");
 125         pixelTestBtn.setOnAction(new EventHandler<ActionEvent>() {
 126             @Override public void handle(ActionEvent e) {
 127                robotPixelTest(result2, robotStage);
 128             }
 129         });
 130 
 131         grid.setConstraints(pixelTestBtn, 0, 1);
 132         grid.getChildren().add(pixelTestBtn);
 133         grid.setConstraints(result2, 1, 1);
 134         grid.getChildren().add(result2);
 135 
 136         //KeyPressRelesase
 137         final TextField writeField = new TextField("");
 138         Group writeFieldGroup = new Group(writeField);
 139         writeFieldGroup.setLayoutX(recX);
 140         writeFieldGroup.setLayoutY(recY + 200);
 141 
 142         final TextField result3 = new TextField("Result");
 143         result3.setEditable(false);
 144 
 145         Button keyTestBtn = new Button("Robot Key Press/Release Test");
 146         keyTestBtn.setOnAction(new EventHandler<ActionEvent>() {
 147             @Override public void handle(ActionEvent e) {
 148                 robotKeyTest(writeField, result3);
 149             }
 150         });
 151 
 152         grid.setConstraints(keyTestBtn, 0, 2);
 153         grid.getChildren().add(keyTestBtn);
 154         grid.setConstraints(result3, 1, 2);
 155         grid.getChildren().add(result3);
 156 
 157         //Mouse wheel
 158         final ListView<String> sv = new ListView<String>();
 159         ObservableList<String> items =FXCollections.observableArrayList (
 160                     "a", "b", "c", "d", "e", "f", "g", "h", "i");
 161         sv.setItems(items);
 162         sv.setPrefWidth(100);
 163         sv.setPrefHeight(100);
 164 
 165         Group svGroup = new Group(sv);
 166         svGroup.setLayoutX(recX);
 167         svGroup.setLayoutY(recY + 250);
 168 
 169         final TextField result4 = new TextField("Result");
 170         result4.setEditable(false);
 171 
 172         Button wheelTestBtn = new Button("Robot Mouse Press/Release/Wheel Test");
 173         wheelTestBtn.setOnAction(new EventHandler<ActionEvent>() {
 174             @Override public void handle(ActionEvent e) {
 175                 robotWheelTest(sv, result4, robotStage);
 176             }
 177         });
 178 
 179         grid.setConstraints(wheelTestBtn, 0, 3);
 180         grid.getChildren().add(wheelTestBtn);
 181         grid.setConstraints(result4, 1, 3);
 182         grid.getChildren().add(result4);
 183 
 184         Button btn = new Button("Back");
 185         btn.setOnAction(new EventHandler<ActionEvent>() {
 186             @Override public void handle(ActionEvent e) {
 187                 if ((screenShot != null) && (screenShot.isShowing())) {
 188                     screenShot.hide();
 189                 }
 190                 globalScene.setRoot(mainBox);
 191             }
 192         });
 193         Group btnGroup = new Group(btn);
 194         btnGroup.setLayoutX(450);
 195         btnGroup.setLayoutY(450);
 196 
 197         allGroup.getChildren().addAll(rec1, rec2, rec3, rec4, grid, lGroup, btnGroup,
 198                                       writeFieldGroup, svGroup);
 199         globalScene.setRoot(allGroup);
 200     }
 201 
 202 
 203     public void robotKeyTest(final TextField field, final TextField result) {
 204         field.requestFocus();
 205         new AnimationTimer() {
 206             long startTime = System.nanoTime();
 207             @Override
 208             public void handle(long now) {
 209                 if (now > startTime + 3000000000l){
 210                     stop();
 211                     field.setText("Failed");
 212                 } else if (field.isFocused()) {
 213                     stop();
 214                     final Robot robot = new Robot();
 215                     robot.keyPress(KeyCode.T);
 216                     robot.keyRelease(KeyCode.T);
 217                     robot.keyPress(KeyCode.E);
 218                     robot.keyRelease(KeyCode.E);
 219                     robot.keyPress(KeyCode.S);
 220                     robot.keyRelease(KeyCode.S);
 221                     robot.keyPress(KeyCode.T);
 222                     robot.keyRelease(KeyCode.T);
 223                     new AnimationTimer() {
 224                         long startTime = System.nanoTime();
 225                         @Override
 226                         public void handle(long now) {
 227                             if (now > startTime + 3000000000l){
 228                                 stop();
 229                                 result.setText("Failed");
 230                             } else if ((field.getText()).equals("test")) {
 231                                 stop();
 232                                 result.setText("Passed");
 233                             }
 234                         }
 235                     }.start();
 236                 }
 237             }
 238         }.start();
 239     }
 240 
 241     public void robotWheelTest(final ListView<String> lv, final TextField result,
 242                                Stage currentStage) {
 243         //Caclulation of ListView minimal coordinates
 244         Bounds bounds = lv.localToScreen(new BoundingBox(0, 0,
 245             lv.getBoundsInParent().getWidth(),
 246             lv.getBoundsInParent().getHeight()));
 247         int x = 10 + (int) bounds.getMinX();
 248         int y = 10 + (int) bounds.getMinY();
 249 
 250         final Robot robot = new Robot();
 251         robot.mouseMove(x, y);
 252         robot.mousePress(MouseButton.PRIMARY);
 253         robot.mouseRelease(MouseButton.PRIMARY);
 254 
 255         new AnimationTimer() {
 256             long startTime = System.nanoTime();
 257             @Override
 258             public void handle(long now) {
 259                 if (now > startTime + 3000000000l){
 260                     stop();
 261                     result.setText("Failed");
 262                 } else if (lv.isFocused()) {
 263                     stop();
 264                     robot.mouseWheel(-5);
 265                     robot.mousePress(MouseButton.PRIMARY);
 266                     robot.mouseRelease(MouseButton.PRIMARY);
 267                     new AnimationTimer() {
 268                         long startTime = System.nanoTime();
 269                         @Override
 270                         public void handle(long now) {
 271                             if (now > startTime + 3000000000l){
 272                                 stop();
 273                                 result.setText("Scroll Down Failed");
 274                             } else if (!lv.getSelectionModel().
 275                                     selectedItemProperty().getValue().
 276                                     equals("a")) {
 277                                         stop();
 278                                     result.setText("Scroll Down Passed");
 279                             }
 280                         }
 281                     }.start();
 282                 }
 283             }
 284         }.start();
 285     }
 286 
 287     public void robotPixelTest(final TextField result, Stage currentStage) {
 288         Bounds bounds = rec1.localToScreen(new BoundingBox(0, 0,
 289                         rec1.getBoundsInParent().getWidth(),
 290                         rec1.getBoundsInParent().getHeight()));
 291         int x = 53 + (int) bounds.getMinX();
 292         int y = 53 + (int) bounds.getMinY();
 293         int answer = assertPixelEquals(x, y, Color.RED) +
 294                      assertPixelEquals(x + 40, y, Color.BLUE) +
 295                      assertPixelEquals(x + 80, y, Color.YELLOW) +
 296                      assertPixelEquals(x + 120, y, Color.GREEN);
 297         if (answer == 4) {
 298             result.setText("Passed");
 299         } else {
 300             result.setText("Failed");
 301         }
 302 
 303     }
 304 
 305     static int colorToRGB(Color c) {
 306         int r = (int) Math.round(c.getRed() * 255.0);
 307         int g = (int) Math.round(c.getGreen() * 255.0);
 308         int b = (int) Math.round(c.getBlue() * 255.0);
 309         return 0xff000000 | (r << 16) | (g << 8) | b;
 310     }
 311 
 312     public int assertPixelEquals(int x, int y, Color expected) {
 313         final Robot robot = new Robot();
 314         int pixel = colorToRGB(robot.getPixelColor(x, y));
 315         int expectedPixel = colorToRGB(expected);
 316         if (checkColor(pixel, expected)) {
 317             return 1;
 318         } else {
 319             System.out.println("Expected color 0x" + Integer.toHexString(expectedPixel) +
 320                     " at " + x + "," + y + " but found 0x" + Integer.toHexString(pixel));
 321         }
 322         return 0;
 323     }
 324 
 325     private boolean checkColor(int value, Color expected) {
 326         double tolerance = 0.07;
 327         double ered = expected.getRed();
 328         double egrn = expected.getGreen();
 329         double eblu = expected.getBlue();
 330 
 331         double vred = ((value & 0xff0000) >> 16) / 255.0;
 332         double vgrn = ((value & 0x00ff00) >> 8) / 255.0;
 333         double vblu = ((value & 0x0000ff)) / 255.0;
 334 
 335         double dred = Math.abs(ered - vred);
 336         double dgrn = Math.abs(egrn - vgrn);
 337         double dblu = Math.abs(eblu - vblu);
 338 
 339         if (dred <= tolerance && dgrn <= tolerance && dblu <= tolerance) {
 340             return true;
 341         }
 342 
 343         return false;
 344     }
 345 
 346     public Popup robotScreenTest(final TextField result, Stage stage) {
 347         Bounds bounds = rec1.localToScreen(new BoundingBox(0, 0,
 348                 rec1.getBoundsInParent().getWidth(),
 349                 rec1.getBoundsInParent().getHeight()));
 350 
 351         int x = 50 + (int) bounds.getMinX();
 352         int y = 50 + (int) bounds.getMinY();
 353         boolean correct = true;
 354         final Robot robot = new Robot();
 355         int width = 160;
 356         int height = 160;
 357         int[] intArr = new int[width * height];
 358         PixelReader pixelReader = robot.getScreenCapture(null, x, y, width, height).getPixelReader();
 359         WritablePixelFormat<IntBuffer> format = WritablePixelFormat.getIntArgbInstance();
 360         pixelReader.getPixels(0, 0, width, height, format, intArr, 0, width);
 361         String filename= "scrCapture.bmp";
 362         File file = new File(filename);
 363         try {
 364             if (!file.exists()) {
 365                 file.createNewFile();
 366             }
 367             BMPOutputStream bmp = new BMPOutputStream(new FileOutputStream(filename), intArr, width, height);
 368         } catch (Exception e) {}
 369 
 370 
 371         for (int i = width; i <= height*(height-1); i += width) {
 372             for (int j = 1; j <= 38; j ++){
 373                 if (!checkColor(intArr[j+i],Color.RED)) {
 374                     System.out.println(" pixel("+j+","+(i/width)+") "+
 375                             Integer.toHexString(intArr[j+i])+" != "+
 376                             Integer.toHexString(colorToRGB(Color.RED)));
 377                     correct = false;
 378                 }
 379              }
 380             for (int j = 41; j <= 78; j ++){
 381                 if (!checkColor(intArr[j+i],Color.BLUE)) {
 382                     System.out.println(" pixel("+j+","+(i/width)+") "+
 383                             Integer.toHexString(intArr[j+i])+" != "+
 384                             Integer.toHexString(colorToRGB(Color.BLUE)));
 385                     correct = false;
 386                 }
 387              }
 388             for (int j = 81; j <= 118; j ++){
 389                 if (!checkColor(intArr[j+i],Color.YELLOW)) {
 390                     System.out.println(" pixel("+j+","+(i/width)+") "+
 391                             Integer.toHexString(intArr[j+i])+" != "+
 392                             Integer.toHexString(colorToRGB(Color.YELLOW)));
 393                     correct = false;
 394                 }
 395              }
 396             for (int j = 121; j <= 158; j ++){
 397                 if (!checkColor(intArr[j+i],Color.GREEN)) {
 398                     System.out.println(" pixel("+j+","+(i/width)+") "+
 399                             Integer.toHexString(intArr[j+i])+" != "+
 400                             Integer.toHexString(colorToRGB(Color.GREEN)));
 401                     correct = false;
 402                 }
 403             }
 404         }
 405         if (correct) {
 406             result.setText("Passed");
 407         } else {
 408             result.setText("Failed");
 409         }
 410         return showImage(stage, width, height, result);
 411     }
 412 
 413     private Popup showImage(Stage stage, int width, int height, TextField tf) {
 414         int frame = 70;
 415         Rectangle rec = new Rectangle(width + frame, height + frame);
 416         FileInputStream os = null;
 417         final File file = new File("scrCapture.bmp");
 418         try {
 419             os = new FileInputStream(file);
 420         } catch (Exception e) {}
 421 
 422         final Popup popup = new Popup();
 423         ImageView iv = new ImageView(new Image(os));
 424         iv.setLayoutX(frame/2);
 425         iv.setLayoutY(frame/2);
 426 
 427         rec.setFill(Color.WHITE);
 428         rec.setStroke(Color.BLACK);
 429         Button exit = new Button("x");
 430         exit.setOnAction(new EventHandler<ActionEvent>() {
 431             @Override public void handle(ActionEvent e) {
 432                 if (file.exists()&&tf.getText().equals("Passed")) {
 433                     file.deleteOnExit();
 434                 }
 435                 popup.hide();
 436             }
 437         });
 438         exit.setLayoutX(width + frame/2);
 439         Pane popupPane = new Pane(rec, iv, exit);
 440         popup.setX(stage.getX() + 550);
 441         popup.setY(stage.getY() + 430);
 442         popup.getContent().addAll(popupPane);
 443         popup.show(stage);
 444         return popup;
 445     }
 446 }