TM-SGNL-iOS/Signal/test/ViewControllers/MediaGalleryCollectionViewUpdaterTest.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

254 lines
8.8 KiB
Swift

//
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import XCTest
@testable import Signal
class CollectionViewLogger: MediaGalleryCollectionViewUpdaterDelegate {
enum Mod: Equatable, CustomDebugStringConvertible {
var debugDescription: String {
switch self {
case .deleteSections(let sections):
return "delete sections at indexes \(Array(sections))"
case .deleteItems(let paths):
return "delete items at paths \(Array(paths))"
case .insertSections(let sections):
return "insert sections at indexes \(Array(sections))"
case .reloadItems(let paths):
return "reload items at paths \(Array(paths))"
case .reloadSections(let sections):
return "reload sections at indexes \(Array(sections))"
}
}
case deleteSections(IndexSet)
case deleteItems([IndexPath])
case insertSections(IndexSet)
case reloadItems([IndexPath])
case reloadSections(IndexSet)
}
private(set) var log = [Mod]()
func updaterDeleteSections(_ sections: Signal.MediaGallerySectionIndexSet) {
log.append(.deleteSections(sections.indexSet))
}
func updaterDeleteItems(at indexPaths: [Signal.MediaGalleryIndexPath]) {
log.append(.deleteItems(indexPaths.map { $0.indexPath }))
}
func updaterInsertSections(_ sections: Signal.MediaGallerySectionIndexSet) {
log.append(.insertSections(sections.indexSet))
}
func updaterReloadItems(at indexPaths: [Signal.MediaGalleryIndexPath]) {
log.append(.reloadItems(indexPaths.map { $0.indexPath }))
}
func updaterReloadSections(_ sections: Signal.MediaGallerySectionIndexSet) {
log.append(.reloadSections(sections.indexSet))
}
func updaterDidFinish(numberOfSectionsBefore: Int, numberOfSectionsAfter: Int) {
}
}
final class MediaGalleryCollectionViewUpdaterTest: SignalBaseTest {
private var logger: CollectionViewLogger!
private var updater: MediaGalleryCollectionViewUpdater!
override func setUp() {
logger = CollectionViewLogger()
}
func makeUpdater(_ itemCounts: [Int]) {
updater = MediaGalleryCollectionViewUpdater(itemCounts: itemCounts)
updater.delegate = logger
}
func testItemDelete() {
makeUpdater([10, 20, 30])
updater.update([.modify(index: 1, changes: [.removeItem(index: 5)])])
XCTAssertEqual(logger.log, [.deleteItems([IndexPath(item: 5, section: 1)])])
}
func testItemDeleteUsesOriginalSectionIndex() {
makeUpdater([10, 20, 30])
updater.update([.remove(index: 0),
.modify(index: 0, changes: [.removeItem(index: 5)])])
XCTAssertEqual(logger.log, [.deleteItems([IndexPath(item: 5, section: 1)]),
.deleteSections(IndexSet([0]))])
}
func testSectionDelete() {
makeUpdater([10, 20, 30])
updater.update([.remove(index: 1)])
XCTAssertEqual(logger.log, [.deleteSections(IndexSet(integer: 1))])
}
func testSectionPrepend() {
makeUpdater([10, 20, 30])
updater.update([.prepend])
XCTAssertEqual(logger.log, [.insertSections(IndexSet(integer: 0))])
}
func testAllChangesArePrepends() {
makeUpdater([])
updater.update([.prepend, .prepend, .prepend])
XCTAssertEqual(logger.log, [.insertSections(IndexSet([0, 1, 2]))])
}
func testSectionAppend() {
makeUpdater([10])
updater.update([.append])
XCTAssertEqual(logger.log, [.insertSections(IndexSet(integer: 1))])
}
func testItemUpdateUsesOriginalSectionIndex() {
makeUpdater([10, 20, 30])
updater.update([
.prepend,
.modify(index: 1, changes: [.updateItem(index: 5)])])
XCTAssertEqual(logger.log,
[.reloadItems([IndexPath(item: 5, section: 0)]),
.insertSections(IndexSet(integer: 0))])
}
func testUpdateOriginalItemAfterRemovingPredecessor() {
makeUpdater([10, 20, 30])
updater.update([
.modify(index: 0,
changes: [
.removeItem(index: 1),
.updateItem(index: 2) // NOTE: This corresponds to the item with original index 3.
])])
XCTAssertEqual(logger.log,
[.reloadItems([IndexPath(item: 3, section: 0)]),
.deleteItems([IndexPath(item: 1, section: 0)])
])
}
func testUpdateDeletedItem() {
makeUpdater([10, 20, 30])
updater.update([
.modify(index: 0,
changes: [
.updateItem(index: 9),
.removeItem(index: 9)
])])
XCTAssertEqual(logger.log,
[.deleteItems([IndexPath(item: 9, section: 0)])])
}
func testUpdateItems() {
makeUpdater([10, 20, 30])
updater.update([.modify(index: 1, changes: [.updateItem(index: 5)])])
XCTAssertEqual(logger.log, [.reloadItems([IndexPath(item: 5, section: 1)])])
}
func testReloadSection() {
makeUpdater([10, 20, 30])
// After a reload, nothing else you do to this section has an effect.
updater.update([.modify(index: 1,
changes: [
.removeItem(index: 0),
.updateItem(index: 1),
.reloadSection,
.updateItem(index: 1),
.removeItem(index: 2)])])
XCTAssertEqual(logger.log, [.reloadSections(IndexSet(integer: 1))])
}
func testRemoveAll() {
makeUpdater([10, 20, 30])
updater.update([.append,
.prepend,
.removeAll])
XCTAssertEqual(logger.log, [.deleteSections(IndexSet([0, 1, 2]))])
}
func testSurvivingSectionsNotAllAppended() {
makeUpdater([10, 20, 30])
updater.update([.append,
.prepend,
.removeAll,
.append,
.prepend])
XCTAssertEqual(logger.log, [.deleteSections(IndexSet([0, 1, 2])),
.insertSections(IndexSet([0, 1]))])
}
func testAllSurvivingSectionsAppended() {
makeUpdater([10, 20, 30])
updater.update([.removeAll,
.append,
.append])
XCTAssertEqual(logger.log, [.deleteSections(IndexSet([0, 1, 2])),
.insertSections(IndexSet([0, 1]))])
}
func testEverything() {
makeUpdater([10, 20, 30])
updater.update([
.remove(index: 0), // 10,20,30 -> 20,30
.prepend, // 20,30 -> ?,20,30
.remove(index: 1), // ?,20,30 -> ?,30
.append, // ?,30 -> ?,30,?
.modify(index: 1, changes: [
.updateItem(index: 29),
.removeItem(index: 2), // ?,30,? -> ?,29,?
.removeItem(index: 3), // ?,30,? -> ?,28,?
.removeItem(index: 4) // ?,30,? -> ?,27,?
]), // ?,28,?
// These two have no effect because they operate on unreported sections.
.modify(index: 0, changes: [
.removeItem(index: 1),
.updateItem(index: 1)]),
.modify(index: 2, changes: [
.removeItem(index: 1),
.updateItem(index: 1)]),
.append, // ?,28,? -> ?,28,?,?
.modify(index: 3, changes: [.removeItem(index: 0)]), // Modifying a novel section so no change
.remove(index: 3) // ?,28,?,? -> ?,28,?
])
XCTAssertEqual(logger.log,
[.reloadItems([IndexPath(item: 29, section: 2)]),
.deleteItems([IndexPath(item: 2, section: 2),
IndexPath(item: 4, section: 2),
IndexPath(item: 6, section: 2)]),
.deleteSections(IndexSet([0, 1])),
.insertSections(IndexSet([0, 2]))])
}
func testNThIndex1() {
XCTAssertEqual(IndexSet([5, 6, 15, 16]).nthIndex(0), 5)
}
func testNThIndex2() {
XCTAssertEqual(IndexSet([5, 6, 15, 16]).nthIndex(1), 6)
}
func testNThIndex3() {
XCTAssertEqual(IndexSet([5, 6, 15, 16]).nthIndex(2), 15)
}
func testNThIndex4() {
XCTAssertEqual(IndexSet([5, 6, 15, 16]).nthIndex(3), 16)
}
func testNThIndex5() {
XCTAssertEqual(IndexSet([5, 6, 15, 16]).nthIndex(4), nil)
}
}