1 /*
   2  * Copyright (c) 2011, 2018, 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 com.sun.javafx.scene.NodeHelper;
  29 import com.sun.javafx.scene.SceneHelper;
  30 import javafx.beans.value.ChangeListener;
  31 import javafx.beans.value.ObservableValue;
  32 import javafx.concurrent.Task;
  33 import javafx.scene.layout.StackPane;
  34 import javafx.scene.layout.TilePane;
  35 import javafx.scene.layout.VBox;
  36 import javafx.scene.shape.Rectangle;
  37 import javafx.scene.transform.Scale;
  38 import javafx.stage.PopupWindow;
  39 import javafx.stage.Stage;
  40 
  41 import test.com.sun.javafx.pgstub.StubScene;
  42 import test.com.sun.javafx.pgstub.StubToolkit;
  43 import com.sun.javafx.sg.prism.NGCamera;
  44 import test.com.sun.javafx.test.MouseEventGenerator;
  45 import com.sun.javafx.tk.Toolkit;
  46 
  47 import javafx.application.Platform;
  48 import javafx.beans.InvalidationListener;
  49 import javafx.beans.Observable;
  50 import javafx.scene.input.MouseEvent;
  51 
  52 import org.junit.After;
  53 import org.junit.Before;
  54 import org.junit.Test;
  55 
  56 import java.util.concurrent.CountDownLatch;
  57 import java.util.concurrent.ExecutionException;
  58 import java.util.concurrent.atomic.AtomicInteger;
  59 
  60 import javafx.scene.Camera;
  61 import javafx.scene.Cursor;
  62 import javafx.scene.CursorShim;
  63 import javafx.scene.Group;
  64 import javafx.scene.Node;
  65 import javafx.scene.ParallelCamera;
  66 import javafx.scene.Parent;
  67 import javafx.scene.PerspectiveCamera;
  68 import javafx.scene.Scene;
  69 import javafx.scene.SceneShim;
  70 import javafx.scene.SubScene;
  71 
  72 import static junit.framework.Assert.assertEquals;
  73 import static junit.framework.Assert.assertNotNull;
  74 import static org.junit.Assert.*;
  75 
  76 /**
  77  * Tests various aspects of Scene.
  78  *
  79  */
  80 public class SceneTest {
  81 
  82     private Stage stage;
  83     private boolean handler1Called = false;
  84     private boolean handler2Called = false;
  85 
  86 
  87     @Before
  88     public void setUp() {
  89         stage = new Stage();
  90         stage.show();
  91         stage.requestFocus();
  92     }
  93 
  94     @After
  95     public void tearDown() {
  96         stage.hide();
  97     }
  98 
  99     /***************************************************************************
 100      *                                                                         *
 101      *                           Lookup related tests                          *
 102      *                                                                         *
 103      **************************************************************************/
 104     @Test
 105     public void testLookupCorrectId() {
 106         Node n;
 107         Group root = new Group();
 108         Scene scene = new Scene(root);
 109         Rectangle a = new Rectangle(); a.setId("a");
 110         Rectangle b = new Rectangle(); a.setId("b");
 111         Rectangle c = new Rectangle(); a.setId("c");
 112         Group g = new Group();
 113         g.setId("d");
 114 
 115         Rectangle r1 = new Rectangle(); a.setId("1");
 116         Rectangle r2 = new Rectangle(); a.setId("2");
 117         Rectangle r3 = new Rectangle(); a.setId("3");
 118         n = new Rectangle(); n.setId("4");
 119         Rectangle r5 = new Rectangle(); a.setId("5");
 120         Rectangle r6 = new Rectangle(); a.setId("6");
 121 
 122         Rectangle e = new Rectangle(); a.setId("e");
 123         Rectangle f = new Rectangle(); a.setId("f");
 124 
 125         g.getChildren().addAll(r1,r2,r3,n,r5,r6);
 126 
 127         root.getChildren().addAll(a,b,c,g,e,f);
 128 
 129         assertEquals(n, scene.lookup("#4"));
 130     }
 131 
 132     @Test
 133     public void testLookupBadId() {
 134         Node n;
 135         Group root = new Group();
 136         Scene scene = new Scene(root);
 137         Rectangle a = new Rectangle(); a.setId("a");
 138         Rectangle b = new Rectangle(); a.setId("b");
 139         Rectangle c = new Rectangle(); a.setId("c");
 140         Group g = new Group();
 141         g.setId("d");
 142 
 143         Rectangle r1 = new Rectangle(); a.setId("1");
 144         Rectangle r2 = new Rectangle(); a.setId("2");
 145         Rectangle r3 = new Rectangle(); a.setId("3");
 146         n = new Rectangle(); n.setId("4");
 147         Rectangle r5 = new Rectangle(); a.setId("5");
 148         Rectangle r6 = new Rectangle(); a.setId("6");
 149 
 150         Rectangle e = new Rectangle(); a.setId("e");
 151         Rectangle f = new Rectangle(); a.setId("f");
 152 
 153         g.getChildren().addAll(r1,r2,r3,n,r5,r6);
 154 
 155         root.getChildren().addAll(a,b,c,g,e,f);
 156 
 157         assertNull(scene.lookup("#4444"));
 158     }
 159 
 160     /***************************************************************************
 161      *                                                                         *
 162      *                          Scene Content Tests                            *
 163      *                                                                         *
 164      **************************************************************************/
 165     @Test
 166     public void isOnFxAppThread() {
 167         assertTrue(Platform.isFxApplicationThread());
 168     }
 169 
 170     @Test(expected=NullPointerException.class)
 171     public void testNullRoot() {
 172         Scene scene = new Scene(null);
 173     }
 174 
 175     @Test(expected=NullPointerException.class)
 176     public void testSetNullRoot() {
 177         Scene scene = new Scene(new Group());
 178         scene.setRoot(null);
 179     }
 180 
 181     @Test
 182     public void testRootInitializedInConstructor() {
 183         Group g = new Group();
 184         Scene scene = new Scene(g);
 185 
 186         assertEquals(g, scene.getRoot());
 187         assertEquals(scene, g.getScene());
 188     }
 189 
 190     @Test
 191     public void testDepthBufferInitializedInConstructor() {
 192         Group g = new Group();
 193         Scene scene = new Scene(g, 100, 100, true);
 194 
 195         assertTrue(scene.isDepthBuffer());
 196     }
 197 
 198     @Test
 199     public void testRootUpdatedWhenAddedToScene() {
 200         Scene scene = new Scene(new Group());
 201 
 202         Group g = new Group();
 203         scene.setRoot(g);
 204 
 205         assertEquals(g, scene.getRoot());
 206         assertEquals(scene, g.getScene());
 207     }
 208 
 209     @Test
 210     public void testRootUpdatedWhenChangedInScene() {
 211         Group g = new Group();
 212         Scene scene = new Scene(g);
 213 
 214         Group g2 = new Group();
 215         scene.setRoot(g2);
 216 
 217         assertNull(g.getScene());
 218         assertEquals(g2, scene.getRoot());
 219         assertEquals(scene, g2.getScene());
 220     }
 221 
 222     @Test
 223     public void testNodeUpdatedWhenAddedToScene() {
 224         Group root = new Group();
 225         Scene scene = new Scene(root);
 226         Rectangle rect = new Rectangle();
 227 
 228         assertNull(rect.getScene());
 229 
 230         root.getChildren().add(rect);
 231 
 232         assertEquals(scene, rect.getScene());
 233     }
 234 
 235     @Test
 236     public void testNodeUpdatedWhenRemovedFromScene() {
 237         Rectangle rect;
 238         Group root = new Group();
 239         Scene scene = new Scene(root);
 240         root.getChildren().add(rect = new Rectangle());
 241 
 242         assertEquals(scene, rect.getScene());
 243 
 244         root.getChildren().remove(rect);
 245 
 246         assertNull(rect.getScene());
 247     }
 248 
 249     @Test
 250     public void testNodeTreeUpdatedWhenAddedToScene() {
 251         Rectangle rect;
 252         Group root = new Group();
 253         Scene scene = new Scene(root);
 254         Group g = new Group();
 255 
 256         g.getChildren().add(rect = new Rectangle());
 257 
 258         assertNull(rect.getScene());
 259         assertNull(g.getScene());
 260 
 261         root.getChildren().add(g);
 262 
 263         assertEquals(scene, g.getScene());
 264         assertEquals(scene, rect.getScene());
 265     }
 266 
 267     @Test
 268     public void testNodeTreeUpdatedWhenRemovedFromScene() {
 269         Rectangle rect;
 270         Group g;
 271         Group root = new Group();
 272         Scene scene = new Scene(root);
 273         root.getChildren().add(g = new Group());
 274 
 275         g.getChildren().add(rect = new Rectangle());
 276 
 277         assertEquals(scene, g.getScene());
 278         assertEquals(scene, rect.getScene());
 279 
 280         root.getChildren().remove(g);
 281 
 282         assertNull(rect.getScene());
 283         assertNull(g.getScene());
 284     }
 285 
 286     @Test
 287     public void testNodeTreeUpdatedWhenAddedToChildOfScene() {
 288         Group parentGroup;
 289         Group root = new Group();
 290         Scene scene = new Scene(root);
 291         root.getChildren().add(parentGroup = new Group());
 292 
 293         Rectangle rect;
 294         Group childGroup = new Group();
 295         childGroup.getChildren().add(rect = new Rectangle());
 296 
 297         assertNull(rect.getScene());
 298         assertNull(childGroup.getScene());
 299         assertEquals(scene, parentGroup.getScene());
 300 
 301         parentGroup.getChildren().add(childGroup);
 302 
 303         assertEquals(scene, rect.getScene());
 304         assertEquals(scene, childGroup.getScene());
 305         assertEquals(scene, parentGroup.getScene());
 306     }
 307 
 308     @Test
 309     public void testNodeTreeUpdatedWhenRemovedFromChildOfScene() {
 310         Group parentGroup;
 311         Group root = new Group();
 312         Scene scene = new Scene(root);
 313         root.getChildren().add(parentGroup = new Group());
 314 
 315         Rectangle rect;
 316         Group childGroup = new Group();
 317         parentGroup.getChildren().add(childGroup);
 318         childGroup.getChildren().add(rect = new Rectangle());
 319 
 320         assertEquals(scene, rect.getScene());
 321         assertEquals(scene, childGroup.getScene());
 322         assertEquals(scene, parentGroup.getScene());
 323 
 324         parentGroup.getChildren().remove(childGroup);
 325 
 326         assertNull(rect.getScene());
 327         assertNull(childGroup.getScene());
 328         assertEquals(scene, parentGroup.getScene());
 329     }
 330 
 331     @Test
 332     public void testSceneSizeSetWhenNotInitialized() {
 333         Group g = new Group();
 334 
 335         Rectangle r = new Rectangle();
 336         r.setX(-20);
 337         r.setY(-20);
 338         r.setWidth(200);
 339         r.setHeight(200);
 340         g.getChildren().add(r);
 341 
 342         Scene scene = new Scene(g);
 343         stage.setScene(scene);
 344 
 345         assertEquals(180, (int) scene.getWidth());
 346         assertEquals(180, (int) scene.getHeight());
 347     }
 348 
 349     @Test
 350     public void testSceneSizeSetWithEffectOnRoot() {
 351         Group g = new Group();
 352 
 353         g.setEffect(new javafx.scene.effect.DropShadow());
 354 
 355         Rectangle r = new Rectangle();
 356         r.setX(-20);
 357         r.setY(-20);
 358         g.getChildren().add(r);
 359         r.setWidth(200);
 360         r.setHeight(200);
 361 
 362         Scene scene = new Scene(g);
 363         stage.setScene(scene);
 364 
 365         assertEquals(189, (int) scene.getWidth());
 366         assertEquals(189, (int) scene.getHeight());
 367     }
 368 
 369     @Test
 370     public void testSceneSizeSetWithClipOnRoot() {
 371         Group g = new Group();
 372 
 373         Rectangle clip = new Rectangle();
 374         clip.setX(20); clip.setY(20); clip.setWidth(150); clip.setHeight(150);
 375 
 376         g.setClip(clip);
 377 
 378         Rectangle r = new Rectangle();
 379 
 380         r.setX(20);
 381         r.setY(20);
 382         g.getChildren().add(r);
 383         r.setWidth(200);
 384         r.setHeight(200);
 385 
 386         Scene scene = new Scene(g);
 387         stage.setScene(scene);
 388 
 389         assertEquals(170,(int) scene.getWidth());
 390         assertEquals(170, (int) scene.getHeight());
 391 
 392     }
 393 
 394     @Test
 395     public void testSceneSizeSetWithTransformOnRoot() {
 396         Group g = new Group();
 397 
 398         Scale s = new Scale(); s.setX(2.0f); s.setY(2.0f);
 399         Rectangle r = new Rectangle();
 400         r.setX(-20);
 401         r.setY(-20);
 402         g.getChildren().add(r);
 403         r.setWidth(200);
 404         r.setHeight(200);
 405 
 406         g.getTransforms().add(s);
 407         Scene scene = new Scene(g);
 408         stage.setScene(scene);
 409 
 410         assertEquals(360,(int) scene.getWidth());
 411         assertEquals(360, (int) scene.getHeight());
 412     }
 413 
 414     @Test
 415     public void testSceneSizeSetWithScaleOnRoot() {
 416         Group g = new Group();
 417 
 418         g.setScaleX(2);
 419         g.setScaleY(2);
 420         Rectangle r = new Rectangle();
 421         r.setX(-20);
 422         r.setY(-20);
 423         r.setWidth(200);
 424         r.setHeight(200);
 425         g.getChildren().add(r);
 426 
 427         Scene scene = new Scene(g);
 428         stage.setScene(scene);
 429 
 430         assertEquals(280,(int) scene.getWidth());
 431         assertEquals(280, (int) scene.getHeight());
 432     }
 433 
 434     @Test
 435     public void testSceneSizeSetWithRotationOnRoot() {
 436         Group g = new Group();
 437         g.setRotate(45);
 438         Rectangle r = new Rectangle();
 439         r.setX(-20);
 440         r.setY(-20);
 441         r.setWidth(200);
 442         r.setHeight(200);
 443         g.getChildren().add(r);
 444 
 445         Scene scene = new Scene(g);
 446         stage.setScene(scene);
 447 
 448         assertTrue(scene.getWidth() > 220.0f && scene.getWidth() < 222.0f);
 449         assertTrue(scene.getHeight() > 220.0f && scene.getHeight() < 222.0f);
 450     }
 451 
 452     @Test
 453     public void testSceneSizeSetWithTranslateOnRoot() {
 454         Group g = new Group();
 455 
 456         g.setTranslateX(10);
 457         g.setTranslateY(10);
 458         Rectangle r = new Rectangle();
 459         r.setX(-20);
 460         r.setY(-20);
 461         r.setWidth(200);
 462         r.setHeight(200);
 463         g.getChildren().add(r);
 464         Scene scene = new Scene(g);
 465         stage.setScene(scene);
 466 
 467         assertEquals(190, (int)scene.getWidth());
 468         assertEquals(190, (int)scene.getHeight());
 469     }
 470 
 471     @Test
 472     public void testSceneSizeSetWithResizableAsRoot() {
 473         StackPane st = new StackPane();
 474 
 475         Rectangle r = new Rectangle();
 476         r.setX(-20);
 477         r.setY(-20);
 478         r.setWidth(200);
 479         r.setHeight(200);
 480         st.getChildren().add(r);
 481 
 482         Scene scene = new Scene(st);
 483         stage.setScene(scene);
 484 
 485         assertEquals(200,(int) scene.getWidth());
 486         assertEquals(200, (int) scene.getHeight());
 487     }
 488 
 489     @Test
 490     public void testSceneSizeWhenWidthInitialized() {
 491         Group g = new Group();
 492 
 493         Rectangle r = new Rectangle();
 494         r.setX(-20);
 495         r.setY(-20);
 496         r.setWidth(100);
 497         r.setHeight(100);
 498         g.getChildren().add(r);
 499 
 500         Scene scene = new Scene(g, 200, -1);
 501         stage.setScene(scene);
 502 
 503         assertEquals(200,(int) scene.getWidth());
 504         assertEquals(80, (int) scene.getHeight());
 505     }
 506 
 507     @Test
 508     public void testSceneSizeWhenHeightInitialized() {
 509         Group g = new Group();
 510 
 511         Rectangle r = new Rectangle();
 512         r.setX(-20);
 513         r.setY(-20);
 514         r.setWidth(100);
 515         r.setHeight(100);
 516         g.getChildren().add(r);
 517 
 518         Scene scene = new Scene(g, -1, 300);
 519         stage.setScene(scene);
 520 
 521         assertEquals(80,(int) scene.getWidth());
 522         assertEquals(300,(int) scene.getHeight());
 523     }
 524 
 525     @Test
 526     public void testSceneSizeWhenWidthAndHeightInitialized() {
 527         Group g = new Group();
 528 
 529         Rectangle r = new Rectangle();
 530         r.setX(-20);
 531         r.setY(-20);
 532         r.setWidth(100);
 533         r.setHeight(100);
 534         g.getChildren().add(r);
 535 
 536         Scene scene = new Scene(g, 400, 400);
 537         stage.setScene(scene);
 538 
 539         assertEquals(400,(int) scene.getWidth());
 540         assertEquals(400, (int) scene.getHeight());
 541     }
 542 
 543     @Test
 544     public void testSceneSizeOverridesResizableRootPrefSize() {
 545         StackPane s = new StackPane();
 546 
 547         Rectangle r = new Rectangle();
 548         r.setX(-20);
 549         r.setY(-20);
 550         r.setWidth(100);
 551         r.setHeight(100);
 552         s.getChildren().add(r);
 553 
 554         Scene scene = new Scene(s, 600, 600);
 555         stage.setScene(scene);
 556 
 557         assertEquals(600, (int) scene.getWidth());
 558         assertEquals(600, (int) scene.getHeight());
 559     }
 560 
 561     @Test
 562     public void testSceneSizeWithContentBiasOnRoot() {
 563         Rectangle r1 = new Rectangle(20, 20);
 564         Rectangle r2 = new Rectangle(20, 20);
 565         Rectangle r3 = new Rectangle(100, 20);
 566 
 567         TilePane tilePane = new TilePane();
 568         tilePane.getChildren().addAll(r1, r2);
 569 
 570         final VBox root = new VBox();
 571         root.getChildren().addAll(tilePane, r3);
 572         Scene scene = new Scene(root);
 573         stage.setScene(scene);
 574 
 575         assertEquals(100, (int) scene.getWidth());
 576         assertEquals(40, (int) scene.getHeight());
 577     }
 578 
 579     @Test
 580     public void focusChangeShouldBeAtomic() {
 581         final Group root = new Group();
 582 
 583         final Rectangle r1 = new Rectangle();
 584         final Rectangle r2 = new Rectangle();
 585 
 586         root.getChildren().addAll(r1, r2);
 587         final Scene scene = new Scene(root, 600, 600);
 588         stage.setScene(scene);
 589 
 590         r1.requestFocus();
 591 
 592         assertTrue(r1.isFocused());
 593         assertFalse(r2.isFocused());
 594 
 595         handler1Called = false;
 596         handler2Called = true;
 597 
 598         r1.focusedProperty().addListener((arg0, arg1, focused) -> {
 599             assertFalse(focused); // r1 is being defocused
 600             assertTrue(r2.isFocused()); // r2 is already focused
 601             handler1Called = true;
 602 
 603             root.getChildren().remove(r2); // be evil: remove r2
 604         });
 605 
 606         r2.focusedProperty().addListener(new ChangeListener<Boolean>() {
 607             @Override
 608             public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean focused) {
 609                 assertTrue(focused); // r2 is being focused
 610                 assertFalse(r1.isFocused()); // r1 is already defocused
 611                 assertTrue(handler1Called); // r1 listener was called first
 612                 handler2Called = true;
 613                 // remove the listener otherwise thi final defocus calls it again
 614                 r2.focusedProperty().removeListener(this);
 615             }
 616         });
 617 
 618         r2.requestFocus();
 619         assertTrue(handler2Called); // both listeners were called
 620     }
 621 
 622     @Test
 623     public void testSetCamera() {
 624         Camera camera = new PerspectiveCamera();
 625         Scene scene = new Scene(new Group(camera));
 626         scene.setCamera(camera);
 627         assertEquals(scene.getCamera(), camera);
 628         scene.setCamera(camera);
 629     }
 630 
 631     @Test
 632     public void testGetDefaultCamera() {
 633         Scene scene = new Scene(new Group());
 634         assertNull(scene.getCamera());
 635     }
 636 
 637     @Test
 638     public void testSetNullCamera() {
 639         Scene scene = new Scene(new Group());
 640         scene.setCamera(null);
 641         assertNull(scene.getCamera());
 642     }
 643 
 644     @Test(expected = IllegalArgumentException.class)
 645     public void testSetIllegalCameraFromOtherScene() {
 646         Camera camera = new PerspectiveCamera();
 647 
 648         Scene scene1 = new Scene(new Group(camera));
 649         Scene scene2 = new Scene(new Group());
 650 
 651         scene1.setCamera(camera);
 652         scene2.setCamera(camera);
 653     }
 654 
 655     @Test(expected = IllegalArgumentException.class)
 656     public void testSetIllegalCameraFromItsSubScene() {
 657         Camera camera = new PerspectiveCamera();
 658 
 659         SubScene subScene = new SubScene(new Group(camera), 150, 150);
 660         Scene scene = new Scene(new Group(subScene));
 661 
 662         subScene.setCamera(camera);
 663         scene.setCamera(camera);
 664     }
 665 
 666     @Test(expected = IllegalArgumentException.class)
 667     public void testSetIllegalCameraFromOtherSubScene() {
 668         Camera camera = new PerspectiveCamera();
 669 
 670         Scene scene = new Scene(new Group());
 671 
 672         SubScene subScene = new SubScene(new Group(camera), 150, 150);
 673         Scene otherScene = new Scene(new Group(subScene));
 674 
 675         subScene.setCamera(camera);
 676         scene.setCamera(camera);
 677     }
 678 
 679     @Test(expected = IllegalArgumentException.class)
 680     public void testSetIllegalCameraFromSubScene() {
 681         Camera camera = new PerspectiveCamera();
 682 
 683         SubScene subScene = new SubScene(new Group(camera), 150, 150);
 684         Scene scene = new Scene(new Group());
 685 
 686         subScene.setCamera(camera);
 687         scene.setCamera(camera);
 688     }
 689 
 690     @Test(expected = IllegalArgumentException.class)
 691     public void testSetIllegalCameraFromNestedSubScene() {
 692         Camera camera = new PerspectiveCamera();
 693 
 694         SubScene nestedSubScene = new SubScene(new Group(camera), 100, 100);
 695         SubScene subScene = new SubScene(new Group(nestedSubScene), 150, 150);
 696         Scene scene = new Scene(new Group(subScene));
 697 
 698         nestedSubScene.setCamera(camera);
 699         scene.setCamera(camera);
 700     }
 701 
 702     @Test
 703     public void testCameraUpdatesPG() {
 704         Scene scene = new Scene(new Group(), 300, 200);
 705         Camera cam = new ParallelCamera();
 706         stage.setScene(scene);
 707 
 708         scene.setCamera(cam);
 709         Toolkit.getToolkit().firePulse();
 710 
 711         // verify it has correct owner
 712         cam.setNearClip(20);
 713         Toolkit.getToolkit().firePulse();
 714         NGCamera ngCamera = ((StubScene) SceneHelper.getPeer(scene)).getCamera();
 715         assertEquals(20, ngCamera.getNearClip(), 0.00001);
 716 
 717         scene.setCamera(null);
 718         Toolkit.getToolkit().firePulse();
 719         // verify owner was removed
 720         cam.setNearClip(30);
 721         Toolkit.getToolkit().firePulse();
 722         assertEquals(20, ngCamera.getNearClip(), 0.00001);
 723 
 724         scene.setCamera(cam);
 725         Toolkit.getToolkit().firePulse();
 726         // verify it has correct owner
 727         cam.setNearClip(40);
 728         Toolkit.getToolkit().firePulse();
 729         assertEquals(40, ngCamera.getNearClip(), 0.00001);
 730 
 731         NGCamera oldCam = ngCamera;
 732         scene.setCamera(new ParallelCamera());
 733         Toolkit.getToolkit().firePulse();
 734         // verify owner was removed
 735         cam.setNearClip(50);
 736         Toolkit.getToolkit().firePulse();
 737         ngCamera = NodeHelper.getPeer(scene.getCamera());
 738         assertEquals(40, oldCam.getNearClip(), 0.00001);
 739         assertEquals(0.1, ngCamera.getNearClip(), 0.00001);
 740     }
 741 
 742     @Test
 743     public void testDefaultCameraUpdatesPG() {
 744         Scene scene = new Scene(new Group(), 300, 200);
 745         stage.setScene(scene);
 746         Toolkit.getToolkit().firePulse();
 747         Camera cam = SceneShim.getEffectiveCamera(scene);
 748 
 749         cam.setNearClip(20);
 750         Toolkit.getToolkit().firePulse();
 751         NGCamera camera = ((StubScene) SceneHelper.getPeer(scene)).getCamera();
 752         assertEquals(20, camera.getNearClip(), 0.00001);
 753     }
 754 
 755     @Test(expected=IllegalArgumentException.class)
 756     public void scenesCannotShareCamera() {
 757         Scene scene = new Scene(new Group(), 300, 200);
 758         Scene scene2 = new Scene(new Group(), 300, 200);
 759         Camera cam = new ParallelCamera();
 760         scene.setCamera(cam);
 761         scene2.setCamera(cam);
 762     }
 763 
 764     @Test(expected=IllegalArgumentException.class)
 765     public void subSceneAndSceneCannotShareCamera() {
 766         SubScene sub = new SubScene(new Group(), 100, 100);
 767         Scene scene = new Scene(new Group(sub), 300, 200);
 768         Camera cam = new ParallelCamera();
 769         sub.setCamera(cam);
 770         scene.setCamera(cam);
 771     }
 772 
 773     @Test
 774     public void shouldBeAbleToSetCameraTwiceToScene() {
 775         Scene scene = new Scene(new Group(), 300, 200);
 776         Camera cam = new ParallelCamera();
 777         try {
 778             scene.setCamera(cam);
 779             scene.setCamera(cam);
 780         } catch (IllegalArgumentException e) {
 781             fail("It didn't allow to 'share' camera with myslef");
 782         }
 783     }
 784 
 785     @Test
 786     public void scenePropertyListenerShouldBeCalledForInitializedScene() {
 787         final Group root = new Group();
 788         final Rectangle rect = new Rectangle();
 789         root.getChildren().add(rect);
 790 
 791         root.sceneProperty().addListener(o -> {
 792             root.getChildren().remove(rect);
 793         });
 794 
 795         Scene scene = new Scene(root, 600, 450);
 796         // if there is no exception, the test passed
 797     }
 798 
 799     @Test
 800     public void testSceneCursorChangePropagatesToScenePeer() {
 801         final StubToolkit toolkit = (StubToolkit) Toolkit.getToolkit();
 802         final MouseEventGenerator generator = new MouseEventGenerator();
 803 
 804         final Scene scene = new Scene(new Group(), 300, 200);
 805         stage.setScene(scene);
 806         SceneHelper.processMouseEvent(scene,
 807                 generator.generateMouseEvent(MouseEvent.MOUSE_ENTERED,
 808                                              100, 100));
 809         toolkit.firePulse();
 810 
 811         scene.setCursor(Cursor.TEXT);
 812         assertTrue(toolkit.isPulseRequested());
 813         toolkit.firePulse();
 814 
 815         assertSame(CursorShim.getCurrentFrame(Cursor.TEXT),
 816                    ((StubScene) SceneHelper.getPeer(scene)).getCursor());
 817     }
 818 
 819     @Test
 820     public void testNodeCursorChangePropagatesToScenePeer() {
 821         final StubToolkit toolkit = (StubToolkit) Toolkit.getToolkit();
 822         final MouseEventGenerator generator = new MouseEventGenerator();
 823 
 824         final Parent root = new Group(new Rectangle(300, 200));
 825         final Scene scene = new Scene(root, 300, 200);
 826         stage.setScene(scene);
 827         SceneHelper.processMouseEvent(scene,
 828                 generator.generateMouseEvent(MouseEvent.MOUSE_ENTERED,
 829                                              100, 100));
 830         toolkit.firePulse();
 831 
 832         root.setCursor(Cursor.TEXT);
 833         assertTrue(toolkit.isPulseRequested());
 834         toolkit.firePulse();
 835 
 836         assertSame(CursorShim.getCurrentFrame(Cursor.TEXT),
 837                    ((StubScene) SceneHelper.getPeer(scene)).getCursor());
 838     }
 839 
 840     @Test public void testProperties() {
 841         final Scene scene = new Scene(new Group(), 300, 200);
 842 
 843         javafx.collections.ObservableMap<Object, Object> properties = scene.getProperties();
 844 
 845         /* If we ask for it, we should get it.
 846          */
 847         assertNotNull(properties);
 848 
 849         /* What we put in, we should get out.
 850          */
 851         properties.put("MyKey", "MyValue");
 852         assertEquals("MyValue", properties.get("MyKey"));
 853 
 854         /* If we ask for it again, we should get the same thing.
 855          */
 856         javafx.collections.ObservableMap<Object, Object> properties2 = scene.getProperties();
 857         assertEquals(properties2, properties);
 858 
 859         /* What we put in to the other one, we should get out of this one because
 860          * they should be the same thing.
 861          */
 862         assertEquals("MyValue", properties2.get("MyKey"));
 863     }
 864 
 865     /***************************************************************************
 866      *                                                                         *
 867      *                   Scene Pulse Listener Tests                            *
 868      *                                                                         *
 869      **************************************************************************/
 870 
 871     @Test(expected = NullPointerException.class)
 872     public void testAddNullPreLayoutPulseListener() {
 873         Scene scene = new Scene(new Group(), 300, 300);
 874         scene.addPreLayoutPulseListener(null);
 875     }
 876 
 877     @Test(expected = NullPointerException.class)
 878     public void testAddNullPostLayoutPulseListener() {
 879         Scene scene = new Scene(new Group(), 300, 300);
 880         scene.addPostLayoutPulseListener(null);
 881     }
 882 
 883     @Test public void testRemoveNullPreLayoutPulseListener_nullListenersList() {
 884         Scene scene = new Scene(new Group(), 300, 300);
 885         scene.removePreLayoutPulseListener(null);
 886         // no failure expected
 887     }
 888 
 889     @Test public void testRemoveNullPostLayoutPulseListener_nullListenersList() {
 890         Scene scene = new Scene(new Group(), 300, 300);
 891         scene.removePostLayoutPulseListener(null);
 892         // no failure expected
 893     }
 894 
 895     @Test public void testRemoveNullPreLayoutPulseListener_nonNullListenersList() {
 896         Scene scene = new Scene(new Group(), 300, 300);
 897         scene.addPreLayoutPulseListener(() -> { });
 898         scene.removePreLayoutPulseListener(null);
 899         // no failure expected
 900     }
 901 
 902     @Test public void testRemoveNullPostLayoutPulseListener_nonNullListenersList() {
 903         Scene scene = new Scene(new Group(), 300, 300);
 904         scene.addPostLayoutPulseListener(() -> { });
 905         scene.removePostLayoutPulseListener(null);
 906         // no failure expected
 907     }
 908 
 909     @Test public void testPreLayoutPulseListenerIsFired() {
 910         Scene scene = new Scene(new Group(), 300, 300);
 911         final AtomicInteger counter = new AtomicInteger(0);
 912 
 913         assertEquals(0, counter.get());
 914         scene.addPreLayoutPulseListener(() -> counter.incrementAndGet());
 915         assertEquals(0, counter.get());
 916 
 917         SceneShim.scenePulseListener_pulse(scene);
 918         assertEquals(1, counter.get());
 919 
 920         SceneShim.scenePulseListener_pulse(scene);
 921         assertEquals(2, counter.get());
 922     }
 923 
 924     @Test public void testPostLayoutPulseListenerIsFired() {
 925         Scene scene = new Scene(new Group(), 300, 300);
 926         final AtomicInteger counter = new AtomicInteger(0);
 927 
 928         assertEquals(0, counter.get());
 929         scene.addPostLayoutPulseListener(() -> counter.incrementAndGet());
 930         assertEquals(0, counter.get());
 931 
 932         SceneShim.scenePulseListener_pulse(scene);
 933         assertEquals(1, counter.get());
 934 
 935         SceneShim.scenePulseListener_pulse(scene);
 936         assertEquals(2, counter.get());
 937     }
 938 
 939     @Test public void testPreLayoutPulseListenerIsFired_untilRemoved() {
 940         Scene scene = new Scene(new Group(), 300, 300);
 941         final AtomicInteger counter = new AtomicInteger(0);
 942 
 943         Runnable r = () -> counter.incrementAndGet();
 944 
 945         assertEquals(0, counter.get());
 946         scene.addPreLayoutPulseListener(r);
 947         assertEquals(0, counter.get());
 948 
 949         SceneShim.scenePulseListener_pulse(scene);
 950         assertEquals(1, counter.get());
 951 
 952         scene.removePreLayoutPulseListener(r);
 953         SceneShim.scenePulseListener_pulse(scene);
 954         assertEquals(1, counter.get());
 955     }
 956 
 957     @Test public void testPostLayoutPulseListenerIsFired_untilRemoved() {
 958         Scene scene = new Scene(new Group(), 300, 300);
 959         final AtomicInteger counter = new AtomicInteger(0);
 960 
 961         Runnable r = () -> counter.incrementAndGet();
 962 
 963         assertEquals(0, counter.get());
 964         scene.addPostLayoutPulseListener(r);
 965         assertEquals(0, counter.get());
 966 
 967         SceneShim.scenePulseListener_pulse(scene);
 968         assertEquals(1, counter.get());
 969 
 970         scene.removePostLayoutPulseListener(r);
 971         SceneShim.scenePulseListener_pulse(scene);
 972         assertEquals(1, counter.get());
 973     }
 974 }