From 2b0ca81c0b4a3cd1829d9f4eb1e2cf26e7f33f8d Mon Sep 17 00:00:00 2001 From: Tim Oliver Date: Mon, 17 Apr 2023 20:41:49 -0700 Subject: [PATCH] Added more coverage cases to list adapter tests Summary: I reenabled a couple of tests that had previously been marked disabled, but work properly now, and I added additional tests to cover some of the coverage cases that had previously been missed. Reviewed By: candance Differential Revision: D45004625 fbshipit-source-id: dacf49b2f0f420f269e3071ad4fc76762c95365c --- Tests/IGListAdapterTests.m | 204 +++++++++++++++++++++++++++++- Tests/IGListAdapterUpdaterTests.m | 122 ++++++++++++++++++ 2 files changed, 319 insertions(+), 7 deletions(-) diff --git a/Tests/IGListAdapterTests.m b/Tests/IGListAdapterTests.m index 87966170..46bd3d65 100644 --- a/Tests/IGListAdapterTests.m +++ b/Tests/IGListAdapterTests.m @@ -140,6 +140,20 @@ XCTAssertTrue(executed); } +- (void)test_whenReloadingEmptyIndexSet_whenInvalidatingLayout_insideBatchUpdateBlock_thatOperationExitsGracefully { + self.dataSource.objects = @[@0, @1, @2]; + [self.adapter performUpdatesAnimated:YES completion:nil]; + IGListSectionController *second = [self.adapter sectionControllerForObject:@1]; + + __block BOOL executed = NO; + [self.adapter performBatchAnimated:YES updates:^(id batchContext) { + [batchContext reloadInSectionController:second atIndexes:[NSIndexSet indexSet]]; + [batchContext invalidateLayoutInSectionController:second atIndexes:[NSIndexSet indexSet]]; + executed = YES; + } completion:nil]; + XCTAssertTrue(executed); +} + - (void)test_whenQueryingReusableIdentifier_thatIdentifierEqualsClassName { NSString *identifier = IGListReusableViewIdentifier(UICollectionViewCell.class, nil, nil); XCTAssertEqualObjects(identifier, @"UICollectionViewCell"); @@ -270,7 +284,7 @@ XCTAssertEqual(self.collectionView.dataSource, adapter1); } -- (void)DISABLED_test_whenCellsExtendBeyondBounds_thatVisibleSectionControllersAreLimited { +- (void)test_whenCellsExtendBeyondBounds_thatVisibleSectionControllersAreLimited { // # of items for each object == [item integerValue], so @2 has 2 items (cells) self.dataSource.objects = @[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12]; [self.adapter reloadDataWithCompletion:nil]; @@ -284,7 +298,7 @@ XCTAssertTrue([visibleSectionControllers containsObject:[self.adapter sectionControllerForObject:@4]]); } -- (void) test_withEmptySectionPlusFooter_thatVisibleSectionControllersAreCorrect { +- (void)test_withEmptySectionPlusFooter_thatVisibleSectionControllersAreCorrect { self.dataSource.objects = @[@0]; [self.adapter reloadDataWithCompletion:nil]; IGTestSupplementarySource *supplementarySource = [IGTestSupplementarySource new]; @@ -301,7 +315,7 @@ XCTAssertTrue(visibleSectionControllers.firstObject.supplementaryViewSource == supplementarySource); } -- (void)DISABLED_test_whenCellsExtendBeyondBounds_thatVisibleCellsExistForSectionControllers { +- (void)test_whenCellsExtendBeyondBounds_thatVisibleCellsExistForSectionControllers { self.dataSource.objects = @[@2, @3, @4, @5, @6]; [self.adapter reloadDataWithCompletion:nil]; id sectionController2 = [self.adapter sectionControllerForObject:@2]; @@ -316,7 +330,7 @@ XCTAssertEqual([self.adapter visibleCellsForSectionController:sectionController6].count, 0); } -- (void)DISABLED_test_whenCellsExtendBeyondBounds_thatVisibleIndexPathsExistForSectionControllers { +- (void)test_whenCellsExtendBeyondBounds_thatVisibleIndexPathsExistForSectionControllers { self.dataSource.objects = @[@2, @3, @4, @5, @6]; [self.adapter reloadDataWithCompletion:nil]; id sectionController2 = [self.adapter sectionControllerForObject:@2]; @@ -629,6 +643,18 @@ XCTAssertEqualObjects(visibleObjects, expectedObjects); } +- (void)test_whenAdapterUpdated_withSkipViewSectionControllerMap_fetchingCellIsValid { + self.adapter.experiments |= IGListExperimentSkipViewSectionControllerMap; + // each section controller returns n items sized 100x10 + self.dataSource.objects = @[@1, @2, @3, @4, @5, @6]; + [self.adapter reloadDataWithCompletion:nil]; + [self.collectionView layoutIfNeeded]; + + IGListSectionController *controller = [self.adapter sectionControllerForObject:@2]; + UICollectionViewCell *cell = [self.adapter cellForItemAtIndex:0 sectionController:controller]; + XCTAssertNotNil(cell); +} + - (void)test_whenAdapterUpdated_thatVisibleCellsForObjectAreFound { // each section controller returns n items sized 100x10 self.dataSource.objects = @[@2, @10, @5]; @@ -669,6 +695,32 @@ XCTAssertEqual(visibleCellsForObject.count, 0); } +- (void)test_whenAdapterUpdated_thatFullyVisibleCellsIsCorrect { + // each section controller returns n items sized 100x10 + self.dataSource.objects = @[@2, @10, @5]; + [self.adapter reloadDataWithCompletion:nil]; + [self.collectionView layoutIfNeeded]; + + // each row is 10 pixels high, and the first object has 2 rows. + // the window is 100 pixels highm so the number of visible cells in the second section should be 8 + IGListSectionController *controller = [self.adapter sectionControllerForObject:@10]; + NSArray *visibleCells = [self.adapter fullyVisibleCellsForSectionController:controller]; + XCTAssertEqual(visibleCells.count, 8); +} + +- (void)test_whenAdapterUpdated_thatFullyVisibleIndexPathsIsCorrect { + // each section controller returns n items sized 100x10 + self.dataSource.objects = @[@2, @10, @5]; + [self.adapter reloadDataWithCompletion:nil]; + [self.collectionView layoutIfNeeded]; + + // each row is 10 pixels high, and the first object has 2 rows. + // the window is 100 pixels highm so the number of visible cells in the second section should be 8 + IGListSectionController *controller = [self.adapter sectionControllerForObject:@10]; + NSArray *visibleCells = [self.adapter visibleIndexPathsForSectionController:controller]; + XCTAssertEqual(visibleCells.count, 8); +} + - (void)test_whenScrollVerticallyToItem { // # of items for each object == [item integerValue], so @2 has 2 items (cells) self.dataSource.objects = @[@1, @2, @3, @4, @5, @6]; @@ -819,6 +871,8 @@ self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; [self.adapter reloadDataWithCompletion:nil]; XCTAssertEqual([self.collectionView numberOfSections], 6); + [self.adapter scrollToObject:@1 supplementaryKinds:nil scrollDirection:UICollectionViewScrollDirectionHorizontal scrollPosition:UICollectionViewScrollPositionLeft additionalOffset:0 animated:NO]; + IGAssertEqualPoint([self.collectionView contentOffset], 0, 0); [self.adapter scrollToObject:@1 supplementaryKinds:nil scrollDirection:UICollectionViewScrollDirectionHorizontal scrollPosition:UICollectionViewScrollPositionNone additionalOffset:0 animated:NO]; IGAssertEqualPoint([self.collectionView contentOffset], 0, 0); [self.adapter scrollToObject:@2 supplementaryKinds:nil scrollDirection:UICollectionViewScrollDirectionHorizontal scrollPosition:UICollectionViewScrollPositionNone additionalOffset:0 animated:NO]; @@ -1069,6 +1123,7 @@ self.dataSource.objects = @[@1, @2, @3, @4]; self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; self.collectionView.frame = CGRectMake(0, 0, 300, 10); + XCTAssertEqual([self.adapter offsetForFirstVisibleItemWithScrollDirection:UICollectionViewScrollDirectionHorizontal], 0); [self.adapter reloadDataWithCompletion:nil]; XCTAssertEqual([self.collectionView numberOfSections], 4); [self.collectionView setContentOffset:CGPointMake(0, 0) animated:NO]; @@ -1269,12 +1324,21 @@ NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; [[mockDelegate expect] collectionView:self.collectionView didSelectItemAtIndexPath:indexPath]; - // simulates the collectionview telling its delegate that it was tapped + // simulates the collection view telling its delegate that it was tapped [self.adapter collectionView:self.collectionView didSelectItemAtIndexPath:indexPath]; [mockDelegate verify]; } +- (void)test_whenSelectingCell_thatSelectionConfirmationDelegatesReturnValues { + self.dataSource.objects = @[@0, @1, @2]; + [self.adapter reloadDataWithCompletion:nil]; + + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; + XCTAssertTrue([self.adapter collectionView:self.collectionView shouldSelectItemAtIndexPath:indexPath]); + XCTAssertTrue([self.adapter collectionView:self.collectionView shouldDeselectItemAtIndexPath:indexPath]); +} + - (void)test_whenSelectingCell_thatSectionControllerReceivesMethod { self.dataSource.objects = @[@0, @1, @2]; [self.adapter reloadDataWithCompletion:nil]; @@ -1391,14 +1455,20 @@ self.adapter.collectionViewDelegate = mockCollectionDelegate; self.adapter.scrollViewDelegate = mockScrollDelegate; + id mockSectionControllerScrollDelegate = [OCMockObject mockForProtocol:@protocol(IGListScrollDelegate)]; + IGListSectionController *firstController = [self.adapter.visibleSectionControllers firstObject]; + firstController.scrollDelegate = mockSectionControllerScrollDelegate; + [[mockCollectionDelegate reject] scrollViewDidEndDecelerating:self.collectionView]; [[mockScrollDelegate expect] scrollViewDidEndDecelerating:self.collectionView]; + [[mockSectionControllerScrollDelegate expect] listAdapter:self.adapter didEndDeceleratingSectionController:firstController]; // simulates the scrollview delegate telling the adapter that it ended decelerating [self.adapter scrollViewDidEndDecelerating:self.collectionView]; [mockCollectionDelegate verify]; [mockScrollDelegate verify]; + [mockSectionControllerScrollDelegate verify]; } - (void)test_whenReloadingObjectsThatDontExist_thatAdapterContinues { @@ -1430,7 +1500,7 @@ XCTAssertTrue([[self.collectionView cellForItemAtIndexPath:path] isSelected]); } -- (void)DISABLED_test_whenScrollingToIndex_withSectionController_thatPositionCorrect { +- (void)test_whenScrollingToIndex_withSectionController_thatPositionCorrect { self.dataSource.objects = @[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19]; [self.adapter reloadDataWithCompletion:nil]; @@ -1465,6 +1535,19 @@ [mockDisplayDelegate verify]; } +- (void)test_whenDisplayingSectionController_fetchingCellIndexIsValid { + self.dataSource.objects = @[@0, @1, @2]; + [self.adapter reloadDataWithCompletion:nil]; + + IGListSectionController *controller = [self.adapter sectionControllerForObject:@1]; + UICollectionViewCell *cell = [self.adapter cellForItemAtIndex:0 sectionController:controller]; + XCTAssertNotNil(cell); + + NSInteger index = [self.adapter indexForCell:cell sectionController:controller]; + XCTAssertNotEqual(index, NSNotFound); + XCTAssertEqual(index, 0); +} + - (void)test_whenEndingDisplayOfSectionController_withOnlySupplementaryView_thatDisplayEventStillSent { self.dataSource.objects = @[@0]; [self.adapter reloadDataWithCompletion:nil]; @@ -1524,6 +1607,23 @@ [mockDelegate verify]; } +- (void)test_whenEndDisplayingSupplementaryView_withSkipViewSectionControllerMap_thatCollectionViewDelegateReceivesEvents { + self.adapter.experiments |= IGListExperimentSkipViewSectionControllerMap; + + // silence display handler asserts + self.dataSource.objects = @[@1, @2]; + [self.adapter reloadDataWithCompletion:nil]; + + id mockDelegate = [OCMockObject mockForProtocol:@protocol(UICollectionViewDelegate)]; + self.adapter.collectionViewDelegate = mockDelegate; + UICollectionReusableView *view = [UICollectionReusableView new]; + NSString *kind = @"kind"; + NSIndexPath *path = [NSIndexPath indexPathForItem:0 inSection:0]; + [[mockDelegate expect] collectionView:self.collectionView didEndDisplayingSupplementaryView:view forElementOfKind:kind atIndexPath:path]; + [self.adapter collectionView:self.collectionView didEndDisplayingSupplementaryView:view forElementOfKind:kind atIndexPath:path]; + [mockDelegate verify]; +} + - (void)test_whenHighlightingCell_thatCollectionViewDelegateReceivesMethod { self.dataSource.objects = @[@0, @1, @2]; [self.adapter reloadDataWithCompletion:nil]; @@ -1648,7 +1748,7 @@ XCTAssertEqual(inset.right, 4); } -- (void)DISABLED_test_whenQueryingInsetContainerSize_thatResultIsBoundsInsetByContent { +- (void)test_whenQueryingInsetContainerSize_thatResultIsBoundsInsetByContent { self.dataSource.objects = @[@2]; [self.adapter reloadDataWithCompletion:nil]; self.collectionView.contentInset = UIEdgeInsetsMake(1, 2, 3, 4); @@ -1658,6 +1758,16 @@ XCTAssertEqual(size.height, 96); } +- (void)test_whenQueryingContainerContentOffset_thatResultIsCorrectlyOffset { + self.dataSource.objects = @[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10]; + [self.adapter reloadDataWithCompletion:nil]; + IGListSectionController *controller = [self.adapter sectionControllerForObject:@3]; + [self.adapter scrollToSectionController:controller atIndex:0 scrollPosition:UICollectionViewScrollPositionTop animated:NO]; + const CGPoint offset = [controller.collectionContext containerContentOffset]; + XCTAssertEqual(offset.x, 0); + XCTAssertEqual(offset.y, 30); +} + - (void)test_whenInsertingAtBeginning_thatAllSectionControllerIndexesUpdateCorrectly_forInsertAtHead { NSNumber *zero = @0; NSNumber *one = @1; @@ -1969,6 +2079,32 @@ XCTAssertEqual(sectionObjects[0], section.sectionObject.objects[2]); } +- (void)test_whenItemsInSectionAreInteractivelyReordered_andReorderingIsDisallowed_thatIndexesUpdateCorrectly { + IGListTestAdapterReorderingDataSource *dataSource = [IGListTestAdapterReorderingDataSource new]; + dataSource.objects = @[@0]; + self.adapter.dataSource = dataSource; + self.adapter.moveDelegate = dataSource; + + NSArray *sectionObjects = @[@0, @1, @2]; + + IGTestReorderableSection *section = (IGTestReorderableSection *)[self.adapter sectionControllerForSection:0]; + section.sectionObject = [IGTestReorderableSectionObject sectionWithObjects:sectionObjects]; + section.isReorderable = NO; + + [self.adapter performUpdatesAnimated:NO completion:nil]; + + NSIndexPath *fromIndexPath, *toIndexPath; + + // move the last item into the first position + fromIndexPath = [NSIndexPath indexPathForItem:2 inSection:0]; + toIndexPath = [NSIndexPath indexPathForItem:0 inSection:0]; + [self.adapter collectionView:self.collectionView moveItemAtIndexPath:fromIndexPath toIndexPath:toIndexPath]; + + XCTAssertEqual(sectionObjects[0], section.sectionObject.objects[0]); + XCTAssertEqual(sectionObjects[1], section.sectionObject.objects[1]); + XCTAssertEqual(sectionObjects[2], section.sectionObject.objects[2]); +} + - (void)test_whenItemsAreInteractivelyReorderedAcrossSections_thatIndexesRevertToOriginalState { IGListTestAdapterReorderingDataSource *dataSource = [IGListTestAdapterReorderingDataSource new]; dataSource.objects = @[@0, @1]; @@ -2008,6 +2144,41 @@ XCTAssertEqual(section1Objects[2], section1.sectionObject.objects[2]); } +- (void)test_whenSingleItemsAreInteractivelyReorderedAcrossSections_thatIndexesRevertToOriginalState { + IGListTestAdapterReorderingDataSource *dataSource = [IGListTestAdapterReorderingDataSource new]; + dataSource.objects = @[@0, @1]; + self.adapter.dataSource = dataSource; + self.adapter.moveDelegate = dataSource; + + NSArray *section0Objects = @[@0]; + NSArray *section1Objects = @[@3]; + + IGTestReorderableSection *section0 = (IGTestReorderableSection *)[self.adapter sectionControllerForSection:0]; + section0.sectionObject = [IGTestReorderableSectionObject sectionWithObjects:section0Objects]; + IGTestReorderableSection *section1 = (IGTestReorderableSection *)[self.adapter sectionControllerForSection:1]; + section1.sectionObject = [IGTestReorderableSectionObject sectionWithObjects:section1Objects]; + section1.isReorderable = YES; + + [self.adapter performUpdatesAnimated:NO completion:nil]; + + NSIndexPath *fromIndexPath, *toIndexPath; + + // move an item from section 1 to section 0 and check that they are reverted + fromIndexPath = [NSIndexPath indexPathForItem:0 inSection:1]; + toIndexPath = [NSIndexPath indexPathForItem:0 inSection:0]; + + [self.collectionView performBatchUpdates:^{ + [self.collectionView moveItemAtIndexPath:fromIndexPath toIndexPath:toIndexPath]; + + [self.collectionView.dataSource collectionView:self.collectionView + moveItemAtIndexPath:fromIndexPath + toIndexPath:toIndexPath]; + } completion:nil]; + + XCTAssertEqual(section0Objects[0], section0.sectionObject.objects[0]); + XCTAssertEqual(section1Objects[0], section1.sectionObject.objects[0]); +} + - (void)test_whenSingleItemInSectionIsInteractivelyReorderedThorughLastSpot_indexesUpdateCorrectly { IGListTestAdapterReorderingDataSource *dataSource = [IGListTestAdapterReorderingDataSource new]; dataSource.objects = @[@0, @1, @2]; @@ -2160,6 +2331,25 @@ [self.adapter performUpdatesAnimated:NO completion:nil]; XCTAssertNotNil([self.adapter viewForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndex:0 sectionController:controller]); + XCTAssertNil([self.adapter viewForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndex:1 sectionController:controller]); +} + +- (void)test_whenSettingSupplementaryView_withSkipViewSectionControllerMap_thatViewForSupplementaryElementExists { + self.adapter.experiments |= IGListExperimentSkipViewSectionControllerMap; + self.dataSource.objects = @[@0]; + [self.adapter reloadDataWithCompletion:nil]; + + IGTestSupplementarySource *supplementarySource = [IGTestSupplementarySource new]; + supplementarySource.collectionContext = self.adapter; + supplementarySource.supportedElementKinds = @[UICollectionElementKindSectionHeader]; + + IGListSectionController *controller = [self.adapter sectionControllerForObject:@0]; + controller.supplementaryViewSource = supplementarySource; + supplementarySource.sectionController = controller; + + [self.adapter performUpdatesAnimated:NO completion:nil]; + XCTAssertNotNil([self.adapter viewForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndex:0 sectionController:controller]); + XCTAssertNil([self.adapter viewForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndex:1 sectionController:controller]); } @end diff --git a/Tests/IGListAdapterUpdaterTests.m b/Tests/IGListAdapterUpdaterTests.m index 8daaa8d2..6a2f1f73 100644 --- a/Tests/IGListAdapterUpdaterTests.m +++ b/Tests/IGListAdapterUpdaterTests.m @@ -16,6 +16,7 @@ #import "IGListMoveIndexInternal.h" #import "IGListTestUICollectionViewDataSource.h" #import "IGListTransitionData.h" +#import "IGListUpdateTransactionBuilder.h" #import "IGTestObject.h" #define genExpectation [self expectationWithDescription:NSStringFromSelector(_cmd)] @@ -97,6 +98,20 @@ XCTAssertTrue([self.updater hasChanges]); } +- (void)test_whenConvertingIndexPathsToSections_thatCorrectValuesAreReturned { + NSArray *indexPaths = @[ + [NSIndexPath indexPathForItem:0 inSection:0], + [NSIndexPath indexPathForItem:0 inSection:1], + [NSIndexPath indexPathForItem:0 inSection:2], + [NSIndexPath indexPathForItem:0 inSection:3] + ]; + + NSIndexSet *sections = IGListSectionIndexFromIndexPaths(indexPaths); + XCTAssertEqual(sections.count, 4); + XCTAssertEqual(sections.firstIndex, 0); + XCTAssertEqual(sections.lastIndex, 3); +} + - (void)test_whenReloadingData_thatCollectionViewUpdates { self.dataSource.sections = @[[IGSectionObject sectionWithObjects:@[]]]; [self.updater reloadDataWithCollectionViewBlock:[self collectionViewBlock] reloadUpdateBlock:^{} completion:nil]; @@ -257,6 +272,29 @@ XCTAssertEqual([self.collectionView numberOfItemsInSection:1], 2); } +- (void)test_whenMovingSections_thatCollectionViewUpdates { + self.dataSource.sections = @[ + [IGSectionObject sectionWithObjects:@[@0, @1]], + [IGSectionObject sectionWithObjects:@[@0, @1, @2]] + ]; + [self.updater reloadDataWithCollectionViewBlock:[self collectionViewBlock] reloadUpdateBlock:^{} completion:nil]; + [self.updater update]; + XCTAssertEqual([self.collectionView numberOfSections], 2); + XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 2); + XCTAssertEqual([self.collectionView numberOfItemsInSection:1], 3); + + self.dataSource.sections = @[ + [IGSectionObject sectionWithObjects:@[@0, @1, @2]], + [IGSectionObject sectionWithObjects:@[@0, @1]] + ]; + + [self.updater moveSectionInCollectionView:self.collectionView fromIndex:0 toIndex:1]; + [self.updater update]; + XCTAssertEqual([self.collectionView numberOfSections], 2); + XCTAssertEqual([self.collectionView numberOfItemsInSection:0], 3); + XCTAssertEqual([self.collectionView numberOfItemsInSection:1], 2); +} + - (void)test_whenCollectionViewNeedsLayout_thatPerformBatchUpdateWorks { NSArray *from = @[ [IGSectionObject sectionWithObjects:@[]], @@ -1229,4 +1267,88 @@ [mockDelegate verify]; } +- (void)test_withSingleItemSectionUpdates_thatPerformBatchUpdateWorks { + self.updater.singleItemSectionUpdates = YES; + + IGSectionObject *first = [IGSectionObject sectionWithObjects:@[@1, @2, @3]]; + IGSectionObject *second =[IGSectionObject sectionWithObjects:@[@4, @5, @6]]; + + NSArray *from = @[first, second]; + NSArray *to = @[second, first]; + + self.dataSource.sections = from; + [self.updater reloadDataWithCollectionViewBlock:[self collectionViewBlock] reloadUpdateBlock:^{} completion:nil]; + [self.updater update]; + + XCTestExpectation *expectation = genExpectation; + [self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] + animated:NO + sectionDataBlock:[self dataBlockFromObjects:from toObjects:to] + applySectionDataBlock:self.applySectionDataBlock + completion:^(BOOL finished) { + XCTAssertEqual([self.collectionView numberOfSections], 2); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:30 handler:nil]; +} + +#pragma mark - Illegal state checking + +- (void)test_whenCollectionViewBlockIsNotCorrectlyApplied_thatTransactionsGetCancelled { + // Test if a nil collection view block is accidentally supplied + UICollectionView *(^listCollectionViewBlock)(void) = ^{ + return self.collectionView; + }; + listCollectionViewBlock = nil; + + IGListAdapterUpdater *updater = [IGListAdapterUpdater new]; + [updater.transactionBuilder addReloadDataWithCollectionViewBlock:listCollectionViewBlock + reloadBlock:^{} + completion:nil]; + [updater update]; + XCTAssertNil(updater.lastTransactionBuilder); +} + +- (void)test_whenReloadBlockIsNotCorrectlyApplied_thatTransactionsGetCancelled { + // Test if a nil collection view block is accidentally supplied + UICollectionView *(^listCollectionViewBlock)(void) = ^{ + return self.collectionView; + }; + + void (^reloadBlock)(void) = ^{}; + reloadBlock = nil; + + IGListAdapterUpdater *updater = [IGListAdapterUpdater new]; + [updater.transactionBuilder addReloadDataWithCollectionViewBlock:listCollectionViewBlock + reloadBlock:reloadBlock + completion:nil]; + [updater update]; + XCTAssertNil(updater.lastTransactionBuilder); +} + +- (void)test_whenDataSourceChangeBlockIsNotCorrectlyApplied_thatTransactionsGetCancelled { + UICollectionView *(^listCollectionViewBlock)(void) = ^{ + return self.collectionView; + }; + + void (^dataSourceChangeBlock)(void) = ^{}; + dataSourceChangeBlock = nil; + + IGListAdapterUpdater *updater = [IGListAdapterUpdater new]; + [updater.transactionBuilder addReloadDataWithCollectionViewBlock:listCollectionViewBlock + reloadBlock:^{} + completion:nil]; + [updater.transactionBuilder addDataSourceChange:dataSourceChangeBlock]; + [updater update]; + XCTAssertNil(updater.lastTransactionBuilder); +} + +- (void)test_whenAddingChangesFromTransactionBuilder_thatOperationExitsGracefully { + IGListUpdateTransactionBuilder *builder = [IGListUpdateTransactionBuilder new]; + builder = nil; + + IGListAdapterUpdater *updater = [IGListAdapterUpdater new]; + [updater.transactionBuilder addChangesFromBuilder:builder]; +} + @end