2019-12-19 17:32:49 +00:00
|
|
|
/*
|
2023-04-06 09:44:16 +00:00
|
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
2016-09-07 22:37:59 +00:00
|
|
|
*
|
2018-05-01 21:33:50 +00:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2016-09-07 22:37:59 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#import <XCTest/XCTest.h>
|
|
|
|
|
|
2019-10-31 15:17:38 +00:00
|
|
|
#import <IGListDiffKit/IGListBatchUpdateData.h>
|
|
|
|
|
|
2018-02-02 21:14:16 +00:00
|
|
|
#import "IGListMoveIndexPathInternal.h"
|
2016-09-07 22:37:59 +00:00
|
|
|
|
|
|
|
|
// IGListMoveIndexInternal.h
|
|
|
|
|
@interface IGListMoveIndex (Private)
|
2017-01-22 22:34:32 +00:00
|
|
|
- (instancetype)initWithFrom:(NSInteger)from to:(NSInteger)to;
|
2016-09-07 22:37:59 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@interface IGListBatchUpdateDataTests : XCTestCase
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation IGListBatchUpdateDataTests
|
|
|
|
|
|
|
|
|
|
static NSIndexSet *indexSet(NSArray<NSNumber *> *arr) {
|
|
|
|
|
NSMutableIndexSet *set = [NSMutableIndexSet new];
|
|
|
|
|
for (NSNumber *n in arr) {
|
|
|
|
|
[set addIndex:[n integerValue]];
|
|
|
|
|
}
|
|
|
|
|
return set;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-22 22:34:32 +00:00
|
|
|
static NSIndexPath *newPath(NSInteger section, NSInteger item) {
|
2016-09-07 22:37:59 +00:00
|
|
|
return [NSIndexPath indexPathForItem:item inSection:section];
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-11 01:52:35 +00:00
|
|
|
static IGListMoveIndexPath *newMovePath(NSInteger fromSection, NSInteger fromItem, NSInteger toSection, NSInteger toItem) {
|
|
|
|
|
return [[IGListMoveIndexPath alloc] initWithFrom:newPath(fromSection, fromItem) to:newPath(toSection, toItem)];
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-22 22:34:32 +00:00
|
|
|
static IGListMoveIndex *newMove(NSInteger from, NSInteger to) {
|
2016-09-07 22:37:59 +00:00
|
|
|
return [[IGListMoveIndex alloc] initWithFrom:from to:to];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)test_whenEmptyUpdates_thatResultExists {
|
|
|
|
|
IGListBatchUpdateData *result = [[IGListBatchUpdateData alloc] initWithInsertSections:indexSet(@[])
|
|
|
|
|
deleteSections:indexSet(@[])
|
|
|
|
|
moveSections:[NSSet new]
|
2017-04-20 00:01:12 +00:00
|
|
|
insertIndexPaths:@[]
|
|
|
|
|
deleteIndexPaths:@[]
|
Add option to update cells instead of delete+insert for section reload
Summary:
We have seen unnecessary flashiness updates for any section reload updates.
Basically we have 1 section for 1 cell, which is recommended by the IGListKit document in https://instagram.github.io/IGListKit/best-practices-and-faq.html, specifically:
> "We highly recommend using single-item sections when possible."
However, the issue is that whenever we update the underlying data model, we would trigger a delete+insert for the section reload.
In order to mitigate that, we can fix this delete+insert changes or we use the `IGListBindingSectionController`, however, the latter one requires more refactoring plus view model wrapper to have `isEqualToDifferable:` to return YES, which seems less than ideal.
I am proposing that we add an option to the `IGListAdapterUpdater`, as `preferItemReloadsForSectionReloads` so that when it's set to YES, we would trigger the proper `-[UICollectionView reloadItemsAtIndexPaths:]` which handles the cell update properly.
This option also handles the case where the number of items in the section is changed before and after the updates, in that case, it will fallback to do the "delete+insert" section operation.
Reviewed By: rnystrom
Differential Revision: D9519519
fbshipit-source-id: 22ecca07679ebdd212cf771c61e40887cb6a9ba8
2018-08-29 06:22:13 +00:00
|
|
|
updateIndexPaths:@[]
|
2017-04-20 00:01:12 +00:00
|
|
|
moveIndexPaths:@[]];
|
2016-09-07 22:37:59 +00:00
|
|
|
XCTAssertNotNil(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)test_whenUpdatesAreClean_thatResultMatches {
|
|
|
|
|
IGListBatchUpdateData *result = [[IGListBatchUpdateData alloc] initWithInsertSections:indexSet(@[@0, @1])
|
|
|
|
|
deleteSections:indexSet(@[@5])
|
|
|
|
|
moveSections:[NSSet setWithArray:@[newMove(3, 4)]]
|
2017-04-11 21:35:31 +00:00
|
|
|
insertIndexPaths:@[newPath(0, 0)]
|
|
|
|
|
deleteIndexPaths:@[newPath(1, 0)]
|
Add option to update cells instead of delete+insert for section reload
Summary:
We have seen unnecessary flashiness updates for any section reload updates.
Basically we have 1 section for 1 cell, which is recommended by the IGListKit document in https://instagram.github.io/IGListKit/best-practices-and-faq.html, specifically:
> "We highly recommend using single-item sections when possible."
However, the issue is that whenever we update the underlying data model, we would trigger a delete+insert for the section reload.
In order to mitigate that, we can fix this delete+insert changes or we use the `IGListBindingSectionController`, however, the latter one requires more refactoring plus view model wrapper to have `isEqualToDifferable:` to return YES, which seems less than ideal.
I am proposing that we add an option to the `IGListAdapterUpdater`, as `preferItemReloadsForSectionReloads` so that when it's set to YES, we would trigger the proper `-[UICollectionView reloadItemsAtIndexPaths:]` which handles the cell update properly.
This option also handles the case where the number of items in the section is changed before and after the updates, in that case, it will fallback to do the "delete+insert" section operation.
Reviewed By: rnystrom
Differential Revision: D9519519
fbshipit-source-id: 22ecca07679ebdd212cf771c61e40887cb6a9ba8
2018-08-29 06:22:13 +00:00
|
|
|
updateIndexPaths:@[]
|
2017-04-11 21:35:31 +00:00
|
|
|
moveIndexPaths:@[newMovePath(6, 0, 6, 1)]];
|
2016-09-07 22:37:59 +00:00
|
|
|
XCTAssertEqualObjects(result.insertSections, indexSet(@[@0, @1]));
|
|
|
|
|
XCTAssertEqualObjects(result.deleteSections, indexSet(@[@5]));
|
|
|
|
|
XCTAssertEqualObjects(result.moveSections, [NSSet setWithArray:@[newMove(3, 4)]]);
|
2017-04-11 21:35:31 +00:00
|
|
|
XCTAssertEqualObjects(result.insertIndexPaths, @[newPath(0, 0)]);
|
|
|
|
|
XCTAssertEqualObjects(result.deleteIndexPaths, @[newPath(1, 0)]);
|
2017-02-11 01:52:35 +00:00
|
|
|
XCTAssertEqual(result.moveIndexPaths.count, 1);
|
2017-04-11 21:35:31 +00:00
|
|
|
XCTAssertEqualObjects(result.moveIndexPaths.firstObject, [[IGListMoveIndexPath alloc] initWithFrom:newPath(6, 0) to:newPath(6, 1)]);
|
2016-09-07 22:37:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)test_whenMovingSections_withItemDeletes_thatResultConvertsConflicts_toDeletesAndInserts {
|
|
|
|
|
IGListBatchUpdateData *result = [[IGListBatchUpdateData alloc] initWithInsertSections:indexSet(@[])
|
|
|
|
|
deleteSections:indexSet(@[])
|
|
|
|
|
moveSections:[NSSet setWithArray:@[newMove(2, 4)]]
|
2017-04-20 00:01:12 +00:00
|
|
|
insertIndexPaths:@[]
|
2017-04-11 21:35:31 +00:00
|
|
|
deleteIndexPaths:@[newPath(2, 0), newPath(3, 4)]
|
Add option to update cells instead of delete+insert for section reload
Summary:
We have seen unnecessary flashiness updates for any section reload updates.
Basically we have 1 section for 1 cell, which is recommended by the IGListKit document in https://instagram.github.io/IGListKit/best-practices-and-faq.html, specifically:
> "We highly recommend using single-item sections when possible."
However, the issue is that whenever we update the underlying data model, we would trigger a delete+insert for the section reload.
In order to mitigate that, we can fix this delete+insert changes or we use the `IGListBindingSectionController`, however, the latter one requires more refactoring plus view model wrapper to have `isEqualToDifferable:` to return YES, which seems less than ideal.
I am proposing that we add an option to the `IGListAdapterUpdater`, as `preferItemReloadsForSectionReloads` so that when it's set to YES, we would trigger the proper `-[UICollectionView reloadItemsAtIndexPaths:]` which handles the cell update properly.
This option also handles the case where the number of items in the section is changed before and after the updates, in that case, it will fallback to do the "delete+insert" section operation.
Reviewed By: rnystrom
Differential Revision: D9519519
fbshipit-source-id: 22ecca07679ebdd212cf771c61e40887cb6a9ba8
2018-08-29 06:22:13 +00:00
|
|
|
updateIndexPaths:@[]
|
2017-04-20 00:01:12 +00:00
|
|
|
moveIndexPaths:@[]];
|
2016-09-07 22:37:59 +00:00
|
|
|
XCTAssertEqualObjects(result.insertSections, indexSet(@[@4]));
|
|
|
|
|
XCTAssertEqualObjects(result.deleteSections, indexSet(@[@2]));
|
2017-04-11 21:35:31 +00:00
|
|
|
XCTAssertEqualObjects(result.deleteIndexPaths, @[newPath(3, 4)]);
|
2016-09-07 22:37:59 +00:00
|
|
|
XCTAssertEqual(result.moveSections.count, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)test_whenMovingSections_withItemInserts_thatResultConvertsConflicts_toDeletesAndInserts {
|
|
|
|
|
IGListBatchUpdateData *result = [[IGListBatchUpdateData alloc] initWithInsertSections:indexSet(@[])
|
|
|
|
|
deleteSections:indexSet(@[])
|
|
|
|
|
moveSections:[NSSet setWithArray:@[newMove(2, 4)]]
|
2017-04-11 21:35:31 +00:00
|
|
|
insertIndexPaths:@[newPath(4, 0), newPath(3, 4)]
|
2017-04-20 00:01:12 +00:00
|
|
|
deleteIndexPaths:@[]
|
Add option to update cells instead of delete+insert for section reload
Summary:
We have seen unnecessary flashiness updates for any section reload updates.
Basically we have 1 section for 1 cell, which is recommended by the IGListKit document in https://instagram.github.io/IGListKit/best-practices-and-faq.html, specifically:
> "We highly recommend using single-item sections when possible."
However, the issue is that whenever we update the underlying data model, we would trigger a delete+insert for the section reload.
In order to mitigate that, we can fix this delete+insert changes or we use the `IGListBindingSectionController`, however, the latter one requires more refactoring plus view model wrapper to have `isEqualToDifferable:` to return YES, which seems less than ideal.
I am proposing that we add an option to the `IGListAdapterUpdater`, as `preferItemReloadsForSectionReloads` so that when it's set to YES, we would trigger the proper `-[UICollectionView reloadItemsAtIndexPaths:]` which handles the cell update properly.
This option also handles the case where the number of items in the section is changed before and after the updates, in that case, it will fallback to do the "delete+insert" section operation.
Reviewed By: rnystrom
Differential Revision: D9519519
fbshipit-source-id: 22ecca07679ebdd212cf771c61e40887cb6a9ba8
2018-08-29 06:22:13 +00:00
|
|
|
updateIndexPaths:@[]
|
2017-04-20 00:01:12 +00:00
|
|
|
moveIndexPaths:@[]];
|
2016-09-07 22:37:59 +00:00
|
|
|
XCTAssertEqualObjects(result.insertSections, indexSet(@[@4]));
|
|
|
|
|
XCTAssertEqualObjects(result.deleteSections, indexSet(@[@2]));
|
2017-04-11 21:35:31 +00:00
|
|
|
XCTAssertEqualObjects(result.insertIndexPaths, @[newPath(3, 4)]);
|
2016-09-07 22:37:59 +00:00
|
|
|
XCTAssertEqual(result.moveSections.count, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-11 01:52:35 +00:00
|
|
|
- (void)test_whenMovingIndexPaths_withSectionDeleted_thatResultDropsTheMove {
|
|
|
|
|
IGListBatchUpdateData *result = [[IGListBatchUpdateData alloc] initWithInsertSections:indexSet(@[])
|
|
|
|
|
deleteSections:indexSet(@[@0])
|
|
|
|
|
moveSections:[NSSet new]
|
2017-04-20 00:01:12 +00:00
|
|
|
insertIndexPaths:@[]
|
|
|
|
|
deleteIndexPaths:@[]
|
Add option to update cells instead of delete+insert for section reload
Summary:
We have seen unnecessary flashiness updates for any section reload updates.
Basically we have 1 section for 1 cell, which is recommended by the IGListKit document in https://instagram.github.io/IGListKit/best-practices-and-faq.html, specifically:
> "We highly recommend using single-item sections when possible."
However, the issue is that whenever we update the underlying data model, we would trigger a delete+insert for the section reload.
In order to mitigate that, we can fix this delete+insert changes or we use the `IGListBindingSectionController`, however, the latter one requires more refactoring plus view model wrapper to have `isEqualToDifferable:` to return YES, which seems less than ideal.
I am proposing that we add an option to the `IGListAdapterUpdater`, as `preferItemReloadsForSectionReloads` so that when it's set to YES, we would trigger the proper `-[UICollectionView reloadItemsAtIndexPaths:]` which handles the cell update properly.
This option also handles the case where the number of items in the section is changed before and after the updates, in that case, it will fallback to do the "delete+insert" section operation.
Reviewed By: rnystrom
Differential Revision: D9519519
fbshipit-source-id: 22ecca07679ebdd212cf771c61e40887cb6a9ba8
2018-08-29 06:22:13 +00:00
|
|
|
updateIndexPaths:@[]
|
2017-04-11 21:35:31 +00:00
|
|
|
moveIndexPaths:@[newMovePath(0, 0, 0, 1)]];
|
2017-02-11 01:52:35 +00:00
|
|
|
XCTAssertEqual(result.moveIndexPaths.count, 0);
|
|
|
|
|
XCTAssertEqualObjects(result.deleteSections, indexSet(@[@0]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)test_whenMovingIndexPaths_withSectionMoved_thatResultConvertsToDeletesAndInserts {
|
|
|
|
|
IGListBatchUpdateData *result = [[IGListBatchUpdateData alloc] initWithInsertSections:indexSet(@[])
|
|
|
|
|
deleteSections:indexSet(@[])
|
|
|
|
|
moveSections:[NSSet setWithArray:@[newMove(0, 1)]]
|
2017-04-20 00:01:12 +00:00
|
|
|
insertIndexPaths:@[]
|
|
|
|
|
deleteIndexPaths:@[]
|
Add option to update cells instead of delete+insert for section reload
Summary:
We have seen unnecessary flashiness updates for any section reload updates.
Basically we have 1 section for 1 cell, which is recommended by the IGListKit document in https://instagram.github.io/IGListKit/best-practices-and-faq.html, specifically:
> "We highly recommend using single-item sections when possible."
However, the issue is that whenever we update the underlying data model, we would trigger a delete+insert for the section reload.
In order to mitigate that, we can fix this delete+insert changes or we use the `IGListBindingSectionController`, however, the latter one requires more refactoring plus view model wrapper to have `isEqualToDifferable:` to return YES, which seems less than ideal.
I am proposing that we add an option to the `IGListAdapterUpdater`, as `preferItemReloadsForSectionReloads` so that when it's set to YES, we would trigger the proper `-[UICollectionView reloadItemsAtIndexPaths:]` which handles the cell update properly.
This option also handles the case where the number of items in the section is changed before and after the updates, in that case, it will fallback to do the "delete+insert" section operation.
Reviewed By: rnystrom
Differential Revision: D9519519
fbshipit-source-id: 22ecca07679ebdd212cf771c61e40887cb6a9ba8
2018-08-29 06:22:13 +00:00
|
|
|
updateIndexPaths:@[]
|
2017-04-11 21:35:31 +00:00
|
|
|
moveIndexPaths:@[newMovePath(0, 0, 0, 1)]];
|
2017-02-11 01:52:35 +00:00
|
|
|
XCTAssertEqual(result.moveIndexPaths.count, 0);
|
|
|
|
|
XCTAssertEqual(result.moveSections.count, 0);
|
|
|
|
|
XCTAssertEqualObjects(result.deleteSections, indexSet(@[@0]));
|
|
|
|
|
XCTAssertEqualObjects(result.insertSections, indexSet(@[@1]));
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-24 05:26:44 +00:00
|
|
|
- (void)test_whenMovingSections_withMoveFromConflictWithDelete_thatResultDropsTheMove {
|
|
|
|
|
IGListBatchUpdateData *result = [[IGListBatchUpdateData alloc] initWithInsertSections:indexSet(@[])
|
|
|
|
|
deleteSections:indexSet(@[@2])
|
|
|
|
|
moveSections:[NSSet setWithArray:@[newMove(2, 6), newMove(0, 2)]]
|
2017-04-20 00:01:12 +00:00
|
|
|
insertIndexPaths:@[]
|
|
|
|
|
deleteIndexPaths:@[]
|
Add option to update cells instead of delete+insert for section reload
Summary:
We have seen unnecessary flashiness updates for any section reload updates.
Basically we have 1 section for 1 cell, which is recommended by the IGListKit document in https://instagram.github.io/IGListKit/best-practices-and-faq.html, specifically:
> "We highly recommend using single-item sections when possible."
However, the issue is that whenever we update the underlying data model, we would trigger a delete+insert for the section reload.
In order to mitigate that, we can fix this delete+insert changes or we use the `IGListBindingSectionController`, however, the latter one requires more refactoring plus view model wrapper to have `isEqualToDifferable:` to return YES, which seems less than ideal.
I am proposing that we add an option to the `IGListAdapterUpdater`, as `preferItemReloadsForSectionReloads` so that when it's set to YES, we would trigger the proper `-[UICollectionView reloadItemsAtIndexPaths:]` which handles the cell update properly.
This option also handles the case where the number of items in the section is changed before and after the updates, in that case, it will fallback to do the "delete+insert" section operation.
Reviewed By: rnystrom
Differential Revision: D9519519
fbshipit-source-id: 22ecca07679ebdd212cf771c61e40887cb6a9ba8
2018-08-29 06:22:13 +00:00
|
|
|
updateIndexPaths:@[]
|
2017-04-20 00:01:12 +00:00
|
|
|
moveIndexPaths:@[]];
|
2017-03-24 05:26:44 +00:00
|
|
|
XCTAssertEqual(result.deleteSections.count, 1);
|
|
|
|
|
XCTAssertEqual(result.moveSections.count, 1);
|
|
|
|
|
XCTAssertEqual(result.insertSections.count, 0);
|
|
|
|
|
XCTAssertEqualObjects(result.deleteSections, indexSet(@[@2]));
|
|
|
|
|
XCTAssertEqualObjects(result.moveSections.anyObject, newMove(0, 2));
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-17 22:26:33 +00:00
|
|
|
- (void)test_whenDeletingSameIndexPathMultipleTimes_thatResultDropsTheDuplicates {
|
|
|
|
|
IGListBatchUpdateData *result = [[IGListBatchUpdateData alloc] initWithInsertSections:indexSet(@[])
|
|
|
|
|
deleteSections:indexSet(@[])
|
|
|
|
|
moveSections:[NSSet new]
|
|
|
|
|
insertIndexPaths:@[]
|
|
|
|
|
deleteIndexPaths:@[newPath(2, 0), newPath(2, 0)]
|
|
|
|
|
updateIndexPaths:@[]
|
|
|
|
|
moveIndexPaths:@[]];
|
|
|
|
|
|
|
|
|
|
XCTAssertEqualObjects(result.deleteIndexPaths, @[newPath(2, 0)]);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-22 02:12:01 +00:00
|
|
|
- (void)test_whenUpdatesAreClean_thatObjectIsEqualToItself {
|
|
|
|
|
IGListBatchUpdateData *result = [[IGListBatchUpdateData alloc] initWithInsertSections:indexSet(@[@0, @1])
|
|
|
|
|
deleteSections:indexSet(@[@5])
|
|
|
|
|
moveSections:[NSSet setWithArray:@[newMove(3, 4)]]
|
|
|
|
|
insertIndexPaths:@[newPath(0, 0)]
|
|
|
|
|
deleteIndexPaths:@[newPath(1, 0)]
|
|
|
|
|
updateIndexPaths:@[]
|
|
|
|
|
moveIndexPaths:@[newMovePath(6, 0, 6, 1)]];
|
|
|
|
|
XCTAssertTrue([result isEqual:result]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)test_whenEmptyUpdates_thatResultDoesNotEqualOtherClasses {
|
|
|
|
|
IGListBatchUpdateData *emptyResult = [[IGListBatchUpdateData alloc] initWithInsertSections:indexSet(@[])
|
|
|
|
|
deleteSections:indexSet(@[])
|
|
|
|
|
moveSections:[NSSet new]
|
|
|
|
|
insertIndexPaths:@[]
|
|
|
|
|
deleteIndexPaths:@[]
|
|
|
|
|
updateIndexPaths:@[]
|
|
|
|
|
moveIndexPaths:@[]];
|
|
|
|
|
|
|
|
|
|
XCTAssertFalse([emptyResult isEqual:[NSObject new]]);
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-07 22:37:59 +00:00
|
|
|
@end
|