1 /* 2 * Copyright (c) 2011, 2015, 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.bounds; 27 28 import static test.com.sun.javafx.test.TestHelper.assertBoundsEqual; 29 import static test.com.sun.javafx.test.TestHelper.assertGroupBounds; 30 import static test.com.sun.javafx.test.TestHelper.box; 31 import static test.com.sun.javafx.test.TestHelper.formatBounds; 32 import static junit.framework.Assert.assertEquals; 33 34 import java.util.LinkedList; 35 36 import javafx.geometry.Bounds; 37 import javafx.scene.Group; 38 import javafx.scene.shape.Rectangle; 39 40 import org.junit.Test; 41 42 public class GroupBoundsTest { 43 44 /*************************************************************************** 45 * * Group Tests * * These are tests related to transforming the basic node 46 * types including * * / 47 **************************************************************************/ 48 49 // test that an empty group has empty bounds 50 public @Test 51 void testGroupBounds_Empty() { 52 assertGroupBounds(new Group()); 53 } 54 55 // test that adding a node to a group updates the Groups bounds 56 public @Test 57 void testGroupBounds_AddRect() { 58 Group g = new Group(); 59 assertGroupBounds(g); 60 61 g.getChildren().add(new Rectangle(20, 20, 50, 50)); 62 assertGroupBounds(g); 63 64 g.getChildren().add(new Rectangle(90, 20, 50, 50)); 65 assertGroupBounds(g); 66 } 67 68 // test that changing a node in a group updates the groups bounds 69 public @Test 70 void testGroupBounds_ChangeRect() { 71 Rectangle r1 = new Rectangle(20, 20, 50, 50); 72 Rectangle r2 = new Rectangle(90, 20, 50, 50); 73 Group g = new Group(r1, r2); 74 75 assertGroupBounds(g); 76 77 r1.setWidth(200); 78 assertGroupBounds(g); 79 r2.setWidth(20); 80 assertGroupBounds(g); 81 } 82 83 // test that removing a node from the group updates the Groups bounds 84 public @Test 85 void testGroupBounds_RemoveRect() { 86 Rectangle r1 = new Rectangle(20, 20, 50, 50); 87 Rectangle r2 = new Rectangle(90, 20, 50, 50); 88 Group g = new Group(r1, r2); 89 90 assertGroupBounds(g); 91 92 g.getChildren().remove(r2); 93 assertGroupBounds(g); 94 g.getChildren().remove(r1); 95 assertGroupBounds(g); 96 } 97 98 public @Test 99 void testGroupBounds_RemoveAllChildNodesAndAddOneBack() { 100 Rectangle r1 = new Rectangle(20, 20, 50, 50); 101 Rectangle r2 = new Rectangle(90, 20, 50, 50); 102 Group g = new Group(r1, r2); 103 104 assertGroupBounds(g); 105 g.getChildren().clear(); 106 assertGroupBounds(g); 107 g.getChildren().add(r1); 108 assertGroupBounds(g); 109 } 110 111 // test that adding the first node to a Group, which node is an invisible 112 // node, gets the right answer for the Group's bounds. 113 public @Test 114 void testGroupBounds_InitialNodeInvisible() { 115 Rectangle r1 = new Rectangle(20, 20, 50, 50); 116 r1.setVisible(false); 117 Group g = new Group(r1); 118 119 assertGroupBounds(g); 120 } 121 122 // test that adding invisible nodes to a group doesn't update the bounds 123 public @Test 124 void testGroupBounds_InvisibleNodes() { 125 Rectangle r1 = new Rectangle(20, 20, 50, 50); 126 Rectangle r2 = new Rectangle(90, 20, 50, 50); 127 Group g = new Group(r1, r2); 128 assertGroupBounds(g); 129 130 Rectangle invisible = new Rectangle(-100, -100, 1, 1); 131 invisible.setVisible(true); 132 g.getChildren().add(invisible); 133 assertGroupBounds(g); 134 } 135 136 // test that adding zero sized nodes does update a Groups bounds 137 public @Test 138 void testGroupBounds_ZeroSizedNodes() { 139 Rectangle r1 = new Rectangle(20, 20, 50, 50); 140 Rectangle r2 = new Rectangle(90, 20, 50, 50); 141 Group g = new Group(r1, r2); 142 assertGroupBounds(g); 143 144 Rectangle small = new Rectangle(-100, -100, 0, 0); 145 g.getChildren().add(small); 146 assertGroupBounds(g); 147 } 148 149 // test that making a node invisible updates the groups bounds 150 public @Test 151 void testGroupBounds_NodeMadeInvisible() { 152 Rectangle r1 = new Rectangle(20, 20, 50, 50); 153 Rectangle r2 = new Rectangle(90, 20, 50, 50); 154 Group g = new Group(r1, r2); 155 assertGroupBounds(g); 156 157 r1.setVisible(false); 158 assertGroupBounds(g); 159 } 160 161 public @Test 162 void testGroupBounds_NodeWithNoWidthMadeInvisible() { 163 Rectangle r1 = new Rectangle(20, 20, 50, 50); 164 Rectangle r2 = new Rectangle(90, 20, 0, 50); 165 Group g = new Group(r1, r2); 166 assertGroupBounds(g); 167 168 r2.setVisible(false); 169 assertGroupBounds(g); 170 } 171 172 public @Test 173 void testGroupBounds_FirstNodeHasNoWidthSecondNodeDoes() { 174 Rectangle r1 = new Rectangle(20, 20, 0, 50); 175 Rectangle r2 = new Rectangle(90, 20, 50, 50); 176 Group g = new Group(r1, r2); 177 assertGroupBounds(g); 178 } 179 180 public @Test 181 void testGroupBounds_FirstNodeHasNoWidthSecondNodeDoes_Added() { 182 Rectangle r1 = new Rectangle(20, 20, 0, 50); 183 Rectangle r2 = new Rectangle(90, 20, 50, 50); 184 Group g = new Group(); 185 assertGroupBounds(g); 186 g.getChildren().add(r1); 187 assertGroupBounds(g); 188 g.getChildren().add(r2); 189 assertGroupBounds(g); 190 } 191 192 public @Test 193 void testGroupBounds_FirstNodeHasNoWidthSecondNodeDoes_ClearedThenAdded() { 194 Rectangle r1 = new Rectangle(20, 20, 0, 50); 195 Rectangle r2 = new Rectangle(90, 20, 50, 50); 196 Rectangle r3 = new Rectangle(10, 15, 75, 20); 197 Group g = new Group(r1, r2); 198 assertGroupBounds(g); 199 g.getChildren().remove(r2); 200 assertGroupBounds(g); 201 g.getChildren().add(r3); 202 assertGroupBounds(g); 203 } 204 205 // this test uncovered a bug when using the GroupBoundsHelper. It needs 206 // two invisible nodes to tickle it You also have to ask assert the bounds 207 // after each change or it may not fail 208 public @Test 209 void testGroupBounds_TwoNodesMadeInvisible_ThenNodeAdded() { 210 Rectangle r1 = new Rectangle(20, 20, 50, 50); 211 Rectangle r2 = new Rectangle(90, 20, 50, 50); 212 Rectangle r3 = new Rectangle(10, 15, 75, 20); 213 Group g = new Group(); 214 215 assertGroupBounds(g); 216 g.getChildren().add(r1); 217 assertGroupBounds(g); 218 r1.setVisible(false); 219 assertGroupBounds(g); 220 g.getChildren().add(r2); 221 assertGroupBounds(g); 222 r2.setVisible(false); 223 assertGroupBounds(g); 224 g.getChildren().add(r3); 225 assertGroupBounds(g); 226 } 227 228 public @Test 229 void testGroupBounds_TogglingVisiblityToFalseAndTrueWhileExpandingBounds() { 230 Rectangle r1 = new Rectangle(20, 20, 50, 50); 231 Rectangle r2 = new Rectangle(90, 20, 50, 50); 232 Rectangle r3 = new Rectangle(10, 15, 75, 20); 233 Group g = new Group(); 234 assertGroupBounds(g); 235 g.getChildren().add(r1); 236 assertGroupBounds(g); 237 g.getChildren().add(r2); 238 assertGroupBounds(g); 239 r2.setVisible(false); 240 assertGroupBounds(g); 241 g.getChildren().add(r3); 242 assertGroupBounds(g); 243 r2.setVisible(true); 244 assertGroupBounds(g); 245 } 246 247 private Group createGroupWithRects() { 248 Group group = new Group(); 249 group.setId("group"); 250 for (int i = 1; i <= 3; i++) { 251 for (int j = 1; j <= 3; j++) { 252 Rectangle r = new Rectangle(j * 100, i * 100, 50, 50); 253 r.setId("rect[" + i + "," + j + "]"); 254 group.getChildren().add(r); 255 } 256 } 257 258 // sanity test check that the size of the group is correct 259 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 260 261 return group; 262 } 263 264 // Started out as a simple test and now pretty well covers all the use 265 // cases. Oops. Bad Richard. 266 public @Test 267 void testMovingNodesTowardCenterOfGroupAndOther() { 268 Group group = createGroupWithRects(); 269 270 // move the 1, 3, 5, 7 nodes in towards the center. Doing so should not 271 // change the group's bounds 272 ((Rectangle) group.getChildren().get(1)).setX(200); 273 ((Rectangle) group.getChildren().get(1)).setY(200); 274 ((Rectangle) group.getChildren().get(3)).setX(200); 275 ((Rectangle) group.getChildren().get(3)).setY(200); 276 ((Rectangle) group.getChildren().get(5)).setX(200); 277 ((Rectangle) group.getChildren().get(5)).setY(200); 278 ((Rectangle) group.getChildren().get(7)).setX(200); 279 ((Rectangle) group.getChildren().get(7)).setY(200); 280 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 281 282 // move the 2 and 6 nodes in towards the center. Doing so should not 283 // change the group's bounds 284 ((Rectangle) group.getChildren().get(2)).setX(200); 285 ((Rectangle) group.getChildren().get(2)).setY(200); 286 ((Rectangle) group.getChildren().get(6)).setX(200); 287 ((Rectangle) group.getChildren().get(6)).setY(200); 288 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 289 290 // move the 0 node into the center. Doing so SHOULD change the bounds 291 // such that all nodes are in the center (200, 200) except for the 292 // number 8 node which is at (300, 300) 293 ((Rectangle) group.getChildren().get(0)).setX(200); 294 ((Rectangle) group.getChildren().get(0)).setY(200); 295 assertEquals(box(200, 200, 150, 150), group.getBoundsInLocal()); 296 297 // move the 8 node a little towards the center (but not all the way) 298 // just to test that moving the bottom-right will adjust things 299 ((Rectangle) group.getChildren().get(8)).setX(250); 300 ((Rectangle) group.getChildren().get(8)).setY(250); 301 assertEquals(box(200, 200, 100, 100), group.getBoundsInLocal()); 302 303 // move the rest of the nodes down past #8 304 for (int i = 0; i <= 7; i++) { 305 ((Rectangle) group.getChildren().get(i)).setX(300); 306 ((Rectangle) group.getChildren().get(i)).setY(300); 307 } 308 assertEquals(box(250, 250, 100, 100), group.getBoundsInLocal()); 309 310 // now move #8 onto the rest 311 ((Rectangle) group.getChildren().get(8)).setX(300); 312 ((Rectangle) group.getChildren().get(8)).setY(300); 313 assertEquals(box(300, 300, 50, 50), group.getBoundsInLocal()); 314 } 315 316 public @Test 317 void testMovingNodesAwayFromCenterOfGroup() { 318 Group group = createGroupWithRects(); 319 320 ((Rectangle) group.getChildren().get(4)).setX(600); 321 ((Rectangle) group.getChildren().get(4)).setY(600); 322 assertEquals(box(100, 100, 550, 550), group.getBoundsInLocal()); 323 } 324 325 public @Test 326 void testMovingNodesWithinInteriorOfGroup() { 327 Group group = createGroupWithRects(); 328 329 ((Rectangle) group.getChildren().get(4)).setX(250); 330 ((Rectangle) group.getChildren().get(4)).setY(250); 331 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 332 } 333 334 public @Test 335 void testMovingExteriorNodesOnExterior() { 336 Group group = createGroupWithRects(); 337 338 ((Rectangle) group.getChildren().get(0)).setY(150); 339 ((Rectangle) group.getChildren().get(1)).setX(100); 340 ((Rectangle) group.getChildren().get(2)).setX(150); 341 ((Rectangle) group.getChildren().get(3)).setY(120); 342 ((Rectangle) group.getChildren().get(5)).setY(100); 343 ((Rectangle) group.getChildren().get(6)).setX(250); 344 ((Rectangle) group.getChildren().get(7)).setX(120); 345 ((Rectangle) group.getChildren().get(8)).setY(200); 346 347 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 348 } 349 350 public @Test 351 void testRemovingInteriorNodes() { 352 Group group = createGroupWithRects(); 353 354 group.getChildren().remove(4); 355 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 356 } 357 358 public @Test 359 void testRemovingExteriorNodes() { 360 Group group = createGroupWithRects(); 361 362 group.getChildren().remove(7); 363 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 364 group.getChildren().remove(6); 365 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 366 group.getChildren().remove(5); 367 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 368 group.getChildren().remove(3); 369 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 370 group.getChildren().remove(2); 371 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 372 group.getChildren().remove(1); 373 assertEquals(box(100, 100, 250, 250), group.getBoundsInLocal()); 374 375 // and now the bounds should be changing 376 group.getChildren().remove(0); 377 assertEquals(box(200, 200, 150, 150), group.getBoundsInLocal()); 378 group.getChildren().remove(1); 379 assertEquals(box(200, 200, 50, 50), group.getBoundsInLocal()); 380 } 381 382 // run a set of tests that add 1000's of random sized and random positioned 383 // rectangles to the Group, and check the groups bounds at each stage. 384 // Also randomly make some invisible, some visible, remove some, toggle 385 // visibility, and so forth. 386 public @Test 387 void testGroupBounds_Stress() { 388 boolean fullTest = Boolean.getBoolean("test.everything"); 389 if (fullTest) { 390 Group g = new Group(); 391 assertGroupBounds(g); 392 for (int j = 0; j < 500; j++) { 393 LinkedList<String> whatHappenedStack = new java.util.LinkedList<String>(); 394 for (int i = 0; i <= 50; i++) { 395 Bounds layoutBounds = g.getLayoutBounds(); 396 int numChildren = g.getChildren().size(); 397 String whatHappened = ""; 398 double rnd = Math.random(); 399 int index = (int) (Math.random() * numChildren); 400 if (rnd > .3 && rnd < .5 && numChildren > 0) { 401 // translate some rect 402 int direction = (Math.random() > .5) ? -1 : 1; 403 Bounds oldBoundsInParent = g.getChildren().get(index) 404 .getBoundsInParent(); 405 g.getChildren() 406 .get(index) 407 .setTranslateX( 408 (int) (Math.random() * 500 * direction)); 409 g.getChildren() 410 .get(index) 411 .setTranslateY( 412 (int) (Math.random() * 500 * direction)); 413 whatHappened = "On iteration " + j + "-"+ i + "I translated a " 414 + g.getChildren().get(index) + " with boundsInParent " 415 + formatBounds(oldBoundsInParent) + " which was the " 416 + index+1 + "th item. Its new bounds are " 417 + formatBounds(g.getChildren().get(index).getBoundsInParent()) + ". " 418 + "The layoutBounds of the Group was " 419 + formatBounds(layoutBounds) + " and is now " + formatBounds(g.getLayoutBounds()) + " and had " 420 + numChildren + " number of child nodes"; 421 whatHappenedStack.add(whatHappened); 422 } else if (rnd > .5 && rnd < .6 && numChildren > 0) { 423 // deletesome rect 424 whatHappened = "On iteration " + j + "-" + i+ " I deleted a " 425 + "" + g.getChildren().get(index) + " with boundsInParent " 426 + "" + formatBounds(g.getChildren().get(index).getBoundsInParent())+ " which was the " 427 + "" + index+1+ "th item. The layoutBounds of the Group was " 428 + "" + formatBounds(layoutBounds)+ " and is now " + formatBounds(g.getLayoutBounds())+ " and had " 429 + "" + numChildren+ " number of child nodes"; 430 g.getChildren().remove(index); 431 whatHappenedStack.add(whatHappened); 432 } else if (rnd > .6 && rnd < .65 && numChildren > 0) { 433 // toggle visibility on some rect 434 g.getChildren().get(index) 435 .setVisible(!g.getChildren().get(index).isVisible()); 436 whatHappened = "On iteration " + j+ "-" + i+ " I toggled the " 437 + "visibility of " + g.getChildren().get(index) + " with boundsInParent " 438 + "" + formatBounds(g.getChildren().get(index).getBoundsInParent())+ " to " 439 + "" + g.getChildren().get(index).isVisible() + ". The layoutBounds of the " 440 + "Group was " + formatBounds(layoutBounds)+ " and is now " + formatBounds(g.getLayoutBounds())+ " and " 441 + "had " + numChildren+ " number of child nodes"; 442 whatHappenedStack.add(whatHappened); 443 } else { 444 Rectangle rect = new Rectangle( 445 ((int) (Math.random() * 1000)), 446 ((int) (Math.random() * 1000)), 447 ((int) (Math.random() * 100)), 448 ((int) (Math.random() * 100))); 449 g.getChildren().add(rect); 450 whatHappened = "On iteration " + j+ "-" + i+ " I added a " + rect+ " with " 451 + "boundsInParent " + formatBounds(rect.getBoundsInParent())+ " to the group. The " 452 + "layoutBounds of the Group was " + formatBounds(layoutBounds)+ " and is now " 453 + "" + formatBounds(g.getLayoutBounds())+ " and had " + numChildren+ " number of " 454 + "child nodes"; 455 whatHappenedStack.add(whatHappened); 456 } 457 try { 458 assertGroupBounds(g); 459 } catch (Exception any) { 460 System.out.println("Something went wrong. Here's what happened:"); 461 for (String item : whatHappenedStack) { 462 System.out.println("{item}"); 463 } 464 throw new RuntimeException(any); 465 } 466 } 467 } 468 } 469 } 470 471 // here is a special test for impl_getPivotX and impl_getPivotY 472 @Test 473 public void testPivotXAndPivotY() { 474 Rectangle rect = new Rectangle(100, 100); 475 assertEquals(50.0f, (float) rect.impl_getPivotX()); 476 assertEquals(50.0f, (float) rect.impl_getPivotY()); 477 rect.setWidth(70.0f); 478 assertEquals(35.0f, (float) rect.impl_getPivotX()); 479 assertEquals(50.0f, (float) rect.impl_getPivotY()); 480 } 481 482 /*************************************************************************** 483 * Group Bounds Sanity Tests * * These tests are for Group 484 * to make sure that their * bounds are reported correctly 485 * before effects or clips or transforms * are taken into account. * * The 486 * group tests here are just basic tests, a full set of Group bounds * tests 487 * are included in their own section later in this file. There are * many 488 * edge cases to group bounds. The primary purpose of these next * few tests 489 * are just to make sure that group's bounds are correct in the * simple 490 * case. * * 491 **************************************************************************/ 492 493 public @Test 494 void testBoundsForGroup() { 495 Rectangle r1 = new Rectangle(20, 20, 50, 50); 496 Rectangle r2 = new Rectangle(90, 20, 50, 50); 497 Group group = new Group(r1, r2); 498 499 assertEquals(box(20, 20, 120, 50), group.getBoundsInLocal()); 500 assertEquals(group.getBoundsInLocal(), group.getBoundsInParent()); 501 assertEquals(group.getBoundsInLocal(), group.getLayoutBounds()); 502 } 503 504 /** 505 * Just a basic test to make sure basic group node bounds calculation works 506 */ 507 public @Test 508 void testBoundsForGroup_childGeomChanging() { 509 Rectangle rect = new Rectangle(); 510 rect.setId("rect"); 511 Rectangle square = new Rectangle(); 512 square.setId("square"); 513 Group group = new Group(rect, square); 514 group.setId("group"); 515 516 assertBoundsEqual(box(0, 0, 0, 0), rect.getBoundsInLocal()); 517 assertBoundsEqual(box(0, 0, 0, 0), square.getBoundsInLocal()); 518 assertBoundsEqual(box(0, 0, 0, 0), group.getBoundsInLocal()); 519 520 rect.setX(50); 521 rect.setY(50); 522 rect.setWidth(100); 523 rect.setHeight(30); 524 525 assertBoundsEqual(box(50, 50, 100, 30), rect.getBoundsInLocal()); 526 assertBoundsEqual(box(0, 0, 0, 0), square.getBoundsInLocal()); 527 assertBoundsEqual(box(0, 0, 150, 80), group.getBoundsInLocal()); 528 529 square.setX(25); 530 square.setY(25); 531 square.setWidth(50); 532 square.setHeight(50); 533 534 assertBoundsEqual(box(50, 50, 100, 30), rect.getBoundsInLocal()); 535 assertBoundsEqual(box(25, 25, 50, 50), square.getBoundsInLocal()); 536 assertBoundsEqual(box(25, 25, 125, 55), group.getBoundsInLocal()); 537 } 538 539 public @Test 540 void testBoundsInParentOfGroup() { 541 Rectangle rect = new Rectangle(50, 50, 100, 30); 542 rect.setId("rect"); 543 Group group = new Group(rect); 544 group.setId("group"); 545 group.setTranslateX(100); 546 group.setTranslateY(100); 547 548 assertBoundsEqual(box(150, 150, 100, 30), group.getBoundsInParent()); 549 } 550 551 /** 552 * Tests that if I have a group with nobody listening to the bounds, and the 553 * bounds change, and then I ask for the new bounds, that the bounds are 554 * valid. 555 */ 556 public @Test 557 void testRequestingBoundsOnGroupWithNoListenersWorks() { 558 Group group = new Group(new Rectangle(50, 50, 100, 30)); 559 560 assertBoundsEqual(box(50, 50, 100, 30), group.getBoundsInLocal()); 561 } 562 563 }