1 /* 2 * Copyright (c) 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 javafx.beans.property; 27 28 import com.sun.javafx.binding.ExpressionHelperUtility; 29 import java.util.Arrays; 30 import java.util.List; 31 import javafx.beans.value.ObservableNumberValue; 32 import javafx.beans.value.ObservableValue; 33 import javafx.beans.value.ObservableValueBase; 34 import static org.junit.Assert.assertEquals; 35 import org.junit.Before; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 import org.junit.runners.Parameterized; 39 40 @RunWith(Parameterized.class) 41 public class PropertyBaseTest<T> { 42 43 @FunctionalInterface 44 public interface PropertyFactory<T> { 45 public Property<T> createProperty(); 46 } 47 48 public static class Factory<T> { 49 50 private PropertyFactory<T> propertyFactory; 51 private PropertyFactory<T> observableFactory; 52 private T value; 53 54 public Factory(PropertyFactory<T> propertyFactory, 55 PropertyFactory<T> observableFactory, T value) 56 { 57 this.propertyFactory = propertyFactory; 58 this.observableFactory = observableFactory; 59 this.value = value; 60 } 61 62 public Property<T> createProperty() { 63 return propertyFactory.createProperty(); 64 } 65 66 public Property<T> createObservable() { 67 return observableFactory.createProperty(); 68 } 69 70 public T getValue() { 71 return value; 72 } 73 } 74 75 private static class NumberPropertyMock extends ObservableValueBase<Number> 76 implements ObservableNumberValue, Property<Number> 77 { 78 private Number value = 0; 79 80 @Override public int intValue() { return value.intValue(); } 81 @Override public long longValue() { return value.longValue(); } 82 @Override public float floatValue() { return value.floatValue(); } 83 @Override public double doubleValue() { return value.doubleValue(); } 84 @Override public Number getValue() { return value; } 85 @Override public void setValue(Number value) { 86 this.value = value; 87 fireValueChangedEvent(); 88 } 89 90 @Override public void bind(ObservableValue<? extends Number> observable) {} 91 @Override public void unbind() {} 92 @Override public boolean isBound() { return false; } 93 @Override public void bindBidirectional(Property<Number> other) {} 94 @Override public void unbindBidirectional(Property<Number> other) {} 95 96 @Override public Object getBean() { return null; } 97 @Override public String getName() { return ""; } 98 } 99 100 @Parameterized.Parameters 101 public static List<Object[]> data() { 102 return Arrays.asList(new Object[][] { 103 // primitive binding 104 // Property->Listener->Value 105 { new Factory(() -> new SimpleBooleanProperty(), () -> new SimpleBooleanProperty(), true) }, 106 { new Factory(() -> new SimpleDoubleProperty(), () -> new SimpleDoubleProperty(), 1.0) }, 107 { new Factory(() -> new SimpleFloatProperty(), () -> new SimpleFloatProperty(), 1.0f) }, 108 { new Factory(() -> new SimpleIntegerProperty(), () -> new SimpleIntegerProperty(), 1) }, 109 { new Factory(() -> new SimpleLongProperty(), () -> new SimpleLongProperty(), 1L) }, 110 // generic with wrapper 111 // Property->Listener->Binding->BindingHelperObserver->Value 112 { new Factory(() -> new SimpleBooleanProperty(), () -> new SimpleObjectProperty<>(), true) }, 113 { new Factory(() -> new SimpleDoubleProperty(), () -> new SimpleObjectProperty<>(), 1.0) }, 114 { new Factory(() -> new SimpleDoubleProperty(), () -> new NumberPropertyMock(), 1.0) }, 115 { new Factory(() -> new SimpleFloatProperty(), () -> new SimpleObjectProperty<>(), 1.0f) }, 116 { new Factory(() -> new SimpleFloatProperty(), () -> new NumberPropertyMock(), 1.0f) }, 117 { new Factory(() -> new SimpleIntegerProperty(), () -> new SimpleObjectProperty<>(), 1) }, 118 { new Factory(() -> new SimpleIntegerProperty(), () -> new NumberPropertyMock(), 1) }, 119 { new Factory(() -> new SimpleLongProperty(), () -> new SimpleObjectProperty<>(), 1L) }, 120 { new Factory(() -> new SimpleLongProperty(), () -> new NumberPropertyMock(), 1L) }, 121 // generic 122 // Property->Listener->Value 123 { new Factory(() -> new SimpleObjectProperty(), () -> new SimpleObjectProperty<>(), new Object()) }, 124 { new Factory(() -> new SimpleStringProperty(), () -> new SimpleObjectProperty<>(), "1") }, 125 // the same as generic 126 { new Factory(() -> new SimpleStringProperty(), () -> new SimpleStringProperty(), "1") }, 127 }); 128 } 129 130 public PropertyBaseTest(Factory<T> factory) { 131 this.factory = factory; 132 } 133 134 @Before 135 public void setUp() { 136 property = factory.createProperty(); 137 observable = factory.createObservable(); 138 value = factory.getValue(); 139 } 140 141 private Factory<T> factory; 142 private Property<T> property; 143 private Property<T> observable; 144 private T value; 145 146 @Test 147 public void testUnbindAfterInvalidation() { 148 property.bind(observable); 149 assertEquals(1, ExpressionHelperUtility.getInvalidationListeners(observable).size()); 150 151 property = null; 152 System.gc(); 153 154 observable.setValue(value); 155 assertEquals(0, ExpressionHelperUtility.getInvalidationListeners(observable).size()); 156 } 157 158 @Test 159 public void testTrimAfterGC() { 160 Property<T> p1 = factory.createProperty(); 161 Property<T> p2 = factory.createProperty(); 162 p1.bind(observable); // creates SingleInvalidation 163 p2.bind(observable); // creates Generic with 2 listeners 164 assertEquals(2, ExpressionHelperUtility.getInvalidationListeners(observable).size()); 165 166 p1 = null; 167 p2 = null; 168 System.gc(); 169 170 property.bind(observable); // calls trim 171 assertEquals(1, ExpressionHelperUtility.getInvalidationListeners(observable).size()); 172 } 173 174 @Test 175 public void testUnbindGenericWrapper() { 176 property.bind(observable); 177 assertEquals(1, ExpressionHelperUtility.getInvalidationListeners(observable).size()); 178 179 property.unbind(); // should unbind wrapper from observable 180 assertEquals(0, ExpressionHelperUtility.getInvalidationListeners(observable).size()); 181 } 182 }