diff --git a/iphone/Maps/Core/iCloud/SynchronizationStateResolver.swift b/iphone/Maps/Core/iCloud/SynchronizationStateResolver.swift index 02c834e9f3..8fc8357b2b 100644 --- a/iphone/Maps/Core/iCloud/SynchronizationStateResolver.swift +++ b/iphone/Maps/Core/iCloud/SynchronizationStateResolver.swift @@ -22,7 +22,7 @@ enum IncomingSynchronizationEvent { case didUpdateCloudContents(contents: CloudContents, update: CloudContentsUpdate) } -enum OutgoingSynchronizationEvent { +enum OutgoingSynchronizationEvent: Equatable { case startDownloading(CloudMetadataItem) case createLocalItem(with: CloudMetadataItem) @@ -110,16 +110,21 @@ final class iCloudSynchronizationStateResolver: SynchronizationStateResolver { - all items that are in the local container but not in the cloud container will be created in the cloud container */ localContents.forEach { localItem in - if let cloudItem = cloudContents.firstByName(localItem), localItem.lastModificationDate != cloudItem.lastModificationDate { - events.append(.resolveInitialSynchronizationConflict(localItem)) - events.append(.updateLocalItem(with: cloudItem)) + if let cloudItem = cloudContents.downloaded.firstByName(localItem), localItem.lastModificationDate != cloudItem.lastModificationDate { + if cloudItem.isDownloaded { + events.append(.resolveInitialSynchronizationConflict(localItem)) + events.append(.updateLocalItem(with: cloudItem)) + } else { + events.append(.startDownloading(cloudItem)) + } } } let itemsToCreateInCloudContainer = localContents.filter { !cloudContents.containsByName($0) } let itemsToCreateInLocalContainer = cloudContents.filter { !localContents.containsByName($0) } - events.append(contentsOf: itemsToCreateInCloudContainer.map { .createCloudItem(with: $0) }) - events.append(contentsOf: itemsToCreateInLocalContainer.map { .createLocalItem(with: $0) }) + itemsToCreateInLocalContainer.notDownloaded.forEach { events.append(.startDownloading($0)) } + itemsToCreateInLocalContainer.downloaded.forEach { events.append(.createLocalItem(with: $0)) } + itemsToCreateInCloudContainer.forEach { events.append(.createCloudItem(with: $0)) } events.append(.didFinishInitialSynchronization) isInitialSynchronization = false diff --git a/iphone/Maps/Tests/Core/iCloudTests/DefaultLocalDirectoryMonitorTests/DefaultLocalDirectoryMonitorTests.swift b/iphone/Maps/Tests/Core/iCloudTests/DefaultLocalDirectoryMonitorTests/DefaultLocalDirectoryMonitorTests.swift index b53e2beb94..dee57229a2 100644 --- a/iphone/Maps/Tests/Core/iCloudTests/DefaultLocalDirectoryMonitorTests/DefaultLocalDirectoryMonitorTests.swift +++ b/iphone/Maps/Tests/Core/iCloudTests/DefaultLocalDirectoryMonitorTests/DefaultLocalDirectoryMonitorTests.swift @@ -108,7 +108,7 @@ final class DefaultLocalDirectoryMonitorTests: XCTestCase { let file3URL = tempDirectory.appendingPathComponent("test3.jpg") let correctFileURL = tempDirectory.appendingPathComponent("test.kml") - var fileData = Data(count: 12) + let fileData = Data(count: 12) try! fileData.write(to: file1URL, options: .atomic) try! fileData.write(to: file2URL, options: .atomic) try! fileData.write(to: file3URL, options: .atomic) diff --git a/iphone/Maps/Tests/Core/iCloudTests/SynchronizationStateManagerTests.swift b/iphone/Maps/Tests/Core/iCloudTests/SynchronizationStateManagerTests.swift index a507743a01..600be93ca2 100644 --- a/iphone/Maps/Tests/Core/iCloudTests/SynchronizationStateManagerTests.swift +++ b/iphone/Maps/Tests/Core/iCloudTests/SynchronizationStateManagerTests.swift @@ -98,6 +98,57 @@ final class SynchronizationtateManagerTests: XCTestCase { XCTAssertTrue(outgoingEvents.contains { if case .createCloudItem(_) = $0 { return true } else { return false } }, "Expected creation of cloud item for localFile") } + func testInitialSynchronizationWhenCloudFilesAreNotDownloadedTheDownloadingShouldStart () { + syncStateManager = iCloudSynchronizationStateResolver(isInitialSynchronization: true) + + let localItem1 = LocalMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1)) + let cloudItem1 = CloudMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(2)) + let cloudItem2 = CloudMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(3), isDownloaded: false) + let cloudItem3 = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(4)) + + let localItems = LocalContents([localItem1]) + let cloudItems = CloudContents([cloudItem1, cloudItem2, cloudItem3]) + + outgoingEvents.append(contentsOf: syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))) + outgoingEvents.append(contentsOf: syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))) + + XCTAssertEqual(outgoingEvents.count, 5) + + outgoingEvents.forEach { event in + switch event { + case .resolveInitialSynchronizationConflict(let item): + // copy local file with a new name and replace the original with the cloud file + XCTAssertEqual(item, localItem1) + case .updateLocalItem(let item): + XCTAssertEqual(item, cloudItem1) + case .startDownloading(let item): + XCTAssertEqual(item, cloudItem2) + case .createLocalItem(let item): + XCTAssertEqual(item, cloudItem3) + case .didFinishInitialSynchronization: + XCTAssertTrue(event == outgoingEvents.last) + default: + XCTFail() + } + } + + // update the cloud items with the new downloaded status + let cloudItem2Downloaded = CloudMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(3)) + let newCloudItems = [cloudItem1, cloudItem2Downloaded, cloudItem3] + let cloudUpdate = CloudContentsUpdate(added: [], updated: [cloudItem2Downloaded], removed: []) + outgoingEvents = syncStateManager.resolveEvent(.didUpdateCloudContents(contents: newCloudItems, update: cloudUpdate)) + + XCTAssertEqual(outgoingEvents.count, 1) + outgoingEvents.forEach { event in + switch event { + case .createLocalItem(let item): + XCTAssertEqual(item, cloudItem2Downloaded) + default: + XCTFail() + } + } + } + // MARK: - Test didFinishGathering without errors and after initial synchronization func testDidFinishGatheringWhenCloudAndLocalIsEmpty() { let localItems: LocalContents = []