Skip to content

Commit

Permalink
ra1028#17: Add stress tests of continous random collection view updat…
Browse files Browse the repository at this point in the history
…es in a one dimentional collection
  • Loading branch information
Usipov committed Aug 28, 2018
1 parent 2d8a676 commit cd79607
Show file tree
Hide file tree
Showing 11 changed files with 570 additions and 0 deletions.
62 changes: 62 additions & 0 deletions DifferenceKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@
6B5B40AB211066EA00A931DB /* AnyDifferentiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5B408B211066B300A931DB /* AnyDifferentiable.swift */; };
6B5B40AC211066EA00A931DB /* ElementPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5B408D211066B300A931DB /* ElementPath.swift */; };
6B956B762110B25300DE3D29 /* UIKitExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5B4088211066B300A931DB /* UIKitExtension.swift */; };
CC98CBFA21357DBD000005F8 /* Cell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC98CBF021357DBD000005F8 /* Cell.swift */; };
CC98CBFB21357DBD000005F8 /* CellDataGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC98CBF121357DBD000005F8 /* CellDataGenerator.swift */; };
CC98CBFC21357DBD000005F8 /* CellData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC98CBF221357DBD000005F8 /* CellData.swift */; };
CC98CBFD21357DBD000005F8 /* CellDataGeneratorResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC98CBF321357DBD000005F8 /* CellDataGeneratorResult.swift */; };
CC98CBFE21357DBD000005F8 /* CollectionViewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC98CBF421357DBD000005F8 /* CollectionViewUpdater.swift */; };
CC98CBFF21357DBD000005F8 /* ObjCExceptionCatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC98CBF721357DBD000005F8 /* ObjCExceptionCatcher.swift */; };
CC98CC0121357DBD000005F8 /* DifferenceKitStressTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC98CBF921357DBD000005F8 /* DifferenceKitStressTests.swift */; };
CCF87E8821358165000CB713 /* ObjCExceptionCatcherHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = CCF87E8621358164000CB713 /* ObjCExceptionCatcherHelper.m */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -60,6 +68,16 @@
6B5B4099211066BF00A931DB /* MeasurementTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeasurementTest.swift; sourceTree = "<group>"; };
6B5B409A211066BF00A931DB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
6B5B409B211066BF00A931DB /* TestTools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestTools.swift; sourceTree = "<group>"; };
CC98CBF021357DBD000005F8 /* Cell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cell.swift; sourceTree = "<group>"; };
CC98CBF121357DBD000005F8 /* CellDataGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellDataGenerator.swift; sourceTree = "<group>"; };
CC98CBF221357DBD000005F8 /* CellData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellData.swift; sourceTree = "<group>"; };
CC98CBF321357DBD000005F8 /* CellDataGeneratorResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellDataGeneratorResult.swift; sourceTree = "<group>"; };
CC98CBF421357DBD000005F8 /* CollectionViewUpdater.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewUpdater.swift; sourceTree = "<group>"; };
CC98CBF721357DBD000005F8 /* ObjCExceptionCatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCExceptionCatcher.swift; sourceTree = "<group>"; };
CC98CBF921357DBD000005F8 /* DifferenceKitStressTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DifferenceKitStressTests.swift; sourceTree = "<group>"; };
CCF87E8521358164000CB713 /* Tests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Bridging-Header.h"; sourceTree = "<group>"; };
CCF87E8621358164000CB713 /* ObjCExceptionCatcherHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjCExceptionCatcherHelper.m; sourceTree = "<group>"; };
CCF87E8721358165000CB713 /* ObjCExceptionCatcherHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjCExceptionCatcherHelper.h; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -136,6 +154,7 @@
6B5B4092211066BF00A931DB /* Tests */ = {
isa = PBXGroup;
children = (
CC98CBEE21357DBD000005F8 /* StressTests */,
6B5B4093211066BF00A931DB /* AlgorithmTest.swift */,
6B5B4094211066BF00A931DB /* ArraySectionTest.swift */,
6B5B4095211066BF00A931DB /* StagedChangesetTest.swift */,
Expand All @@ -145,10 +164,43 @@
6B5B4099211066BF00A931DB /* MeasurementTest.swift */,
6B5B409B211066BF00A931DB /* TestTools.swift */,
6B5B409A211066BF00A931DB /* Info.plist */,
CCF87E8521358164000CB713 /* Tests-Bridging-Header.h */,
);
path = Tests;
sourceTree = "<group>";
};
CC98CBEE21357DBD000005F8 /* StressTests */ = {
isa = PBXGroup;
children = (
CC98CBEF21357DBD000005F8 /* Support */,
CC98CBF921357DBD000005F8 /* DifferenceKitStressTests.swift */,
);
path = StressTests;
sourceTree = "<group>";
};
CC98CBEF21357DBD000005F8 /* Support */ = {
isa = PBXGroup;
children = (
CC98CBF021357DBD000005F8 /* Cell.swift */,
CC98CBF121357DBD000005F8 /* CellDataGenerator.swift */,
CC98CBF221357DBD000005F8 /* CellData.swift */,
CC98CBF321357DBD000005F8 /* CellDataGeneratorResult.swift */,
CC98CBF421357DBD000005F8 /* CollectionViewUpdater.swift */,
CC98CBF521357DBD000005F8 /* ObjCExceptionCatcher */,
);
path = Support;
sourceTree = "<group>";
};
CC98CBF521357DBD000005F8 /* ObjCExceptionCatcher */ = {
isa = PBXGroup;
children = (
CC98CBF721357DBD000005F8 /* ObjCExceptionCatcher.swift */,
CCF87E8721358165000CB713 /* ObjCExceptionCatcherHelper.h */,
CCF87E8621358164000CB713 /* ObjCExceptionCatcherHelper.m */,
);
path = ObjCExceptionCatcher;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
Expand Down Expand Up @@ -275,13 +327,21 @@
buildActionMask = 2147483647;
files = (
6B5B40A1211066BF00A931DB /* ChangesetTest.swift in Sources */,
CC98CBFE21357DBD000005F8 /* CollectionViewUpdater.swift in Sources */,
6B5B409C211066BF00A931DB /* AlgorithmTest.swift in Sources */,
CC98CC0121357DBD000005F8 /* DifferenceKitStressTests.swift in Sources */,
6B5B40A0211066BF00A931DB /* ElementPathTest.swift in Sources */,
6B5B40A4211066BF00A931DB /* TestTools.swift in Sources */,
CC98CBFD21357DBD000005F8 /* CellDataGeneratorResult.swift in Sources */,
6B5B40A2211066BF00A931DB /* MeasurementTest.swift in Sources */,
CC98CBFB21357DBD000005F8 /* CellDataGenerator.swift in Sources */,
CC98CBFF21357DBD000005F8 /* ObjCExceptionCatcher.swift in Sources */,
CC98CBFA21357DBD000005F8 /* Cell.swift in Sources */,
6B5B409D211066BF00A931DB /* ArraySectionTest.swift in Sources */,
6B5B409F211066BF00A931DB /* AnyDifferentiableTest.swift in Sources */,
6B5B409E211066BF00A931DB /* StagedChangesetTest.swift in Sources */,
CCF87E8821358165000CB713 /* ObjCExceptionCatcherHelper.m in Sources */,
CC98CBFC21357DBD000005F8 /* CellData.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -493,6 +553,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = com.ryo.DifferenceKitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Tests/Tests-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2,3,4";
Expand All @@ -515,6 +576,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = com.ryo.DifferenceKitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Tests/Tests-Bridging-Header.h";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2,3,4";
};
Expand Down
130 changes: 130 additions & 0 deletions Tests/StressTests/DifferenceKitStressTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import XCTest
import UIKit

private let collectionSize = 100
private let iterationsCount = 100

final class DifferenceKitStressTests: XCTestCase {
func test_differenceKit_doesNotCrashUICollectionView_duringStressCollectionChanges_1() {
performTest_differenceKit_doesNotCrashUICollectionView_duringStressCollectionChanges()
}

func test_differenceKit_doesNotCrashUICollectionView_duringStressCollectionChanges_2() {
performTest_differenceKit_doesNotCrashUICollectionView_duringStressCollectionChanges()
}

func test_differenceKit_doesNotCrashUICollectionView_duringStressCollectionChanges_3() {
performTest_differenceKit_doesNotCrashUICollectionView_duringStressCollectionChanges()
}

func test_differenceKit_doesNotCrashUICollectionView_duringStressCollectionChanges_4() {
performTest_differenceKit_doesNotCrashUICollectionView_duringStressCollectionChanges()
}

func test_differenceKit_doesNotCrashUICollectionView_duringStressCollectionChanges_5() {
performTest_differenceKit_doesNotCrashUICollectionView_duringStressCollectionChanges()
}

// MARK: - Private
private func performTest_differenceKit_doesNotCrashUICollectionView_duringStressCollectionChanges() {
let expectations = (0..<iterationsCount).map {
expectation(description: "async expectation of iteration \($0)")
}

performTests(expectations: expectations)
waitForExpectations(timeout: 60)
}

private func performTests(expectations: [XCTestExpectation]) {
performTest(
index: 0,
expectations: expectations,
previousCellDataList: []
)
}

private func performTest(
index: Int,
expectations: [XCTestExpectation],
previousCellDataList: [CellData])
{
guard index < expectations.count else { return }

autoreleasepool {
print("testing iteration \(index)")

let cellDataGenerator = CellDataGenerator()

// Given
let cellDataGeneratorResult: CellDataGeneratorResult
if index == 0 {
cellDataGeneratorResult = cellDataGenerator
.generateCellData(count: collectionSize)
} else if index == iterationsCount / 2 {
// Special case: applying no changes to original data
cellDataGeneratorResult = cellDataGenerator
.performNoActionsOnCellData(
previousCellDataList
)
} else {
cellDataGeneratorResult = cellDataGenerator
.performRandomActionsOnCellData(
previousCellDataList,
minimumCountAfterActions: collectionSize / 10,
maximumCountAfterActions: collectionSize * 10
)
}

print(" from \(cellDataGeneratorResult.from.count), to: \(cellDataGeneratorResult.to.count).")

print(" deletes \(cellDataGeneratorResult.deletes), "
+ "inserts: \(cellDataGeneratorResult.inserts), "
+ "moves \(cellDataGeneratorResult.moves), "
+ "updates: \(cellDataGeneratorResult.updates)."
)

// When
let collectionViewUpdater = CollectionViewUpdater()
collectionViewUpdater.updateCollectionView(
from: cellDataGeneratorResult.from,
to: cellDataGeneratorResult.to,
completion: { [weak self] result in
// Then
switch result {
case let .exception(exception):
XCTFail("Failed to update collection view using DifferenceKit. exception: \(exception)")
case .noUpdateRequired:
break
case let .success(visibleCellDataList, expectedCellDataList):
XCTAssert(
visibleCellDataList.count == expectedCellDataList.count,
"Update the layout to fit all cells in the screen"
)

let zippedCellDataLists = zip(visibleCellDataList, expectedCellDataList)
for (index, (visibleCellData, expectedCellData)) in zippedCellDataLists.enumerated() {
XCTAssert(
visibleCellData.isContentEqual(to: expectedCellData),
"visible cell data and expected cell data are not in sync at index: \(index)"
)
}
}

print("")

collectionViewUpdater.cleanUp()

expectations[index].fulfill()

DispatchQueue.main.async {
self?.performTest(
index: index + 1,
expectations: expectations,
previousCellDataList: cellDataGeneratorResult.to
)
}
}
)
}
}
}
7 changes: 7 additions & 0 deletions Tests/StressTests/Support/Cell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import UIKit

final class Cell: UICollectionViewCell {
var cellData: CellData?

static let reuseIdentifier = "Cell.reuseIdentifier"
}
33 changes: 33 additions & 0 deletions Tests/StressTests/Support/CellData.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import DifferenceKit
import XCTest

final class CellData: Differentiable {
// MARK: - Properties
let title: String
let subtitle: String

// MARK: - Differentiable
let differenceIdentifier: UUID

// MARK: - Init
init(
differenceIdentifier: UUID,
title: String,
subtitle: String)
{
self.differenceIdentifier = differenceIdentifier
self.title = title
self.subtitle = subtitle
}

// MARK: - Differentiable
func isContentEqual(to source: CellData) -> Bool {
XCTAssert(
self.differenceIdentifier == source.differenceIdentifier,
"We expect the algorythm to compare items only with same `differenceIdentifier`"
)

return self.title == source.title
&& self.subtitle == source.subtitle
}
}
Loading

0 comments on commit cd79607

Please sign in to comment.