From 098dbbf44b9d80d458ee0bac25576021d6bcb795 Mon Sep 17 00:00:00 2001 From: Martin Richter Date: Thu, 5 Feb 2015 10:36:55 +0100 Subject: [PATCH] Generalize API of Changeset model and add tests --- ObjectiveGoal.xcodeproj/project.pbxproj | 4 + ObjectiveGoal/Model/Changeset.h | 2 +- ObjectiveGoal/Model/Changeset.m | 11 ++- ObjectiveGoal/ViewModel/MatchesViewModel.m | 2 +- ObjectiveGoalTests/ChangesetTests.m | 102 +++++++++++++++++++++ 5 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 ObjectiveGoalTests/ChangesetTests.m diff --git a/ObjectiveGoal.xcodeproj/project.pbxproj b/ObjectiveGoal.xcodeproj/project.pbxproj index 735aff1..9c24e45 100644 --- a/ObjectiveGoal.xcodeproj/project.pbxproj +++ b/ObjectiveGoal.xcodeproj/project.pbxproj @@ -29,6 +29,7 @@ 0FA2AE021A228F79002801D2 /* OpenSans-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0FA2ADFF1A228F79002801D2 /* OpenSans-Light.ttf */; }; 0FA2AE031A228F79002801D2 /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0FA2AE001A228F79002801D2 /* OpenSans-Regular.ttf */; }; 0FA2AE041A228F79002801D2 /* OpenSans-Semibold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0FA2AE011A228F79002801D2 /* OpenSans-Semibold.ttf */; }; + 0FBA66641A835BDB0089A3E8 /* ChangesetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0FBA66631A835BDB0089A3E8 /* ChangesetTests.m */; }; 0FC0BFE71A229D45006A6695 /* PlayerCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0FC0BFE61A229D45006A6695 /* PlayerCell.m */; }; 0FC0BFEA1A22A25C006A6695 /* Player.m in Sources */ = {isa = PBXBuildFile; fileRef = 0FC0BFE91A22A25C006A6695 /* Player.m */; }; 0FDBE03F1A1FD9F5001CA0ED /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0FDBE03E1A1FD9F5001CA0ED /* main.m */; }; @@ -88,6 +89,7 @@ 0FA2ADFF1A228F79002801D2 /* OpenSans-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "OpenSans-Light.ttf"; sourceTree = ""; }; 0FA2AE001A228F79002801D2 /* OpenSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "OpenSans-Regular.ttf"; sourceTree = ""; }; 0FA2AE011A228F79002801D2 /* OpenSans-Semibold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "OpenSans-Semibold.ttf"; sourceTree = ""; }; + 0FBA66631A835BDB0089A3E8 /* ChangesetTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChangesetTests.m; sourceTree = ""; }; 0FC0BFE51A229D45006A6695 /* PlayerCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PlayerCell.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 0FC0BFE61A229D45006A6695 /* PlayerCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PlayerCell.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 0FC0BFE81A22A25C006A6695 /* Player.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Player.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; @@ -210,6 +212,7 @@ children = ( 0FA2ADFD1A224772002801D2 /* TestHelper.h */, 0FA2ADFB1A22475F002801D2 /* TestHelper.m */, + 0FBA66631A835BDB0089A3E8 /* ChangesetTests.m */, 0FDCB2A51A1FFC0900656AF2 /* MatchesViewModelTests.m */, 0FA2ADE71A2139C8002801D2 /* EditMatchViewModelTests.m */, 0FA2ADF91A224631002801D2 /* ManagePlayersViewModelTests.m */, @@ -494,6 +497,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0FBA66641A835BDB0089A3E8 /* ChangesetTests.m in Sources */, 0FA2ADE81A2139C8002801D2 /* EditMatchViewModelTests.m in Sources */, 0F9E47411A2E6A880022AA38 /* RankedPlayersViewModelTests.m in Sources */, 0FA2ADFA1A224631002801D2 /* ManagePlayersViewModelTests.m in Sources */, diff --git a/ObjectiveGoal/Model/Changeset.h b/ObjectiveGoal/Model/Changeset.h index 991f976..5454a0a 100644 --- a/ObjectiveGoal/Model/Changeset.h +++ b/ObjectiveGoal/Model/Changeset.h @@ -13,6 +13,6 @@ @property (nonatomic, copy, readonly) NSArray *deletions; @property (nonatomic, copy, readonly) NSArray *insertions; -+ (Changeset *)changesetFromMatches:(NSArray *)oldMatches toMatches:(NSArray *)newMatches; ++ (Changeset *)changesetOfIndexPathsFromItems:(NSArray *)oldItems toItems:(NSArray *)newItems; @end diff --git a/ObjectiveGoal/Model/Changeset.m b/ObjectiveGoal/Model/Changeset.m index 96fdbb2..56bd059 100644 --- a/ObjectiveGoal/Model/Changeset.m +++ b/ObjectiveGoal/Model/Changeset.m @@ -29,18 +29,19 @@ - (instancetype)initWithDeletions:(NSArray *)deletions insertions:(NSArray *)ins #pragma mark - Match Changesets -+ (Changeset *)changesetFromMatches:(NSArray *)oldMatches toMatches:(NSArray *)newMatches { ++ (Changeset *)changesetOfIndexPathsFromItems:(NSArray *)oldItems toItems:(NSArray *)newItems +{ NSMutableArray *mutableDeletions = [NSMutableArray array]; NSMutableArray *mutableInsertions = [NSMutableArray array]; - [oldMatches enumerateObjectsUsingBlock:^(NSObject *match, NSUInteger index, BOOL *stop) { - if (![newMatches containsObject:match]) { + [oldItems enumerateObjectsUsingBlock:^(NSObject *item, NSUInteger index, BOOL *stop) { + if (![newItems containsObject:item]) { [mutableDeletions addObject:[NSIndexPath indexPathForRow:index inSection:0]]; } }]; - [newMatches enumerateObjectsUsingBlock:^(NSObject *match, NSUInteger index, BOOL *stop) { - if (![oldMatches containsObject:match]) { + [newItems enumerateObjectsUsingBlock:^(NSObject *item, NSUInteger index, BOOL *stop) { + if (![oldItems containsObject:item]) { [mutableInsertions addObject:[NSIndexPath indexPathForRow:index inSection:0]]; } }]; diff --git a/ObjectiveGoal/ViewModel/MatchesViewModel.m b/ObjectiveGoal/ViewModel/MatchesViewModel.m index 1f9e5fa..fa27097 100644 --- a/ObjectiveGoal/ViewModel/MatchesViewModel.m +++ b/ObjectiveGoal/ViewModel/MatchesViewModel.m @@ -52,7 +52,7 @@ - (instancetype)initWithAPIClient:(APIClient *)apiClient { _contentChangesSignal = [updatedContentSignal combinePreviousWithStart:@[] reduce:^(NSArray *previousMatches, NSArray *currentMatches) { - return [Changeset changesetFromMatches:previousMatches toMatches:currentMatches]; + return [Changeset changesetOfIndexPathsFromItems:previousMatches toItems:currentMatches]; }]; _refreshIndicatorVisibleSignal = [RACSignal merge:@[ diff --git a/ObjectiveGoalTests/ChangesetTests.m b/ObjectiveGoalTests/ChangesetTests.m new file mode 100644 index 0000000..4852a43 --- /dev/null +++ b/ObjectiveGoalTests/ChangesetTests.m @@ -0,0 +1,102 @@ +// +// ChangesetTests.m +// ObjectiveGoal +// +// Created by Martin Richter on 05/02/15. +// Copyright (c) 2015 Martin Richter. All rights reserved. +// + +#import +#import +#import "Changeset.h" + +@interface ChangesetTests : XCTestCase + +@end + +@implementation ChangesetTests + +- (void)testChangesetBetweenEmptyArrays +{ + Changeset *changeset = [Changeset changesetOfIndexPathsFromItems:@[] toItems:@[]]; + + XCTAssertEqual(changeset.deletions.count, 0); + XCTAssertEqual(changeset.insertions.count, 0); +} + +- (void)testChangesetFromEmptyToNonEmptyArray +{ + Changeset *changeset = [Changeset changesetOfIndexPathsFromItems:@[] toItems:@[[NSObject new]]]; + + XCTAssertEqual(changeset.deletions.count, 0); + XCTAssertEqual(changeset.insertions.count, 1); + XCTAssertEqual(changeset.insertions.firstObject, [NSIndexPath indexPathForRow:0 inSection:0]); +} + +- (void)testChangesetFromNonEmptyToEmptyArray +{ + Changeset *changeset = [Changeset changesetOfIndexPathsFromItems:@[[NSObject new]] toItems:@[]]; + + XCTAssertEqual(changeset.deletions.count, 1); + XCTAssertEqual(changeset.deletions.firstObject, [NSIndexPath indexPathForRow:0 inSection:0]); + XCTAssertEqual(changeset.insertions.count, 0); +} + +- (void)testChangesetWithInsertedItems +{ + NSObject *firstItem = [NSObject new]; + NSObject *secondItem = [NSObject new]; + NSObject *firstInsertedItem = [NSObject new]; + NSObject *secondInsertedItem = [NSObject new]; + + Changeset *changeset = [Changeset + changesetOfIndexPathsFromItems:@[firstItem, secondItem] + toItems:@[firstItem, firstInsertedItem, secondItem, secondInsertedItem]]; + + XCTAssertEqual(changeset.deletions.count, 0); + XCTAssertEqual(changeset.insertions.count, 2); + XCTAssertEqual(changeset.insertions[0], [NSIndexPath indexPathForRow:1 inSection:0]); + XCTAssertEqual(changeset.insertions[1], [NSIndexPath indexPathForRow:3 inSection:0]); +} + +- (void)testChangesetWithDeletedItems +{ + NSObject *firstItem = [NSObject new]; + NSObject *secondItem = [NSObject new]; + NSObject *thirdItem = [NSObject new]; + NSObject *fourthItem = [NSObject new]; + + Changeset *changeset = [Changeset + changesetOfIndexPathsFromItems:@[firstItem, secondItem, thirdItem, fourthItem] + toItems:@[firstItem, thirdItem]]; + + XCTAssertEqual(changeset.deletions.count, 2); + XCTAssertEqual(changeset.deletions[0], [NSIndexPath indexPathForRow:1 inSection:0]); + XCTAssertEqual(changeset.deletions[1], [NSIndexPath indexPathForRow:3 inSection:0]); + XCTAssertEqual(changeset.insertions.count, 0); +} + +- (void)testChangesetWithDeletedAndInsertedItems +{ + NSObject *firstItem = [NSObject new]; + NSObject *secondItem = [NSObject new]; + NSObject *thirdItem = [NSObject new]; + NSObject *fourthItem = [NSObject new]; + + NSObject *firstInsertedItem = [NSObject new]; + NSObject *secondInsertedItem = [NSObject new]; + + Changeset *changeset = [Changeset + changesetOfIndexPathsFromItems:@[firstItem, secondItem, thirdItem, fourthItem] + toItems:@[firstInsertedItem, firstItem, secondInsertedItem, thirdItem]]; + + XCTAssertEqual(changeset.deletions.count, 2); + XCTAssertEqual(changeset.deletions[0], [NSIndexPath indexPathForRow:1 inSection:0]); + XCTAssertEqual(changeset.deletions[1], [NSIndexPath indexPathForRow:3 inSection:0]); + + XCTAssertEqual(changeset.insertions.count, 2); + XCTAssertEqual(changeset.insertions[0], [NSIndexPath indexPathForRow:0 inSection:0]); + XCTAssertEqual(changeset.insertions[1], [NSIndexPath indexPathForRow:2 inSection:0]); +} + +@end