-
Bug
-
Resolution: Fixed
-
P4
-
8u20
Below are some failing tests around notification of changeListeners
Basic setup: newList.equals(oldList) && newList != oldList
Expected behaviour: changeListener is notified
Actual behaviour: changeListener is notified only if ListProperty not in bidi-binding. Traced down to being a single listener vs. multiple listeners
Digging a bit reveals that ListPropertyBase.set(...) and ListExpressionHelper.SingleChange.fireValueChangeEvent(..) test against identity resulting in expected behaviour, while GenericChange.fireValueChangeEvent(...) tests against equality, resulting in failure to notify.
Though I think that the bidi tests will behave as expected when GenericChange is fixed I kept them just in case there's yet another issue
/**
* Test notification of changeListeners on setting a list that equals the old
* value but is a different instance.
*
* Passes without bidi-binding.
*/
@Test
public void testListPropertyNotificationSingleListener() {
ObservableList<String> initialValue = createObservableList(false);
ListProperty<String> property = new SimpleListProperty<>(initialValue);
ChangeReport report = new ChangeReport(property);
ObservableList<String> otherValue = createObservableList(false);
assertTrue("sanity: FXCollections returns a new instance", otherValue != initialValue);
property.set(otherValue);
assertEquals(1, report.getEventCount());
assertSame(initialValue, report.getLastOldValue());
assertSame(otherValue, report.getLastNewValue());
}
/**
* Bug: ListPropertyBase fails to notify change listeners if multiple
* listeners registered
*/
@Test
public void testListPropertyNotificationMultipleListener() {
ObservableList<String> initialValue = createObservableList(false);
ListProperty<String> property = new SimpleListProperty<>(initialValue);
ChangeReport report = new ChangeReport(property);
ChangeReport otherListener = new ChangeReport(property);
ObservableList<String> otherValue = createObservableList(false);
assertTrue("sanity: FXCollections returns a new instance", otherValue != initialValue);
property.set(otherValue);
assertEquals(1, report.getEventCount());
assertSame(initialValue, report.getLastOldValue());
assertSame(otherValue, report.getLastNewValue());
}
/**
* Asserts bidi binding between source/target ListPropery
* (too much for a decent test, but then ..)
* - value of target is synched to source
* - ChangeListener on target is notified on binding
*
* @param withData
*/
protected void assertListPropertyBidiBinding(boolean withData) {
ObservableList<String> initialValue = createObservableList(withData);
ListProperty<String> target = new SimpleListProperty<>(initialValue);
ObservableList<String> sourceValue = createObservableList(withData);
ListProperty<String> source = new SimpleListProperty<>(sourceValue);
ChangeReport targetReport = new ChangeReport(target);
target.bindBidirectional(source);
assertSame("bidi binding updates", target.get(), source.get());
assertSame("property is target of bidi", sourceValue, target.get());
assertSame("other is unchanged", sourceValue, source.get());
// this passes: listeners on target are notified installing the bidibinding
assertEquals("change listener on property must be notified", 1, targetReport.getEventCount());
targetReport.clear();
ChangeReport sourceReport = new ChangeReport(source);
ObservableList<String> thirdValue = createObservableList(withData);
source.set(thirdValue);
assertSame("source value taken", thirdValue, source.get());
// was meant as sanity testing .. but the bidibinding prevents
// the notification that is just fine if unbound ... see test above
// assertEquals("sanity: source listener notified", 1, sourceReport.getEventCount());
assertSame("property must be updated to new value of other", thirdValue, target.get());
// this fails: listeners on target are not notified after updating the source of the bidibinding
assertEquals("change listener on property must be notified", 1, targetReport.getEventCount());
}
@Test
public void testListPropertyBidiBindingNotificationListEmpty() {
assertListPropertyBidiBinding(false);
}
@Test
public void testListPropertyBidiBindingNotificationListWithData() {
assertListPropertyBidiBinding(true);
}
static final String[] DATA = {"just", "some", "content"};
protected ObservableList<String> createObservableList(boolean withData) {
return withData ? FXCollections.observableArrayList(DATA) : FXCollections.observableArrayList();
}
Basic setup: newList.equals(oldList) && newList != oldList
Expected behaviour: changeListener is notified
Actual behaviour: changeListener is notified only if ListProperty not in bidi-binding. Traced down to being a single listener vs. multiple listeners
Digging a bit reveals that ListPropertyBase.set(...) and ListExpressionHelper.SingleChange.fireValueChangeEvent(..) test against identity resulting in expected behaviour, while GenericChange.fireValueChangeEvent(...) tests against equality, resulting in failure to notify.
Though I think that the bidi tests will behave as expected when GenericChange is fixed I kept them just in case there's yet another issue
/**
* Test notification of changeListeners on setting a list that equals the old
* value but is a different instance.
*
* Passes without bidi-binding.
*/
@Test
public void testListPropertyNotificationSingleListener() {
ObservableList<String> initialValue = createObservableList(false);
ListProperty<String> property = new SimpleListProperty<>(initialValue);
ChangeReport report = new ChangeReport(property);
ObservableList<String> otherValue = createObservableList(false);
assertTrue("sanity: FXCollections returns a new instance", otherValue != initialValue);
property.set(otherValue);
assertEquals(1, report.getEventCount());
assertSame(initialValue, report.getLastOldValue());
assertSame(otherValue, report.getLastNewValue());
}
/**
* Bug: ListPropertyBase fails to notify change listeners if multiple
* listeners registered
*/
@Test
public void testListPropertyNotificationMultipleListener() {
ObservableList<String> initialValue = createObservableList(false);
ListProperty<String> property = new SimpleListProperty<>(initialValue);
ChangeReport report = new ChangeReport(property);
ChangeReport otherListener = new ChangeReport(property);
ObservableList<String> otherValue = createObservableList(false);
assertTrue("sanity: FXCollections returns a new instance", otherValue != initialValue);
property.set(otherValue);
assertEquals(1, report.getEventCount());
assertSame(initialValue, report.getLastOldValue());
assertSame(otherValue, report.getLastNewValue());
}
/**
* Asserts bidi binding between source/target ListPropery
* (too much for a decent test, but then ..)
* - value of target is synched to source
* - ChangeListener on target is notified on binding
*
* @param withData
*/
protected void assertListPropertyBidiBinding(boolean withData) {
ObservableList<String> initialValue = createObservableList(withData);
ListProperty<String> target = new SimpleListProperty<>(initialValue);
ObservableList<String> sourceValue = createObservableList(withData);
ListProperty<String> source = new SimpleListProperty<>(sourceValue);
ChangeReport targetReport = new ChangeReport(target);
target.bindBidirectional(source);
assertSame("bidi binding updates", target.get(), source.get());
assertSame("property is target of bidi", sourceValue, target.get());
assertSame("other is unchanged", sourceValue, source.get());
// this passes: listeners on target are notified installing the bidibinding
assertEquals("change listener on property must be notified", 1, targetReport.getEventCount());
targetReport.clear();
ChangeReport sourceReport = new ChangeReport(source);
ObservableList<String> thirdValue = createObservableList(withData);
source.set(thirdValue);
assertSame("source value taken", thirdValue, source.get());
// was meant as sanity testing .. but the bidibinding prevents
// the notification that is just fine if unbound ... see test above
// assertEquals("sanity: source listener notified", 1, sourceReport.getEventCount());
assertSame("property must be updated to new value of other", thirdValue, target.get());
// this fails: listeners on target are not notified after updating the source of the bidibinding
assertEquals("change listener on property must be notified", 1, targetReport.getEventCount());
}
@Test
public void testListPropertyBidiBindingNotificationListEmpty() {
assertListPropertyBidiBinding(false);
}
@Test
public void testListPropertyBidiBindingNotificationListWithData() {
assertListPropertyBidiBinding(true);
}
static final String[] DATA = {"just", "some", "content"};
protected ObservableList<String> createObservableList(boolean withData) {
return withData ? FXCollections.observableArrayList(DATA) : FXCollections.observableArrayList();
}