1 /*
   2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  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 javafx.scene;
  27 
  28 import com.sun.javafx.pgstub.StubToolkit;
  29 import com.sun.javafx.sg.prism.NGGroup;
  30 import com.sun.javafx.tk.Toolkit;
  31 import java.util.concurrent.atomic.AtomicBoolean;
  32 import javafx.scene.shape.Rectangle;
  33 import javafx.stage.Stage;
  34 import org.junit.After;
  35 import org.junit.Before;
  36 import org.junit.Test;
  37 
  38 import static org.junit.Assert.*;
  39 
  40 public class ParentTest {
  41     private StubToolkit toolkit;
  42     private Stage stage;
  43 
  44     @Before
  45     public void setUp() {
  46         toolkit = (StubToolkit) Toolkit.getToolkit();
  47         stage = new Stage();
  48     }
  49 
  50     @After
  51     public void tearDown() {
  52         stage.close();
  53     }
  54 
  55     @Test
  56     public void testGroupLookupCorrectId() {
  57         Rectangle rectA = new Rectangle();
  58         Rectangle rectB = new Rectangle();
  59         Rectangle rectC = new Rectangle();
  60         Rectangle rect1 = new Rectangle();
  61         Rectangle rect2 = new Rectangle();
  62         Rectangle rect3 = new Rectangle();
  63         Rectangle rect4 = new Rectangle();
  64         Rectangle rect5 = new Rectangle();
  65         Rectangle rect6 = new Rectangle();
  66         Rectangle rectE = new Rectangle();
  67         Rectangle rectF = new Rectangle();
  68 
  69         rectA.setId("a");
  70         rectB.setId("b");
  71         rectC.setId("c");
  72         rect1.setId("1");
  73         rect2.setId("2");
  74         rect3.setId("3");
  75         rect4.setId("4");
  76         rect5.setId("5");
  77         rect6.setId("6");
  78         rectE.setId("e");
  79         rectF.setId("f");
  80 
  81         Group groupD = new Group(rect1, rect2, rect3, rect4, rect5, rect6);
  82         groupD.setId("d");
  83         Group group = new Group(rectA, rectB, rectC, groupD, rectE, rectF);
  84 
  85         assertEquals(rect1, group.lookup("#1"));
  86         assertEquals(rect2, group.lookup("#2"));
  87         assertEquals(rect3, group.lookup("#3"));
  88         assertEquals(rect4, group.lookup("#4"));
  89         assertEquals(rect5, group.lookup("#5"));
  90         assertEquals(rect6, group.lookup("#6"));
  91         assertEquals(rectA, group.lookup("#a"));
  92         assertEquals(rectB, group.lookup("#b"));
  93         assertEquals(rectC, group.lookup("#c"));
  94         assertEquals(groupD, group.lookup("#d"));
  95         assertEquals(rectE, group.lookup("#e"));
  96         assertEquals(rectF, group.lookup("#f"));
  97     }
  98 
  99     @Test
 100     public void testGroupLookupBadId() {
 101         Rectangle rectA = new Rectangle();
 102         Rectangle rectB = new Rectangle();
 103         Rectangle rectC = new Rectangle();
 104         Rectangle rect1 = new Rectangle();
 105         Rectangle rect2 = new Rectangle();
 106         Rectangle rect3 = new Rectangle();
 107         Rectangle rect4 = new Rectangle();
 108         Rectangle rect5 = new Rectangle();
 109         Rectangle rect6 = new Rectangle();
 110         Rectangle rectE = new Rectangle();
 111         Rectangle rectF = new Rectangle();
 112 
 113         rectA.setId("a");
 114         rectB.setId("b");
 115         rectC.setId("c");
 116         rect1.setId("1");
 117         rect2.setId("2");
 118         rect3.setId("3");
 119         rect4.setId("4");
 120         rect5.setId("5");
 121         rect6.setId("6");
 122         rectE.setId("e");
 123         rectF.setId("f");
 124 
 125 
 126         Group groupD = new Group(rect1, rect2, rect3, rect4, rect5, rect6);
 127         groupD.setId("d");
 128         Group group = new Group(rectA, rectB, rectC, groupD, rectE, rectF);
 129 
 130         assertNull(group.lookup("#4444"));
 131     }
 132 
 133     @Test
 134     public void testRemoveChild() {
 135         Rectangle rect1 = new Rectangle();
 136         Rectangle rect2 = new Rectangle();
 137         Rectangle rect3 = new Rectangle();
 138         Rectangle rect4 = new Rectangle();
 139         Rectangle rect5 = new Rectangle();
 140         Rectangle rect6 = new Rectangle();
 141 
 142         Group g = new Group();
 143         Scene s = new Scene(g);
 144         stage.setScene(s);
 145         stage.show();
 146 
 147         g.getChildren().addAll(rect1, rect2, rect3, rect4, rect5, rect6);
 148         toolkit.fireTestPulse();
 149 
 150         // try removing node from the end of the observableArrayList
 151         g.getChildren().remove(rect6);
 152         toolkit.fireTestPulse();
 153         final NGGroup peer = g.impl_getPeer();
 154         assertEquals(5, g.getChildren().size());
 155         assertEquals(5, peer.getChildren().size());
 156 
 157         // try removing node from the beginning of the observableArrayList
 158         g.getChildren().remove(rect1);
 159         toolkit.fireTestPulse();
 160         assertEquals(4, g.getChildren().size());
 161         assertEquals(4, peer.getChildren().size());
 162 
 163         // try removing node from the middle of the observableArrayList
 164         g.getChildren().remove(rect3);
 165         toolkit.fireTestPulse();
 166         assertEquals(3, g.getChildren().size());
 167         assertEquals(3, peer.getChildren().size());
 168     }
 169 
 170     @Test
 171     public void testSetChild() {
 172         Rectangle rect1 = new Rectangle();
 173         Rectangle rect2 = new Rectangle();
 174         Rectangle rect3 = new Rectangle();
 175 
 176         Group g = new Group();
 177         Scene s = new Scene(g);
 178         stage.setScene(s);
 179         stage.show();
 180 
 181         g.getChildren().addAll(rect1, rect2);
 182         toolkit.fireTestPulse();
 183 
 184         // try setting node at given index
 185         g.getChildren().set(1, rect3);
 186         toolkit.fireTestPulse();
 187         assertEquals(2, g.getChildren().size());
 188         assertEquals(2, ((NGGroup)g.impl_getPeer()).getChildren().size());
 189     }
 190 
 191     @Test
 192     public void testSetSameChild() {
 193         Rectangle rect1 = new Rectangle();
 194         Rectangle rect2 = new Rectangle();
 195 
 196         Group g = new Group();
 197         Scene s = new Scene(g);
 198         stage.setScene(s);
 199         stage.show();
 200 
 201         g.getChildren().addAll(rect1, rect2);
 202         toolkit.fireTestPulse();
 203 
 204         // try setting the same node at given index
 205         g.getChildren().set(1, rect2);
 206 
 207         toolkit.fireTestPulse();
 208         assertEquals(2, g.getChildren().size());
 209         assertEquals(2, ((NGGroup)g.impl_getPeer()).getChildren().size());
 210     }
 211 
 212     @Test
 213     public void testRemoveAddSameChild() {
 214         Rectangle rect1 = new Rectangle();
 215         Rectangle rect2 = new Rectangle();
 216         Rectangle rect3 = new Rectangle();
 217         Rectangle rect4 = new Rectangle();
 218         Rectangle rect5 = new Rectangle();
 219         Rectangle rect6 = new Rectangle();
 220 
 221         Group g = new Group();
 222         Scene s = new Scene(g);
 223         stage.setScene(s);
 224         stage.show();
 225 
 226         g.getChildren().addAll(rect1, rect2, rect3, rect4, rect5, rect6);
 227         toolkit.fireTestPulse();
 228 
 229         // try removing node from the end of the observableArrayList
 230         // and add it afterwords
 231         g.getChildren().remove(rect6);
 232         g.getChildren().add(rect6);
 233         toolkit.fireTestPulse();
 234         assertEquals(6, g.getChildren().size());
 235         assertEquals(6, ((NGGroup)g.impl_getPeer()).getChildren().size());
 236     }
 237 
 238     @Test
 239     public void testRemoveAddDiferentChild() {
 240         Rectangle rect1 = new Rectangle();
 241         Rectangle rect2 = new Rectangle();
 242         Rectangle rect3 = new Rectangle();
 243         Rectangle rect4 = new Rectangle();
 244         Rectangle rect5 = new Rectangle();
 245         Rectangle rect6 = new Rectangle();
 246 
 247         Group g = new Group();
 248         Scene s = new Scene(g);
 249         stage.setScene(s);
 250         stage.show();
 251 
 252         g.getChildren().addAll(rect1, rect2, rect3, rect4, rect5);
 253         toolkit.fireTestPulse();
 254 
 255         // try removing node from the end of the observableArrayList
 256         // and add a different one
 257         g.getChildren().remove(rect5);
 258         g.getChildren().add(rect6);
 259         toolkit.fireTestPulse();
 260         assertEquals(5, g.getChildren().size());
 261         assertEquals(5, ((NGGroup)g.impl_getPeer()).getChildren().size());
 262     }
 263 
 264     @Test
 265     public void testGetChildrenUnmodifiable() {
 266         Rectangle rect1 = new Rectangle();
 267         Rectangle rect2 = new Rectangle();
 268         Rectangle rect3 = new Rectangle();
 269 
 270         Group g = new Group();
 271         g.getChildren().addAll(rect1,rect2,rect3);
 272 
 273         assertEquals(3, g.getChildrenUnmodifiable().size());
 274         assertSame(rect1, g.getChildrenUnmodifiable().get(0));
 275         assertSame(rect2, g.getChildrenUnmodifiable().get(1));
 276         assertSame(rect3, g.getChildrenUnmodifiable().get(2));
 277     }
 278 
 279     @Test
 280     public void testGetChildrenUnmodifiableCantBeModified() {
 281         Rectangle rect1 = new Rectangle();
 282         Rectangle rect2 = new Rectangle();
 283         Rectangle rect3 = new Rectangle();
 284 
 285         Group g = new Group();
 286         g.getChildren().addAll(rect1,rect2,rect3);
 287 
 288         try {
 289             g.getChildrenUnmodifiable().add(new Rectangle());
 290             fail("UnsupportedOperationException should have been thrown.");
 291         } catch (UnsupportedOperationException uoe) {
 292             // expected
 293         }
 294     }
 295 
 296     @Test
 297     public void testRequestLayoutWithoutParent() {
 298         Group g = new Group();
 299         g.layout();
 300         // this shouldn't fail even when the group doesn't have a parent
 301         g.requestLayout();
 302     }
 303 
 304     @Test
 305     public void testRequestLayoutWithoutScene() {
 306         Group g = new Group();
 307         g.setManaged(false);
 308         g.layout();
 309         // this shouldn't fail even when the scene is not set
 310         g.requestLayout();
 311     }
 312 
 313     @Test
 314     public void testRequestLayoutClearsCache() {
 315         Group g = new Group();
 316         Rectangle r = new Rectangle(100,200);
 317         g.getChildren().add(r);
 318 
 319         g.requestLayout();
 320         assertEquals(100, g.prefWidth(-1), 1e-100);
 321         assertEquals(200, g.prefHeight(-1), 1e-100);
 322 
 323         r.setWidth(150);
 324         r.setHeight(250);
 325         g.requestLayout();
 326 
 327         assertEquals(150, g.prefWidth(-1), 1e-100);
 328         assertEquals(250, g.prefHeight(-1), 1e-100);
 329     }
 330 
 331     @Test
 332     public void testPrefWidthIncludesChildLayoutX() {
 333         Rectangle r = new Rectangle(10,10,100,100);
 334         r.setLayoutX(10);
 335         MockParent p = new MockParent(r);
 336 
 337         assertEquals(120, p.prefWidth(-1), 0);
 338     }
 339 
 340     @Test
 341     public void testPrefHeightIncludesChildLayoutY() {
 342         Rectangle r = new Rectangle(10,10,100,100);
 343         r.setLayoutY(10);
 344         MockParent p = new MockParent(r);
 345 
 346         assertEquals(120, p.prefHeight(-1), 0);
 347     }
 348 
 349     @Test
 350     public void testDuplicates() {
 351         Group g = new Group();
 352 
 353         Rectangle r1 = new Rectangle();
 354         Rectangle r2 = new Rectangle();
 355         Rectangle r3 = new Rectangle();
 356         Rectangle r4 = new Rectangle();
 357 
 358         try {
 359         g.getChildren().addAll(r1, r2, r3, r4);
 360         g.getChildren().add(r2);
 361         fail();
 362         } catch (IllegalArgumentException e) {
 363 
 364         }
 365     }
 366 
 367     @Test(expected=NullPointerException.class)
 368     public void testAddingNullChild() {
 369         Group g = new Group();
 370         g.getChildren().add(null);
 371     }
 372 
 373     @Test(expected=NullPointerException.class)
 374     public void testNullCheckIsDoneBeforeTestForDuplicates() {
 375         Group g = new Group();
 376         g.getChildren().addAll(null, new Rectangle(), null);
 377     }
 378 
 379     @Test(expected=IllegalArgumentException.class)
 380     public void testAddingClipNodeTwice() {
 381         Group g = new Group();
 382 
 383         Node clipParent = new Rectangle();
 384         Node clipNode = new Rectangle();
 385 
 386         clipParent.setClip(clipNode);
 387         try {
 388             // try to add node which is already set as a clip
 389             g.getChildren().add(clipNode);
 390             fail();
 391         } catch (IllegalArgumentException e) {
 392         }
 393 
 394         // try again
 395         g.getChildren().add(clipNode);
 396     }
 397 
 398     @Test
 399     public void testAddingFixedClipNode() {
 400         Group g = new Group();
 401 
 402         Node clipParent = new Rectangle();
 403         Node clipNode = new Rectangle();
 404 
 405         clipParent.setClip(clipNode);
 406         try {
 407             // try to add node which is already set as a clip
 408             g.getChildren().add(clipNode);
 409             fail();
 410         } catch (IllegalArgumentException e) {
 411         }
 412 
 413         // fix the problem and add again
 414         clipParent.setClip(null);
 415         g.getChildren().add(clipNode);
 416     }
 417 
 418     @Test(expected=IllegalArgumentException.class)
 419     public void testFalsePermutation() {
 420         Group g = new Group();
 421 
 422         Rectangle r1 = new Rectangle();
 423         Rectangle r2 = new Rectangle();
 424         Rectangle r3 = new Rectangle();
 425         Rectangle r4 = new Rectangle();
 426 
 427         g.getChildren().addAll(r1, r2, r3, r4);
 428         g.getChildren().setAll(r1, r2, r2, r4);
 429     }
 430 
 431     @Test
 432     public void testFalseDuplicates() {
 433         Group g = new Group();
 434 
 435         Rectangle r1 = new Rectangle();
 436         Rectangle r2 = new Rectangle();
 437         Rectangle r3 = new Rectangle();
 438         Rectangle r4 = new Rectangle();
 439 
 440         g.getChildren().addAll(r1, r2);
 441         try {
 442             g.getChildren().addAll(r3, r4, r2);
 443             fail();
 444         } catch (IllegalArgumentException e) {
 445         }
 446         try {
 447             g.getChildren().add(r3);
 448         } catch (IllegalArgumentException e) {
 449             fail();
 450         }
 451 
 452     }
 453 
 454     @Test
 455     public void nodeCanBeAddedDuringLayout() {
 456         final Rectangle rect = new Rectangle(100, 100);
 457         final Group g = new Group(rect);
 458 
 459         Group root = new Group() {
 460             @Override protected void layoutChildren() {
 461                 getChildren().setAll(g);
 462             }
 463         };
 464 
 465         Scene scene = new Scene(root);
 466 
 467         stage.setScene(scene);
 468         stage.show();
 469 
 470         // there are assertions tested down the stack (see RT-21746)
 471     }
 472 
 473     private static class LGroup extends Group {
 474 
 475         private boolean layoutCalled;
 476 
 477         @Override
 478         public void requestLayout() {
 479             super.requestLayout();
 480             layoutCalled = true;
 481         }
 482 
 483         public void assertAndClear(boolean b) {
 484             assertEquals(b, layoutCalled);
 485             layoutCalled = false;
 486         }
 487 
 488         public void clear() {
 489             layoutCalled = false;
 490         }
 491 
 492     }
 493 
 494     @Test
 495     public void requestLayoutAlwaysCalledUpToTheLayoutRoot() {
 496         final Group root = new Group();
 497         final LGroup lroot = new LGroup();
 498         lroot.setManaged(false);
 499         root.getChildren().add(lroot);
 500         final LGroup sub = new LGroup();
 501         lroot.getChildren().add(sub);
 502 
 503         lroot.clear();
 504         sub.clear();
 505         root.layout();
 506 
 507         lroot.assertAndClear(false);
 508         sub.assertAndClear(false);
 509 
 510         sub.requestLayout();
 511 
 512         lroot.assertAndClear(true);
 513         sub.assertAndClear(true);
 514 
 515         sub.requestLayout();
 516         lroot.assertAndClear(true);
 517         sub.assertAndClear(true);
 518     }
 519     
 520     @Test
 521     public void unmanagedParentTest() {
 522         final LGroup innerGroup = new LGroup();
 523         innerGroup.setManaged(false);
 524 
 525         final LGroup outerGroup = new LGroup();
 526         outerGroup.setManaged(false);
 527 
 528         Scene s = new Scene(outerGroup);
 529 
 530         innerGroup.assertAndClear(false);
 531         outerGroup.assertAndClear(true);
 532 
 533         outerGroup.getChildren().add(innerGroup);
 534         innerGroup.requestLayout();
 535 
 536         innerGroup.assertAndClear(true);
 537         outerGroup.assertAndClear(true);
 538 
 539         outerGroup.getChildren().remove(innerGroup);
 540 
 541         innerGroup.assertAndClear(false);
 542         outerGroup.assertAndClear(false);
 543 
 544         final LGroup intermediate = new LGroup();
 545         intermediate.setManaged(false);
 546         intermediate.setAutoSizeChildren(false);
 547         outerGroup.getChildren().add(intermediate);
 548 
 549         innerGroup.assertAndClear(false);
 550         intermediate.assertAndClear(true);
 551         outerGroup.assertAndClear(true);
 552 
 553         innerGroup.requestLayout();
 554         intermediate.getChildren().add(innerGroup);
 555 
 556         innerGroup.assertAndClear(true);
 557         intermediate.assertAndClear(true);
 558         outerGroup.assertAndClear(false);
 559 
 560     }
 561 
 562     @Test
 563     public void requestLayoutTriggersPulse() {
 564         final Group root = new Group();
 565         final LGroup lroot = new LGroup();
 566         lroot.setManaged(false);
 567         root.getChildren().add(lroot);
 568         final LGroup sub = new LGroup();
 569         lroot.getChildren().add(sub);
 570 
 571         toolkit.clearPulseRequested();
 572         sub.requestLayout();
 573         Scene scene = new Scene(root);
 574         assertTrue(toolkit.isPulseRequested());
 575         toolkit.clearPulseRequested();
 576         root.layout();
 577 
 578         sub.requestLayout();
 579 
 580         assertTrue(toolkit.isPulseRequested());
 581     }
 582 
 583     @Test
 584     public void requestLayoutNotPropagatingDuringLayout() {
 585         final LGroup lroot = new LGroup();
 586         lroot.setManaged(false);
 587         final LGroup sub = new LGroup() {
 588 
 589             @Override
 590             protected void layoutChildren() {
 591                 super.layoutChildren();
 592                 requestLayout();
 593             }
 594 
 595         };
 596         lroot.getChildren().add(sub);
 597         lroot.clear();
 598         sub.clear();
 599 
 600         sub.requestLayout();
 601         lroot.assertAndClear(true);
 602         sub.assertAndClear(true);
 603 
 604         lroot.layout();
 605 
 606         lroot.assertAndClear(false);
 607         sub.assertAndClear(true);
 608     }
 609 
 610     @Test
 611     public void testChildrenPermutationInvalidatesManagedChildrenAndLayout() {
 612         LGroup root = new LGroup();
 613         Rectangle r1 = new Rectangle();
 614         Rectangle r2 = new Rectangle();
 615 
 616         root.getChildren().addAll(r1, r2);
 617 
 618         root.clear();
 619 
 620         root.getManagedChildren().equals(root.getChildren());
 621 
 622         root.getChildren().setAll(r2, r1);
 623 
 624         root.getManagedChildren().equals(root.getChildren());
 625         root.assertAndClear(true);
 626 
 627         r2.toFront();
 628 
 629         root.getManagedChildren().equals(root.getChildren());
 630         root.assertAndClear(true);
 631     }
 632 
 633     @Test
 634     public void newChildInvalidatesLayoutWhenLayoutBoundsAreValidatedImmediately() {
 635         Group root = new Group();
 636         final AtomicBoolean layoutCalled = new AtomicBoolean();
 637         final AtomicBoolean testReady = new AtomicBoolean();
 638         LGroup sub = new LGroup() {
 639 
 640             @Override
 641             protected void layoutChildren() {
 642                 if (testReady.get()) {
 643                     assertAndClear(true);
 644                     layoutCalled.set(true);
 645                 }
 646             }
 647 
 648         };
 649         root.getChildren().add(sub);
 650         root.getLayoutBounds(); // validate
 651         sub.getBoundsInParent(); // validate
 652 
 653         root.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> {
 654             // ChangeListener will immediately validate the bounds
 655         });
 656         sub.clear();
 657 
 658         testReady.set(true);
 659         sub.getChildren().add(new Rectangle());
 660         assertTrue(layoutCalled.get());
 661     }
 662 
 663     @Test
 664     public void sceneListenerCanAddChild() {
 665         final Group root = new Group();
 666         final Scene scene = new Scene(root, 600, 450);
 667 
 668         final Group child = new Group(new Group(), new Group(), new Group());
 669 
 670         child.getChildren().get(1).sceneProperty().addListener(o -> child.getChildren().add(2, new Group()));
 671 
 672         root.getChildren().add(child);
 673 
 674         assertSame(scene, child.getChildren().get(3).getScene());
 675     }
 676 
 677     public static class MockParent extends Parent {
 678         public MockParent(Node... children) {
 679             getChildren().addAll(children);
 680         }
 681     }
 682 }