FLEX-35043 PROBLEM: The assumptions in SF_ORIG_list_events_tester.List_events_collectionKind_move made obvious another problem caused by FLEX-34885: calling itemUpdated(item) did not reposition the item according to the sorting rules (unless the Sort had a customCompareFunction).
CAUSE: FLEX-34885 assumed that the initial logic of checking for !updateEntry.property was to detect when entire objects were replaced with others in the collection. However, it was also detecting when the developer signalled that something (undefined) about an object had changed, and they wanted the sorting / filtering to be reapplied. SOLUTION: now we call moveItemInView() also when updateEntry.property is null and there are no oldValue or newValue specified in the CollectionEvent. NOTES: -also updated ListCollectionView_PropertyChangeEvent_Tests and added a new function for objects which are not bindable. As expected, there is a failure without this change to ListCollectionView, and with the change all unit tests pass. -made explicit a conversion from Object into Boolean in a call to moveItemInView(). -reformated event instantiations to be on one line. -made some minor changes to SF_ORIG_ListBasic.mxml, which is used by SF_ORIG_list_events_tester. Project: http://git-wip-us.apache.org/repos/asf/flex-sdk/repo Commit: http://git-wip-us.apache.org/repos/asf/flex-sdk/commit/2c14851e Tree: http://git-wip-us.apache.org/repos/asf/flex-sdk/tree/2c14851e Diff: http://git-wip-us.apache.org/repos/asf/flex-sdk/diff/2c14851e Branch: refs/heads/develop Commit: 2c14851e9549ec390e45c0609257e10bda7cc0dd Parents: f7077c6 Author: Mihai Chira <mih...@apache.org> Authored: Thu Mar 31 15:17:18 2016 +0200 Committer: Mihai Chira <mih...@apache.org> Committed: Thu Mar 31 15:17:18 2016 +0200 ---------------------------------------------------------------------- .../framework/src/mx/collections/ArrayList.as | 3 +- .../src/mx/collections/ListCollectionView.as | 20 ++-- ...tCollectionView_PropertyChangeEvent_Tests.as | 113 ++++++++++++++++--- .../Sort/SWFs/SF_ORIG_ListBasic.mxml | 5 +- 4 files changed, 109 insertions(+), 32 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/2c14851e/frameworks/projects/framework/src/mx/collections/ArrayList.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/framework/src/mx/collections/ArrayList.as b/frameworks/projects/framework/src/mx/collections/ArrayList.as index dbfbb42..2a89718 100644 --- a/frameworks/projects/framework/src/mx/collections/ArrayList.as +++ b/frameworks/projects/framework/src/mx/collections/ArrayList.as @@ -620,8 +620,7 @@ public class ArrayList extends EventDispatcher oldValue:Object = null, newValue:Object = null):void { - var event:PropertyChangeEvent = - new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE); + var event:PropertyChangeEvent = new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE); event.kind = PropertyChangeEventKind.UPDATE; event.source = item; http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/2c14851e/frameworks/projects/framework/src/mx/collections/ListCollectionView.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/framework/src/mx/collections/ListCollectionView.as b/frameworks/projects/framework/src/mx/collections/ListCollectionView.as index 15fc15d..f03556f 100644 --- a/frameworks/projects/framework/src/mx/collections/ListCollectionView.as +++ b/frameworks/projects/framework/src/mx/collections/ListCollectionView.as @@ -1489,7 +1489,9 @@ public class ListCollectionView extends Proxy implements ICollectionView, IList, { var oldOrNewValueSpecified:Boolean = updateInfo.oldValue != null || updateInfo.newValue != null; var objectReplacedInCollection:Boolean = updateInfo.property == null && oldOrNewValueSpecified; + var somethingUnknownAboutTheObjectChanged:Boolean = updateInfo.property == null && !oldOrNewValueSpecified; updateEntry = {item: item, move: defaultMove, events: [updateInfo], + undefinedChange:somethingUnknownAboutTheObjectChanged, objectReplacedWithAnother: objectReplacedInCollection, oldItem: updateInfo.oldValue}; updatedItems.push(updateEntry); } @@ -1500,6 +1502,7 @@ public class ListCollectionView extends Proxy implements ICollectionView, IList, updateEntry.move = updateEntry.move || filterFunction != null || updateEntry.objectReplacedWithAnother + || updateEntry.undefinedChange || (sort && sort.propertyAffectsSort(String(updateInfo.property))); } @@ -1515,7 +1518,7 @@ public class ListCollectionView extends Proxy implements ICollectionView, IList, { removeItemsFromView([updateEntry.oldItem], -1, true); } - moveItemInView(updateEntry.item, updateEntry.item, eventItems); + moveItemInView(updateEntry.item, updateEntry.item != null, eventItems); } else { @@ -1648,8 +1651,7 @@ public class ListCollectionView extends Proxy implements ICollectionView, IList, pendingUpdates = null; if (dispatch) { - var refreshEvent:CollectionEvent = - new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); + var refreshEvent:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); refreshEvent.kind = CollectionEventKind.REFRESH; dispatchEvent(refreshEvent); } @@ -1690,8 +1692,7 @@ public class ListCollectionView extends Proxy implements ICollectionView, IList, if (dispatch) { - var event:CollectionEvent = - new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); + var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); event.items.push(item); if (updateEventItems && addLocation == removeLocation && addLocation > -1) { @@ -1782,8 +1783,7 @@ public class ListCollectionView extends Proxy implements ICollectionView, IList, } if (dispatch && removedItems.length > 0) { - var event:CollectionEvent = - new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); + var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); event.kind = CollectionEventKind.REMOVE; event.location = (!localIndex || removedItems.length == 1) ? removeLocation @@ -1823,8 +1823,7 @@ public class ListCollectionView extends Proxy implements ICollectionView, IList, } else { - var event:CollectionEvent = - new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); + var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); event.kind = CollectionEventKind.REPLACE; event.location = location; event.items = changeEvents; @@ -1841,8 +1840,7 @@ public class ListCollectionView extends Proxy implements ICollectionView, IList, internalRefresh(false); if (dispatchResetEvent) { - var event:CollectionEvent = - new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); + var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); event.kind = CollectionEventKind.RESET; dispatchEvent(event); } http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/2c14851e/frameworks/projects/framework/tests/mx/collections/ListCollectionView_PropertyChangeEvent_Tests.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/framework/tests/mx/collections/ListCollectionView_PropertyChangeEvent_Tests.as b/frameworks/projects/framework/tests/mx/collections/ListCollectionView_PropertyChangeEvent_Tests.as index 56d0b40..a51827a 100644 --- a/frameworks/projects/framework/tests/mx/collections/ListCollectionView_PropertyChangeEvent_Tests.as +++ b/frameworks/projects/framework/tests/mx/collections/ListCollectionView_PropertyChangeEvent_Tests.as @@ -22,6 +22,8 @@ package mx.collections { import flash.events.UncaughtErrorEvent; import mx.core.FlexGlobals; + import mx.events.CollectionEvent; + import mx.events.CollectionEventKind; import mx.events.PropertyChangeEvent; import mx.events.PropertyChangeEventKind; import mx.utils.ObjectUtil; @@ -32,6 +34,8 @@ package mx.collections { public class ListCollectionView_PropertyChangeEvent_Tests { + private static const NO_WORKOUTS:int = 10; + private static var _sut:ListCollectionView; private static var _firstWorkout:WorkoutVO; @@ -76,6 +80,7 @@ package mx.collections { { _sut = null; _lastFilteredObject = null; + _firstWorkout = null; PROPERTY_CHANGE_EVENT = null; PROPERTY_CHANGE_EVENT_UPDATE = null; @@ -136,7 +141,7 @@ package mx.collections { assertEquals(positionOfFirstWorkout, _sut.getItemIndex(_firstWorkout)); } - [Test] //FLEX-35043 + [Test] //FLEX-35043 FLEX-34885 public function test_when_collection_item_dispatches_PropertyChangeEvent_null_is_not_removed_from_list():void { //given @@ -174,7 +179,7 @@ package mx.collections { assertEquals(positionOfFirstWorkout, _sut.getItemIndex(_firstWorkout)); } - [Test] //FLEX-35043 + [Test] //FLEX-35043 FLEX-34885 public function test_when_item_is_changed_bypassing_binding_and_collection_notified_of_itemUpdated_with_property_null_is_not_removed_from_list():void { //given @@ -194,7 +199,7 @@ package mx.collections { assertEquals(positionOfFirstWorkout, _sut.getItemIndex(_firstWorkout)); } - [Test] //FLEX-35043 + [Test] //FLEX-35043 FLEX-34885 public function test_when_item_is_changed_bypassing_binding_and_collection_notified_of_itemUpdated_without_property_null_is_not_removed_from_list():void { //given @@ -215,7 +220,7 @@ package mx.collections { assertEquals(positionOfFirstWorkout, _sut.getItemIndex(_firstWorkout)); } - [Test] //FLEX-35043 + [Test] //FLEX-35043 FLEX-34885 public function test_when_collection_item_dispatches_PropertyChangeEvent_with_UPDATE_null_is_not_removed_from_list():void { //given @@ -275,7 +280,7 @@ package mx.collections { assertEquals(positionOfFirstWorkout, _sut.getItemIndex(_firstWorkout)); } - [Test] //FLEX-35043 + [Test] //FLEX-35043 FLEX-34885 public function test_when_collection_item_dispatches_PropertyChangeEvent_item_is_added_in_correct_place_based_on_sort_and_there_is_no_fatal():void { //given @@ -296,7 +301,7 @@ package mx.collections { assertEquals(positionOfFirstWorkout, _sut.getItemIndex(_firstWorkout)); } - [Test] //FLEX-35043 + [Test] //FLEX-35043 FLEX-34885 public function test_when_collection_item_dispatches_PropertyChangeEvent_sort_compare_function_not_called_with_null():void { function compareWorkouts(a:Object, b:Object, fields:Array = null):int @@ -320,30 +325,91 @@ package mx.collections { assertNull(_uncaughtError); } - [Test] //FLEX-35043 - public function test_when_collection_notified_of_itemUpdated_without_property_or_oldValue_sort_compare_function_not_called_with_null():void + [Test] //FLEX-35043 FLEX-34885 + public function test_when_collection_notified_of_itemUpdated_without_property_or_oldValue_sort_compare_function_not_called_with_null_and_item_repositioned():void { - function compareWorkouts(a:Object, b:Object, fields:Array = null):int + function orderByDurationDescending(a:Object, b:Object, fields:Array = null):int { if(a.duration > b.duration) - return 1; - if(a.duration < b.duration) return -1; + if(a.duration < b.duration) + return 1; return 0; } + + //make sure the workout is on the first position + assertEquals("Initial assumption wrong - position", 0, _sut.getItemIndex(_firstWorkout)); + assertEquals("Initial assumption wrong - duration", 0, _firstWorkout.duration); + //given - var sort:InspectableSort = new InspectableSort([], compareWorkouts); + var sort:InspectableSort = new InspectableSort([], orderByDurationDescending); _sut.sort = sort; _sut.refresh(); + //then + assertEquals("The first item should have the highest duration", NO_WORKOUTS - 1, WorkoutVO(_sut.getItemAt(0)).duration); + assertEquals("The first workout should have been moved to the end of the list", _sut.length - 1, _sut.getItemIndex(_firstWorkout)); + //when _firstWorkout.setMuscleGroupsWithoutTriggeringBinding("chest"); _firstWorkout.setMinAgeWithoutTriggeringBinding(20); + _firstWorkout.setDurationWithoutTriggeringBinding(int.MAX_VALUE); _sut.itemUpdated(_firstWorkout); - //then - no fatal because compareWorkouts was not called with null + //then + // 1. no fatal because compare function was not called with null assertNull(_uncaughtError); + // 2. the workout was repositioned because its duration changed + assertEquals("The workout should have been moved to the start of the list, as it has the highest duration", 0, _sut.getItemIndex(_firstWorkout)); + } + + [Test] //FLEX-35043 FLEX-34885 + public function test_when_collection_of_non_binding_objects_notified_of_itemUpdated_without_property_or_oldValue_item_is_repositioned():void + { + function onCollectionChange(event:CollectionEvent):void + { + moveDispatched = event.kind == CollectionEventKind.MOVE; + } + + function orderByDurationAscending(a:Object, b:Object, fields:Array = null):int + { + if(a.duration > b.duration) + return 1; + if(a.duration < b.duration) + return -1; + + return 0; + } + + //given + var moveDispatched:Boolean = false; + + var testWorkout:Object = {duration:10}; + _sut.removeAll(); + _sut.addAll(new ArrayList([testWorkout, {duration:20}, {duration:5}])); + + var sortField:SortField = new SortField("duration"); + sortField.sortCompareType = SortFieldCompareTypes.NUMERIC; + var sort:InspectableSort = new InspectableSort([sortField]); + _sut.sort = sort; + _sut.refresh(); + + _sut.addEventListener(CollectionEvent.COLLECTION_CHANGE, onCollectionChange); + + //then + assertEquals("The ten minute workout should be in the second position", 1, _sut.getItemIndex(testWorkout)); + + //when + testWorkout.duration = int.MAX_VALUE; + _sut.itemUpdated(testWorkout); + + //then + // 1. no fatal because compare function was not called with null + assertNull(_uncaughtError); + // 2. the workout was repositioned because its duration changed + assertTrue(moveDispatched); + assertEquals("The workout should have been moved to the end of the list, as it now has the highest duration", _sut.length - 1, _sut.getItemIndex(testWorkout)); } private static function allowAll(object:Object):Boolean @@ -356,7 +422,7 @@ package mx.collections { private static function createWorkouts():IList { var result:ArrayList = new ArrayList(); - for (var i:int = 0; i < 10; i++) + for (var i:int = 0; i < NO_WORKOUTS; i++) { result.addItem(new WorkoutVO("Workout" + i, i)); } @@ -380,15 +446,15 @@ import spark.collections.Sort; [Bindable] class WorkoutVO { - public var duration:int; public var name:String; + private var _duration:int; private var _muscleGroups:String; private var _minAge:uint; public function WorkoutVO(name:String, duration:int) { this.name = name; - this.duration = duration; + this._duration = duration; } public function get muscleGroups():String @@ -420,6 +486,21 @@ class WorkoutVO { _minAge = age; } + + public function setDurationWithoutTriggeringBinding(duration:int):void + { + _duration = duration; + } + + public function get duration():int + { + return _duration; + } + + public function set duration(value:int):void + { + _duration = value; + } } class InspectableSort extends Sort http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/2c14851e/mustella/tests/spark/collections/Sort/SWFs/SF_ORIG_ListBasic.mxml ---------------------------------------------------------------------- diff --git a/mustella/tests/spark/collections/Sort/SWFs/SF_ORIG_ListBasic.mxml b/mustella/tests/spark/collections/Sort/SWFs/SF_ORIG_ListBasic.mxml index 46cb932..8f38745 100644 --- a/mustella/tests/spark/collections/Sort/SWFs/SF_ORIG_ListBasic.mxml +++ b/mustella/tests/spark/collections/Sort/SWFs/SF_ORIG_ListBasic.mxml @@ -247,8 +247,7 @@ var sortField:SortField = new SortField("value",false); this.addStyleClient(sort); this.addStyleClient(sortField); -// var sort:mx.collections.Sort = new mx.collections.Sort(); -// var sortField:mx.collections.SortField = new mx.collections.SortField("value",true); + sort.fields = [sortField]; ArrayCollection(list1.dataProvider).sort = sort; ArrayCollection(list1.dataProvider).refresh(); @@ -259,7 +258,7 @@ lb.text = to.value; to.value = "yello1"; - list1.dataProvider.itemUpdated(list1.dataProvider.getItemAt(0)); + list1.dataProvider.itemUpdated(to); return 1; } ]]>