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