1 /*
   2  * Copyright (c) 2010, 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  * questions.
  24  */
  25 
  26 package test.javafx.scene;
  27 
  28 import test.javafx.scene.shape.TestUtils;
  29 import test.javafx.scene.shape.CircleTest;
  30 import com.sun.javafx.geom.PickRay;
  31 import com.sun.javafx.geom.transform.Affine2D;
  32 import com.sun.javafx.geom.transform.Affine3D;
  33 import com.sun.javafx.geom.transform.BaseTransform;
  34 import com.sun.javafx.geom.transform.Translate2D;
  35 import test.com.sun.javafx.pgstub.StubToolkit;
  36 import com.sun.javafx.scene.DirtyBits;
  37 import com.sun.javafx.scene.NodeHelper;
  38 import com.sun.javafx.scene.input.PickResultChooser;
  39 import com.sun.javafx.scene.shape.RectangleHelper;
  40 import com.sun.javafx.sg.prism.NGGroup;
  41 import com.sun.javafx.sg.prism.NGNode;
  42 import com.sun.javafx.sg.prism.NGRectangle;
  43 import test.com.sun.javafx.test.objects.TestScene;
  44 import test.com.sun.javafx.test.objects.TestStage;
  45 import com.sun.javafx.tk.Toolkit;
  46 import com.sun.javafx.util.Utils;
  47 import javafx.beans.property.*;
  48 import javafx.geometry.BoundingBox;
  49 import javafx.geometry.Bounds;
  50 import javafx.geometry.NodeOrientation;
  51 import javafx.geometry.Point2D;
  52 import javafx.geometry.Point3D;
  53 import javafx.scene.effect.DropShadow;
  54 import javafx.scene.effect.Effect;
  55 import javafx.scene.shape.*;
  56 import javafx.scene.transform.Rotate;
  57 import javafx.scene.transform.Transform;
  58 import org.junit.Rule;
  59 import org.junit.Test;
  60 import org.junit.rules.ExpectedException;
  61 
  62 import java.lang.reflect.Method;
  63 import java.util.Comparator;
  64 import javafx.scene.Group;
  65 import javafx.scene.GroupShim;
  66 import javafx.scene.Node;
  67 import javafx.scene.NodeShim;
  68 import javafx.scene.ParallelCamera;
  69 import javafx.scene.ParentShim;
  70 import javafx.scene.PerspectiveCamera;
  71 import javafx.scene.Scene;
  72 import javafx.scene.SceneShim;
  73 import javafx.scene.SubScene;
  74 import javafx.scene.layout.AnchorPane;
  75 import javafx.scene.transform.Affine;
  76 import javafx.scene.transform.Scale;
  77 import javafx.scene.transform.Shear;
  78 import javafx.scene.transform.Translate;
  79 import javafx.stage.Stage;
  80 
  81 import static org.junit.Assert.*;
  82 /**
  83  * Tests various aspects of Node.
  84  *
  85  */
  86 public class NodeTest {
  87     @Rule
  88     public ExpectedException thrown = ExpectedException.none();
  89 
  90     // Things to test:
  91         // When parent is changed, should cursor on toolkit change as well if
  92         // the current node has the mouse over it and didn't explicitly set a
  93         // cursor??
  94 
  95         // Test CSS integration
  96 
  97         // Events:
  98             // Events should *not* be delivered to invisible nodes as per the
  99             // specification for visible
 100 
 101         // A Node must lose focus when it is no longer visible
 102 
 103         // A node made invisible must cause the cursor to be updated
 104 
 105         // Setting the cursor should override the parent cursor when hover
 106         // (test that this happens both when the node already has hover set and
 107         // when hover is changed to true)
 108 
 109         // Setting the cursor to null should revert to parent cursor when hover
 110         // (test that this happens both when the node already has hover set and
 111         // when hover is changed to true)
 112 
 113         // Clip:
 114             // Test setting/clearing the clip affects the bounds
 115             // Test changing bounds / smooth / etc on clip updates bounds of clipped Node
 116 
 117         // Effect:
 118             // Test setting/clearing the effect affects the bounds
 119             // Test changing state on Effect updates bounds of Node
 120 
 121         // Test that a disabled Group affects the disabled property of child nodes
 122 
 123         // Test contains, intersects methods
 124         // Test parentToLocal/localToStage/etc
 125 
 126         // Test computeCompleteBounds
 127         // (other bounds test situtations explicitly tested in BoundsTest)
 128 
 129         // Test transforms end up setting the correct matrix on the peer
 130         // In particular, test that pivots are taken correctly into account
 131 
 132         // Test hover is updated when mouse enters
 133         // Test hover is updated when mouse exists
 134         // Test hover is updated when mouse was over but a higher node then
 135         // turns on blocks mouse
 136         // Test hover is updated when node moves out from under the cursor
 137         // TODO most of these cases cannot be handled until/unless we update
 138         // the list of nodes under the cursor on pulse events
 139 
 140         // Test pressed is updated when mouse is pressed
 141         // Test pressed is updated when mouse is released
 142         // TODO shoudl pressed obey the semantics of a button that is armed & pressed?
 143         // Or should "armed" be put on Node? What to do here?
 144 
 145         // Test various onMouseXXX event handlers
 146 
 147         // Test onKeyXXX handlers
 148 
 149         // Test focused is updated?
 150         // Test nodes which are not focusable are not focused!
 151         // Test focus... (SHOULD NOT DEPEND ON KEY LISTENERS BEING INSTALLED!!)
 152 
 153         // Test that clip is taken into account for both "contains" and
 154         // "intersects". See http://javafx-jira.kenai.com/browse/RT-646
 155 
 156 
 157 
 158     /***************************************************************************
 159      *                                                                         *
 160      *                              Basic Node Tests                           *
 161      *                                                                         *
 162      **************************************************************************/
 163 
 164 // TODO disable this because it depends on TestNode
 165 //    @Test public void testPeerNotifiedOfVisibilityChanges() {
 166 //        Rectangle rect = new Rectangle();
 167 //        Node peer = rect.impl_getPGNode();
 168 //        assertEquals(peer.visible, rect.visible);
 169 //
 170 //        rect.visible = false;
 171 //        assertEquals(peer.visible, rect.visible);
 172 //
 173 //        rect.visible = true;
 174 //        assertEquals(peer.visible, rect.visible);
 175 //    }
 176 
 177     /***************************************************************************
 178      *                                                                         *
 179      *                            Testing Node Bounds                          *
 180      *                                                                         *
 181      **************************************************************************/
 182 
 183 // TODO disable this because it depends on TestNode
 184 //     public function testContainsCallsPeer():Void {
 185 //         var rect = Rectangle { };
 186 //         var peer = rect.impl_getPGNode() as TestNode;
 187 //         peer.numTimesContainsCalled = 0;
 188 //
 189 //         rect.contains(0, 0);
 190 //         assertEquals(1, peer.numTimesContainsCalled);
 191 //
 192 //         rect.contains(Point2D { x:10, y:10 });
 193 //         assertEquals(2, peer.numTimesContainsCalled);
 194 //     }
 195 
 196 // TODO disable this because it depends on TestNode
 197 //     public function testIntersectsCallsPeer():Void {
 198 //         var rect = Rectangle { };
 199 //         var peer = rect.impl_getPGNode() as TestNode;
 200 //         peer.numTimesIntersectsCalled = 0;
 201 //
 202 //         rect.intersects(0, 0, 10, 10);
 203 //         assertEquals(1, peer.numTimesIntersectsCalled);
 204 //
 205 //         rect.intersects(BoundingBox { minX:10, minY:10, width:100, height:100 });
 206 //         assertEquals(2, peer.numTimesIntersectsCalled);
 207 //     }
 208 
 209     /***************************************************************************
 210      *                                                                         *
 211      *                          Testing Node transforms                        *
 212      *                                                                         *
 213      **************************************************************************/
 214 
 215     /**
 216      * Tests that the function which converts a com.sun.javafx.geom.Point2D
 217      * in parent coords to local coords works properly.
 218      */
 219     @Test public void testParentToLocalGeomPoint() {
 220         Rectangle rect = new Rectangle();
 221         rect.setTranslateX(10);
 222         rect.setTranslateY(10);
 223         rect.setWidth(100);
 224         rect.setHeight(100);
 225         rect.getTransforms().clear();
 226         rect.getTransforms().addAll(Transform.scale(2, 2), Transform.translate(30, 30));
 227 
 228         Point2D pt = new Point2D(0, 0);
 229         pt = rect.parentToLocal(pt);
 230         assertEquals(new Point2D(-35, -35), pt);
 231     }
 232 
 233     // TODO need to test with some observableArrayList of transforms which cannot be
 234     // cleanly inverted so that we can test that code path
 235 
 236     @Test public void testLocalToParentGeomPoint() {
 237         Rectangle rect = new Rectangle();
 238         rect.setTranslateX(10);
 239         rect.setTranslateY(10);
 240         rect.setWidth(100);
 241         rect.setHeight(100);
 242         rect.getTransforms().clear();
 243         rect.getTransforms().addAll(Transform.scale(2, 2), Transform.translate(30, 30));
 244 
 245         Point2D pt = new Point2D(0, 0);
 246         pt = rect.localToParent(pt);
 247         assertEquals(new Point2D(70, 70), pt);
 248     }
 249 
 250     @Test public void testPickingNodeDirectlyNoTransforms() {
 251         Rectangle rect = new Rectangle();
 252         rect.setX(10);
 253         rect.setY(10);
 254         rect.setWidth(100);
 255         rect.setHeight(100);
 256 
 257         // needed since picking doesn't work unless rooted in a scene and visible
 258         Scene scene = new Scene(new Group());
 259         ParentShim.getChildren(scene.getRoot()).add(rect);
 260 
 261         PickResultChooser res = new PickResultChooser();
 262         NodeHelper.pickNode(rect, new PickRay(50, 50, 1, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), res);
 263         assertSame(rect, res.getIntersectedNode());
 264         res = new PickResultChooser();
 265         NodeHelper.pickNode(rect, new PickRay(0, 0, 1, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), res);
 266         assertNull(res.getIntersectedNode());
 267     }
 268 
 269     @Test public void testPickingNodeDirectlyWithTransforms() {
 270         Rectangle rect = new Rectangle();
 271         rect.setTranslateX(10);
 272         rect.setTranslateY(10);
 273         rect.setWidth(100);
 274         rect.setHeight(100);
 275 
 276         // needed since picking doesn't work unless rooted in a scene and visible
 277         Scene scene = new Scene(new Group());
 278         ParentShim.getChildren(scene.getRoot()).add(rect);
 279 
 280         PickResultChooser res = new PickResultChooser();
 281         NodeHelper.pickNode(rect, new PickRay(50, 50, 1, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), res);
 282         assertSame(rect, res.getIntersectedNode());
 283         res = new PickResultChooser();
 284         NodeHelper.pickNode(rect, new PickRay(0, 0, 1, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), res);
 285         assertNull(res.getIntersectedNode());
 286     }
 287 
 288     @Test public void testEffectSharedOnNodes() {
 289         Effect effect = new DropShadow();
 290         Rectangle node = new Rectangle();
 291         node.setEffect(effect);
 292 
 293         Rectangle node2 = new Rectangle();
 294         node2.setEffect(effect);
 295 
 296         assertEquals(effect, node.getEffect());
 297         assertEquals(effect, node2.getEffect());
 298     }
 299 
 300     public static void testBooleanPropertyPropagation(
 301         final Node node,
 302         final String propertyName,
 303         final boolean initialValue,
 304         final boolean newValue) throws Exception {
 305 
 306         final StringBuilder propertyNameBuilder = new StringBuilder(propertyName);
 307         propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0)));
 308         final String setterName = new StringBuilder("set").append(propertyNameBuilder).toString();
 309         final String getterName = new StringBuilder("is").append(propertyNameBuilder).toString();
 310 
 311         final Class<? extends Node> nodeClass = node.getClass();
 312         final Method setter = nodeClass.getMethod(setterName, boolean.class);
 313         final Method getter = nodeClass.getMethod(getterName);
 314 
 315         final NGNode peer = NodeHelper.getPeer(node);
 316         final Class<? extends NGNode> impl_class = peer.getClass();
 317         final Method impl_getter = impl_class.getMethod(getterName);
 318 
 319 
 320         // 1. Create test scene
 321         final Scene scene = new Scene(new Group());
 322         ParentShim.getChildren(scene.getRoot()).add(node);
 323 
 324         // 2. Initial setup
 325         setter.invoke(node, initialValue);
 326         NodeHelper.syncPeer(node);
 327         assertEquals(initialValue, getter.invoke(node));
 328         assertEquals(initialValue, impl_getter.invoke(peer));
 329 
 330         // 3. Change value of the property
 331         setter.invoke(node, newValue);
 332 
 333         // 4. Check that the property value has changed but has not propagated to PGNode
 334         assertEquals(newValue, getter.invoke(node));
 335         assertEquals(initialValue, impl_getter.invoke(peer));
 336 
 337         // 5. Propagate the property value to PGNode
 338         NodeHelper.syncPeer(node);
 339 
 340         // 6. Check that the value has been propagated to PGNode
 341         assertEquals(newValue, impl_getter.invoke(peer));
 342     }
 343 
 344 
 345     public static void testFloatPropertyPropagation(
 346         final Node node,
 347         final String propertyName,
 348         final float initialValue,
 349         final float newValue) throws Exception {
 350 
 351         testFloatPropertyPropagation(node, propertyName, propertyName, initialValue, newValue);
 352     }
 353 
 354     public static void syncNode(Node node) {
 355         NodeShim.updateBounds(node);
 356         NodeHelper.syncPeer(node);
 357     }
 358 
 359     public static void assertBooleanPropertySynced(
 360             final Node node,
 361             final String propertyName,
 362             final String pgPropertyName,
 363             final boolean value) throws Exception {
 364 
 365         final Scene scene = new Scene(new Group(), 500, 500);
 366 
 367         final StringBuilder propertyNameBuilder = new StringBuilder(propertyName);
 368         propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0)));
 369         final String getterName = new StringBuilder("is").append(propertyNameBuilder).toString();
 370         Method getterMethod = node.getClass().getMethod(getterName, new Class[]{});
 371         Boolean defaultValue = (Boolean)getterMethod.invoke(node);
 372         BooleanProperty v = new SimpleBooleanProperty(defaultValue);
 373 
 374         Method modelMethod = node.getClass().getMethod(
 375                 propertyName + "Property",
 376                 new Class[]{});
 377         BooleanProperty model = (BooleanProperty)modelMethod.invoke(node);
 378         model.bind(v);
 379 
 380         ParentShim.getChildren(scene.getRoot()).add(node);
 381 
 382         NodeTest.syncNode(node);
 383         assertEquals(defaultValue, TestUtils.getBooleanValue(node, pgPropertyName));
 384 
 385         v.set(value);
 386         NodeTest.syncNode(node);
 387         assertEquals(value, TestUtils.getBooleanValue(node, pgPropertyName));
 388     }
 389 
 390     public static void assertIntPropertySynced(
 391             final Node node,
 392             final String propertyName,
 393             final String pgPropertyName,
 394             final int value) throws Exception {
 395 
 396         final Scene scene = new Scene(new Group(), 500, 500);
 397 
 398         final StringBuilder propertyNameBuilder = new StringBuilder(propertyName);
 399         propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0)));
 400         final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString();
 401         Method getterMethod = node.getClass().getMethod(getterName, new Class[]{});
 402         Integer defaultValue = (Integer)getterMethod.invoke(node);
 403         IntegerProperty v = new SimpleIntegerProperty(defaultValue);
 404 
 405         Method modelMethod = node.getClass().getMethod(
 406                 propertyName + "Property",
 407                 new Class[]{});
 408         IntegerProperty model = (IntegerProperty)modelMethod.invoke(node);
 409         model.bind(v);
 410 
 411         ParentShim.getChildren(scene.getRoot()).add(node);
 412 
 413         NodeTest.syncNode(node);
 414         assertTrue(numbersEquals(defaultValue,
 415                 (Number)TestUtils.getObjectValue(node, pgPropertyName)));
 416 
 417         v.set(value);
 418         NodeTest.syncNode(node);
 419         assertTrue(numbersEquals(new Integer(value),
 420                 (Number)TestUtils.getObjectValue(node, pgPropertyName)));
 421     }
 422 
 423     public static boolean numbersEquals(Number expected, Number value) {
 424         return numbersEquals(expected, value, 0.001);
 425     }
 426 
 427     public static boolean numbersEquals(Number expected, Number value, double delta) {
 428         boolean res = (Math.abs(expected.doubleValue() - value.doubleValue()) < delta);
 429         if (!res) {
 430             System.err.println("expected=" + expected + ", value=" + value);
 431         }
 432         return res;
 433     }
 434 
 435     public static void assertDoublePropertySynced(
 436             final Node node,
 437             final String propertyName,
 438             final String pgPropertyName,
 439             final double value) throws Exception {
 440 
 441         final Scene scene = new Scene(new Group(), 500, 500);
 442 
 443         final StringBuilder propertyNameBuilder = new StringBuilder(propertyName);
 444         propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0)));
 445         final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString();
 446         Method getterMethod = node.getClass().getMethod(getterName, new Class[]{});
 447         Double defaultValue = (Double)getterMethod.invoke(node);
 448         DoubleProperty v = new SimpleDoubleProperty(defaultValue);
 449 
 450         Method modelMethod = node.getClass().getMethod(
 451                 propertyName + "Property",
 452                 new Class[]{});
 453         DoubleProperty model = (DoubleProperty)modelMethod.invoke(node);
 454         model.bind(v);
 455 
 456         ParentShim.getChildren(scene.getRoot()).add(node);
 457 
 458         NodeTest.syncNode(node);
 459         assertTrue(numbersEquals(defaultValue,
 460                 (Number)TestUtils.getObjectValue(node, pgPropertyName)));
 461 
 462         v.set(value);
 463         NodeTest.syncNode(node);
 464         assertTrue(numbersEquals(new Double(value),
 465                 (Number)TestUtils.getObjectValue(node, pgPropertyName)));
 466     }
 467 
 468 
 469     public static void assertObjectPropertySynced(
 470             final Node node,
 471             final String propertyName,
 472             final String pgPropertyName,
 473             final Object value) throws Exception {
 474 
 475         final Scene scene = new Scene(new Group(), 500, 500);
 476 
 477         final StringBuilder propertyNameBuilder = new StringBuilder(propertyName);
 478         propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0)));
 479         final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString();
 480         Method getterMethod = node.getClass().getMethod(getterName, new Class[]{});
 481         Object defaultValue = getterMethod.invoke(node);
 482         ObjectProperty v = new SimpleObjectProperty(defaultValue);
 483 
 484         Method modelMethod = node.getClass().getMethod(
 485                 propertyName + "Property",
 486                 new Class[]{});
 487         ObjectProperty model = (ObjectProperty)modelMethod.invoke(node);
 488         model.bind(v);
 489 
 490         ParentShim.getChildren(scene.getRoot()).add(node);
 491 
 492         NodeTest.syncNode(node);
 493         // sometimes enum is used on node but int on PGNode
 494         Object result1 = TestUtils.getObjectValue(node, pgPropertyName);
 495         if (result1 instanceof Integer) {
 496             assertTrue(((Enum)defaultValue).ordinal() == ((Integer)result1).intValue());
 497         } else {
 498             assertEquals(defaultValue, TestUtils.getObjectValue(node, pgPropertyName));
 499         }
 500 
 501         v.set(value);
 502         NodeTest.syncNode(node);
 503 
 504         Object result2 = TestUtils.getObjectValue(node, pgPropertyName);
 505         if (result2 instanceof Integer) {
 506             assertTrue(((Enum)value).ordinal() == ((Integer)result2).intValue());
 507         } else {
 508             assertEquals(value, TestUtils.getObjectValue(node, pgPropertyName));
 509         }
 510     }
 511 
 512 
 513 
 514     public static void assertObjectProperty_AsStringSynced(
 515             final Node node,
 516             final String propertyName,
 517             final String pgPropertyName,
 518             final Object value) throws Exception {
 519 
 520         final Scene scene = new Scene(new Group(), 500, 500);
 521 
 522         final StringBuilder propertyNameBuilder = new StringBuilder(propertyName);
 523         propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0)));
 524         final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString();
 525         Method getterMethod = node.getClass().getMethod(getterName, new Class[]{});
 526         Object defaultValue = getterMethod.invoke(node);
 527         ObjectProperty v = new SimpleObjectProperty(defaultValue);
 528 
 529         Method modelMethod = node.getClass().getMethod(
 530                 propertyName + "Property",
 531                 new Class[]{});
 532         ObjectProperty model = (ObjectProperty)modelMethod.invoke(node);
 533         model.bind(v);
 534 
 535         ParentShim.getChildren(scene.getRoot()).add(node);
 536 
 537         NodeTest.syncNode(node);
 538         assertEquals(
 539                 defaultValue.toString(),
 540                 TestUtils.getObjectValue(node, pgPropertyName).toString());
 541 
 542         v.set(value);
 543         NodeTest.syncNode(node);
 544 
 545         assertEquals(
 546                 value.toString(),
 547                 TestUtils.getObjectValue(node, pgPropertyName).toString());
 548     }
 549 
 550     public static void assertStringPropertySynced(
 551             final Node node,
 552             final String propertyName,
 553             final String pgPropertyName,
 554             final String value) throws Exception {
 555 
 556         final Scene scene = new Scene(new Group(), 500, 500);
 557 
 558         final StringBuilder propertyNameBuilder = new StringBuilder(propertyName);
 559         propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0)));
 560         final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString();
 561         Method getterMethod = node.getClass().getMethod(getterName, new Class[]{});
 562         String defaultValue = (String)getterMethod.invoke(node);
 563         StringProperty v = new SimpleStringProperty(defaultValue);
 564 
 565         Method modelMethod = node.getClass().getMethod(
 566                 propertyName + "Property",
 567                 new Class[]{});
 568         StringProperty model = (StringProperty)modelMethod.invoke(node);
 569         model.bind(v);
 570 
 571         ParentShim.getChildren(scene.getRoot()).add(node);
 572 
 573         NodeTest.syncNode(node);
 574         assertEquals(
 575                 defaultValue,
 576                 TestUtils.getStringValue(node, pgPropertyName));
 577 
 578         v.set(value);
 579         NodeTest.syncNode(node);
 580 
 581         assertEquals(
 582                 value,
 583                 TestUtils.getStringValue(node, pgPropertyName));
 584     }
 585 
 586     public static void testFloatPropertyPropagation(
 587         final Node node,
 588         final String propertyName,
 589         final String pgPropertyName,
 590         final float initialValue,
 591         final float newValue) throws Exception {
 592 
 593         final StringBuilder propertyNameBuilder = new StringBuilder(propertyName);
 594         propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0)));
 595 
 596         final StringBuilder pgPropertyNameBuilder = new StringBuilder(pgPropertyName);
 597         pgPropertyNameBuilder.setCharAt(0, Character.toUpperCase(pgPropertyName.charAt(0)));
 598 
 599         final String setterName = new StringBuilder("set").append(propertyNameBuilder).toString();
 600         final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString();
 601         final String pgGetterName = new StringBuilder("get").append(pgPropertyNameBuilder).toString();
 602 
 603         final Class<? extends Node> nodeClass = node.getClass();
 604         final Method setter = nodeClass.getMethod(setterName, float.class);
 605         final Method getter = nodeClass.getMethod(getterName);
 606 
 607         final NGNode peer = NodeHelper.getPeer(node);
 608         final Class<? extends NGNode> impl_class = peer.getClass();
 609         final Method impl_getter = impl_class.getMethod(pgGetterName);
 610 
 611 
 612         // 1. Create test scene
 613         final Scene scene = new Scene(new Group());
 614         ParentShim.getChildren(scene.getRoot()).add(node);
 615 
 616         // 2. Initial setup
 617         setter.invoke(node, initialValue);
 618         NodeHelper.syncPeer(node);
 619         assertEquals(initialValue, (Float) getter.invoke(node), 1e-100);
 620         assertEquals(initialValue, (Float) impl_getter.invoke(peer), 1e-100);
 621 
 622         // 3. Change value of the property
 623         setter.invoke(node, newValue);
 624 
 625         // 4. Check that the property value has changed but has not propagated to PGNode
 626         assertEquals(newValue, (Float) getter.invoke(node), 1e-100);
 627         assertEquals(initialValue, (Float) impl_getter.invoke(peer), 1e-100);
 628 
 629         // 5. Propagate the property value to PGNode
 630         NodeHelper.syncPeer(node);
 631 
 632         // 6. Check that the value has been propagated to PGNode
 633         assertEquals(newValue, (Float) impl_getter.invoke(peer), 1e-100);
 634     }
 635 
 636     public static void testDoublePropertyPropagation(
 637         final Node node,
 638         final String propertyName,
 639         final double initialValue,
 640         final double newValue) throws Exception {
 641 
 642         testDoublePropertyPropagation(node, propertyName, propertyName, initialValue, newValue);
 643     }
 644 
 645 
 646     public static void testDoublePropertyPropagation(
 647         final Node node,
 648         final String propertyName,
 649         final String pgPropertyName,
 650         final double initialValue,
 651         final double newValue) throws Exception {
 652 
 653         final StringBuilder propertyNameBuilder = new StringBuilder(propertyName);
 654         propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0)));
 655 
 656         final StringBuilder pgPropertyNameBuilder = new StringBuilder(pgPropertyName);
 657         pgPropertyNameBuilder.setCharAt(0, Character.toUpperCase(pgPropertyName.charAt(0)));
 658 
 659         final String setterName = new StringBuilder("set").append(propertyNameBuilder).toString();
 660         final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString();
 661         final String pgGetterName = new StringBuilder("get").append(pgPropertyNameBuilder).toString();
 662 
 663         final Class<? extends Node> nodeClass = node.getClass();
 664         final Method setter = nodeClass.getMethod(setterName, double.class);
 665         final Method getter = nodeClass.getMethod(getterName);
 666 
 667         final NGNode peer = NodeHelper.getPeer(node);
 668         final Class<? extends NGNode> impl_class = peer.getClass();
 669         final Method impl_getter = impl_class.getMethod(pgGetterName);
 670 
 671 
 672         // 1. Create test scene
 673         final Scene scene = new Scene(new Group());
 674         ParentShim.getChildren(scene.getRoot()).add(node);
 675 
 676         // 2. Initial setup
 677         setter.invoke(node, initialValue);
 678         NodeHelper.syncPeer(node);
 679         assertEquals(initialValue, (Double) getter.invoke(node), 1e-100);
 680         assertEquals((float) initialValue, (Float) impl_getter.invoke(peer), 1e-100);
 681 
 682         // 3. Change value of the property
 683         setter.invoke(node, newValue);
 684 
 685         // 4. Check that the property value has changed but has not propagated to PGNode
 686         assertEquals(newValue, (Double) getter.invoke(node), 1e-100);
 687         assertEquals((float) initialValue, (Float) impl_getter.invoke(peer), 1e-100);
 688 
 689         // 5. Propagate the property value to PGNode
 690         NodeHelper.syncPeer(node);
 691 
 692         // 6. Check that the value has been propagated to PGNode
 693         assertEquals((float) newValue, (Float) impl_getter.invoke(peer), 1e-100);
 694     }
 695 
 696     public interface ObjectValueConvertor {
 697         Object toSg(Object pgValue);
 698     }
 699 
 700     public static final Comparator DEFAULT_OBJ_COMPARATOR =
 701             (sgValue, pgValue) -> {
 702                 assertEquals(sgValue, pgValue);
 703                 return 0;
 704             };
 705 
 706     public static void testObjectPropertyPropagation(
 707         final Node node,
 708         final String propertyName,
 709         final Object initialValue,
 710         final Object newValue) throws Exception {
 711 
 712         testObjectPropertyPropagation(node, propertyName, propertyName, initialValue, newValue);
 713     }
 714 
 715     public static void testObjectPropertyPropagation(
 716             final Node node,
 717             final String propertyName,
 718             final String pgPropertyName,
 719             final Object initialValue,
 720             final Object newValue) throws Exception {
 721         testObjectPropertyPropagation(node, propertyName, pgPropertyName,
 722                 initialValue, newValue, DEFAULT_OBJ_COMPARATOR);
 723     }
 724 
 725     public static void testObjectPropertyPropagation(
 726             final Node node,
 727             final String propertyName,
 728             final String pgPropertyName,
 729             final Object initialValue,
 730             final Object newValue,
 731             final ObjectValueConvertor convertor) throws Exception {
 732         testObjectPropertyPropagation(
 733                 node, propertyName, pgPropertyName,
 734                 initialValue, newValue,
 735                 (sgValue, pgValue) -> {
 736                     assertEquals(sgValue, convertor.toSg(pgValue));
 737                     return 0;
 738                 }
 739         );
 740     }
 741 
 742     public static void testObjectPropertyPropagation(
 743             final Node node,
 744             final String propertyName,
 745             final String pgPropertyName,
 746             final Object initialValue,
 747             final Object newValue,
 748             final Comparator comparator) throws Exception {
 749         final StringBuilder propertyNameBuilder = new StringBuilder(propertyName);
 750         propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0)));
 751 
 752         final StringBuilder pgPropertyNameBuilder = new StringBuilder(pgPropertyName);
 753         pgPropertyNameBuilder.setCharAt(0, Character.toUpperCase(pgPropertyName.charAt(0)));
 754 
 755         final String setterName = new StringBuilder("set").append(propertyNameBuilder).toString();
 756         final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString();
 757         final String pgGetterName = new StringBuilder("get").append(pgPropertyNameBuilder).toString();
 758 
 759         final Class<? extends Node> nodeClass = node.getClass();
 760         final Method getter = nodeClass.getMethod(getterName);
 761         final Method setter = nodeClass.getMethod(setterName, getter.getReturnType());
 762 
 763         final NGNode peer = NodeHelper.getPeer(node);
 764         final Class<? extends NGNode> impl_class = peer.getClass();
 765         final Method impl_getter = impl_class.getMethod(pgGetterName);
 766 
 767 
 768         // 1. Create test scene
 769         final Scene scene = new Scene(new Group());
 770         ParentShim.getChildren(scene.getRoot()).add(node);
 771 
 772         // 2. Initial setup
 773         setter.invoke(node, initialValue);
 774         NodeHelper.syncPeer(node);
 775         assertEquals(initialValue, getter.invoke(node));
 776         assertEquals(0, comparator.compare(initialValue,
 777                                            impl_getter.invoke(peer)));
 778 
 779         // 3. Change value of the property
 780         setter.invoke(node, newValue);
 781 
 782         // 4. Check that the property value has changed but has not propagated to PGNode
 783         assertEquals(newValue, getter.invoke(node));
 784         assertEquals(0, comparator.compare(initialValue,
 785                                            impl_getter.invoke(peer)));
 786 
 787         // 5. Propagate the property value to PGNode
 788         NodeHelper.syncPeer(node);
 789 
 790         // 6. Check that the value has been propagated to PGNode
 791         assertEquals(0, comparator.compare(newValue,
 792                                            impl_getter.invoke(peer)));
 793     }
 794 
 795 
 796     public static void testIntPropertyPropagation(
 797         final Node node,
 798         final String propertyName,
 799         final int initialValue,
 800         final int newValue) throws Exception {
 801 
 802         testIntPropertyPropagation(node, propertyName, propertyName, initialValue, newValue);
 803     }
 804 
 805 
 806     public static void testIntPropertyPropagation(
 807         final Node node,
 808         final String propertyName,
 809         final String pgPropertyName,
 810         final int initialValue,
 811         final int newValue) throws Exception {
 812 
 813         final StringBuilder propertyNameBuilder = new StringBuilder(propertyName);
 814         propertyNameBuilder.setCharAt(0, Character.toUpperCase(propertyName.charAt(0)));
 815 
 816         final StringBuilder pgPropertyNameBuilder = new StringBuilder(pgPropertyName);
 817         pgPropertyNameBuilder.setCharAt(0, Character.toUpperCase(pgPropertyName.charAt(0)));
 818 
 819         final String setterName = new StringBuilder("set").append(propertyNameBuilder).toString();
 820         final String getterName = new StringBuilder("get").append(propertyNameBuilder).toString();
 821         final String pgGetterName = new StringBuilder("get").append(pgPropertyNameBuilder).toString();
 822 
 823         final Class<? extends Node> nodeClass = node.getClass();
 824         final Method getter = nodeClass.getMethod(getterName);
 825         final Method setter = nodeClass.getMethod(setterName, getter.getReturnType());
 826 
 827         final NGNode peer = NodeHelper.getPeer(node);
 828         final Class<? extends NGNode> impl_class = peer.getClass();
 829         final Method impl_getter = impl_class.getMethod(pgGetterName);
 830 
 831 
 832         // 1. Create test scene
 833         final Scene scene = new Scene(new Group());
 834         ParentShim.getChildren(scene.getRoot()).add(node);
 835 
 836         // 2. Initial setup
 837         setter.invoke(node, initialValue);
 838         assertEquals(initialValue, getter.invoke(node));
 839         NodeHelper.syncPeer(node);
 840         assertEquals(initialValue, ((Number) impl_getter.invoke(peer)).intValue());
 841 
 842         // 3. Change value of the property
 843         setter.invoke(node, newValue);
 844 
 845         // 4. Check that the property value has changed but has not propagated to PGNode
 846         assertEquals(newValue, getter.invoke(node));
 847         assertEquals(initialValue, ((Number) impl_getter.invoke(peer)).intValue());
 848 
 849         // 5. Propagate the property value to PGNode
 850         NodeHelper.syncPeer(node);
 851 
 852         // 6. Check that the value has been propagated to PGNode
 853         assertEquals(newValue, ((Number) impl_getter.invoke(peer)).intValue());
 854     }
 855 
 856     public static void callSyncPGNode(final Node node) {
 857         NodeHelper.syncPeer(node);
 858     }
 859 
 860     @Test
 861     public void testToFront() {
 862         Rectangle rect1 = new Rectangle();
 863         Rectangle rect2 = new Rectangle();
 864         Group g = new Group();
 865 
 866         Scene scene = new Scene(g);
 867         ParentShim.getChildren(g).add(rect1);
 868         ParentShim.getChildren(g).add(rect2);
 869 
 870         rect1.toFront();
 871         rect2.toFront();
 872 
 873         // toFront should not remove rectangle from scene
 874         assertEquals(scene, rect2.getScene());
 875         assertEquals(scene, rect1.getScene());
 876         // test corect order of scene content
 877         assertEquals(rect2, ParentShim.getChildren(g).get(1));
 878         assertEquals(rect1, ParentShim.getChildren(g).get(0));
 879 
 880         rect1.toFront();
 881         assertEquals(scene, rect2.getScene());
 882         assertEquals(scene, rect1.getScene());
 883         assertEquals(rect1, ParentShim.getChildren(g).get(1));
 884         assertEquals(rect2, ParentShim.getChildren(g).get(0));
 885     }
 886 
 887     @Test
 888     public void testClip() {
 889         Rectangle rect1 = new Rectangle();
 890         Rectangle rect2 = new Rectangle();
 891         rect1.setClip(rect2);
 892 
 893         Scene scene = new Scene(new Group());
 894         ParentShim.getChildren(scene.getRoot()).add(rect1);
 895         assertEquals(rect2, rect1.getClip());
 896         assertEquals(scene, rect2.getScene());
 897 
 898     }
 899 
 900     @Test
 901     public void testInvalidClip() {
 902         Rectangle rectA = new Rectangle(300, 300);
 903         Rectangle clip1 = new Rectangle(10, 10);
 904         Rectangle clip2 = new Rectangle(100, 100);
 905         clip2.setClip(rectA);
 906         rectA.setClip(clip1);
 907         assertEquals(rectA.getClip(), clip1);
 908         thrown.expect(IllegalArgumentException.class);
 909         try {
 910             rectA.setClip(clip2);
 911         } catch (final IllegalArgumentException e) {
 912             assertNotSame(rectA.getClip(), clip2);
 913             throw e;
 914         }
 915     }
 916 
 917     @Test public void testProperties() {
 918         Rectangle node = new Rectangle();
 919         javafx.collections.ObservableMap<Object, Object> properties = node.getProperties();
 920 
 921         /* If we ask for it, we should get it.
 922          */
 923         assertNotNull(properties);
 924 
 925         /* What we put in, we should get out.
 926          */
 927         properties.put("MyKey", "MyValue");
 928         assertEquals("MyValue", properties.get("MyKey"));
 929 
 930         /* If we ask for it again, we should get the same thing.
 931          */
 932         javafx.collections.ObservableMap<Object, Object> properties2 = node.getProperties();
 933         assertEquals(properties2, properties);
 934 
 935         /* What we put in to the other one, we should get out of this one because
 936          * they should be the same thing.
 937          */
 938         assertEquals("MyValue", properties2.get("MyKey"));
 939     }
 940 
 941     public static boolean isDirty(Node node, DirtyBits[] dbs) {
 942         for(DirtyBits db:dbs) {
 943             if (!NodeShim.isDirty(node, db)) {
 944                 System.out.printf("@NodeTest:check dirty: %s [%d]\n",db,db.ordinal());
 945                 return false;
 946             }
 947         }
 948         return true;
 949     }
 950 
 951     @Test
 952     public void testDefaultValueForViewOrderIsZeroWhenReadFromGetter() {
 953         final Node node = new Rectangle();
 954         assertEquals(0, node.getViewOrder(), .005);
 955     }
 956 
 957     @Test
 958     public void testDefaultValueForViewOrderIsZeroWhenReadFromProperty() {
 959         final Node node = new Rectangle();
 960         assertEquals(0, node.viewOrderProperty().get(), .005);
 961     }
 962 
 963     @Test
 964     public void settingViewOrderThroughSetterShouldAffectBothGetterAndProperty() {
 965         final Node node = new Rectangle();
 966         node.setViewOrder(.5);
 967         assertEquals(.5, node.getViewOrder(), .005);
 968         assertEquals(.5, node.viewOrderProperty().get(), .005);
 969     }
 970 
 971     @Test
 972     public void settingViewOrderThroughPropertyShouldAffectBothGetterAndProperty() {
 973         final Node node = new Rectangle();
 974         node.viewOrderProperty().set(.5);
 975         assertEquals(.5, node.getViewOrder(), .005);
 976         assertEquals(.5, node.viewOrderProperty().get(), .005);
 977     }
 978 
 979     @Test
 980     public void testDefaultValueForOpacityIsOneWhenReadFromGetter() {
 981         final Node node = new Rectangle();
 982         assertEquals(1, node.getOpacity(), .005);
 983     }
 984 
 985     @Test
 986     public void testDefaultValueForOpacityIsOneWhenReadFromProperty() {
 987         final Node node = new Rectangle();
 988         assertEquals(1, node.opacityProperty().get(), .005);
 989     }
 990 
 991     @Test
 992     public void settingOpacityThroughSetterShouldAffectBothGetterAndProperty() {
 993         final Node node = new Rectangle();
 994         node.setOpacity(.5);
 995         assertEquals(.5, node.getOpacity(), .005);
 996         assertEquals(.5, node.opacityProperty().get(), .005);
 997     }
 998 
 999     @Test
1000     public void settingOpacityThroughPropertyShouldAffectBothGetterAndProperty() {
1001         final Node node = new Rectangle();
1002         node.opacityProperty().set(.5);
1003         assertEquals(.5, node.getOpacity(), .005);
1004         assertEquals(.5, node.opacityProperty().get(), .005);
1005     }
1006 
1007     @Test
1008     public void testDefaultValueForVisibleIsTrueWhenReadFromGetter() {
1009         final Node node = new Rectangle();
1010         assertTrue(node.isVisible());
1011     }
1012 
1013     @Test
1014     public void testDefaultValueForVisibleIsTrueWhenReadFromProperty() {
1015         final Node node = new Rectangle();
1016         assertTrue(node.visibleProperty().get());
1017     }
1018 
1019     @Test
1020     public void settingVisibleThroughSetterShouldAffectBothGetterAndProperty() {
1021         final Node node = new Rectangle();
1022         node.setVisible(false);
1023         assertFalse(node.isVisible());
1024         assertFalse(node.visibleProperty().get());
1025     }
1026 
1027     @Test
1028     public void settingVisibleThroughPropertyShouldAffectBothGetterAndProperty() {
1029         final Node node = new Rectangle();
1030         node.visibleProperty().set(false);
1031         assertFalse(node.isVisible());
1032         assertFalse(node.visibleProperty().get());
1033     }
1034 
1035     @Test
1036     public void testDefaultStyleIsEmptyString() {
1037         final Node node = new Rectangle();
1038         assertEquals("", node.getStyle());
1039         assertEquals("", node.styleProperty().get());
1040         node.setStyle(null);
1041         assertEquals("", node.styleProperty().get());
1042         assertEquals("", node.getStyle());
1043     }
1044 
1045     @Test
1046     public void testSynchronizationOfInvisibleNodes() {
1047         final Group g = new Group();
1048         final Circle c = new CircleTest.StubCircle(50);
1049         final NGGroup sg = NodeHelper.getPeer(g);
1050         final CircleTest.StubNGCircle sc = NodeHelper.getPeer(c);
1051         ParentShim.getChildren(g).add(c);
1052 
1053         syncNode(g);
1054         syncNode(c);
1055         assertFalse(sg.getChildren().isEmpty());
1056         assertEquals(50.0, sc.getRadius(), 0.01);
1057 
1058         g.setVisible(false);
1059 
1060         syncNode(g);
1061         syncNode(c);
1062         assertFalse(sg.isVisible());
1063 
1064         final Rectangle r = new Rectangle();
1065         ParentShim.getChildren(g).add(r);
1066         c.setRadius(100);
1067 
1068         syncNode(g);
1069         syncNode(c);
1070         // Group with change in children will always be synced even if it is invisible
1071         assertEquals(2, sg.getChildren().size());
1072         assertEquals(50.0, sc.getRadius(), 0.01);
1073 
1074         g.setVisible(true);
1075 
1076         syncNode(g);
1077         syncNode(c);
1078         assertEquals(2, sg.getChildren().size());
1079         assertEquals(100.0, sc.getRadius(), 0.01);
1080 
1081     }
1082 
1083     @Test
1084     public void testIsTreeVisible() {
1085         final Group g = new Group();
1086         final Circle c = new CircleTest.StubCircle(50);
1087 
1088         ParentShim.getChildren(g).add(c);
1089 
1090         Scene s = new Scene(g);
1091         Stage st = new Stage();
1092 
1093         assertTrue(NodeHelper.isTreeVisible(g));
1094         assertTrue(NodeHelper.isTreeVisible(c));
1095         assertFalse(NodeHelper.isTreeShowing(g));
1096         assertFalse(NodeHelper.isTreeShowing(c));
1097 
1098         st.show();
1099         st.setScene(s);
1100 
1101         assertTrue(NodeHelper.isTreeVisible(g));
1102         assertTrue(NodeHelper.isTreeVisible(c));
1103         assertTrue(NodeHelper.isTreeShowing(g));
1104         assertTrue(NodeHelper.isTreeShowing(c));
1105 
1106         SceneShim.scenePulseListener_pulse(s);
1107 
1108         assertTrue(NodeHelper.isTreeVisible(g));
1109         assertTrue(NodeHelper.isTreeVisible(c));
1110         assertTrue(NodeHelper.isTreeShowing(g));
1111         assertTrue(NodeHelper.isTreeShowing(c));
1112 
1113         g.setVisible(false);
1114         SceneShim.scenePulseListener_pulse(s);
1115 
1116         assertFalse(NodeHelper.isTreeVisible(g));
1117         assertFalse(NodeHelper.isTreeVisible(c));
1118         assertFalse(NodeHelper.isTreeShowing(g));
1119         assertFalse(NodeHelper.isTreeShowing(c));
1120 
1121         g.setVisible(true);
1122         SceneShim.scenePulseListener_pulse(s);
1123 
1124         assertTrue(NodeHelper.isTreeVisible(g));
1125         assertTrue(NodeHelper.isTreeVisible(c));
1126         assertTrue(NodeHelper.isTreeShowing(g));
1127         assertTrue(NodeHelper.isTreeShowing(c));
1128 
1129         c.setVisible(false);
1130         SceneShim.scenePulseListener_pulse(s);
1131 
1132         assertTrue(NodeHelper.isTreeVisible(g));
1133         assertFalse(NodeHelper.isTreeVisible(c));
1134         assertTrue(NodeHelper.isTreeShowing(g));
1135         assertFalse(NodeHelper.isTreeShowing(c));
1136 
1137         c.setVisible(true);
1138         SceneShim.scenePulseListener_pulse(s);
1139 
1140         assertTrue(NodeHelper.isTreeVisible(g));
1141         assertTrue(NodeHelper.isTreeVisible(c));
1142         assertTrue(NodeHelper.isTreeShowing(g));
1143         assertTrue(NodeHelper.isTreeShowing(c));
1144 
1145         s.setRoot(new Group());
1146         SceneShim.scenePulseListener_pulse(s);
1147 
1148         assertTrue(NodeHelper.isTreeVisible(g));
1149         assertTrue(NodeHelper.isTreeVisible(c));
1150         assertFalse(NodeHelper.isTreeShowing(g));
1151         assertFalse(NodeHelper.isTreeShowing(c));
1152 
1153         s.setRoot(g);
1154         SceneShim.scenePulseListener_pulse(s);
1155 
1156         assertTrue(NodeHelper.isTreeVisible(g));
1157         assertTrue(NodeHelper.isTreeVisible(c));
1158         assertTrue(NodeHelper.isTreeShowing(g));
1159         assertTrue(NodeHelper.isTreeShowing(c));
1160 
1161         st.hide();
1162         SceneShim.scenePulseListener_pulse(s);
1163 
1164         assertTrue(NodeHelper.isTreeVisible(g));
1165         assertTrue(NodeHelper.isTreeVisible(c));
1166         assertFalse(NodeHelper.isTreeShowing(g));
1167         assertFalse(NodeHelper.isTreeShowing(c));
1168 
1169     }
1170 
1171     @Test
1172     public void testSynchronizationOfInvisibleNodes_2() {
1173         final Group g = new Group();
1174         final Circle c = new CircleTest.StubCircle(50);
1175 
1176         Scene s = new Scene(g);
1177         Stage st = new Stage();
1178         st.show();
1179         st.setScene(s);
1180 
1181         final NGGroup sg = NodeHelper.getPeer(g);
1182         final CircleTest.StubNGCircle sc = NodeHelper.getPeer(c);
1183 
1184         ParentShim.getChildren(g).add(c);
1185 
1186         SceneShim.scenePulseListener_pulse(s);
1187 
1188         g.setVisible(false);
1189 
1190         SceneShim.scenePulseListener_pulse(s);
1191 
1192         assertFalse(sg.isVisible());
1193         assertTrue(sc.isVisible());
1194 
1195         c.setCenterX(10);             // Make the circle dirty. It won't be synchronized as it is practically invisible (through the parent)
1196 
1197         SceneShim.scenePulseListener_pulse(s);
1198 
1199         c.setVisible(false);         // As circle is invisible and dirty, this won't trigger a synchronization
1200 
1201         SceneShim.scenePulseListener_pulse(s);
1202 
1203         assertFalse(sg.isVisible());
1204         assertTrue(sc.isVisible()); // This has not been synchronized, as it's not necessary
1205                                     // The rendering will stop at the Group, which is invisible
1206 
1207         g.setVisible(true);
1208 
1209         SceneShim.scenePulseListener_pulse(s);
1210 
1211         assertTrue(sg.isVisible());
1212         assertFalse(sc.isVisible()); // Now the group is visible again, we need to synchronize also
1213                                      // the Circle
1214     }
1215 
1216     @Test
1217     public void testSynchronizationOfInvisibleNodes_2_withClip() {
1218         final Group g = new Group();
1219         final Circle c = new CircleTest.StubCircle(50);
1220 
1221         Scene s = new Scene(g);
1222         Stage st = new Stage();
1223         st.show();
1224         st.setScene(s);
1225 
1226         final NGGroup sg = NodeHelper.getPeer(g);
1227         final CircleTest.StubNGCircle sc = NodeHelper.getPeer(c);
1228 
1229         g.setClip(c);
1230 
1231         SceneShim.scenePulseListener_pulse(s);
1232 
1233         g.setVisible(false);
1234 
1235         SceneShim.scenePulseListener_pulse(s);
1236 
1237         assertFalse(sg.isVisible());
1238         assertTrue(sc.isVisible());
1239 
1240         c.setCenterX(10);             // Make the circle dirty. It won't be synchronized as it is practically invisible (through the parent)
1241 
1242         SceneShim.scenePulseListener_pulse(s);
1243 
1244         c.setVisible(false);         // As circle is invisible and dirty, this won't trigger a synchronization
1245 
1246         SceneShim.scenePulseListener_pulse(s);
1247 
1248         assertFalse(sg.isVisible());
1249         assertTrue(sc.isVisible()); // This has not been synchronized, as it's not necessary
1250                                     // The rendering will stop at the Group, which is invisible
1251 
1252         g.setVisible(true);
1253 
1254         SceneShim.scenePulseListener_pulse(s);
1255 
1256         assertTrue(sg.isVisible());
1257         assertFalse(sc.isVisible()); // Now the group is visible again, we need to synchronize also
1258                                      // the Circle
1259     }
1260 
1261     @Test
1262     public void testLocalToScreen() {
1263         Rectangle rect = new Rectangle();
1264 
1265         rect.setTranslateX(10);
1266         rect.setTranslateY(20);
1267 
1268         TestScene scene = new TestScene(new Group(rect));
1269         final TestStage testStage = new TestStage("");
1270         testStage.setX(100);
1271         testStage.setY(200);
1272         scene.set_window(testStage);
1273         Point2D p = rect.localToScreen(new Point2D(1, 2));
1274         assertEquals(111.0, p.getX(), 0.0001);
1275         assertEquals(222.0, p.getY(), 0.0001);
1276         Bounds b = rect.localToScreen(new BoundingBox(1, 2, 3, 4));
1277         assertEquals(111.0, b.getMinX(), 0.0001);
1278         assertEquals(222.0, b.getMinY(), 0.0001);
1279         assertEquals(3.0, b.getWidth(), 0.0001);
1280         assertEquals(4.0, b.getHeight(), 0.0001);
1281     }
1282 
1283     @Test
1284     public void testLocalToScreen3D() {
1285         Box box = new Box(10, 10, 10);
1286 
1287         box.setTranslateX(10);
1288         box.setTranslateY(20);
1289 
1290         TestScene scene = new TestScene(new Group(box));
1291         scene.setCamera(new PerspectiveCamera());
1292         final TestStage testStage = new TestStage("");
1293         testStage.setX(100);
1294         testStage.setY(200);
1295         scene.set_window(testStage);
1296 
1297         Point2D p = box.localToScreen(new Point3D(1, 2, -5));
1298         assertEquals(111.42, p.getX(), 0.1);
1299         assertEquals(223.14, p.getY(), 0.1);
1300         Bounds b = box.localToScreen(new BoundingBox(1, 2, -5, 1, 2, 10));
1301         assertEquals(110.66, b.getMinX(), 0.1);
1302         assertEquals(221.08, b.getMinY(), 0.1);
1303         assertEquals(1.88, b.getWidth(), 0.1);
1304         assertEquals(4.3, b.getHeight(), 0.1);
1305         assertEquals(0.0, b.getDepth(), 0.0001);
1306     }
1307 
1308     @Test
1309     public void testScreenToLocal() {
1310         Rectangle rect = new Rectangle();
1311 
1312         rect.setTranslateX(10);
1313         rect.setTranslateY(20);
1314 
1315         TestScene scene = new TestScene(new Group(rect));
1316         final TestStage testStage = new TestStage("");
1317         testStage.setX(100);
1318         testStage.setY(200);
1319         scene.set_window(testStage);
1320 
1321         assertEquals(new Point2D(1, 2), rect.screenToLocal(new Point2D(111, 222)));
1322         assertEquals(new BoundingBox(1, 2, 3, 4), rect.screenToLocal(new BoundingBox(111, 222, 3, 4)));
1323     }
1324 
1325     @Test
1326     public void testLocalToScreenWithTranslatedCamera() {
1327         Rectangle rect = new Rectangle();
1328 
1329         rect.setTranslateX(10);
1330         rect.setTranslateY(20);
1331 
1332         ParallelCamera cam = new ParallelCamera();
1333         TestScene scene = new TestScene(new Group(rect, cam));
1334         scene.setCamera(cam);
1335         final TestStage testStage = new TestStage("");
1336         testStage.setX(100);
1337         testStage.setY(200);
1338         cam.setTranslateX(30);
1339         cam.setTranslateY(20);
1340         scene.set_window(testStage);
1341 
1342         Point2D p = rect.localToScreen(new Point2D(1, 2));
1343         assertEquals(81.0, p.getX(), 0.0001);
1344         assertEquals(202.0, p.getY(), 0.0001);
1345         Bounds b = rect.localToScreen(new BoundingBox(1, 2, 3, 4));
1346         assertEquals(81.0, b.getMinX(), 0.0001);
1347         assertEquals(202.0, b.getMinY(), 0.0001);
1348         assertEquals(3.0, b.getWidth(), 0.0001);
1349         assertEquals(4.0, b.getHeight(), 0.0001);
1350     }
1351 
1352     @Test
1353     public void testScreenToLocalWithTranslatedCamera() {
1354         Rectangle rect = new Rectangle();
1355 
1356         rect.setTranslateX(10);
1357         rect.setTranslateY(20);
1358 
1359         ParallelCamera cam = new ParallelCamera();
1360         TestScene scene = new TestScene(new Group(rect, cam));
1361         scene.setCamera(cam);
1362         final TestStage testStage = new TestStage("");
1363         testStage.setX(100);
1364         testStage.setY(200);
1365         cam.setTranslateX(30);
1366         cam.setTranslateY(20);
1367         scene.set_window(testStage);
1368 
1369         assertEquals(new Point2D(31, 22), rect.screenToLocal(new Point2D(111, 222)));
1370         assertEquals(new BoundingBox(31, 22, 3, 4), rect.screenToLocal(new BoundingBox(111, 222, 3, 4)));
1371     }
1372 
1373     @Test
1374     public void testLocalToScreenInsideSubScene() {
1375         Rectangle rect = new Rectangle();
1376         rect.setTranslateX(4);
1377         rect.setTranslateY(9);
1378         SubScene subScene = new SubScene(new Group(rect), 100, 100);
1379         subScene.setTranslateX(6);
1380         subScene.setTranslateY(11);
1381 
1382         TestScene scene = new TestScene(new Group(subScene));
1383         final TestStage testStage = new TestStage("");
1384         testStage.setX(100);
1385         testStage.setY(200);
1386         scene.set_window(testStage);
1387 
1388         Point2D p = rect.localToScreen(new Point2D(1, 2));
1389         assertEquals(111.0, p.getX(), 0.0001);
1390         assertEquals(222.0, p.getY(), 0.0001);
1391         Bounds b = rect.localToScreen(new BoundingBox(1, 2, 3, 4));
1392         assertEquals(111.0, b.getMinX(), 0.0001);
1393         assertEquals(222.0, b.getMinY(), 0.0001);
1394         assertEquals(3.0, b.getWidth(), 0.0001);
1395         assertEquals(4.0, b.getHeight(), 0.0001);
1396     }
1397 
1398     @Test
1399     public void testScreenToLocalInsideSubScene() {
1400         Rectangle rect = new Rectangle();
1401         rect.setTranslateX(4);
1402         rect.setTranslateY(9);
1403         SubScene subScene = new SubScene(new Group(rect), 100, 100);
1404         subScene.setTranslateX(6);
1405         subScene.setTranslateY(11);
1406 
1407         TestScene scene = new TestScene(new Group(subScene));
1408         final TestStage testStage = new TestStage("");
1409         testStage.setX(100);
1410         testStage.setY(200);
1411         scene.set_window(testStage);
1412 
1413         assertEquals(new Point2D(1, 2), rect.screenToLocal(new Point2D(111, 222)));
1414         assertEquals(new BoundingBox(1, 2, 3, 4), rect.screenToLocal(new BoundingBox(111, 222, 3, 4)));
1415     }
1416 
1417     @Test
1418     public void test2DLocalToScreenOn3DRotatedSubScene() {
1419         Rectangle rect = new Rectangle();
1420         rect.setTranslateX(5);
1421         rect.setTranslateY(10);
1422         SubScene subScene = new SubScene(new Group(rect), 100, 100);
1423         subScene.setTranslateX(5);
1424         subScene.setTranslateY(10);
1425         subScene.setRotationAxis(Rotate.Y_AXIS);
1426         subScene.setRotate(40);
1427 
1428         TestScene scene = new TestScene(new Group(subScene));
1429         scene.setCamera(new PerspectiveCamera());
1430         final TestStage testStage = new TestStage("");
1431         testStage.setX(100);
1432         testStage.setY(200);
1433         scene.set_window(testStage);
1434 
1435         Point2D p = rect.localToScreen(new Point2D(1, 2));
1436         assertEquals(124.36, p.getX(), 0.1);
1437         assertEquals(226.0, p.getY(), 0.1);
1438         Bounds b = rect.localToScreen(new BoundingBox(1, 2, 3, 4));
1439         assertEquals(124.36, b.getMinX(), 0.1);
1440         assertEquals(225.75, b.getMinY(), 0.1);
1441         assertEquals(1.85, b.getWidth(), 0.1);
1442         assertEquals(3.76, b.getHeight(), 0.1);
1443     }
1444 
1445     @Test
1446     public void test2DScreenToLocalTo3DRotatedSubScene() {
1447         Rectangle rect = new Rectangle();
1448         rect.setTranslateX(5);
1449         rect.setTranslateY(10);
1450         SubScene subScene = new SubScene(new Group(rect), 100, 100);
1451         subScene.setTranslateX(5);
1452         subScene.setTranslateY(10);
1453         subScene.setRotationAxis(Rotate.Y_AXIS);
1454         subScene.setRotate(40);
1455 
1456         TestScene scene = new TestScene(new Group(subScene));
1457         scene.setCamera(new PerspectiveCamera());
1458         final TestStage testStage = new TestStage("");
1459         testStage.setX(100);
1460         testStage.setY(200);
1461         scene.set_window(testStage);
1462 
1463         Point2D p = rect.screenToLocal(new Point2D(124.36, 226.0));
1464         assertEquals(1, p.getX(), 0.1);
1465         assertEquals(2, p.getY(), 0.1);
1466         Bounds b = rect.screenToLocal(new BoundingBox(124.36, 225.75, 1.85, 3.76));
1467         assertEquals(1, b.getMinX(), 0.1);
1468         assertEquals(1.72, b.getMinY(), 0.1);
1469         assertEquals(3, b.getWidth(), 0.1);
1470         assertEquals(4.52, b.getHeight(), 0.1);
1471     }
1472 
1473     @Test
1474     public void testScreenToLocalWithNonInvertibleTransform() {
1475         Rectangle rect = new Rectangle();
1476 
1477         rect.setScaleX(0.0);
1478 
1479         TestScene scene = new TestScene(new Group(rect));
1480         final TestStage testStage = new TestStage("");
1481         testStage.setX(100);
1482         testStage.setY(200);
1483         scene.set_window(testStage);
1484 
1485         assertNull(rect.screenToLocal(new Point2D(111, 222)));
1486         assertNull(rect.screenToLocal(new BoundingBox(111, 222, 3, 4)));
1487     }
1488 
1489     @Test
1490     public void testScreenToLocalInsideNonInvertibleSubScene() {
1491         Rectangle rect = new Rectangle();
1492         rect.setTranslateX(4);
1493         rect.setTranslateY(9);
1494         SubScene subScene = new SubScene(new Group(rect), 100, 100);
1495         subScene.setScaleX(0.0);
1496 
1497         TestScene scene = new TestScene(new Group(subScene));
1498         final TestStage testStage = new TestStage("");
1499         testStage.setX(100);
1500         testStage.setY(200);
1501         scene.set_window(testStage);
1502 
1503         assertNull(rect.screenToLocal(new Point2D(111, 222)));
1504         assertNull(rect.screenToLocal(new BoundingBox(111, 222, 3, 4)));
1505     }
1506 
1507     @Test
1508     public void testRootMirroringWithTranslate() {
1509         final Group rootGroup = new Group();
1510         rootGroup.setTranslateX(20);
1511         rootGroup.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT);
1512         final Scene scene = new Scene(rootGroup, 200, 200);
1513 
1514         final Point2D trPoint = scene.getRoot().localToScene(0, 0);
1515         assertEquals(180, trPoint.getX(), 0.1);
1516     }
1517 
1518 
1519     @Test
1520     public void testLayoutXYTriggersParentSizeChange() {
1521         final Group rootGroup = new Group();
1522         final Group subGroup = new Group();
1523         ParentShim.getChildren(rootGroup).add(subGroup);
1524 
1525         Rectangle r = new Rectangle(50,50);
1526         r.setManaged(false);
1527         Rectangle staticR = new Rectangle(1,1);
1528         ParentShim.getChildren(subGroup).addAll(r, staticR);
1529 
1530         assertEquals(50,subGroup.getLayoutBounds().getWidth(), 1e-10);
1531         assertEquals(50,subGroup.getLayoutBounds().getHeight(), 1e-10);
1532 
1533         r.setLayoutX(50);
1534 
1535         rootGroup.layout();
1536 
1537         assertEquals(100,subGroup.getLayoutBounds().getWidth(), 1e-10);
1538         assertEquals(50,subGroup.getLayoutBounds().getHeight(), 1e-10);
1539 
1540     }
1541 
1542     @Test
1543     public void testLayoutXYWontBreakLayout() {
1544         final Group rootGroup = new Group();
1545         final AnchorPane pane = new AnchorPane();
1546         ParentShim.getChildren(rootGroup).add(pane);
1547 
1548         Rectangle r = new Rectangle(50,50);
1549         ParentShim.getChildren(pane).add(r);
1550 
1551         AnchorPane.setLeftAnchor(r, 10d);
1552         AnchorPane.setTopAnchor(r, 10d);
1553 
1554         rootGroup.layout();
1555 
1556         assertEquals(10, r.getLayoutX(), 1e-10);
1557         assertEquals(10, r.getLayoutY(), 1e-10);
1558 
1559         r.setLayoutX(50);
1560 
1561         assertEquals(50, r.getLayoutX(), 1e-10);
1562         assertEquals(10, r.getLayoutY(), 1e-10);
1563 
1564         rootGroup.layout();
1565 
1566         assertEquals(10, r.getLayoutX(), 1e-10);
1567         assertEquals(10, r.getLayoutY(), 1e-10);
1568 
1569     }
1570 
1571     @Test
1572     public void clipShouldUpdateAfterParentVisibilityChange() {
1573 
1574         final Group root = new Group();
1575         Scene scene = new Scene(root, 300, 300);
1576 
1577         final Group parent = new Group();
1578         parent.setVisible(false);
1579 
1580         final Circle circle = new Circle(100, 100, 100);
1581         ParentShim.getChildren(parent).add(circle);
1582 
1583         final Rectangle clip = new StubRect(100, 100);
1584         circle.setClip(clip);
1585 
1586         ParentShim.getChildren(root).add(parent);
1587         parent.setVisible(true);
1588 
1589         Stage stage = new Stage();
1590         stage.setScene(scene);
1591         stage.show();
1592 
1593         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1594 
1595         clip.setWidth(300);
1596 
1597         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1598 
1599         assertEquals(300, ((MockNGRect) NodeHelper.getPeer(clip)).w, 1e-10);
1600     }
1601 
1602     @Test
1603     public void untransformedNodeShouldSyncIdentityTransform() {
1604         final Node node = createTestRect();
1605         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1606         assertSame(BaseTransform.IDENTITY_TRANSFORM,
1607                 ((MockNGRect) NodeHelper.getPeer(node)).t);
1608     }
1609 
1610     @Test
1611     public void nodeTransfomedByIdentitiesShouldSyncIdentityTransform() {
1612         final Node node = createTestRect();
1613         node.setRotationAxis(Rotate.X_AXIS);
1614         node.getTransforms().add(new Translate());
1615         node.getTransforms().add(new Scale());
1616         node.getTransforms().add(new Affine());
1617         node.getTransforms().add(new Rotate(0, Rotate.Y_AXIS));
1618         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1619         assertSame(BaseTransform.IDENTITY_TRANSFORM,
1620                 ((MockNGRect) NodeHelper.getPeer(node)).t);
1621     }
1622 
1623     @Test
1624     public void translatedNodeShouldSyncTranslateTransform1() {
1625         final Node node = createTestRect();
1626         node.setTranslateX(30);
1627         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1628         assertSame(Translate2D.class,
1629                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1630     }
1631 
1632     @Test
1633     public void translatedNodeShouldSyncTranslateTransform2() {
1634         final Node node = createTestRect();
1635         node.getTransforms().add(new Translate(20, 10));
1636         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1637         assertSame(Translate2D.class,
1638                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1639     }
1640 
1641     @Test
1642     public void multitranslatedNodeShouldSyncTranslateTransform() {
1643         final Node node = createTestRect();
1644         node.setTranslateX(30);
1645         node.getTransforms().add(new Translate(20, 10));
1646         node.getTransforms().add(new Translate(10, 20));
1647         node.getTransforms().add(new Translate(5, 5, 0));
1648         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1649         assertSame(Translate2D.class,
1650                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1651     }
1652 
1653     @Test
1654     public void mirroringShouldSyncAffine2DTransform() {
1655         final Node node = createTestRect();
1656         node.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT);
1657         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1658         assertSame(Affine2D.class,
1659                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1660     }
1661 
1662     @Test
1663     public void rotatedNodeShouldSyncAffine2DTransform1() {
1664         final Node node = createTestRect();
1665         node.setRotate(20);
1666         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1667         assertSame(Affine2D.class,
1668                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1669     }
1670 
1671     @Test
1672     public void rotatedNodeShouldSyncAffine2DTransform2() {
1673         final Node node = createTestRect();
1674         node.getTransforms().add(new Rotate(20));
1675         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1676         assertSame(Affine2D.class,
1677                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1678     }
1679 
1680     @Test
1681     public void multiRotatedNodeShouldSyncAffine2DTransform() {
1682         final Node node = createTestRect();
1683         node.setRotate(20);
1684         node.getTransforms().add(new Rotate(20));
1685         node.getTransforms().add(new Rotate(0, Rotate.X_AXIS));
1686         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1687         assertSame(Affine2D.class,
1688                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1689     }
1690 
1691     @Test
1692     public void scaledNodeShouldSyncAffine2DTransform1() {
1693         final Node node = createTestRect();
1694         node.setScaleX(2);
1695         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1696         assertSame(Affine2D.class,
1697                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1698     }
1699 
1700     @Test
1701     public void scaledNodeShouldSyncAffine2DTransform2() {
1702         final Node node = createTestRect();
1703         node.getTransforms().add(new Scale(2, 1));
1704         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1705         assertSame(Affine2D.class,
1706                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1707     }
1708 
1709     @Test
1710     public void multiScaledNodeShouldSyncAffine2DTransform() {
1711         final Node node = createTestRect();
1712         node.setScaleX(20);
1713         node.getTransforms().add(new Scale(2, 1));
1714         node.getTransforms().add(new Scale(0.5, 2, 1));
1715         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1716         assertSame(Affine2D.class,
1717                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1718     }
1719 
1720     @Test
1721     public void shearedNodeShouldSyncAffine2DTransform() {
1722         final Node node = createTestRect();
1723         node.getTransforms().add(new Shear(2, 1));
1724         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1725         assertSame(Affine2D.class,
1726                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1727     }
1728 
1729     @Test
1730     public void ztranslatedNodeShouldSyncAffine3DTransform1() {
1731         final Node node = createTestRect();
1732         node.setTranslateZ(30);
1733         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1734         assertSame(Affine3D.class,
1735                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1736     }
1737 
1738     @Test
1739     public void ztranslatedNodeShouldSyncAffine3DTransform2() {
1740         final Node node = createTestRect();
1741         node.getTransforms().add(new Translate(0, 0, 10));
1742         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1743         assertSame(Affine3D.class,
1744                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1745     }
1746 
1747     @Test
1748     public void zscaledNodeShouldSyncAffine3DTransform1() {
1749         final Node node = createTestRect();
1750         node.setScaleZ(0.5);
1751         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1752         assertSame(Affine3D.class,
1753                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1754     }
1755 
1756     @Test
1757     public void zscaledNodeShouldSyncAffine3DTransform2() {
1758         final Node node = createTestRect();
1759         node.getTransforms().add(new Scale(1, 1, 2));
1760         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1761         assertSame(Affine3D.class,
1762                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1763     }
1764 
1765     @Test
1766     public void nonZRotatedNodeShouldSyncAffine3DTransform1() {
1767         final Node node = createTestRect();
1768         node.setRotationAxis(Rotate.Y_AXIS);
1769         node.setRotate(10);
1770         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1771         assertSame(Affine3D.class,
1772                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1773     }
1774 
1775     @Test
1776     public void nonZRotatedNodeShouldSyncAffine3DTransform2() {
1777         final Node node = createTestRect();
1778         node.getTransforms().add(new Rotate(10, Rotate.X_AXIS));
1779         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1780         assertSame(Affine3D.class,
1781                 ((MockNGRect) NodeHelper.getPeer(node)).t.getClass());
1782     }
1783 
1784     @Test
1785     public void translateTransformShouldBeReusedWhenPossible() {
1786         final Node node = createTestRect();
1787         node.setTranslateX(10);
1788         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1789 
1790         BaseTransform t = ((MockNGRect) NodeHelper.getPeer(node)).t;
1791 
1792         ((MockNGRect) NodeHelper.getPeer(node)).t = null;
1793         node.setTranslateX(20);
1794         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1795 
1796         assertSame(t, ((MockNGRect) NodeHelper.getPeer(node)).t);
1797     }
1798 
1799     @Test
1800     public void affine2DTransformShouldBeReusedWhenPossible() {
1801         final Node node = createTestRect();
1802         node.setScaleX(10);
1803         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1804 
1805         BaseTransform t = ((MockNGRect) NodeHelper.getPeer(node)).t;
1806 
1807         ((MockNGRect) NodeHelper.getPeer(node)).t = null;
1808         node.setRotate(20);
1809         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1810 
1811         assertSame(t, ((MockNGRect) NodeHelper.getPeer(node)).t);
1812     }
1813 
1814     @Test
1815     public void affine3DTransformShouldBeReusedWhenPossible() {
1816         final Node node = createTestRect();
1817         node.setScaleZ(10);
1818         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1819 
1820         BaseTransform t = ((MockNGRect) NodeHelper.getPeer(node)).t;
1821 
1822         ((MockNGRect) NodeHelper.getPeer(node)).t = null;
1823         node.setRotate(20);
1824         ((StubToolkit) Toolkit.getToolkit()).firePulse();
1825 
1826         assertSame(t, ((MockNGRect) NodeHelper.getPeer(node)).t);
1827     }
1828 
1829     @Test
1830     public void rtlSceneSizeShouldBeComputedCorrectly() {
1831         Scene scene = new Scene(new Group(new Rectangle(100, 100)));
1832         scene.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT);
1833         Stage stage = new Stage();
1834         stage.setScene(scene);
1835         stage.show();
1836         assertEquals(100.0, scene.getWidth(), 0.00001);
1837     }
1838 
1839     private Node createTestRect() {
1840         final Rectangle rect = new StubRect();
1841         Scene scene = new Scene(new Group(rect));
1842         Stage stage = new Stage();
1843         stage.setScene(scene);
1844         stage.show();
1845         return rect;
1846     }
1847 
1848     private static class MockNGRect extends NGRectangle {
1849         double w = 0;
1850         BaseTransform t = null;
1851 
1852         @Override public void updateRectangle(float x, float y, float width,
1853                 float height, float arcWidth, float arcHeight) {
1854             w = width;
1855         }
1856 
1857         @Override
1858         public void setTransformMatrix(BaseTransform tx) {
1859             t = tx;
1860         }
1861     }
1862 
1863     static class StubRect extends Rectangle {
1864         static {
1865             StubRectHelper.setStubRectAccessor(new StubRectHelper.StubRectAccessor() {
1866                 @Override
1867                 public NGNode doCreatePeer(Node node) {
1868                     return ((StubRect) node).doCreatePeer();
1869                 }
1870             });
1871         }
1872 
1873         StubRect() {
1874             super();
1875             StubRectHelper.initHelper(this);
1876         }
1877 
1878         StubRect(double width, double height) {
1879             super(width, height);
1880             StubRectHelper.initHelper(this);
1881         }
1882 
1883         private NGNode doCreatePeer() {
1884             return new MockNGRect();
1885         }
1886     }
1887 
1888     public static class StubRectHelper extends RectangleHelper {
1889 
1890         private static final StubRectHelper theInstance;
1891         private static StubRectAccessor stubRectAccessor;
1892 
1893         static {
1894             theInstance = new StubRectHelper();
1895             Utils.forceInit(StubRect.class);
1896         }
1897 
1898         private static StubRectHelper getInstance() {
1899             return theInstance;
1900         }
1901 
1902         public static void initHelper(StubRect stubRect) {
1903             setHelper(stubRect, getInstance());
1904         }
1905 
1906         public static void setStubRectAccessor(final StubRectAccessor newAccessor) {
1907             if (stubRectAccessor != null) {
1908                 throw new IllegalStateException();
1909             }
1910 
1911             stubRectAccessor = newAccessor;
1912         }
1913 
1914         @Override
1915         protected NGNode createPeerImpl(Node node) {
1916             return stubRectAccessor.doCreatePeer(node);
1917         }
1918 
1919         public interface StubRectAccessor {
1920             NGNode doCreatePeer(Node node);
1921         }
1922 
1923     }
1924 }