1 /*
2 * Copyright (c) 2009, 2012, 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.io.File;
27 import java.util.ArrayList;
28 import java.util.LinkedList;
29 import java.util.List;
30 import javafx.scene.Node;
31 import javafx.scene.Scene;
32 import javafx.scene.layout.Pane;
33 import javafx.stage.Stage;
34 import org.jemmy.Rectangle;
35 import org.jemmy.TimeoutExpiredException;
36 import org.jemmy.action.GetAction;
37 import org.jemmy.control.Wrap;
38 import org.jemmy.env.Environment;
39 import org.jemmy.fx.ByWindowType;
40 import org.jemmy.fx.NodeDock;
41 import org.jemmy.fx.Root;
42 import org.jemmy.image.Image;
43 import org.jemmy.interfaces.Parent;
44 import org.jemmy.timing.State;
45 import org.junit.Assert;
46 import test.javaclient.shared.AbstractTestableApplication;
47 import test.javaclient.shared.JemmyUtils;
48 import test.javaclient.shared.TestUtil;
49 import test.javaclient.shared.description.FXSceneTreeBuilder;
50 import test.javaclient.shared.description.FXSimpleNodeDescription;
51 import test.javaclient.shared.description.TreeNode;
52
53 /**
54 * @author Andrey Glushchenko
55 */
56 public class ScreenshotUtils {
57 //private static Wrap<? extends Scene> scene = null;
58 private static AbstractTestableApplication application;
59 /**
60 * Verify or generate golden screenshot for a test depending on
61 * <code>IS_GOLDEN_MODE</code> status.
62 *
63 * @param testClass
64 * @param testName test name for a test
65 * @param scene scene to take a screenshot of
66 */
67 public static void checkScreenshot(Class testClass, String testName, Wrap node) {
68 checkScreenshot(testClass.getName() + "." + testName, node);
69 }
70 /**
71 * Verify or generate golden screenshot for a test depending on
72 * <code>IS_GOLDEN_MODE</code> status.
73 *
74 * @param testName test name for a test
75 * @param scene scene to take a screenshot of
76 */
77 public static void checkScreenshot(String testName, Wrap node) {
78 checkScreenshot(testName, node, null);
79 }
80
81 public static void checkScreenshot(String testName, Wrap node, int width, int height) {
82 checkScreenshot(testName, node, new Rectangle(width, height));
83 }
84
85 public static void checkScreenshot(String testName, final Wrap node, Rectangle rect) {
86 Image sceneImage;
87 if (rect != null) {
88 sceneImage = node.getScreenImage(rect);
89 } else {
90 sceneImage = node.getScreenImage();
91 }
92 String name = ImagesManager.getInstance().lookupGoldenScreenshot(testName);
93 String descrName = ImagesManager.getInstance().getDescriptionPath(testName);
94 Assert.assertNotNull("Invalid images path", name);
95 if (TestUtil.IS_GOLDEN_MODE) {
96 //TODO: specified directory
97 sceneImage.save(ImagesManager.getInstance().getScreenshotPath(testName));
98 } else {
99 String diffName = ImagesManager.getInstance().getScreenshotPath(testName + "-diff");
100 String resName = ImagesManager.getInstance().getScreenshotPath(testName);
101 if (TestUtil.IS_DESCRIPTION_MODE) {
102 String descr_name = ImagesManager.getInstance().lookupGoldenDescription(testName);
103 if (new File(descr_name).exists()) {
104 try {
105 List<TreeNode<FXSimpleNodeDescription>> list = (List<TreeNode<FXSimpleNodeDescription>>) TestUtil.read(descr_name);
106 node.waitState(new State<List<TreeNode<FXSimpleNodeDescription>>>() {
107 public List<TreeNode<FXSimpleNodeDescription>> reached() {
108 return FXSceneTreeBuilder.build(node);
109 }
110
111 @Override
112 public String toString() {
113 return "Control having expected image description";
114 }
115 }, list);
116 } catch (TimeoutExpiredException e) {
117 if (diffName != null) {
118 node.getEnvironment().getOutput(Wrap.OUTPUT).println("Saving difference to " + diffName);
119 final Image diffImage = node.getScreenImage().compareTo(node.getEnvironment().getImageLoader().load(name));
120 if (diffImage != null) {
121 diffImage.save(diffName);
122 }
123 }
124 throw e;
125 } finally {
126 if (resName != null) {
127 node.getEnvironment().getOutput(Wrap.OUTPUT).println("Saving result to " + resName);
128 node.getScreenImage().save(resName);
129 }
130 }
131 } else {
132 Assert.fail("Golden image description (" + descr_name + ") not found. Actual image is saved as " + descrName);
133 ArrayList<TreeNode<FXSimpleNodeDescription>> description = FXSceneTreeBuilder.build(node);
134 TestUtil.write(description, descrName);
135 }
136 } else {
137 if (new File(name).exists()) {
138 try {
139 if (rect != null) {
140 node.waitImage(node.getEnvironment().getImageLoader().load(name), rect, resName, diffName);
141 } else {
142 node.waitImage(node.getEnvironment().getImageLoader().load(name), resName, diffName);
143 }
144 } catch (TimeoutExpiredException imagesAreDifferentException) {
145 //If images are really different, then not only diff and
146 //original images are to put in the results folder, but also golden image.
147 try {
148 String expectedName = ImagesManager.getInstance().getScreenshotPath(testName + "-expected");
149 node.getEnvironment().getImageLoader().load(name).save(expectedName);
150 } catch (Throwable ex) {
151 ex.printStackTrace();
152 }
153 throw imagesAreDifferentException;
154 }
155 if (TestUtil.IS_DESCRIBING_MODE) {
156 ArrayList<TreeNode<FXSimpleNodeDescription>> description = FXSceneTreeBuilder.build(node);
157 TestUtil.write(description, descrName);
158 }
159 } else {
160 sceneImage.save(resName);
161 Assert.fail("Golden image (" + name + ") not found. Actual image is saved as " + resName);
162 }
163 }
164 }
165 }
166 private static List<Throwable> screenshotErrors = new LinkedList<Throwable>();
167
168 public static void setApplication(AbstractTestableApplication application) {
169 ScreenshotUtils.application = application;
170 }
171 public static void setScene(Wrap<? extends Scene> scene) {
172 // ScreenshotUtils.scene = scene;
173 }
174
175 private static void error(Throwable ex){
176 screenshotErrors.add(ex);
177 ex.printStackTrace();
178 }
179 /**
180 * Set image comparator distance. Should be called from constructor or {}
181 * class initialization block.
182 *
183 * @param comparatorDistance distance
184 */
185 public static void setComparatorDistance(float comparatorDistance) {
186 JemmyUtils.comparatorDistance = comparatorDistance;
187 }
188 public static void throwScreenshotErrors(){
189 StringBuilder sb = new StringBuilder();
190 for(Throwable ex: screenshotErrors){
191 sb.append(ex.toString()).append("\n");
192 }
193 screenshotErrors.clear();
194 String msg = sb.toString();
195 if(!"".equals(msg) && msg!=null){
196 Assert.fail(msg);
197 }
198 }
199
200 public static void checkPageContentScreenshot(String name) {
201 checkPageContentScreenshot(name, false);
202 }
203
204 public static void checkPageContentScreenshot(String name, boolean valuable_rect) {
205 try {
206 if(application.getNodeForScreenshot()==null){
207 final Wrap<? extends Scene> scene = Root.ROOT.lookup(new ByWindowType(Stage.class)).lookup(Scene.class).wrap(0);
208
209 checkScreenshot(name, scene);
210 }else{
211 if (valuable_rect) {
212 checkScreenshot(name, getPageContent(), getPageContentSize());
213 } else {
214 checkScreenshot(name, getPageContent());
215 }
216 }
217 } catch (Throwable th) {
218 error(th);
219 }
220 }
221 public static Wrap<? extends Node> getPageContent() {
222 Node n = application.getNodeForScreenshot();
223 final Wrap<? extends Scene> scene = Root.ROOT.lookup(new ByWindowType(Stage.class)).lookup(Scene.class).wrap(0);
224 return new NodeDock(scene.as(Parent.class, Node.class), n.getId()).wrap();
225 }
226 /**
227 *
228 * @return rectangle contains all children of page content. This rectangle
229 * may have different size then size of Content.
230 */
231 public static Rectangle getPageContentSize() {
232 Node node = getPageContent().getControl();
233 Rectangle rect = new Rectangle();
234 if (node instanceof Pane) {
235 Pane p = (Pane) node;
236 for (Node n : p.getChildren()) {
237 rect.add((int) n.getBoundsInParent().getMaxX(), (int) n.getBoundsInParent().getMaxY());
238 }
239 } else {
240 rect.add((int) node.getBoundsInParent().getMaxX(), (int) node.getBoundsInParent().getMaxY());
241 }
242 return rect;
243 }
244
245 }
|
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 }
172
173 /**
174 *
175 * @return rectangle contains all children of page content. This rectangle may have different
176 * size then size of Content.
177 */
178 public static Rectangle getPageContentSize() {
179 Node node = getPageContent().getControl();
180 Rectangle rect = new Rectangle();
181 if (node instanceof Pane) {
182 Pane p = (Pane) node;
183 for (Node n : p.getChildren()) {
184 rect.add((int) n.getBoundsInParent().getMaxX(), (int) n.getBoundsInParent().getMaxY());
185 }
186 } else {
187 rect.add((int) node.getBoundsInParent().getMaxX(), (int) node.getBoundsInParent().getMaxY());
188 }
189 return rect;
190 }
191
192 }
|