From b2efc813d598652519ed95b2fb2e8259a158a88c Mon Sep 17 00:00:00 2001 From: Tieme van Veen Date: Thu, 8 Oct 2020 14:59:47 +0200 Subject: [PATCH 1/2] Add tests that show failing duplicate identifier detection --- Tests/RxDataSourcesTests/AlgorithmTests.swift | 106 +++++++++++++++++- 1 file changed, 100 insertions(+), 6 deletions(-) diff --git a/Tests/RxDataSourcesTests/AlgorithmTests.swift b/Tests/RxDataSourcesTests/AlgorithmTests.swift index e90a464c..844b537f 100644 --- a/Tests/RxDataSourcesTests/AlgorithmTests.swift +++ b/Tests/RxDataSourcesTests/AlgorithmTests.swift @@ -320,8 +320,77 @@ extension AlgorithmTests { // errors extension AlgorithmTests { - func testThrowsErrorOnDuplicateItem() { - let initial: [s] = [ + func testThrowsErrorOnDuplicateItem_inInitialSections() { + let sections: [s] = [ + s(1, [ + i(1111, ""), + i(1111, "") + ]) + ] + + do { + _ = try Diff.differencesForSectionedView(initialSections: sections, finalSections: sections) + XCTFail("Should throw exception") + } + catch let exception { + guard case let .duplicateItem(item) = exception as! Diff.Error else { + XCTFail("Not required error") + return + } + + XCTAssertEqual(item as! i, i(1111, "")) + } + } + + func testThrowsErrorOnDuplicateItem_inFinalSections() { + let final: [s] = [ + s(1, [ + i(1111, ""), + i(1111, "") + ]) + ] + + do { + _ = try Diff.differencesForSectionedView(initialSections: [], finalSections: final) + XCTFail("Should throw exception") + } + catch let exception { + guard case let .duplicateItem(item) = exception as! Diff.Error else { + XCTFail("Not required error") + return + } + + XCTAssertEqual(item as! i, i(1111, "")) + } + } + + func testThrowsErrorOnDuplicateItemInDifferentSection_inInitialSections() { + let sections: [s] = [ + s(1, [ + i(1111, "") + ]), + s(2, [ + i(1111, "") + ]) + + ] + + do { + _ = try Diff.differencesForSectionedView(initialSections: sections, finalSections: sections) + XCTFail("Should throw exception") + } + catch let exception { + guard case let .duplicateItem(item) = exception as! Diff.Error else { + XCTFail("Not required error") + return + } + + XCTAssertEqual(item as! i, i(1111, "")) + } + } + + func testThrowsErrorOnDuplicateItemInDifferentSection_inFinalSections() { + let final: [s] = [ s(1, [ i(1111, "") ]), @@ -332,7 +401,7 @@ extension AlgorithmTests { ] do { - _ = try Diff.differencesForSectionedView(initialSections: initial, finalSections: initial) + _ = try Diff.differencesForSectionedView(initialSections: [], finalSections: final) XCTFail("Should throw exception") } catch let exception { @@ -345,8 +414,33 @@ extension AlgorithmTests { } } - func testThrowsErrorOnDuplicateSection() { - let initial: [s] = [ + func testThrowsErrorOnDuplicateSection_inInitialSections() { + let sections: [s] = [ + s(1, [ + i(1111, "") + ]), + s(1, [ + i(1112, "") + ]) + ] + + do { + _ = try Diff.differencesForSectionedView(initialSections: sections, finalSections: sections) + XCTFail("Should throw exception") + } + catch let exception { + guard case let .duplicateSection(section) = exception as! Diff.Error else { + XCTFail("Not required error") + return + } + + XCTAssertEqual(section as! s, s(1, [ + i(1112, "") + ])) + } + } + func testThrowsErrorOnDuplicateSection_inFinalSections() { + let final: [s] = [ s(1, [ i(1111, "") ]), @@ -357,7 +451,7 @@ extension AlgorithmTests { ] do { - _ = try Diff.differencesForSectionedView(initialSections: initial, finalSections: initial) + _ = try Diff.differencesForSectionedView(initialSections: [], finalSections: final) XCTFail("Should throw exception") } catch let exception { From c1f8a49e1c8da697e1022af70937c59503a209cc Mon Sep 17 00:00:00 2001 From: Tieme van Veen Date: Thu, 8 Oct 2020 15:59:10 +0200 Subject: [PATCH 2/2] Fix failing duplicate identity detection --- CHANGELOG.md | 1 + Sources/Differentiator/Diff.swift | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 566cf1b3..16c2db3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. #### Master * Added support of mutable CellViewModels. +* Fixes failing duplicate identity detection ## [4.0.1](https://github.com/RxSwiftCommunity/RxDataSources/releases/tag/4.0.1) diff --git a/Sources/Differentiator/Diff.swift b/Sources/Differentiator/Diff.swift index 7e4b8712..a393adb0 100644 --- a/Sources/Differentiator/Diff.swift +++ b/Sources/Differentiator/Diff.swift @@ -199,11 +199,19 @@ public enum Diff { dictionary[key] = i } + var finalKeys = Set>() + for (i, items) in finalItemCache.enumerated() { for j in 0 ..< items.count { let item = items[j] var identity = item.identity let key = OptimizedIdentity(&identity) + + if finalKeys.contains(key) { + throw Error.duplicateItem(item: item) + } + finalKeys.insert(key) + guard let initialItemPathIndex = dictionary[key] else { continue } @@ -518,8 +526,16 @@ public enum Diff { var initialSectionData = ContiguousArray(repeating: SectionAssociatedData.initial, count: initialSections.count) var finalSectionData = ContiguousArray(repeating: SectionAssociatedData.initial, count: finalSections.count) + var finalSectionIdentities = Set() + for (i, section) in finalSections.enumerated() { finalSectionData[i].itemCount = finalSections[i].items.count + + if finalSectionIdentities.contains(section.identity) { + throw Error.duplicateSection(section: section) + } + finalSectionIdentities.insert(section.identity) + guard let initialSectionIndex = initialSectionIndexes[section.identity] else { continue }