Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,134 @@
import XCTest
@testable import CoMaps__Debug_
final class DefaultLocalDirectoryMonitorTests: XCTestCase {
let fileManager = FileManager.default
let tempDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
var directoryMonitor: FileSystemDispatchSourceMonitor!
var mockDelegate: LocalDirectoryMonitorDelegateMock!
override func setUpWithError() throws {
try super.setUpWithError()
// Setup with a temporary directory and a mock delegate
directoryMonitor = try FileSystemDispatchSourceMonitor(fileManager: fileManager, directory: tempDirectory)
mockDelegate = LocalDirectoryMonitorDelegateMock()
directoryMonitor.delegate = mockDelegate
}
override func tearDownWithError() throws {
directoryMonitor.stop()
mockDelegate = nil
try? fileManager.removeItem(at: tempDirectory)
try super.tearDownWithError()
}
func testInitialization() {
XCTAssertEqual(directoryMonitor.directory, tempDirectory, "Monitor initialized with incorrect directory.")
XCTAssertTrue(directoryMonitor.state == .stopped, "Monitor should be stopped initially.")
}
func testStartMonitoring() {
let startExpectation = expectation(description: "Start monitoring")
directoryMonitor.start { result in
switch result {
case .success:
XCTAssertTrue(self.directoryMonitor.state == .started, "Monitor should be started.")
case .failure(let error):
XCTFail("Monitoring failed to start with error: \(error)")
}
startExpectation.fulfill()
}
wait(for: [startExpectation], timeout: 5.0)
}
func testStopMonitoring() {
directoryMonitor.start()
directoryMonitor.stop()
XCTAssertTrue(directoryMonitor.state == .stopped, "Monitor should be stopped.")
}
func testPauseAndResumeMonitoring() {
directoryMonitor.start()
directoryMonitor.pause()
XCTAssertTrue(directoryMonitor.state == .paused, "Monitor should be paused.")
directoryMonitor.resume()
XCTAssertTrue(directoryMonitor.state == .started, "Monitor should be started.")
}
func testDelegateDidFinishGathering() {
mockDelegate.didFinishGatheringExpectation = expectation(description: "didFinishGathering called")
directoryMonitor.start()
wait(for: [mockDelegate.didFinishGatheringExpectation!], timeout: 5.0)
}
func testDelegateDidReceiveError() {
mockDelegate.didReceiveErrorExpectation = expectation(description: "didReceiveLocalMonitorError called")
let error = NSError(domain: NSCocoaErrorDomain, code: NSFileNoSuchFileError, userInfo: nil)
directoryMonitor.delegate?.didReceiveLocalMonitorError(error)
wait(for: [mockDelegate.didReceiveErrorExpectation!], timeout: 1.0)
}
func testContentUpdateDetection() {
let startExpectation = expectation(description: "Start monitoring")
let didFinishGatheringExpectation = expectation(description: "didFinishGathering called")
let didUpdateExpectation = expectation(description: "didUpdate called")
mockDelegate.didFinishGatheringExpectation = didFinishGatheringExpectation
mockDelegate.didUpdateExpectation = didUpdateExpectation
directoryMonitor.start { result in
if case .success = result {
XCTAssertTrue(self.directoryMonitor.state == .started, "Monitor should be started.")
}
startExpectation.fulfill()
}
wait(for: [startExpectation], timeout: 5)
let fileURL = tempDirectory.appendingPathComponent("test.kml")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.fileManager.createFile(atPath: fileURL.path, contents: Data(), attributes: nil)
}
wait(for: [didFinishGatheringExpectation, didUpdateExpectation], timeout: 20)
}
func testFileWithIncorrectExtension() {
let startExpectation = expectation(description: "Start monitoring")
let didFinishGatheringExpectation = expectation(description: "didFinishGathering called")
mockDelegate.didFinishGatheringExpectation = didFinishGatheringExpectation
let file1URL = tempDirectory.appendingPathComponent("test.kml.tmp")
let file2URL = tempDirectory.appendingPathComponent("test2.tmp")
let file3URL = tempDirectory.appendingPathComponent("test3.jpg")
let correctFileURL = tempDirectory.appendingPathComponent("test.kml")
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)
try! fileData.write(to: correctFileURL, options: .atomic)
directoryMonitor.start { result in
switch result {
case .failure(let error):
XCTFail("Monitoring failed to start with error: \(error)")
case .success:
XCTAssertTrue(self.directoryMonitor.state == .started, "Monitor should be started.")
startExpectation.fulfill()
}
}
wait(for: [startExpectation, didFinishGatheringExpectation], timeout: 5)
let contents = self.mockDelegate.contents.map { $0.fileUrl }
XCTAssertFalse(contents.contains(file1URL), "File with incorrect extension should not be included")
XCTAssertFalse(contents.contains(file2URL), "File with incorrect extension should not be included")
XCTAssertFalse(contents.contains(file3URL), "File with incorrect extension should not be included")
XCTAssertTrue(contents.contains(correctFileURL), "File with correct extension should be included")
}
}

View file

@ -0,0 +1,24 @@
import XCTest
@testable import CoMaps__Debug_
class LocalDirectoryMonitorDelegateMock: LocalDirectoryMonitorDelegate {
var contents = LocalContents()
var didFinishGatheringExpectation: XCTestExpectation?
var didUpdateExpectation: XCTestExpectation?
var didReceiveErrorExpectation: XCTestExpectation?
func didFinishGathering(_ contents: LocalContents) {
self.contents = contents
didFinishGatheringExpectation?.fulfill()
}
func didUpdate(_ contents: LocalContents, _ update: LocalContentsUpdate) {
self.contents = contents
didUpdateExpectation?.fulfill()
}
func didReceiveLocalMonitorError(_ error: Error) {
didReceiveErrorExpectation?.fulfill()
}
}

View file

@ -0,0 +1,30 @@
@testable import CoMaps__Debug_
extension LocalMetadataItem {
static func stub(fileName: String,
lastModificationDate: TimeInterval) -> LocalMetadataItem {
let item = LocalMetadataItem(fileName: fileName,
fileUrl: URL(string: "url")!,
lastModificationDate: lastModificationDate)
return item
}
}
extension CloudMetadataItem {
static func stub(fileName: String,
lastModificationDate: TimeInterval,
isDownloaded: Bool = true,
percentDownloaded: NSNumber = 100.0,
hasUnresolvedConflicts: Bool = false) -> CloudMetadataItem {
let item = CloudMetadataItem(fileName: fileName,
fileUrl: URL(string: "url")!,
isDownloaded: isDownloaded,
percentDownloaded: percentDownloaded,
lastModificationDate: lastModificationDate,
downloadingError: nil,
uploadingError: nil,
hasUnresolvedConflicts: hasUnresolvedConflicts)
return item
}
}

View file

@ -0,0 +1,573 @@
import XCTest
@testable import CoMaps__Debug_
final class SynchronizationtateManagerTests: XCTestCase {
var syncStateManager: SynchronizationStateResolver!
var outgoingEvents: [OutgoingSynchronizationEvent] = []
override func setUp() {
super.setUp()
syncStateManager = iCloudSynchronizationStateResolver(isInitialSynchronization: false)
}
override func tearDown() {
syncStateManager = nil
outgoingEvents.removeAll()
super.tearDown()
}
// MARK: - Test didFinishGathering without errors and on initial synchronization
func testInitialSynchronization() {
syncStateManager = iCloudSynchronizationStateResolver(isInitialSynchronization: true)
let localItem1 = LocalMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let localItem2 = LocalMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(3)) // Local only item
let cloudItem1 = CloudMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(2)) // Conflicting item
let cloudItem3 = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(4)) // Cloud only item
let localItems: LocalContents = [localItem1, localItem2]
let cloudItems: CloudContents = [cloudItem1, cloudItem3]
outgoingEvents.append(contentsOf: syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems)))
outgoingEvents.append(contentsOf: syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems)))
XCTAssertTrue(outgoingEvents.contains { event in
if case .resolveInitialSynchronizationConflict(let item) = event, item == localItem1 {
return true
}
return false
}, "Expected to resolve initial synchronization conflict for localItem1")
XCTAssertTrue(outgoingEvents.contains { event in
if case .createLocalItem(let item) = event, item == cloudItem3 {
return true
}
return false
}, "Expected to create local item for cloudItem3")
XCTAssertTrue(outgoingEvents.contains { event in
if case .createCloudItem(let item) = event, item == localItem2 {
return true
}
return false
}, "Expected to create cloud item for localItem2")
XCTAssertTrue(outgoingEvents.contains { event in
if case .didFinishInitialSynchronization = event {
return true
}
return false
}, "Expected to finish initial synchronization")
}
func testInitialSynchronizationWithNewerCloudItem() {
syncStateManager = iCloudSynchronizationStateResolver(isInitialSynchronization: true)
let localItem = LocalMetadataItem.stub(fileName: "file", lastModificationDate: TimeInterval(1))
let cloudItem = CloudMetadataItem.stub(fileName: "file", lastModificationDate: TimeInterval(2))
outgoingEvents.append(contentsOf: syncStateManager.resolveEvent(.didFinishGatheringLocalContents([localItem])))
outgoingEvents.append(contentsOf: syncStateManager.resolveEvent(.didFinishGatheringCloudContents([cloudItem])))
XCTAssertTrue(outgoingEvents.contains { if case .resolveInitialSynchronizationConflict(_) = $0 { return true } else { return false } }, "Expected conflict resolution for a newer cloud item")
}
func testInitialSynchronizationWithNewerLocalItem() {
syncStateManager = iCloudSynchronizationStateResolver(isInitialSynchronization: true)
let localItem = LocalMetadataItem.stub(fileName: "file", lastModificationDate: TimeInterval(2))
let cloudItem = CloudMetadataItem.stub(fileName: "file", lastModificationDate: TimeInterval(1))
outgoingEvents.append(contentsOf: syncStateManager.resolveEvent(.didFinishGatheringLocalContents([localItem])))
outgoingEvents.append(contentsOf: syncStateManager.resolveEvent(.didFinishGatheringCloudContents([cloudItem])))
XCTAssertTrue(outgoingEvents.contains { if case .resolveInitialSynchronizationConflict(_) = $0 { return true } else { return false } }, "Expected conflict resolution for a newer local item")
}
func testInitialSynchronizationWithNonConflictingItems() {
syncStateManager = iCloudSynchronizationStateResolver(isInitialSynchronization: true)
let localItem = LocalMetadataItem.stub(fileName: "localFile", lastModificationDate: TimeInterval(1))
let cloudItem = CloudMetadataItem.stub(fileName: "cloudFile", lastModificationDate: TimeInterval(2))
outgoingEvents.append(contentsOf: syncStateManager.resolveEvent(.didFinishGatheringLocalContents([localItem])))
outgoingEvents.append(contentsOf: syncStateManager.resolveEvent(.didFinishGatheringCloudContents([cloudItem])))
XCTAssertTrue(outgoingEvents.contains { if case .createLocalItem(_) = $0 { return true } else { return false } }, "Expected creation of local item for cloudFile")
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, percentDownloaded: 0.0)
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 = []
let cloudItems: CloudContents = []
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 0)
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
XCTAssertEqual(outgoingEvents.count, 0)
}
func testDidFinishGatheringWhenOnlyCloudIsEmpty() {
let localItem1 = LocalMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let localItem2 = LocalMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let localItem3 = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let localItems: LocalContents = LocalContents([localItem1, localItem2, localItem3])
let cloudItems: CloudContents = []
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 0)
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
outgoingEvents.forEach { event in
switch event {
case .createCloudItem(let item):
XCTAssertTrue(localItems.containsByName(item))
default:
XCTFail()
}
}
}
func testDidFinishGatheringWhenOnlyLocalIsEmpty() {
let cloudItem1 = CloudMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let cloudItem2 = CloudMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let cloudItem3 = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let localItems = LocalContents()
let cloudItems = CloudContents([cloudItem1, cloudItem2, cloudItem3])
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 0)
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
outgoingEvents.forEach { event in
switch event {
case .createLocalItem(let item):
XCTAssertTrue(cloudItems.containsByName(item))
default:
XCTFail()
}
}
}
func testDidFinishGatheringWhenTreCloudIsEmpty() {
let localItem1 = LocalMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let localItem2 = LocalMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let localItem3 = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let localItems = [localItem1, localItem2, localItem3]
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents([]))
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
XCTAssertEqual(outgoingEvents.count, 3)
outgoingEvents.forEach { event in
switch event {
case .createCloudItem(let item):
XCTAssertTrue(localItems.containsByName(item))
default:
XCTFail()
}
}
}
func testDidFinishGatheringWhenLocalIsEmpty() {
let cloudItem1 = CloudMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let cloudItem2 = CloudMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let cloudItem3 = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let localItems = LocalContents()
let cloudItems = CloudContents([cloudItem1, cloudItem2, cloudItem3])
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 0)
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
XCTAssertEqual(outgoingEvents.count, 3)
outgoingEvents.forEach { event in
switch event {
case .createLocalItem(let item):
XCTAssertTrue(cloudItems.containsByName(item))
default:
XCTFail()
}
}
}
func testDidFinishGatheringWhenLocalAndCloudAreNotEmptyAndAllFilesEqual() {
let localItem1 = LocalMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let localItem2 = LocalMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let localItem3 = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let cloudItem1 = CloudMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let cloudItem2 = CloudMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let cloudItem3 = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let localItems = LocalContents([localItem1, localItem2, localItem3])
let cloudItems = CloudContents([cloudItem1, cloudItem2, cloudItem3])
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
XCTAssertEqual(outgoingEvents.count, 0)
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 0)
}
func testDidFinishGatheringWhenLocalAndCloudAreNotEmptyAndSomeLocalItemsAreNewer() {
let localItem1 = LocalMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let localItem2 = LocalMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(3))
let localItem3 = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(4))
let cloudItem1 = CloudMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let cloudItem2 = CloudMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let cloudItem3 = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let localItems = LocalContents([localItem1, localItem2, localItem3])
let cloudItems = CloudContents([cloudItem1, cloudItem2, cloudItem3])
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
XCTAssertEqual(outgoingEvents.count, 0)
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 2)
outgoingEvents.forEach { event in
switch event {
case .updateCloudItem(let item):
XCTAssertTrue([localItem2, localItem3].containsByName(item))
default:
XCTFail()
}
}
}
func testDidFinishGatheringWhenLocalAndCloudAreNotEmptyAndSomeCloudItemsAreNewer() {
let localItem1 = LocalMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let localItem2 = LocalMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let localItem3 = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let cloudItem1 = CloudMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(4))
let cloudItem2 = CloudMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let cloudItem3 = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(7))
let localItems = LocalContents([localItem1, localItem2, localItem3])
let cloudItems = CloudContents([cloudItem1, cloudItem2, cloudItem3])
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
XCTAssertEqual(outgoingEvents.count, 0)
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 2)
outgoingEvents.forEach { event in
switch event {
case .updateLocalItem(let item):
XCTAssertTrue([cloudItem1, cloudItem3].containsByName(item))
default:
XCTFail()
}
}
}
func testDidFinishGatheringWhenCloudFileNewerThanLocal() {
let localItem = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let cloudItem = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(8))
let localItems = LocalContents([localItem])
let cloudItems = CloudContents([cloudItem])
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
XCTAssertEqual(outgoingEvents.count, 0)
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 1)
outgoingEvents.forEach { event in
switch event {
case .updateLocalItem(let item):
XCTAssertEqual(item, cloudItem)
default:
XCTFail()
}
}
}
func testDidFinishGatheringWhenCloudHaveSameFileBothInTrashedAndNotAndTrashedBotLocalIsNewer() {
let localItem3 = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(9))
let cloudItem3 = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(2))
let cloudItem3Trashed = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(6))
let localItems = LocalContents([localItem3])
let cloudItems = CloudContents([cloudItem3, cloudItem3Trashed])
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
XCTAssertEqual(outgoingEvents.count, 0)
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 1)
outgoingEvents.forEach { event in
switch event {
case .updateCloudItem(let item):
XCTAssertEqual(item, localItem3)
default:
XCTFail()
}
}
}
// MARK: - Test didUpdateLocalContents
func testDidUpdateLocalContentsWhenContentWasNotChanged() {
let localItem1 = LocalMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let localItem2 = LocalMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let localItem3 = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let localItems = LocalContents([localItem1, localItem2, localItem3])
let cloudItems = CloudContents([])
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 3)
let newLocalItems = LocalContents([localItem1, localItem2, localItem3])
let update = LocalContentsUpdate(added: [], updated: [], removed: [])
outgoingEvents = syncStateManager.resolveEvent(.didUpdateLocalContents(contents: newLocalItems, update: update))
XCTAssertEqual(outgoingEvents.count, 0)
}
func testDidUpdateLocalContentsWhenNewLocalItemWasAdded() {
let localItem1 = LocalMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let localItem2 = LocalMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let localItem3 = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let cloudItem1 = CloudMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let cloudItem2 = CloudMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let cloudItem3 = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let localItems = LocalContents([localItem1, localItem2, localItem3])
let cloudItems = CloudContents([cloudItem1, cloudItem2, cloudItem3])
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 0)
let localItem4 = LocalMetadataItem.stub(fileName: "file4", lastModificationDate: TimeInterval(4))
let newLocalItems = LocalContents([localItem1, localItem2, localItem3])
let update = LocalContentsUpdate(added: [localItem4], updated: [], removed: [])
outgoingEvents = syncStateManager.resolveEvent(.didUpdateLocalContents(contents: newLocalItems, update: update))
XCTAssertEqual(outgoingEvents.count, 1)
outgoingEvents.forEach { event in
switch event {
case .createCloudItem(let item):
XCTAssertEqual(item, localItem4)
default:
XCTFail()
}
}
}
func testDidUpdateLocalContentsWhenLocalItemWasUpdated() {
let localItem1 = LocalMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let localItem2 = LocalMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let localItem3 = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let cloudItem1 = CloudMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let cloudItem2 = CloudMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let cloudItem3 = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let localItems = LocalContents([localItem1, localItem2, localItem3])
let cloudItems = CloudContents([cloudItem1, cloudItem2, cloudItem3])
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 0)
let localItem2Updated = LocalMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(3))
let localItem3Updated = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(4))
let newLocalItems = LocalContents([localItem1, localItem2Updated, localItem3Updated])
let update = LocalContentsUpdate(added: [], updated: [localItem2Updated, localItem3Updated], removed: [])
outgoingEvents = syncStateManager.resolveEvent(.didUpdateLocalContents(contents: newLocalItems, update: update))
XCTAssertEqual(outgoingEvents.count, 2)
outgoingEvents.forEach { event in
switch event {
case .updateCloudItem(let item):
XCTAssertTrue([localItem2Updated, localItem3Updated].containsByName(item))
default:
XCTFail()
}
}
}
func testDidUpdateLocalContentsWhenLocalItemWasRemoved() {
let localItem1 = LocalMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let localItem2 = LocalMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let localItem3 = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let cloudItem1 = CloudMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let cloudItem2 = CloudMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let cloudItem3 = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let localItems = LocalContents([localItem1, localItem2, localItem3])
let cloudItems = CloudContents([cloudItem1, cloudItem2, cloudItem3])
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 0)
let newLocalItems = LocalContents([localItem1, localItem2])
let update = LocalContentsUpdate(added: [], updated: [], removed: [localItem3])
outgoingEvents = syncStateManager.resolveEvent(.didUpdateLocalContents(contents: newLocalItems, update: update))
XCTAssertEqual(outgoingEvents.count, 1)
outgoingEvents.forEach { event in
switch event {
case .removeCloudItem(let item):
XCTAssertEqual(item, cloudItem3)
default:
XCTFail()
}
}
}
// TODO: Test didUpdateCloudContents
func testDidUpdateCloudContentsWhenContentWasNotChanged() {
let localItem1 = LocalMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let localItem2 = LocalMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let localItem3 = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let cloudItem1 = CloudMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let cloudItem2 = CloudMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let cloudItem3 = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let localItems = LocalContents([localItem1, localItem2, localItem3])
let cloudItems = CloudContents([cloudItem1, cloudItem2, cloudItem3])
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 0)
let newCloudItems = CloudContents([cloudItem1, cloudItem2, cloudItem3])
let update = CloudContentsUpdate(added: [], updated: [], removed: [])
outgoingEvents = syncStateManager.resolveEvent(.didUpdateCloudContents(contents: newCloudItems, update: update))
XCTAssertEqual(outgoingEvents.count, 0)
}
func testDidUpdateCloudContentsWhenContentItemWasAdded() {
let localItem1 = LocalMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let localItem2 = LocalMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let localItem3 = LocalMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let cloudItem1 = CloudMetadataItem.stub(fileName: "file1", lastModificationDate: TimeInterval(1))
let cloudItem2 = CloudMetadataItem.stub(fileName: "file2", lastModificationDate: TimeInterval(2))
let cloudItem3 = CloudMetadataItem.stub(fileName: "file3", lastModificationDate: TimeInterval(3))
let localItems = LocalContents([localItem1, localItem2, localItem3])
var cloudItems = CloudContents([cloudItem1, cloudItem2, cloudItem3])
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringLocalContents(localItems))
outgoingEvents = syncStateManager.resolveEvent(.didFinishGatheringCloudContents(cloudItems))
XCTAssertEqual(outgoingEvents.count, 0)
var cloudItem4 = CloudMetadataItem.stub(fileName: "file4", lastModificationDate: TimeInterval(3), isDownloaded: false, percentDownloaded: 0.0)
cloudItems.append(cloudItem4)
var update = CloudContentsUpdate(added: [cloudItem4], updated: [], removed: [])
outgoingEvents = syncStateManager.resolveEvent(.didUpdateCloudContents(contents: cloudItems, update: update))
XCTAssertEqual(outgoingEvents.count, 1)
outgoingEvents.forEach { event in
switch event {
case .startDownloading(let cloudMetadataItem):
XCTAssertEqual(cloudMetadataItem, cloudItem4)
default:
XCTFail()
}
}
cloudItem4.percentDownloaded = 50.0
update = CloudContentsUpdate(added: [], updated: [cloudItem4], removed: [])
outgoingEvents = syncStateManager.resolveEvent(.didUpdateCloudContents(contents: cloudItems, update: update))
XCTAssertEqual(outgoingEvents.count, 0)
cloudItem4.percentDownloaded = 100.0
update = CloudContentsUpdate(added: [], updated: [cloudItem4], removed: [])
outgoingEvents = syncStateManager.resolveEvent(.didUpdateCloudContents(contents: cloudItems, update: update))
XCTAssertEqual(outgoingEvents.count, 0)
cloudItem4.isDownloaded = true
// recreate collection
cloudItems = [cloudItem1, cloudItem2, cloudItem3, cloudItem4]
update = CloudContentsUpdate(added: [], updated: [cloudItem4], removed: [])
outgoingEvents = syncStateManager.resolveEvent(.didUpdateCloudContents(contents: cloudItems, update: update))
XCTAssertEqual(outgoingEvents.count, 1)
outgoingEvents.forEach { event in
switch event {
case .createLocalItem(let cloudMetadataItem):
XCTAssertEqual(cloudMetadataItem, cloudItem4)
default:
XCTFail()
}
}
}
}

View file

@ -0,0 +1,13 @@
class FileManagerMock: FileManager {
var stubUbiquityIdentityToken: UbiquityIdentityToken?
var shouldReturnContainerURL: Bool = true
var stubCloudDirectory: URL?
override var ubiquityIdentityToken: (any UbiquityIdentityToken)? {
return stubUbiquityIdentityToken
}
override func url(forUbiquityContainerIdentifier identifier: String?) -> URL? {
return shouldReturnContainerURL ? stubCloudDirectory ?? URL(fileURLWithPath: NSTemporaryDirectory()) : nil
}
}

View file

@ -0,0 +1,31 @@
import XCTest
@testable import CoMaps__Debug_
class UbiquitousDirectoryMonitorDelegateMock: CloudDirectoryMonitorDelegate {
var didFinishGatheringCalled = false
var didUpdateCalled = false
var didReceiveErrorCalled = false
var didFinishGatheringExpectation: XCTestExpectation?
var didUpdateExpectation: XCTestExpectation?
var didReceiveErrorExpectation: XCTestExpectation?
var contents = CloudContents()
func didFinishGathering(_ contents: CloudContents) {
didFinishGatheringCalled = true
didFinishGatheringExpectation?.fulfill()
self.contents = contents
}
func didUpdate(_ contents: CloudContents, _ update: CloudContentsUpdate) {
didUpdateCalled = true
didUpdateExpectation?.fulfill()
self.contents = contents
}
func didReceiveCloudMonitorError(_ error: Error) {
didReceiveErrorCalled = true
didReceiveErrorExpectation?.fulfill()
}
}

View file

@ -0,0 +1,40 @@
import XCTest
@testable import CoMaps__Debug_
typealias UbiquityIdentityToken = NSCoding & NSCopying & NSObjectProtocol
class iCloudDirectoryMonitorTests: XCTestCase {
var cloudMonitor: iCloudDocumentsMonitor!
var mockFileManager: FileManagerMock!
var mockDelegate: UbiquitousDirectoryMonitorDelegateMock!
var cloudContainerIdentifier: String = "iCloud.app.comaps.debug"
override func setUp() {
super.setUp()
mockFileManager = FileManagerMock()
mockDelegate = UbiquitousDirectoryMonitorDelegateMock()
cloudMonitor = iCloudDocumentsMonitor(fileManager: mockFileManager, cloudContainerIdentifier: cloudContainerIdentifier, fileType: .kml)
cloudMonitor.delegate = mockDelegate
}
override func tearDown() {
cloudMonitor = nil
mockFileManager = nil
mockDelegate = nil
super.tearDown()
}
func testInitialization() {
XCTAssertNotNil(cloudMonitor)
XCTAssertEqual(cloudMonitor.containerIdentifier, cloudContainerIdentifier)
}
func testCloudAvailability() {
mockFileManager.stubUbiquityIdentityToken = NSString(string: "mockToken")
XCTAssertTrue(cloudMonitor.isCloudAvailable())
mockFileManager.stubUbiquityIdentityToken = nil
XCTAssertFalse(cloudMonitor.isCloudAvailable())
}
}