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.binding.Bindings; 29 import javafx.beans.property.*; 30 import org.junit.Before; 31 import org.junit.Test; 32 import org.junit.runner.RunWith; 33 import org.junit.runners.Parameterized; 34 35 import java.util.Arrays; 36 import java.util.Collection; 37 import javafx.beans.value.ObservableValue; 38 39 import static org.junit.Assert.*; 40 41 @RunWith(Parameterized.class) 42 public class BidirectionalBindingTest<T> { 43 44 @FunctionalInterface 45 public static interface PropertyFactory<T> { 46 Property<T> createProperty(); 47 } 48 49 public static class Factory<T> { 50 51 private PropertyFactory<T> propertyFactory; 52 private T[] values; 53 public Factory(PropertyFactory<T> propertyFactory, T[] values) { 54 this.propertyFactory = propertyFactory; 55 this.values = values; 56 } 57 public Property<T> createProperty() { 58 return propertyFactory.createProperty(); 59 } 60 public T[] getValues() { 61 return values; 62 } 63 } 64 65 private Factory<T> factory; 66 private Property<T> op1; 67 private Property<T> op2; 68 private Property<T> op3; 69 private Property<T> op4; 70 private T[] v; 71 72 public BidirectionalBindingTest(Factory<T> factory) { 73 this.factory = factory; 74 } 75 76 @Before 77 public void setUp() { 78 op1 = factory.createProperty(); 79 op2 = factory.createProperty(); 80 op3 = factory.createProperty(); 81 op4 = factory.createProperty(); 82 v = factory.getValues(); 83 op1.setValue(v[0]); 84 op2.setValue(v[1]); 85 } 86 87 @Test 88 public void testBind() { 89 Bindings.bindBidirectional(op1, op2); 90 Bindings.bindBidirectional(op1, op2); 91 System.gc(); // making sure we did not not overdo weak references 92 assertEquals(v[1], op1.getValue()); 93 assertEquals(v[1], op2.getValue()); 94 95 op1.setValue(v[2]); 96 assertEquals(v[2], op1.getValue()); 97 assertEquals(v[2], op2.getValue()); 98 99 op2.setValue(v[3]); 100 assertEquals(v[3], op1.getValue()); 101 assertEquals(v[3], op2.getValue()); 102 } 103 104 @Test 105 public void testUnbind() { 106 // unbind non-existing binding => no-op 107 Bindings.unbindBidirectional(op1, op2); 108 109 // unbind properties of different beans 110 Bindings.bindBidirectional(op1, op2); 111 System.gc(); // making sure we did not not overdo weak references 112 assertEquals(v[1], op1.getValue()); 113 assertEquals(v[1], op2.getValue()); 114 115 Bindings.unbindBidirectional(op1, op2); 116 System.gc(); 117 assertEquals(v[1], op1.getValue()); 118 assertEquals(v[1], op2.getValue()); 119 120 op1.setValue(v[2]); 121 assertEquals(v[2], op1.getValue()); 122 assertEquals(v[1], op2.getValue()); 123 124 op2.setValue(v[3]); 125 assertEquals(v[2], op1.getValue()); 126 assertEquals(v[3], op2.getValue()); 127 } 128 129 @Test 130 public void testChaining() { 131 op3.setValue(v[2]); 132 Bindings.bindBidirectional(op1, op2); 133 Bindings.bindBidirectional(op2, op3); 134 System.gc(); // making sure we did not not overdo weak references 135 assertEquals(v[2], op1.getValue()); 136 assertEquals(v[2], op2.getValue()); 137 assertEquals(v[2], op3.getValue()); 138 139 op1.setValue(v[3]); 140 assertEquals(v[3], op1.getValue()); 141 assertEquals(v[3], op2.getValue()); 142 assertEquals(v[3], op3.getValue()); 143 144 op2.setValue(v[0]); 145 assertEquals(v[0], op1.getValue()); 146 assertEquals(v[0], op2.getValue()); 147 assertEquals(v[0], op3.getValue()); 148 149 op3.setValue(v[1]); 150 assertEquals(v[1], op1.getValue()); 151 assertEquals(v[1], op2.getValue()); 152 assertEquals(v[1], op3.getValue()); 153 154 // now unbind 155 Bindings.unbindBidirectional(op1, op2); 156 System.gc(); // making sure we did not not overdo weak references 157 assertEquals(v[1], op1.getValue()); 158 assertEquals(v[1], op2.getValue()); 159 assertEquals(v[1], op3.getValue()); 160 161 op1.setValue(v[2]); 162 assertEquals(v[2], op1.getValue()); 163 assertEquals(v[1], op2.getValue()); 164 assertEquals(v[1], op3.getValue()); 165 166 op2.setValue(v[3]); 167 assertEquals(v[2], op1.getValue()); 168 assertEquals(v[3], op2.getValue()); 169 assertEquals(v[3], op3.getValue()); 170 171 op3.setValue(v[0]); 172 assertEquals(v[2], op1.getValue()); 173 assertEquals(v[0], op2.getValue()); 174 assertEquals(v[0], op3.getValue()); 175 } 176 177 private int getListenerCount(ObservableValue<T> v) { 178 return ExpressionHelperUtility.getChangeListeners(v).size(); 179 } 180 181 @Test 182 public void testWeakReferencing() { 183 Bindings.bindBidirectional(op1, op2); 184 185 assertEquals(1, getListenerCount(op1)); 186 assertEquals(1, getListenerCount(op2)); 187 188 op1 = null; 189 System.gc(); 190 op2.setValue(v[2]); 191 assertEquals(0, getListenerCount(op2)); 192 193 Bindings.bindBidirectional(op2, op3); 194 assertEquals(1, getListenerCount(op2)); 195 assertEquals(1, getListenerCount(op3)); 196 197 op3 = null; 198 System.gc(); 199 op2.setValue(v[0]); 200 assertEquals(0, getListenerCount(op2)); 201 } 202 203 @Test 204 public void testHashCode() { 205 final int hc1 = BidirectionalBinding.bind(op1, op2).hashCode(); 206 final int hc2 = BidirectionalBinding.bind(op2, op1).hashCode(); 207 assertEquals(hc1, hc2); 208 } 209 210 @Test 211 public void testEquals() { 212 final BidirectionalBinding golden = BidirectionalBinding.bind(op1, op2); 213 214 assertTrue(golden.equals(golden)); 215 assertFalse(golden.equals(null)); 216 assertFalse(golden.equals(op1)); 217 assertTrue(golden.equals(BidirectionalBinding.bind(op1, op2))); 218 assertTrue(golden.equals(BidirectionalBinding.bind(op2, op1))); 219 assertFalse(golden.equals(BidirectionalBinding.bind(op1, op3))); 220 assertFalse(golden.equals(BidirectionalBinding.bind(op3, op1))); 221 assertFalse(golden.equals(BidirectionalBinding.bind(op3, op2))); 222 assertFalse(golden.equals(BidirectionalBinding.bind(op2, op3))); 223 } 224 225 @Test 226 public void testEqualsWithGCedProperty() { 227 final BidirectionalBinding binding1 = BidirectionalBinding.bind(op1, op2); 228 final BidirectionalBinding binding2 = BidirectionalBinding.bind(op1, op2); 229 final BidirectionalBinding binding3 = BidirectionalBinding.bind(op2, op1); 230 final BidirectionalBinding binding4 = BidirectionalBinding.bind(op2, op1); 231 op1 = null; 232 System.gc(); 233 234 assertTrue(binding1.equals(binding1)); 235 assertFalse(binding1.equals(binding2)); 236 assertFalse(binding1.equals(binding3)); 237 238 assertTrue(binding3.equals(binding3)); 239 assertFalse(binding3.equals(binding1)); 240 assertFalse(binding3.equals(binding4)); 241 } 242 243 @Test(expected=NullPointerException.class) 244 public void testBind_Null_X() { 245 Bindings.bindBidirectional(null, op2); 246 } 247 248 @Test(expected=NullPointerException.class) 249 public void testBind_X_Null() { 250 Bindings.bindBidirectional(op1, null); 251 } 252 253 @Test(expected=IllegalArgumentException.class) 254 public void testBind_X_Self() { 255 Bindings.bindBidirectional(op1, op1); 256 } 257 258 @Test(expected=NullPointerException.class) 259 public void testUnbind_Null_X() { 260 Bindings.unbindBidirectional(null, op2); 261 } 262 263 @Test(expected=NullPointerException.class) 264 public void testUnbind_X_Null() { 265 Bindings.unbindBidirectional(op1, null); 266 } 267 268 @Test(expected=IllegalArgumentException.class) 269 public void testUnbind_X_Self() { 270 Bindings.unbindBidirectional(op1, op1); 271 } 272 273 @Test 274 public void testBrokenBind() { 275 Bindings.bindBidirectional(op1, op2); 276 op1.bind(op3); 277 assertEquals(op3.getValue(), op1.getValue()); 278 assertEquals(op2.getValue(), op1.getValue()); 279 280 op2.setValue(v[2]); 281 assertEquals(op3.getValue(), op1.getValue()); 282 assertEquals(op2.getValue(), op1.getValue()); 283 } 284 285 @Test 286 public void testDoubleBrokenBind() { 287 Bindings.bindBidirectional(op1, op2); 288 op1.bind(op3); 289 op4.setValue(v[0]); 290 291 op2.bind(op4); 292 assertEquals(op4.getValue(), op2.getValue()); 293 assertEquals(op3.getValue(), op1.getValue()); 294 // Test that bidirectional binding was unbound in this case 295 op3.setValue(v[0]); 296 op4.setValue(v[1]); 297 assertEquals(op4.getValue(), op2.getValue()); 298 assertEquals(op3.getValue(), op1.getValue()); 299 assertEquals(v[0], op1.getValue()); 300 assertEquals(v[1], op2.getValue()); 301 } 302 303 @Parameterized.Parameters 304 public static Collection<Object[]> parameters() { 305 final Boolean[] booleanData = new Boolean[] {true, false, true, false}; 306 final Double[] doubleData = new Double[] {2348.2345, -92.214, -214.0214, -908.214}; 307 final Float[] floatData = new Float[] {-3592.9f, 234872.8347f, 3897.274f, 3958.938745f}; 308 final Integer[] integerData = new Integer[] {248, -9384, -234, -34}; 309 final Long[] longData = new Long[] {9823984L, 2908934L, -234234L, 9089234L}; 310 final Object[] objectData = new Object[] {new Object(), new Object(), new Object(), new Object()}; 311 final String[] stringData = new String[] {"A", "B", "C", "D"}; 312 313 return Arrays.asList(new Object[][] { 314 { new Factory(() -> new SimpleBooleanProperty(), booleanData) }, 315 { new Factory(() -> new SimpleDoubleProperty(), doubleData) }, 316 { new Factory(() -> new SimpleFloatProperty(), floatData) }, 317 { new Factory(() -> new SimpleIntegerProperty(), integerData) }, 318 { new Factory(() -> new SimpleLongProperty(), longData) }, 319 { new Factory(() -> new SimpleObjectProperty<>(), objectData) }, 320 { new Factory(() -> new SimpleStringProperty(), stringData) }, 321 { new Factory(() -> new ReadOnlyBooleanWrapper(), booleanData) }, 322 { new Factory(() -> new ReadOnlyDoubleWrapper(), doubleData) }, 323 { new Factory(() -> new ReadOnlyFloatWrapper(), floatData) }, 324 { new Factory(() -> new ReadOnlyIntegerWrapper(), integerData) }, 325 { new Factory(() -> new ReadOnlyLongWrapper(), longData) }, 326 { new Factory(() -> new ReadOnlyObjectWrapper<>(), objectData) }, 327 { new Factory(() -> new ReadOnlyStringWrapper(), stringData) }, 328 }); 329 } 330 }