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 }