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 }