1 /*
   2  * Copyright (c) 2009, 2016, 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  */
  24 package test.javaclient.shared.screenshots;
  25 
  26 import java.nio.file.Paths;
  27 import java.util.LinkedList;
  28 import java.util.List;
  29 import javafx.scene.Node;
  30 import javafx.scene.Scene;
  31 import javafx.scene.layout.Pane;
  32 import javafx.stage.Stage;
  33 import org.jemmy.Rectangle;
  34 import org.jemmy.TimeoutExpiredException;
  35 import org.jemmy.control.Wrap;
  36 import org.jemmy.fx.ByWindowType;
  37 import org.jemmy.fx.NodeDock;
  38 import org.jemmy.fx.Root;
  39 import org.jemmy.image.Image;
  40 import org.jemmy.image.ImageLoader;
  41 import org.jemmy.interfaces.Parent;
  42 import org.junit.Assert;
  43 import test.javaclient.shared.AbstractTestableApplication;
  44 import test.javaclient.shared.JemmyUtils;
  45 
  46 /**
  47  * @author Andrey Glushchenko, andrey.rusakov@oracle.com
  48  */
  49 public class ScreenshotUtils {
  50 
  51     private static final List<Throwable> SCREENSHOT_ERRORS = new LinkedList<>();
  52     private static AbstractTestableApplication application;
  53 
  54     /**
  55      * Verify or generate golden screenshot for a test.
  56      *
  57      * @param testClass
  58      * @param testName test name for a test
  59      * @param scene scene to take a screenshot of
  60      */
  61     public static void checkScreenshot(Class testClass, String testName, Wrap node) {
  62         checkScreenshot(testClass.getName() + "." + testName, node);
  63     }
  64 
  65     /**
  66      * Verify or generate golden screenshot for a test.
  67      *
  68      * @param testName test name for a test
  69      * @param scene scene to take a screenshot of
  70      */
  71     public static void checkScreenshot(String testName, Wrap node) {
  72         checkScreenshot(testName, node, null);
  73     }
  74 
  75     public static void checkScreenshot(String testName, Wrap node, int width, int height) {
  76         checkScreenshot(testName, node, new Rectangle(width, height));
  77     }
  78 
  79     public static void checkScreenshot(String testName, final Wrap node, Rectangle rect) {
  80         ImageLoader imgLoader = node.getEnvironment().getImageLoader();
  81         List<String> goldenImages = GoldenImageManager.getTestImages(testName, ".png");
  82         String resultPath = GoldenImageManager.getScreenshotPath(testName);
  83         if (goldenImages.isEmpty()) {
  84             Image sceneImage = (rect == null) ? node.getScreenImage() : node.getScreenImage(rect);
  85             sceneImage.save(resultPath);
  86             throw new RuntimeException("No golden images found for " + testName
  87                     + ", actual image saved to " + resultPath);
  88         }
  89         boolean nothingMatched = true;
  90         for (int i = 0; i < goldenImages.size(); i++) {
  91             String goldenPath = goldenImages.get(i);
  92             String goldenName = Paths.get(goldenPath).getFileName().toString()
  93                     .replaceFirst("\\.[^\\.]*$", String.format("-%02d", i));
  94             String diffPath = GoldenImageManager.getScreenshotPath(goldenName + "-diff");
  95             Image image = imgLoader.load(goldenPath);
  96             try {
  97                 if (rect != null) {
  98                     node.waitImage(image, rect, resultPath, diffPath);
  99                 } else {
 100                     node.waitImage(image, resultPath, diffPath);
 101                 }
 102                 nothingMatched = false;
 103                 break;
 104             } catch (TimeoutExpiredException imagesAreDifferentException) {
 105                 try {
 106                     String expectedName = GoldenImageManager.getScreenshotPath(goldenName + "-expected");
 107                     imgLoader.load(goldenPath).save(expectedName);
 108                 } catch (Throwable ex) {
 109                     System.err.println(ex);
 110                 }
 111             }
 112         }
 113         if (nothingMatched) {
 114             throw new TimeoutExpiredException("Control having expected image has not been reached");
 115         }
 116     }
 117 
 118     public static void setApplication(AbstractTestableApplication application) {
 119         ScreenshotUtils.application = application;
 120     }
 121 
 122     public static void setScene(Wrap<? extends Scene> scene) {
 123         // ScreenshotUtils.scene = scene;
 124     }
 125 
 126     private static void error(Throwable ex) {
 127         SCREENSHOT_ERRORS.add(ex);
 128         ex.printStackTrace();
 129     }
 130 
 131     public static void setComparatorDistance(float comparatorDistance) {
 132         JemmyUtils.comparatorDistance = comparatorDistance;
 133     }
 134 
 135     public static void throwScreenshotErrors() {
 136         StringBuilder sb = new StringBuilder();
 137         for (Throwable ex : SCREENSHOT_ERRORS) {
 138             sb.append(ex.toString()).append("\n");
 139         }
 140         SCREENSHOT_ERRORS.clear();
 141         String msg = sb.toString();
 142         if (!"".equals(msg) && msg != null) {
 143             Assert.fail(msg);
 144         }
 145     }
 146 
 147     public static void checkPageContentScreenshot(String name) {
 148         checkPageContentScreenshot(name, false);
 149     }
 150 
 151     public static void checkPageContentScreenshot(String name, boolean valuable_rect) {
 152         try {
 153             if (application.getNodeForScreenshot() == null) {
 154                 final Wrap<? extends Scene> scene = Root.ROOT.lookup(new ByWindowType(Stage.class)).lookup(Scene.class).wrap(0);
 155 
 156                 checkScreenshot(name, scene);
 157             } else if (valuable_rect) {
 158                 checkScreenshot(name, getPageContent(), getPageContentSize());
 159             } else {
 160                 checkScreenshot(name, getPageContent());
 161             }
 162         } catch (Throwable th) {
 163             error(th);
 164         }
 165     }
 166 
 167     public static Wrap<? extends Node> getPageContent() {
 168         Node n = application.getNodeForScreenshot();
 169         final Wrap<? extends Scene> scene = Root.ROOT.lookup(new ByWindowType(Stage.class)).lookup(Scene.class).wrap(0);
 170         //return new NodeDock(scene.as(Parent.class, Node.class), n.getId()).wrap();
 171         return new NodeDock((Parent<Node>)scene.as(Parent.class, Node.class), n.getId()).wrap();
 172     }
 173 
 174     /**
 175      *
 176      * @return rectangle contains all children of page content. This rectangle may have different
 177      * size then size of Content.
 178      */
 179     public static Rectangle getPageContentSize() {
 180         Node node = getPageContent().getControl();
 181         Rectangle rect = new Rectangle();
 182         if (node instanceof Pane) {
 183             Pane p = (Pane) node;
 184             for (Node n : p.getChildren()) {
 185                 rect.add((int) n.getBoundsInParent().getMaxX(), (int) n.getBoundsInParent().getMaxY());
 186             }
 187         } else {
 188             rect.add((int) node.getBoundsInParent().getMaxX(), (int) node.getBoundsInParent().getMaxY());
 189         }
 190         return rect;
 191     }
 192 
 193 }