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