1 /* 2 * Copyright (c) 2012, 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 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 package test.robot.javafx.scene.layout; 26 27 import javafx.scene.Group; 28 import javafx.scene.Scene; 29 import javafx.scene.paint.Color; 30 import javafx.scene.paint.Paint; 31 import javafx.stage.Stage; 32 import java.util.ArrayList; 33 import java.util.LinkedList; 34 import java.util.List; 35 import java.util.concurrent.atomic.AtomicReference; 36 import javafx.scene.layout.Background; 37 import javafx.scene.layout.BackgroundFill; 38 import javafx.scene.layout.Border; 39 import javafx.scene.layout.CornerRadii; 40 import javafx.scene.layout.Region; 41 import test.robot.testharness.VisualTestBase; 42 43 /** 44 */ 45 public abstract class RegionUITestBase extends VisualTestBase { 46 static final int WIDTH = 400; 47 static final int HEIGHT = 300; 48 static final int REGION_TOP = 50; 49 static final int REGION_LEFT = 50; 50 static final int REGION_RIGHT = 350; 51 static final int REGION_BOTTOM = 250; 52 static final int REGION_WIDTH = 300; 53 static final int REGION_HEIGHT = 200; 54 static final Color SCENE_FILL = Color.WHITE; 55 56 protected Stage stage; 57 protected Scene scene; 58 protected Group root; 59 protected Region region; 60 61 @Override 62 public void doSetup() { 63 super.doSetup(); 64 runAndWait(() -> { 65 stage = getStage(); 66 region = new Region(); 67 region.setPrefSize(REGION_WIDTH, REGION_HEIGHT); 68 region.relocate(REGION_LEFT, REGION_TOP); 69 scene = new Scene(root = new Group(region), WIDTH, HEIGHT); 70 scene.setFill(SCENE_FILL); 71 stage.setScene(scene); 72 stage.show(); 73 }); 74 } 75 76 protected void setStyle(final String style) { 77 runAndWait(() -> region.setStyle(style)); 78 waitFirstFrame(); 79 } 80 81 static final double TOLERANCE = 0.07; 82 83 protected void assertColorEquals(Color expected, int x, int y, double tolerance) { 84 Color actual = getColorThreadSafe(x, y); 85 try { 86 assertColorEquals(expected, actual, tolerance); 87 } catch (AssertionError error) { 88 throw new AssertionError(error.getMessage() + " at position x=" + x + ", y=" + y); 89 } 90 } 91 92 protected void assertColorDoesNotEqual(Color notExpected, int x, int y, double tolerance) { 93 Color actual = getColorThreadSafe(x, y); 94 assertColorDoesNotEqual(notExpected, actual, tolerance); 95 } 96 97 private Color getColorThreadSafe(int x, int y) { 98 AtomicReference<Color> color = new AtomicReference<>(); 99 runAndWait(() -> color.set(getColor(scene, x, y))); 100 return color.get(); 101 } 102 103 /** 104 * Using the information available in Background and Border for the region, 105 * this method will check that the scene color is used for those places that 106 * are outside the region boundary (including the empty part of a rounded corner) 107 * and that the expected color is used for the interior of the region. If the 108 * given expected color is null, then this test will just make sure that 109 * the color IS NOT the scene background. For example, when doing a gradient fill 110 * or image fill it might not be possible to really know the expected color, but 111 * at least we can make sure we're not seeing the scene background color. 112 */ 113 protected void checkRegionCornersAndBoundariesForFills() { 114 Background background = region.getBackground(); 115 Border border = region.getBorder(); 116 if (border != null) { 117 throw new AssertionError("No implementation of this method for borders yet"); 118 } 119 120 if (region.getShape() != null) { 121 throw new AssertionError("No implementation of this method for regions with a shape yet"); 122 } 123 124 Paint lastFill = SCENE_FILL; 125 for (BackgroundFill fill : background.getFills()) { 126 checkRegionCornersAndBoundariesOfBackgroundFill(fill, lastFill); 127 lastFill = fill.getFill(); 128 } 129 } 130 131 protected void checkRegionLeftBoundary(BackgroundFill fill, Paint outsideFill) { 132 checkRegionLeftBoundary(fill, fill.getFill(), outsideFill); 133 } 134 135 protected void checkRegionLeftBoundary(BackgroundFill fill, Paint insideFill, Paint outsideFill) { 136 checkRegionLeftBoundary(fill, insideFill, outsideFill, 1, TOLERANCE); 137 } 138 139 protected void checkRegionLeftBoundary(BackgroundFill fill, Paint insideFill, Paint outsideFill, int distance, double tolerance) { 140 checkRegionLeftBoundary(fill.getInsets().getLeft(), insideFill, outsideFill, distance, tolerance); 141 } 142 143 protected void checkRegionLeftBoundary(Paint outsideFill) { 144 checkRegionLeftBoundary(0, null, outsideFill, 1, TOLERANCE); 145 } 146 147 protected void checkRegionLeftBoundary(double leftInset, Paint insideFill, Paint outsideFill, int distance, double tolerance) { 148 List<TestParameters> params = new ArrayList<>(); 149 final int x1 = (int) (region.getLayoutX() + leftInset); 150 final int midY = HEIGHT / 2; 151 params.add(new TestParameters(outsideFill, insideFill, x1 - 1 - distance, midY, tolerance)); // left outside 152 params.add(new TestParameters(insideFill, outsideFill, x1 + distance, midY, tolerance)); // left inside 153 runTests(params); 154 } 155 156 protected void checkRegionTopBoundary(BackgroundFill fill, Paint outsideFill) { 157 checkRegionTopBoundary(fill, fill.getFill(), outsideFill); 158 } 159 160 protected void checkRegionTopBoundary(BackgroundFill fill, Paint insideFill, Paint outsideFill) { 161 checkRegionTopBoundary(fill, insideFill, outsideFill, 1, TOLERANCE); 162 } 163 164 protected void checkRegionTopBoundary(BackgroundFill fill, Paint insideFill, Paint outsideFill, int distance, double tolerance) { 165 double outermostTop = fill.getInsets().getTop(); 166 checkRegionTopBoundary(outermostTop, insideFill, outsideFill, distance, tolerance); 167 } 168 169 protected void checkRegionTopBoundary(Paint outsideFill) { 170 checkRegionTopBoundary(0, null, outsideFill, 1, TOLERANCE); 171 } 172 173 protected void checkRegionTopBoundary(double topInset, Paint insideFill, Paint outsideFill, int distance, double tolerance) { 174 List<TestParameters> params = new LinkedList<>(); 175 final int y1 = (int) (region.getLayoutY() + topInset); 176 final int midX = WIDTH / 2; 177 params.add(new TestParameters(outsideFill, insideFill, midX, y1 - 1 - distance, tolerance)); // top outside 178 params.add(new TestParameters(insideFill, outsideFill, midX, y1 + distance, tolerance)); // top inside 179 runTests(params); 180 } 181 182 protected void checkRegionRightBoundary(BackgroundFill fill, Paint outsideFill) { 183 checkRegionRightBoundary(fill, fill.getFill(), outsideFill); 184 } 185 186 protected void checkRegionRightBoundary(BackgroundFill fill, Paint insideFill, Paint outsideFill) { 187 checkRegionRightBoundary(fill, insideFill, outsideFill, 1, TOLERANCE); 188 } 189 190 protected void checkRegionRightBoundary(BackgroundFill fill, Paint insideFill, Paint outsideFill, int distance, double tolerance) { 191 double outermostRight = fill.getInsets().getRight(); 192 checkRegionRightBoundary(outermostRight, insideFill, outsideFill, distance, tolerance); 193 } 194 195 protected void checkRegionRightBoundary(Paint outsideFill) { 196 checkRegionRightBoundary(0, null, outsideFill, 1, TOLERANCE); 197 } 198 199 protected void checkRegionRightBoundary(double rightInset, Paint insideFill, Paint outsideFill, int distance, double tolerance) { 200 List<TestParameters> params = new LinkedList<>(); 201 final int x2 = (int) Math.ceil(region.getLayoutX() + region.getWidth() - rightInset); 202 final int midY = HEIGHT / 2; 203 params.add(new TestParameters(outsideFill, insideFill, x2 + distance, midY, tolerance)); // right outside 204 params.add(new TestParameters(insideFill, outsideFill, x2 - 1 - distance, midY, tolerance)); // right inside 205 runTests(params); 206 } 207 208 protected void checkRegionBottomBoundary(BackgroundFill fill, Paint outsideFill) { 209 checkRegionBottomBoundary(fill, fill.getFill(), outsideFill); 210 } 211 212 protected void checkRegionBottomBoundary(BackgroundFill fill, Paint insideFill, Paint outsideFill) { 213 checkRegionBottomBoundary(fill, insideFill, outsideFill, 1, TOLERANCE); 214 } 215 216 protected void checkRegionBottomBoundary(BackgroundFill fill, Paint insideFill, Paint outsideFill, int distance, double tolerance) { 217 double outermostBottom = fill.getInsets().getBottom(); 218 checkRegionBottomBoundary(outermostBottom, insideFill, outsideFill, distance, tolerance); 219 } 220 221 protected void checkRegionBottomBoundary(Paint outsideFill) { 222 checkRegionBottomBoundary(0, null, outsideFill, 0, TOLERANCE); 223 } 224 225 protected void checkRegionBottomBoundary(double bottomInset, Paint insideFill, Paint outsideFill, int distance, double tolerance) { 226 List<TestParameters> params = new LinkedList<>(); 227 final int y2 = (int) Math.ceil(region.getLayoutY() + region.getHeight() - bottomInset); 228 final int midX = WIDTH / 2; 229 params.add(new TestParameters(outsideFill, insideFill, midX, y2 + distance, tolerance)); // bottom outside 230 params.add(new TestParameters(insideFill, outsideFill, midX, y2 - 1 - distance, tolerance)); // bottom inside 231 runTests(params); 232 } 233 234 // .5 * 1/Math.sqrt(2). If you multiply this value by the diameter of the 235 // circle, then you will get half the length of the side of the square which 236 // is inscribed within the circle. This will tell me the point of the arc where 237 // I want to do my testing. 238 static final float HALF_SQRT_HALF = .35355339059327f; 239 240 protected void checkRegionTopLeftCorner(BackgroundFill fill, Paint outsideFill) { 241 checkRegionTopLeftCorner(fill, fill.getFill(), outsideFill); 242 } 243 244 protected void checkRegionTopLeftCorner(BackgroundFill fill, Paint insideFill, Paint outsideFill) { 245 checkRegionTopLeftCorner(fill, insideFill, outsideFill, 1, TOLERANCE); 246 } 247 248 protected void checkRegionTopLeftCorner(BackgroundFill fill, Paint insideFill, Paint outsideFill, int distance, double tolerance) { 249 List<TestParameters> params = new LinkedList<>(); 250 251 // Now check the corners of each side. Each corner is a quarter-ellipse. What needs to be done is, 252 // for each background fill, figure out the x,y that constitutes the center of the arc (45 degrees) 253 // and then, for each point, find the one that is closest to the corner of the window. That point 254 // will be the one that constitutes the edge of the arc, and we can then test just outside that point 255 // and just inside that point. 256 257 final CornerRadii radii = fill.getRadii(); 258 double hr = radii.getTopLeftHorizontalRadius(); 259 double h = radii.isTopLeftHorizontalRadiusAsPercentage() ? hr * region.getWidth() : hr; 260 double vr = radii.getTopLeftVerticalRadius(); 261 double v = radii.isTopLeftVerticalRadiusAsPercentage() ? vr * region.getHeight() : vr; 262 double topLeftX = region.getLayoutX() + (fill.getInsets().getLeft() + (h - h * 2 * HALF_SQRT_HALF)); 263 double topLeftY = region.getLayoutY() + (fill.getInsets().getTop() + (v - v * 2 * HALF_SQRT_HALF)); 264 params.add(new TestParameters(outsideFill, insideFill, (int) topLeftX - 1 - distance, (int) topLeftY - 1 - distance, tolerance)); // outside 265 params.add(new TestParameters(insideFill, outsideFill, (int) topLeftX + distance, (int) topLeftY + distance, tolerance)); // inside 266 runTests(params); 267 } 268 269 protected void checkRegionTopRightCorner(BackgroundFill fill, Paint outsideFill) { 270 checkRegionTopRightCorner(fill, fill.getFill(), outsideFill); 271 } 272 273 protected void checkRegionTopRightCorner(BackgroundFill fill, Paint insideFill, Paint outsideFill) { 274 checkRegionTopRightCorner(fill, insideFill, outsideFill, 1, TOLERANCE); 275 } 276 277 protected void checkRegionTopRightCorner(BackgroundFill fill, Paint insideFill, Paint outsideFill, int distance, double tolerance) { 278 List<TestParameters> params = new LinkedList<>(); 279 final CornerRadii radii = fill.getRadii(); 280 double hr = radii.getTopRightHorizontalRadius(); 281 double h = radii.isTopRightHorizontalRadiusAsPercentage() ? hr * region.getWidth() : hr; 282 double vr = radii.getTopRightVerticalRadius(); 283 double v = radii.isTopRightVerticalRadiusAsPercentage() ? vr * region.getHeight() : vr; 284 double topRightX = region.getLayoutX() + region.getWidth() - (fill.getInsets().getRight() + (h - h * 2 * HALF_SQRT_HALF)); 285 double topRightY = region.getLayoutY() + (fill.getInsets().getTop() + (v - v * 2 * HALF_SQRT_HALF)); 286 params.add(new TestParameters(outsideFill, insideFill, (int) Math.ceil(topRightX + distance), (int) topRightY - 1 - distance, tolerance)); // outside 287 params.add(new TestParameters(insideFill, outsideFill, (int) Math.ceil(topRightX - distance) - 1, (int) topRightY + distance, tolerance)); // inside 288 runTests(params); 289 } 290 291 protected void checkRegionBottomRightCorner(BackgroundFill fill, Paint outsideFill) { 292 checkRegionBottomRightCorner(fill, fill.getFill(), outsideFill); 293 } 294 295 protected void checkRegionBottomRightCorner(BackgroundFill fill, Paint insideFill, Paint outsideFill) { 296 checkRegionBottomRightCorner(fill, insideFill, outsideFill, 1, TOLERANCE); 297 } 298 299 protected void checkRegionBottomRightCorner(BackgroundFill fill, Paint insideFill, Paint outsideFill, int distance, double tolerance) { 300 List<TestParameters> params = new LinkedList<>(); 301 final CornerRadii radii = fill.getRadii(); 302 double hr = radii.getBottomRightHorizontalRadius(); 303 double h = radii.isBottomRightHorizontalRadiusAsPercentage() ? hr * region.getWidth() : hr; 304 double vr = radii.getBottomRightVerticalRadius(); 305 double v = radii.isBottomRightVerticalRadiusAsPercentage() ? vr * region.getHeight() : vr; 306 double bottomRightX = region.getLayoutX() + region.getWidth() - (fill.getInsets().getRight() + (h - h * 2 * HALF_SQRT_HALF)); 307 double bottomRightY = region.getLayoutY() + region.getHeight() - (fill.getInsets().getBottom() + (v - v * 2 * HALF_SQRT_HALF)); 308 params.add(new TestParameters(outsideFill, insideFill, (int) Math.ceil(bottomRightX + distance), (int) Math.ceil(bottomRightY + distance), tolerance)); // outside 309 params.add(new TestParameters(insideFill, outsideFill, (int) Math.ceil(bottomRightX - distance) - 1, (int) Math.ceil(bottomRightY - distance) - 1, tolerance)); // inside 310 runTests(params); 311 } 312 313 protected void checkRegionBottomLeftCorner(BackgroundFill fill, Paint outsideFill) { 314 checkRegionBottomLeftCorner(fill, fill.getFill(), outsideFill); 315 } 316 317 protected void checkRegionBottomLeftCorner(BackgroundFill fill, Paint insideFill, Paint outsideFill) { 318 checkRegionBottomLeftCorner(fill, insideFill, outsideFill, 1, TOLERANCE); 319 } 320 321 protected void checkRegionBottomLeftCorner(BackgroundFill fill, Paint insideFill, Paint outsideFill, int distance, double tolerance) { 322 List<TestParameters> params = new LinkedList<>(); 323 final CornerRadii radii = fill.getRadii(); 324 double hr = radii.getBottomLeftHorizontalRadius(); 325 double h = radii.isBottomLeftHorizontalRadiusAsPercentage() ? hr * region.getWidth() : hr; 326 double vr = radii.getBottomLeftVerticalRadius(); 327 double v = radii.isBottomLeftVerticalRadiusAsPercentage() ? vr * region.getHeight() : vr; 328 double bottomLeftX = region.getLayoutX() + (fill.getInsets().getLeft() + (h - h * 2 * HALF_SQRT_HALF)); 329 double bottomLeftY = region.getLayoutY() + region.getHeight() - (fill.getInsets().getBottom() + (v - v * 2 * HALF_SQRT_HALF)); 330 params.add(new TestParameters(outsideFill, insideFill, (int) bottomLeftX - 1 - distance, (int) Math.ceil(bottomLeftY + distance), tolerance)); // outside 331 params.add(new TestParameters(insideFill, outsideFill, (int) bottomLeftX + distance, (int) Math.ceil(bottomLeftY - 1 - distance), tolerance)); // inside 332 runTests(params); 333 } 334 335 protected void checkRegionCornersAndBoundariesOfBackgroundFill(BackgroundFill fill, Paint outsideFill) { 336 checkRegionCornersAndBoundariesOfBackgroundFill(fill, fill.getFill(), outsideFill); 337 } 338 339 protected void checkRegionCornersAndBoundariesOfBackgroundFill(BackgroundFill fill, Paint insideFill, Paint lastFill) { 340 checkRegionCornersAndBoundariesOfBackgroundFill(fill, insideFill, lastFill, 1, TOLERANCE); 341 } 342 343 protected void checkRegionCornersAndBoundariesOfBackgroundFill(BackgroundFill fill, Paint insideFill, Paint lastFill, int distance, double tolerance) { 344 checkRegionLeftBoundary(fill, insideFill, lastFill, distance, tolerance); 345 checkRegionTopLeftCorner(fill, insideFill, lastFill, distance, tolerance); 346 checkRegionTopBoundary(fill, insideFill, lastFill, distance, tolerance); 347 checkRegionTopRightCorner(fill, insideFill, lastFill, distance, tolerance); 348 checkRegionRightBoundary(fill, insideFill, lastFill, distance, tolerance); 349 checkRegionBottomRightCorner(fill, insideFill, lastFill, distance, tolerance); 350 checkRegionBottomBoundary(fill, insideFill, lastFill, distance, tolerance); 351 checkRegionBottomLeftCorner(fill, insideFill, lastFill, distance, tolerance); 352 } 353 354 private void runTests(List<TestParameters> params) { 355 for (TestParameters p : params) { 356 boolean exactMatch = p.expected instanceof Color; 357 if (exactMatch) { 358 assertColorEquals((Color) p.expected, p.x, p.y, p.tolerance); 359 } else { 360 if (!(p.notExpected instanceof Color) || 361 p.expected == p.notExpected || 362 (p.expected != null && p.expected.equals(p.notExpected))) { 363 // I won't be able to distinguish, so just have to skip 364 System.err.println("WARNING: Had to skip RegionUITest case because there was not an easy " + 365 "way to distinguish pass vs. fail"); 366 continue; 367 } 368 assertColorDoesNotEqual((Color) p.notExpected, p.x, p.y, p.tolerance); 369 } 370 } 371 } 372 373 private static final class TestParameters { 374 Paint expected; 375 Paint notExpected; 376 int x, y; 377 double tolerance; 378 379 TestParameters(Paint expected, Paint notExpected, int x, int y, double tolerance) { 380 this.expected = expected; 381 this.notExpected = notExpected; 382 this.x = x; 383 this.y = y; 384 this.tolerance = tolerance; 385 } 386 } 387 }