/* * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package test.javafx.scene.bounds; import com.sun.javafx.scene.NodeHelper; import static test.com.sun.javafx.test.TestHelper.assertBoundsEqual; import static test.com.sun.javafx.test.TestHelper.assertGroupBounds; import static test.com.sun.javafx.test.TestHelper.box; import static test.com.sun.javafx.test.TestHelper.formatBounds; import static junit.framework.Assert.assertEquals; import java.util.LinkedList; import javafx.geometry.Bounds; import javafx.scene.Group; import javafx.scene.shape.Rectangle; import org.junit.Test; public class GroupBoundsTest { /*************************************************************************** * * Group Tests * * These are tests related to transforming the basic node * types including * * / **************************************************************************/ // test that an empty group has empty bounds public @Test void testGroupBounds_Empty() { assertGroupBounds(new Group()); } // test that adding a node to a group updates the Groups bounds public @Test void testGroupBounds_AddRect() { Group g = new Group(); assertGroupBounds(g); g.getChildren().add(new Rectangle(20, 20, 50, 50)); assertGroupBounds(g); g.getChildren().add(new Rectangle(90, 20, 50, 50)); assertGroupBounds(g); } // test that changing a node in a group updates the groups bounds public @Test void testGroupBounds_ChangeRect() { Rectangle r1 = new Rectangle(20, 20, 50, 50); Rectangle r2 = new Rectangle(90, 20, 50, 50); Group g = new Group(r1, r2); assertGroupBounds(g); r1.setWidth(200); assertGroupBounds(g); r2.setWidth(20); assertGroupBounds(g); } // test that removing a node from the group updates the Groups bounds public @Test void testGroupBounds_RemoveRect() { Rectangle r1 = new Rectangle(20, 20, 50, 50); Rectangle r2 = new Rectangle(90, 20, 50, 50); Group g = new Group(r1, r2); assertGroupBounds(g); g.getChildren().remove(r2); assertGroupBounds(g); g.getChildren().remove(r1); assertGroupBounds(g); } public @Test void testGroupBounds_RemoveAllChildNodesAndAddOneBack() { Rectangle r1 = new Rectangle(20, 20, 50, 50); Rectangle r2 = new Rectangle(90, 20, 50, 50); Group g = new Group(r1, r2); assertGroupBounds(g); g.getChildren().clear(); assertGroupBounds(g); g.getChildren().add(r1); assertGroupBounds(g); } // test that adding the first node to a Group, which node is an invisible // node, gets the right answer for the Group's bounds. public @Test void testGroupBounds_InitialNodeInvisible() { Rectangle r1 = new Rectangle(20, 20, 50, 50); r1.setVisible(false); Group g = new Group(r1); assertGroupBounds(g); } // test that adding invisible nodes to a group doesn't update the bounds public @Test void testGroupBounds_InvisibleNodes() { Rectangle r1 = new Rectangle(20, 20, 50, 50); Rectangle r2 = new Rectangle(90, 20, 50, 50); Group g = new Group(r1, r2); assertGroupBounds(g); Rectangle invisible = new Rectangle(-100, -100, 1, 1); invisible.setVisible(true); g.getChildren().add(invisible); assertGroupBounds(g); } // test that adding zero sized nodes does update a Groups bounds public @Test void testGroupBounds_ZeroSizedNodes() { Rectangle r1 = new Rectangle(20, 20, 50, 50); Rectangle r2 = new Rectangle(90, 20, 50, 50); Group g = new Group(r1, r2); assertGroupBounds(g); Rectangle small = new Rectangle(-100, -100, 0, 0); g.getChildren().add(small); assertGroupBounds(g); } // test that making a node invisible updates the groups bounds public @Test void testGroupBounds_NodeMadeInvisible() { Rectangle r1 = new Rectangle(20, 20, 50, 50); Rectangle r2 = new Rectangle(90, 20, 50, 50); Group g = new Group(r1, r2); assertGroupBounds(g); r1.setVisible(false); assertGroupBounds(g); } public @Test void testGroupBounds_NodeWithNoWidthMadeInvisible() { Rectangle r1 = new Rectangle(20, 20, 50, 50); Rectangle r2 = new Rectangle(90, 20, 0, 50); Group g = new Group(r1, r2); assertGroupBounds(g); r2.setVisible(false); assertGroupBounds(g); } public @Test void testGroupBounds_FirstNodeHasNoWidthSecondNodeDoes() { Rectangle r1 = new Rectangle(20, 20, 0, 50); Rectangle r2 = new Rectangle(90, 20, 50, 50); Group g = new Group(r1, r2); assertGroupBounds(g); } public @Test void testGroupBounds_FirstNodeHasNoWidthSecondNodeDoes_Added() { Rectangle r1 = new Rectangle(20, 20, 0, 50); Rectangle r2 = new Rectangle(90, 20, 50, 50); Group g = new Group(); assertGroupBounds(g); g.getChildren().add(r1); assertGroupBounds(g); g.getChildren().add(r2); assertGroupBounds(g); } public @Test void testGroupBounds_FirstNodeHasNoWidthSecondNodeDoes_ClearedThenAdded() { Rectangle r1 = new Rectangle(20, 20, 0, 50); Rectangle r2 = new Rectangle(90, 20, 50, 50); Rectangle r3 = new Rectangle(10, 15, 75, 20); Group g = new Group(r1, r2); assertGroupBounds(g); g.getChildren().remove(r2); assertGroupBounds(g); g.getChildren().add(r3); assertGroupBounds(g); } // this test uncovered a bug when using the GroupBoundsHelper. It needs // two invisible nodes to tickle it You also have to ask assert the bounds // after each change or it may not fail public @Test void testGroupBounds_TwoNodesMadeInvisible_ThenNodeAdded() { Rectangle r1 = new Rectangle(20, 20, 50, 50); Rectangle r2 = new Rectangle(90, 20, 50, 50); Rectangle r3 = new Rectangle(10, 15, 75, 20); Group g = new Group(); assertGroupBounds(g); g.getChildren().add(r1); assertGroupBounds(g); r1.setVisible(false); assertGroupBounds(g); g.getChildren().add(r2); assertGroupBounds(g); r2.setVisible(false); assertGroupBounds(g); g.getChildren().add(r3); assertGroupBounds(g); } public @Test void testGroupBounds_TogglingVisiblityToFalseAndTrueWhileExpandingBounds() { Rectangle r1 = new Rectangle(20, 20, 50, 50); Rectangle r2 = new Rectangle(90, 20, 50, 50); Rectangle r3 = new Rectangle(10, 15, 75, 20); Group g = new Group(); assertGroupBounds(g); g.getChildren().add(r1); assertGroupBounds(g); g.getChildren().add(r2); assertGroupBounds(g); r2.setVisible(false); assertGroupBounds(g); g.getChildren().add(r3); assertGroupBounds(g); r2.setVisible(true); assertGroupBounds(g); } private Group createGroupWithRects() { Group group = new Group(); group.setId("group"); for (int i = 1; i <= 3; i++) { for (int j = 1; j <= 3; j++) { Rectangle r = new Rectangle(j * 100, i * 100, 50, 50); r.setId("rect[" + i + "," + j + "]"); group.getChildren().add(r); } } // sanity test check that the size of the group is correct assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); return group; } // Started out as a simple test and now pretty well covers all the use // cases. Oops. Bad Richard. public @Test void testMovingNodesTowardCenterOfGroupAndOther() { Group group = createGroupWithRects(); // move the 1, 3, 5, 7 nodes in towards the center. Doing so should not // change the group's bounds ((Rectangle) group.getChildren().get(1)).setX(200); ((Rectangle) group.getChildren().get(1)).setY(200); ((Rectangle) group.getChildren().get(3)).setX(200); ((Rectangle) group.getChildren().get(3)).setY(200); ((Rectangle) group.getChildren().get(5)).setX(200); ((Rectangle) group.getChildren().get(5)).setY(200); ((Rectangle) group.getChildren().get(7)).setX(200); ((Rectangle) group.getChildren().get(7)).setY(200); assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); // move the 2 and 6 nodes in towards the center. Doing so should not // change the group's bounds ((Rectangle) group.getChildren().get(2)).setX(200); ((Rectangle) group.getChildren().get(2)).setY(200); ((Rectangle) group.getChildren().get(6)).setX(200); ((Rectangle) group.getChildren().get(6)).setY(200); assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); // move the 0 node into the center. Doing so SHOULD change the bounds // such that all nodes are in the center (200, 200) except for the // number 8 node which is at (300, 300) ((Rectangle) group.getChildren().get(0)).setX(200); ((Rectangle) group.getChildren().get(0)).setY(200); assertEquals(box(200, 200, 150, 150), group.getBoundsInLocal()); // move the 8 node a little towards the center (but not all the way) // just to test that moving the bottom-right will adjust things ((Rectangle) group.getChildren().get(8)).setX(250); ((Rectangle) group.getChildren().get(8)).setY(250); assertEquals(box(200, 200, 100, 100), group.getBoundsInLocal()); // move the rest of the nodes down past #8 for (int i = 0; i <= 7; i++) { ((Rectangle) group.getChildren().get(i)).setX(300); ((Rectangle) group.getChildren().get(i)).setY(300); } assertEquals(box(250, 250, 100, 100), group.getBoundsInLocal()); // now move #8 onto the rest ((Rectangle) group.getChildren().get(8)).setX(300); ((Rectangle) group.getChildren().get(8)).setY(300); assertEquals(box(300, 300, 50, 50), group.getBoundsInLocal()); } public @Test void testMovingNodesAwayFromCenterOfGroup() { Group group = createGroupWithRects(); ((Rectangle) group.getChildren().get(4)).setX(600); ((Rectangle) group.getChildren().get(4)).setY(600); assertEquals(box(100, 100, 550, 550), group.getBoundsInLocal()); } public @Test void testMovingNodesWithinInteriorOfGroup() { Group group = createGroupWithRects(); ((Rectangle) group.getChildren().get(4)).setX(250); ((Rectangle) group.getChildren().get(4)).setY(250); assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); } public @Test void testMovingExteriorNodesOnExterior() { Group group = createGroupWithRects(); ((Rectangle) group.getChildren().get(0)).setY(150); ((Rectangle) group.getChildren().get(1)).setX(100); ((Rectangle) group.getChildren().get(2)).setX(150); ((Rectangle) group.getChildren().get(3)).setY(120); ((Rectangle) group.getChildren().get(5)).setY(100); ((Rectangle) group.getChildren().get(6)).setX(250); ((Rectangle) group.getChildren().get(7)).setX(120); ((Rectangle) group.getChildren().get(8)).setY(200); assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); } public @Test void testRemovingInteriorNodes() { Group group = createGroupWithRects(); group.getChildren().remove(4); assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); } public @Test void testRemovingExteriorNodes() { Group group = createGroupWithRects(); group.getChildren().remove(7); assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); group.getChildren().remove(6); assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); group.getChildren().remove(5); assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); group.getChildren().remove(3); assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); group.getChildren().remove(2); assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); group.getChildren().remove(1); assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); // and now the bounds should be changing group.getChildren().remove(0); assertEquals(box(200, 200, 150, 150), group.getBoundsInLocal()); group.getChildren().remove(1); assertEquals(box(200, 200, 50, 50), group.getBoundsInLocal()); } // run a set of tests that add 1000's of random sized and random positioned // rectangles to the Group, and check the groups bounds at each stage. // Also randomly make some invisible, some visible, remove some, toggle // visibility, and so forth. public @Test void testGroupBounds_Stress() { boolean fullTest = Boolean.getBoolean("test.everything"); if (fullTest) { Group g = new Group(); assertGroupBounds(g); for (int j = 0; j < 500; j++) { LinkedList whatHappenedStack = new java.util.LinkedList(); for (int i = 0; i <= 50; i++) { Bounds layoutBounds = g.getLayoutBounds(); int numChildren = g.getChildren().size(); String whatHappened = ""; double rnd = Math.random(); int index = (int) (Math.random() * numChildren); if (rnd > .3 && rnd < .5 && numChildren > 0) { // translate some rect int direction = (Math.random() > .5) ? -1 : 1; Bounds oldBoundsInParent = g.getChildren().get(index) .getBoundsInParent(); g.getChildren() .get(index) .setTranslateX( (int) (Math.random() * 500 * direction)); g.getChildren() .get(index) .setTranslateY( (int) (Math.random() * 500 * direction)); whatHappened = "On iteration " + j + "-"+ i + "I translated a " + g.getChildren().get(index) + " with boundsInParent " + formatBounds(oldBoundsInParent) + " which was the " + index+1 + "th item. Its new bounds are " + formatBounds(g.getChildren().get(index).getBoundsInParent()) + ". " + "The layoutBounds of the Group was " + formatBounds(layoutBounds) + " and is now " + formatBounds(g.getLayoutBounds()) + " and had " + numChildren + " number of child nodes"; whatHappenedStack.add(whatHappened); } else if (rnd > .5 && rnd < .6 && numChildren > 0) { // deletesome rect whatHappened = "On iteration " + j + "-" + i+ " I deleted a " + "" + g.getChildren().get(index) + " with boundsInParent " + "" + formatBounds(g.getChildren().get(index).getBoundsInParent())+ " which was the " + "" + index+1+ "th item. The layoutBounds of the Group was " + "" + formatBounds(layoutBounds)+ " and is now " + formatBounds(g.getLayoutBounds())+ " and had " + "" + numChildren+ " number of child nodes"; g.getChildren().remove(index); whatHappenedStack.add(whatHappened); } else if (rnd > .6 && rnd < .65 && numChildren > 0) { // toggle visibility on some rect g.getChildren().get(index) .setVisible(!g.getChildren().get(index).isVisible()); whatHappened = "On iteration " + j+ "-" + i+ " I toggled the " + "visibility of " + g.getChildren().get(index) + " with boundsInParent " + "" + formatBounds(g.getChildren().get(index).getBoundsInParent())+ " to " + "" + g.getChildren().get(index).isVisible() + ". The layoutBounds of the " + "Group was " + formatBounds(layoutBounds)+ " and is now " + formatBounds(g.getLayoutBounds())+ " and " + "had " + numChildren+ " number of child nodes"; whatHappenedStack.add(whatHappened); } else { Rectangle rect = new Rectangle( ((int) (Math.random() * 1000)), ((int) (Math.random() * 1000)), ((int) (Math.random() * 100)), ((int) (Math.random() * 100))); g.getChildren().add(rect); whatHappened = "On iteration " + j+ "-" + i+ " I added a " + rect+ " with " + "boundsInParent " + formatBounds(rect.getBoundsInParent())+ " to the group. The " + "layoutBounds of the Group was " + formatBounds(layoutBounds)+ " and is now " + "" + formatBounds(g.getLayoutBounds())+ " and had " + numChildren+ " number of " + "child nodes"; whatHappenedStack.add(whatHappened); } try { assertGroupBounds(g); } catch (Exception any) { System.out.println("Something went wrong. Here's what happened:"); for (String item : whatHappenedStack) { System.out.println("{item}"); } throw new RuntimeException(any); } } } } } // here is a special test for getPivotX and getPivotY @Test public void testPivotXAndPivotY() { Rectangle rect = new Rectangle(100, 100); assertEquals(50.0f, (float) NodeHelper.getPivotX(rect)); assertEquals(50.0f, (float) NodeHelper.getPivotY(rect)); rect.setWidth(70.0f); assertEquals(35.0f, (float) NodeHelper.getPivotX(rect)); assertEquals(50.0f, (float) NodeHelper.getPivotY(rect)); } /*************************************************************************** * Group Bounds Sanity Tests * * These tests are for Group * to make sure that their * bounds are reported correctly * before effects or clips or transforms * are taken into account. * * The * group tests here are just basic tests, a full set of Group bounds * tests * are included in their own section later in this file. There are * many * edge cases to group bounds. The primary purpose of these next * few tests * are just to make sure that group's bounds are correct in the * simple * case. * * **************************************************************************/ public @Test void testBoundsForGroup() { Rectangle r1 = new Rectangle(20, 20, 50, 50); Rectangle r2 = new Rectangle(90, 20, 50, 50); Group group = new Group(r1, r2); assertEquals(box(20, 20, 120, 50), group.getBoundsInLocal()); assertEquals(group.getBoundsInLocal(), group.getBoundsInParent()); assertEquals(group.getBoundsInLocal(), group.getLayoutBounds()); } /** * Just a basic test to make sure basic group node bounds calculation works */ public @Test void testBoundsForGroup_childGeomChanging() { Rectangle rect = new Rectangle(); rect.setId("rect"); Rectangle square = new Rectangle(); square.setId("square"); Group group = new Group(rect, square); group.setId("group"); assertBoundsEqual(box(0, 0, 0, 0), rect.getBoundsInLocal()); assertBoundsEqual(box(0, 0, 0, 0), square.getBoundsInLocal()); assertBoundsEqual(box(0, 0, 0, 0), group.getBoundsInLocal()); rect.setX(50); rect.setY(50); rect.setWidth(100); rect.setHeight(30); assertBoundsEqual(box(50, 50, 100, 30), rect.getBoundsInLocal()); assertBoundsEqual(box(0, 0, 0, 0), square.getBoundsInLocal()); assertBoundsEqual(box(0, 0, 150, 80), group.getBoundsInLocal()); square.setX(25); square.setY(25); square.setWidth(50); square.setHeight(50); assertBoundsEqual(box(50, 50, 100, 30), rect.getBoundsInLocal()); assertBoundsEqual(box(25, 25, 50, 50), square.getBoundsInLocal()); assertBoundsEqual(box(25, 25, 125, 55), group.getBoundsInLocal()); } public @Test void testBoundsInParentOfGroup() { Rectangle rect = new Rectangle(50, 50, 100, 30); rect.setId("rect"); Group group = new Group(rect); group.setId("group"); group.setTranslateX(100); group.setTranslateY(100); assertBoundsEqual(box(150, 150, 100, 30), group.getBoundsInParent()); } /** * Tests that if I have a group with nobody listening to the bounds, and the * bounds change, and then I ask for the new bounds, that the bounds are * valid. */ public @Test void testRequestingBoundsOnGroupWithNoListenersWorks() { Group group = new Group(new Rectangle(50, 50, 100, 30)); assertBoundsEqual(box(50, 50, 100, 30), group.getBoundsInLocal()); } }