1 /*
   2  * Copyright (c) 2011, 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 com.sun.javafx.binding;
  27 
  28 import javafx.beans.InvalidationListener;
  29 import javafx.beans.binding.Bindings;
  30 import javafx.beans.property.*;
  31 import javafx.beans.value.ChangeListener;
  32 import org.junit.Before;
  33 import org.junit.Test;
  34 import org.junit.runner.RunWith;
  35 import org.junit.runners.Parameterized;
  36 
  37 import java.util.Arrays;
  38 import java.util.Collection;
  39 
  40 import static org.junit.Assert.*;
  41 import org.junit.Rule;
  42 import org.junit.rules.ExpectedException;
  43 
  44 @RunWith(Parameterized.class)
  45 public class BidirectionalBindingTest<T> {
  46         
  47     private static final float EPSILON_FLOAT = 1e-5f;
  48     private static final double EPSILON_DOUBLE = 1e-10;
  49 
  50     public static interface Functions<S> {
  51         PropertyMock<S> create();
  52         void bind(PropertyMock<S> obj1, PropertyMock<S> obj2);
  53         void unbind(PropertyMock<S> obj1, PropertyMock<S> obj2);
  54         BidirectionalBinding createBindingDirectly(PropertyMock<S> op1, PropertyMock<S> op2);
  55         void check(S expected, S actual);
  56     }
  57 
  58     private final Functions<T> func;
  59     private final T[] v;
  60     
  61     @Rule
  62     public ExpectedException thrown = ExpectedException.none();
  63 
  64     private PropertyMock<T> op1;
  65     private PropertyMock<T> op2;
  66     private PropertyMock<T> op3;
  67     private PropertyMock<T> op4;
  68     
  69     public BidirectionalBindingTest(Functions<T> func, T[] v) {
  70         this.op1 = func.create();
  71         this.op2 = func.create();
  72         this.op3 = func.create();
  73         this.op4 = func.create();
  74         this.func = func;
  75         this.v = v;
  76     }
  77 
  78     @Before
  79     public void setUp() {
  80         op1.setValue(v[0]);
  81         op2.setValue(v[1]);
  82     }
  83     
  84     @Test
  85     public void testBind() {
  86         func.bind(op1, op2);
  87                 System.gc(); // making sure we did not not overdo weak references
  88                 func.check(v[1], op1.getValue());
  89                 func.check(v[1], op2.getValue());
  90                 
  91                 op1.setValue(v[2]);
  92                 func.check(v[2], op1.getValue());
  93                 func.check(v[2], op2.getValue());
  94                 
  95                 op2.setValue(v[3]);
  96                 func.check(v[3], op1.getValue());
  97                 func.check(v[3], op2.getValue());
  98     }
  99 
 100         @Test
 101         public void testUnbind() {
 102                 // unbind non-existing binding => no-op
 103                 func.unbind(op1, op2);
 104                 
 105                 // unbind properties of different beans
 106         func.bind(op1, op2);
 107                 System.gc(); // making sure we did not not overdo weak references
 108                 func.check(v[1], op1.getValue());
 109                 func.check(v[1], op2.getValue());
 110                 
 111                 func.unbind(op1, op2);
 112                 System.gc();
 113                 func.check(v[1], op1.getValue());
 114                 func.check(v[1], op2.getValue());
 115                 
 116                 op1.setValue(v[2]);
 117                 func.check(v[2], op1.getValue());
 118                 func.check(v[1], op2.getValue());
 119                 
 120                 op2.setValue(v[3]);
 121                 func.check(v[2], op1.getValue());
 122                 func.check(v[3], op2.getValue());
 123         }
 124         
 125         @Test
 126         public void testChaining() {
 127                 op3.setValue(v[2]);
 128                 func.bind(op1, op2);
 129                 func.bind(op2, op3);
 130                 System.gc(); // making sure we did not not overdo weak references
 131                 func.check(v[2], op1.getValue());
 132                 func.check(v[2], op2.getValue());
 133                 func.check(v[2], op3.getValue());
 134                 
 135                 op1.setValue(v[3]);
 136                 func.check(v[3], op1.getValue());
 137                 func.check(v[3], op2.getValue());
 138                 func.check(v[3], op3.getValue());
 139                 
 140                 op2.setValue(v[0]);
 141                 func.check(v[0], op1.getValue());
 142                 func.check(v[0], op2.getValue());
 143                 func.check(v[0], op3.getValue());
 144                 
 145                 op3.setValue(v[1]);
 146                 func.check(v[1], op1.getValue());
 147                 func.check(v[1], op2.getValue());
 148                 func.check(v[1], op3.getValue());
 149                 
 150                 // now unbind 
 151                 func.unbind(op1, op2);
 152                 System.gc(); // making sure we did not not overdo weak references
 153                 func.check(v[1], op1.getValue());
 154                 func.check(v[1], op2.getValue());
 155                 func.check(v[1], op3.getValue());
 156                 
 157                 op1.setValue(v[2]);
 158                 func.check(v[2], op1.getValue());
 159                 func.check(v[1], op2.getValue());
 160                 func.check(v[1], op3.getValue());
 161                 
 162                 op2.setValue(v[3]);
 163                 func.check(v[2], op1.getValue());
 164                 func.check(v[3], op2.getValue());
 165                 func.check(v[3], op3.getValue());
 166                 
 167                 op3.setValue(v[0]);
 168                 func.check(v[2], op1.getValue());
 169                 func.check(v[0], op2.getValue());
 170                 func.check(v[0], op3.getValue());
 171         }
 172         
 173         @Test
 174         public void testWeakReferencing() {
 175                 func.bind(op1, op2);
 176                 assertEquals(1, op1.getListenerCount());
 177                 assertEquals(1, op2.getListenerCount());
 178                 
 179                 op1 = null;
 180                 System.gc();
 181                 op2.setValue(v[2]);
 182                 assertEquals(0, op2.getListenerCount());
 183                 
 184                 func.bind(op2, op3);
 185                 assertEquals(1, op2.getListenerCount());
 186         assertEquals(1, op3.getListenerCount());
 187 
 188                 op3 = null;
 189                 System.gc();
 190                 op2.setValue(v[0]);
 191                 assertEquals(0, op2.getListenerCount());
 192         }
 193         
 194         @Test
 195         public void testHashCode() {
 196                 final int hc1 = func.createBindingDirectly(op1, op2).hashCode();
 197                 final int hc2 = func.createBindingDirectly(op2, op1).hashCode();
 198                 assertEquals(hc1, hc2);
 199         }
 200         
 201         
 202         @Test
 203         public void testEquals() {
 204                 final BidirectionalBinding golden = func.createBindingDirectly(op1, op2);
 205                 
 206                 assertTrue(golden.equals(golden));
 207                 assertFalse(golden.equals(null));
 208                 assertFalse(golden.equals(op1));
 209                 assertTrue(golden.equals(func.createBindingDirectly(op1, op2)));
 210                 assertTrue(golden.equals(func.createBindingDirectly(op2, op1)));
 211                 assertFalse(golden.equals(func.createBindingDirectly(op1, op3)));
 212                 assertFalse(golden.equals(func.createBindingDirectly(op3, op1)));
 213                 assertFalse(golden.equals(func.createBindingDirectly(op3, op2)));
 214                 assertFalse(golden.equals(func.createBindingDirectly(op2, op3)));
 215         }
 216         
 217         @Test
 218         public void testEqualsWithGCedProperty() {
 219                 final BidirectionalBinding binding1 = func.createBindingDirectly(op1, op2);
 220                 final BidirectionalBinding binding2 = func.createBindingDirectly(op1, op2);
 221                 final BidirectionalBinding binding3 = func.createBindingDirectly(op2, op1);
 222                 final BidirectionalBinding binding4 = func.createBindingDirectly(op2, op1);
 223                 op1 = null;
 224                 System.gc();
 225 
 226                 assertTrue(binding1.equals(binding1));
 227                 assertFalse(binding1.equals(binding2));
 228                 assertFalse(binding1.equals(binding3));
 229 
 230                 assertTrue(binding3.equals(binding3));
 231                 assertFalse(binding3.equals(binding1));
 232                 assertFalse(binding3.equals(binding4));
 233         }
 234         
 235         @Test(expected=NullPointerException.class)
 236         public void testBind_Null_X() {
 237                 func.bind(null, op2);
 238         }
 239 
 240         @Test(expected=NullPointerException.class)
 241         public void testBind_X_Null() {
 242                 func.bind(op1, null);
 243         }
 244 
 245         @Test(expected=IllegalArgumentException.class)
 246         public void testBind_X_Self() {
 247                 func.bind(op1, op1);
 248         }
 249         
 250         @Test(expected=NullPointerException.class)
 251         public void testUnbind_Null_X() {
 252                 func.unbind(null, op2);
 253         }
 254 
 255         @Test(expected=NullPointerException.class)
 256         public void testUnbind_X_Null() {
 257                 func.unbind(op1, null);
 258         }
 259 
 260         @Test(expected=IllegalArgumentException.class)
 261         public void testUnbind_X_Self() {
 262                 func.unbind(op1, op1);
 263         }
 264         
 265         @Test
 266         public void testBrokenBind() {
 267             func.bind(op1, op2);
 268             op1.bind(op3);
 269             assertEquals(op3.getValue(), op1.getValue());
 270             assertEquals(op2.getValue(), op1.getValue());
 271 
 272             op2.setValue(v[2]);
 273             assertEquals(op3.getValue(), op1.getValue());
 274             assertEquals(op2.getValue(), op1.getValue());
 275         }
 276         
 277         @Test
 278         public void testDoubleBrokenBind() {
 279             func.bind(op1, op2);
 280             op1.bind(op3);
 281             op4.setValue(v[0]);
 282 
 283             op2.bind(op4);
 284             assertEquals(op4.getValue(), op2.getValue());
 285             assertEquals(op3.getValue(), op1.getValue());
 286             // Test that bidirectional binding was unbound in this case
 287             op3.setValue(v[0]);
 288             op4.setValue(v[1]);
 289             assertEquals(op4.getValue(), op2.getValue());
 290             assertEquals(op3.getValue(), op1.getValue());
 291             assertEquals(v[0], op1.getValue());
 292             assertEquals(v[1], op2.getValue());
 293         }
 294         
 295     @Parameterized.Parameters
 296     public static Collection<Object[]> parameters() {
 297         final Boolean[] booleanData = new Boolean[] {true, false, true, false};
 298         final Double[] doubleData = new Double[] {2348.2345, -92.214, -214.0214, -908.214};
 299         final Float[] floatData = new Float[] {-3592.9f, 234872.8347f, 3897.274f, 3958.938745f};
 300         final Integer[] integerData = new Integer[] {248, -9384, -234, -34};
 301         final Long[] longData = new Long[] {9823984L, 2908934L, -234234L, 9089234L};
 302         final Object[] objectData = new Object[] {new Object(), new Object(), new Object(), new Object()};
 303         final String[] stringData = new String[] {"A", "B", "C", "D"};
 304 
 305         return Arrays.asList(new Object[][] {
 306             // boolean
 307             {
 308                 new Functions<Boolean>() {
 309                     @Override
 310                     public PropertyMock<Boolean> create() {
 311                         return new BooleanPropertyMock();
 312                     }
 313                     @Override
 314                     public void bind(PropertyMock<Boolean> op1, PropertyMock<Boolean> op2) {
 315                         Bindings.bindBidirectional(op1, op2);
 316                     }
 317                     @Override
 318                     public void unbind(PropertyMock<Boolean> op1, PropertyMock<Boolean> op2) {
 319                         Bindings.unbindBidirectional(op1, op2);
 320                     }
 321                     @Override
 322                     public BidirectionalBinding createBindingDirectly(PropertyMock<Boolean> op1, PropertyMock<Boolean> op2) {
 323                         return BidirectionalBinding.bind(op1, op2);
 324                     }
 325                     @Override
 326                     public void check(Boolean expected, Boolean actual) {
 327                         assertEquals(expected, actual);
 328                     }
 329                 },
 330                 booleanData
 331             },
 332             // double
 333             {
 334                 new Functions<Number>() {
 335                     @Override
 336                     public PropertyMock<Number> create() {
 337                         return new DoublePropertyMock();
 338                     }
 339                     @Override
 340                     public void bind(PropertyMock<Number> op1, PropertyMock<Number> op2) {
 341                         Bindings.bindBidirectional(op1, op2);
 342                     }
 343                     @Override
 344                     public void unbind(PropertyMock<Number> op1, PropertyMock<Number> op2) {
 345                         Bindings.unbindBidirectional(op1, op2);
 346                     }
 347                     @Override
 348                     public BidirectionalBinding createBindingDirectly(PropertyMock<Number> op1, PropertyMock<Number> op2) {
 349                         return BidirectionalBinding.bind(op1, op2);
 350                     }
 351                     @Override
 352                     public void check(Number expected, Number actual) {
 353                         assertEquals(expected.doubleValue(), actual.doubleValue(), EPSILON_DOUBLE);
 354                     }
 355                 },
 356                 doubleData
 357             },
 358             // float
 359             {
 360                 new Functions<Number>() {
 361                     @Override
 362                     public PropertyMock<Number> create() {
 363                         return new FloatPropertyMock();
 364                     }
 365                     @Override
 366                     public void bind(PropertyMock<Number> op1, PropertyMock<Number> op2) {
 367                         Bindings.bindBidirectional(op1, op2);
 368                     }
 369                     @Override
 370                     public void unbind(PropertyMock<Number> op1, PropertyMock<Number> op2) {
 371                         Bindings.unbindBidirectional(op1, op2);
 372                     }
 373                     @Override
 374                     public BidirectionalBinding createBindingDirectly(PropertyMock<Number> op1, PropertyMock<Number> op2) {
 375                         return BidirectionalBinding.bind(op1, op2);
 376                     }
 377                     @Override
 378                     public void check(Number expected, Number actual) {
 379                         assertEquals(expected.floatValue(), actual.floatValue(), EPSILON_FLOAT);
 380                     }
 381                 },
 382                 floatData
 383             },
 384             // integer
 385             {
 386                 new Functions<Number>() {
 387                     @Override
 388                     public PropertyMock<Number> create() {
 389                         return new IntegerPropertyMock();
 390                     }
 391                     @Override
 392                     public void bind(PropertyMock<Number> op1, PropertyMock<Number> op2) {
 393                         Bindings.bindBidirectional(op1, op2);
 394                     }
 395                     @Override
 396                     public void unbind(PropertyMock<Number> op1, PropertyMock<Number> op2) {
 397                         Bindings.unbindBidirectional(op1, op2);
 398                     }
 399                     @Override
 400                     public BidirectionalBinding createBindingDirectly(PropertyMock<Number> op1, PropertyMock<Number> op2) {
 401                         return BidirectionalBinding.bind(op1, op2);
 402                     }
 403                     @Override
 404                     public void check(Number expected, Number actual) {
 405                         assertEquals(expected.intValue(), actual.intValue());
 406                     }
 407                 },
 408                 integerData
 409             },
 410             // long
 411             {
 412                 new Functions<Number>() {
 413                     @Override
 414                     public PropertyMock<Number> create() {
 415                         return new LongPropertyMock();
 416                     }
 417                     @Override
 418                     public void bind(PropertyMock<Number> op1, PropertyMock<Number> op2) {
 419                         Bindings.bindBidirectional(op1, op2);
 420                     }
 421                     @Override
 422                     public void unbind(PropertyMock<Number> op1, PropertyMock<Number> op2) {
 423                         Bindings.unbindBidirectional(op1, op2);
 424                     }
 425                     @Override
 426                     public BidirectionalBinding createBindingDirectly(PropertyMock<Number> op1, PropertyMock<Number> op2) {
 427                         return BidirectionalBinding.bind(op1, op2);
 428                     }
 429                     @Override
 430                     public void check(Number expected, Number actual) {
 431                         assertEquals(expected.longValue(), actual.longValue());
 432                     }
 433                 },
 434                 longData
 435             },
 436             // object
 437             {
 438                 new Functions<Object>() {
 439                     @Override
 440                     public PropertyMock<Object> create() {
 441                         return new ObjectPropertyMock<Object>();
 442                     }
 443                                         @Override
 444                     public void bind(PropertyMock<Object> op1, PropertyMock<Object> op2) {
 445                         Bindings.bindBidirectional(op1, op2);
 446                     }
 447                                         @Override
 448                     public void unbind(PropertyMock<Object> op1, PropertyMock<Object> op2) {
 449                         Bindings.unbindBidirectional(op1, op2);
 450                     }
 451                                         @Override
 452                     public BidirectionalBinding createBindingDirectly(PropertyMock<Object> op1, PropertyMock<Object> op2) {
 453                         return BidirectionalBinding.bind(op1, op2);
 454                     }
 455                     @Override
 456                     public void check(Object expected, Object actual) {
 457                         assertEquals(expected, actual);
 458                     }
 459                 },
 460                 objectData
 461             },
 462             // string
 463             {
 464                 new Functions<String>() {
 465                     @Override
 466                     public PropertyMock<String> create() {
 467                         return new StringPropertyMock();
 468                     }
 469                     @Override
 470                     public void bind(PropertyMock<String> op1, PropertyMock<String> op2) {
 471                         Bindings.bindBidirectional(op1, op2);
 472                     }
 473                     @Override
 474                     public void unbind(PropertyMock<String> op1, PropertyMock<String> op2) {
 475                         Bindings.unbindBidirectional(op1, op2);
 476                     }
 477                     @Override
 478                     public BidirectionalBinding createBindingDirectly(PropertyMock<String> op1, PropertyMock<String> op2) {
 479                         return BidirectionalBinding.bind(op1, op2);
 480                     }
 481                     @Override
 482                     public void check(String expected, String actual) {
 483                         assertEquals(expected, actual);
 484                     }
 485                 },
 486                 stringData
 487             },
 488         });
 489     }
 490     
 491     private interface PropertyMock<T> extends Property<T> {
 492         int getListenerCount();
 493     }
 494     
 495     private static class BooleanPropertyMock extends SimpleBooleanProperty implements PropertyMock<Boolean> {
 496         
 497         private int listenerCount = 0;
 498         
 499         @Override
 500         public int getListenerCount() {
 501             return listenerCount;
 502         }
 503         
 504         @Override 
 505         public void addListener(ChangeListener<? super Boolean> listener) {
 506             super.addListener(listener);
 507             listenerCount++;
 508         }
 509         
 510         @Override 
 511         public void removeListener(ChangeListener<? super Boolean> listener) {
 512             super.removeListener(listener);
 513             listenerCount--;
 514         }
 515     }
 516     
 517     private static class DoublePropertyMock extends SimpleDoubleProperty implements PropertyMock<Number> {
 518         
 519         private int listenerCount = 0;
 520         
 521         @Override
 522         public int getListenerCount() {
 523             return listenerCount;
 524         }
 525         
 526         @Override 
 527         public void addListener(ChangeListener<? super Number> listener) {
 528             super.addListener(listener);
 529             listenerCount++;
 530         }
 531         
 532         @Override 
 533         public void removeListener(ChangeListener<? super Number> listener) {
 534             super.removeListener(listener);
 535             listenerCount--;
 536         }
 537     }
 538     
 539     private static class FloatPropertyMock extends SimpleFloatProperty implements PropertyMock<Number> {
 540         
 541         private int listenerCount = 0;
 542         
 543         @Override
 544         public int getListenerCount() {
 545             return listenerCount;
 546         }
 547         
 548         @Override 
 549         public void addListener(ChangeListener<? super Number> listener) {
 550             super.addListener(listener);
 551             listenerCount++;
 552         }
 553         
 554         @Override 
 555         public void removeListener(ChangeListener<? super Number> listener) {
 556             super.removeListener(listener);
 557             listenerCount--;
 558         }
 559     }
 560     
 561     private static class IntegerPropertyMock extends SimpleIntegerProperty implements PropertyMock<Number> {
 562         
 563         private int listenerCount = 0;
 564         
 565         @Override
 566         public int getListenerCount() {
 567             return listenerCount;
 568         }
 569         
 570         @Override 
 571         public void addListener(ChangeListener<? super Number> listener) {
 572             super.addListener(listener);
 573             listenerCount++;
 574         }
 575         
 576         @Override 
 577         public void removeListener(ChangeListener<? super Number> listener) {
 578             super.removeListener(listener);
 579             listenerCount--;
 580         }
 581     }
 582     
 583     private static class LongPropertyMock extends SimpleLongProperty implements PropertyMock<Number> {
 584         
 585         private int listenerCount = 0;
 586         
 587         @Override
 588         public int getListenerCount() {
 589             return listenerCount;
 590         }
 591         
 592         @Override 
 593         public void addListener(ChangeListener<? super Number> listener) {
 594             super.addListener(listener);
 595             listenerCount++;
 596         }
 597         
 598         @Override 
 599         public void removeListener(ChangeListener<? super Number> listener) {
 600             super.removeListener(listener);
 601             listenerCount--;
 602         }
 603     }
 604     
 605     private static class ObjectPropertyMock<T> extends SimpleObjectProperty<T> implements PropertyMock<T> {
 606         
 607         private int listenerCount = 0;
 608         
 609         @Override
 610         public int getListenerCount() {
 611             return listenerCount;
 612         }
 613         
 614         @Override
 615         public void addListener(ChangeListener<? super T> listener) {
 616             super.addListener(listener);
 617             listenerCount++;
 618         }
 619         
 620         @Override 
 621         public void removeListener(ChangeListener<? super T> listener) {
 622             super.removeListener(listener);
 623             listenerCount--;
 624         }
 625     }
 626     
 627     private static class StringPropertyMock extends SimpleStringProperty implements PropertyMock<String> {
 628         
 629         private int listenerCount = 0;
 630         
 631         @Override
 632         public int getListenerCount() {
 633             return listenerCount;
 634         }
 635         
 636         @Override 
 637         public void addListener(ChangeListener<? super String> listener) {
 638             super.addListener(listener);
 639             listenerCount++;
 640         }
 641         
 642         @Override 
 643         public void removeListener(ChangeListener<? super String> listener) {
 644             super.removeListener(listener);
 645             listenerCount--;
 646         }
 647     }
 648 }