diff --git a/Sources/WebDAV/WebDAV+DiskCache.swift b/Sources/WebDAV/WebDAV+DiskCache.swift index 6fd6a90..6114620 100644 --- a/Sources/WebDAV/WebDAV+DiskCache.swift +++ b/Sources/WebDAV/WebDAV+DiskCache.swift @@ -23,9 +23,16 @@ public extension WebDAV { func cachedDataURL(forItemAtPath path: String, account: A) -> URL? { guard let encodedDescription = UnwrappedAccount(account: account)?.encodedDescription, let caches = cacheFolder else { return nil } - return caches - .appendingPathComponent(encodedDescription) - .appendingPathComponent(path.trimmingCharacters(in: AccountPath.slash)) + let trimmedPath = path.trimmingCharacters(in: AccountPath.slash) + + if trimmedPath.isEmpty { + return caches + .appendingPathComponent(encodedDescription) + } else { + return caches + .appendingPathComponent(encodedDescription) + .appendingPathComponent(trimmedPath) + } } /// Get the local cached data URL for the item at the specified path if there is cached data there. @@ -178,13 +185,14 @@ extension WebDAV { func cleanupDiskCache(at path: String, account: A, files: [WebDAVFile]) throws { let fm = FileManager.default - guard let url = cachedDataURL(forItemAtPath: path, account: account),fm.fileExists(atPath: url.path) else { return } + guard let url = cachedDataURL(forItemAtPath: path, account: account), + fm.fileExists(atPath: url.path) else { return } - let goodFilePaths = Set(files.compactMap { cachedDataURL(forItemAtPath: $0.path, account: account)?.path }) + let goodFilePaths = files.compactMap { cachedDataURL(forItemAtPath: $0.path, account: account)?.path } let infoPlist = filesCacheURL?.path for path in try fm.contentsOfDirectory(atPath: url.path).map({ url.appendingPathComponent($0).path }) - where !goodFilePaths.contains(path) + where !goodFilePaths.contains(where: { path.starts(with: $0) }) && path != infoPlist { try fm.removeItem(atPath: path) } diff --git a/Sources/WebDAV/WebDAV.swift b/Sources/WebDAV/WebDAV.swift index 6ad38c6..eb54d73 100644 --- a/Sources/WebDAV/WebDAV.swift +++ b/Sources/WebDAV/WebDAV.swift @@ -541,7 +541,7 @@ extension WebDAV { for (key, _) in filesCache where key.path != directory && key.path.starts(with: directory) - && !files.contains(where: { key.path.starts(with: $0.path) }) { + && !files.contains(where: { key.path.starts(with: $0.path.trimmingCharacters(in: AccountPath.slash)) }) { filesCache.removeValue(forKey: key) changed = true } diff --git a/Tests/WebDAVTests/WebDAVTests.swift b/Tests/WebDAVTests/WebDAVTests.swift index 3556906..2f0063e 100644 --- a/Tests/WebDAVTests/WebDAVTests.swift +++ b/Tests/WebDAVTests/WebDAVTests.swift @@ -356,29 +356,46 @@ final class WebDAVTests: XCTestCase { func testCleanupFilesCacheRoot() { guard let (account, password) = getAccount() else { return XCTFail() } + let listRealDirExpectation = XCTestExpectation(description: "List files from real directory.") let expectation = XCTestExpectation(description: "List files from WebDAV") - let path = UUID().uuidString - let accountPath = AccountPath(account: account, path: path) + let realDir = createFolder(account: account, password: password) + let realDirAccountPath = AccountPath(account: account, path: realDir) + + let fakeDir = UUID().uuidString + let fakeDirAccountPath = AccountPath(account: account, path: fakeDir) + + // Load real file into cache + uploadData(to: realDir, account: account, password: password) + webDAV.listFiles(atPath: realDir, account: account, password: password, caching: .doNotReturnCachedResult) { _, _ in + listRealDirExpectation.fulfill() + } + + wait(for: [listRealDirExpectation], timeout: 10000.0) + + XCTAssertNotNil(webDAV.filesCache[realDirAccountPath]) // Place fake cache - webDAV.filesCache[accountPath] = [WebDAVFile(path: path + "/fake.txt", id: "0", isDirectory: true, lastModified: Date(), size: 0, etag: "0")] - XCTAssertNotNil(webDAV.filesCache[accountPath]) + webDAV.filesCache[fakeDirAccountPath] = [WebDAVFile(path: fakeDir + "/fake.txt", id: "0", isDirectory: true, lastModified: Date(), size: 0, etag: "0")] + XCTAssertNotNil(webDAV.filesCache[fakeDirAccountPath]) // List files - webDAV.listFiles(atPath: "/", account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in + webDAV.listFiles(atPath: "/", account: account, password: password, caching: .doNotReturnCachedResult) { files, error in XCTAssertNotNil(files) XCTAssertNil(error) expectation.fulfill() } - wait(for: [expectation], timeout: 10.0) + wait(for: [expectation], timeout: 10000.0) // Check that the fake cached file was cleaned up + XCTAssertNil(webDAV.filesCache[fakeDirAccountPath]) + // Check that the real file still exists + XCTAssertNotNil(webDAV.filesCache[realDirAccountPath]) - XCTAssertNil(webDAV.filesCache[accountPath]) + deleteFile(path: realDir, account: account, password: password) } func testCleanupFilesCacheSubdirectory() { @@ -397,7 +414,7 @@ final class WebDAVTests: XCTestCase { // List files - webDAV.listFiles(atPath: folder, account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in + webDAV.listFiles(atPath: folder, account: account, password: password, foldersFirst: false, caching: .doNotReturnCachedResult) { files, error in XCTAssertNotNil(files) XCTAssertNil(error) expectation.fulfill() @@ -413,23 +430,31 @@ final class WebDAVTests: XCTestCase { //MARK: Disk Cache Cleanup - func testCleanupDiskCacheFile() { + func testCleanupDiskCacheFileRoot() { guard let (account, password) = getAccount() else { return XCTFail() } let expectation = XCTestExpectation(description: "List files from WebDAV") // Add dummy file to disk cache - let path = UUID().uuidString + ".txt" + let dummyPath = UUID().uuidString + ".txt" let data = UUID().uuidString.data(using: .utf8)! - let tempFileURL = webDAV.cachedDataURL(forItemAtPath: path, account: account)! - XCTAssertNoThrow(try webDAV.saveDataToDiskCache(data, url: tempFileURL)) + let dummyFileURL = webDAV.cachedDataURL(forItemAtPath: dummyPath, account: account)! + XCTAssertNoThrow(try webDAV.saveDataToDiskCache(data, url: dummyFileURL)) - XCTAssert(FileManager.default.fileExists(atPath: tempFileURL.path)) + XCTAssert(FileManager.default.fileExists(atPath: dummyFileURL.path)) + + // Create real file + + let realFile = uploadData(account: account, password: password) + let realFileURL = webDAV.cachedDataURL(forItemAtPath: realFile.fileName, account: account)! + downloadData(path: realFile.fileName, account: account, password: password) + + XCTAssert(FileManager.default.fileExists(atPath: realFileURL.path)) // List files - webDAV.listFiles(atPath: "/", account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in + webDAV.listFiles(atPath: "/", account: account, password: password, foldersFirst: false, caching: .doNotReturnCachedResult) { files, error in XCTAssertNotNil(files) XCTAssertNil(error) expectation.fulfill() @@ -438,8 +463,11 @@ final class WebDAVTests: XCTestCase { wait(for: [expectation], timeout: 10.0) // Check that the fake cached file was cleaned up + XCTAssertFalse(FileManager.default.fileExists(atPath: dummyFileURL.path)) + // Check that the real file still exists + XCTAssert(FileManager.default.fileExists(atPath: realFileURL.path)) - XCTAssertFalse(FileManager.default.fileExists(atPath: tempFileURL.path)) + deleteFile(path: realFile.fileName, account: account, password: password) } func testCleanupDiskCacheFolder() { @@ -450,13 +478,23 @@ final class WebDAVTests: XCTestCase { // Add dummy folder to disk cache let path = UUID().uuidString - let tempFileURL = webDAV.cachedDataURL(forItemAtPath: path, account: account)! - XCTAssertNoThrow(try FileManager.default.createDirectory(at: tempFileURL, withIntermediateDirectories: true)) - XCTAssert(FileManager.default.fileExists(atPath: tempFileURL.path)) + let dummyFileURL = webDAV.cachedDataURL(forItemAtPath: path, account: account)! + XCTAssertNoThrow(try FileManager.default.createDirectory(at: dummyFileURL, withIntermediateDirectories: true)) + + XCTAssert(FileManager.default.fileExists(atPath: dummyFileURL.path)) + + // Create real folder + + let folder = createFolder(account: account, password: password) + let realFile = uploadData(to: folder, account: account, password: password) + downloadData(path: realFile.fileName, account: account, password: password) + let realFileURL = webDAV.cachedDataURL(forItemAtPath: realFile.fileName, account: account)! + + XCTAssert(FileManager.default.fileExists(atPath: realFileURL.path)) // List files - webDAV.listFiles(atPath: "/", account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in + webDAV.listFiles(atPath: "/", account: account, password: password, foldersFirst: false, caching: .doNotReturnCachedResult) { files, error in XCTAssertNotNil(files) XCTAssertNil(error) expectation.fulfill() @@ -465,8 +503,11 @@ final class WebDAVTests: XCTestCase { wait(for: [expectation], timeout: 10.0) // Check that the fake cached folder was cleaned up + XCTAssertFalse(FileManager.default.fileExists(atPath: dummyFileURL.path)) + // Check that the real file still exists + XCTAssert(FileManager.default.fileExists(atPath: realFileURL.path)) - XCTAssertFalse(FileManager.default.fileExists(atPath: tempFileURL.path)) + deleteFile(path: folder, account: account, password: password) } func testCleanupDiskCacheWithGoodFile() { @@ -496,7 +537,7 @@ final class WebDAVTests: XCTestCase { // List files - webDAV.listFiles(atPath: directory.path, account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in + webDAV.listFiles(atPath: directory.path, account: account, password: password, caching: .doNotReturnCachedResult) { files, error in XCTAssertNotNil(files) XCTAssertNil(error) expectation.fulfill() @@ -511,6 +552,34 @@ final class WebDAVTests: XCTestCase { XCTAssertNoThrow(try webDAV.deleteCachedData(forItemAtPath: imagePath, account: account)) } + func testCleanupDiskCacheKeepingThumbnail() { + guard let (account, password) = getAccount() else { return XCTFail() } + guard let imagePath = ProcessInfo.processInfo.environment["image_path"] else { + return XCTFail("You need to set the image_path in the environment.") + } + + let expectation = XCTestExpectation(description: "List files from WebDAV") + + // Download Thumbnail + downloadThumbnail(imagePath: imagePath, account: account, password: password) + let cachedThumbnailURL = webDAV.cachedThumbnailURL(forItemAtPath: imagePath, account: account, with: .default)! + XCTAssert(FileManager.default.fileExists(atPath: cachedThumbnailURL.path)) + + // List files + + let imageURL = URL(fileURLWithPath: imagePath, isDirectory: false) + let directory = imageURL.deletingLastPathComponent() + + webDAV.listFiles(atPath: directory.path, account: account, password: password, caching: .doNotReturnCachedResult) { _, _ in + expectation.fulfill() + } + + wait(for: [expectation], timeout: 10.0) + + // Check that the cached thumbnail still exists + XCTAssert(FileManager.default.fileExists(atPath: cachedThumbnailURL.path)) + } + //MARK: Images func testDownloadImage() { @@ -719,11 +788,17 @@ final class WebDAVTests: XCTestCase { wait(for: [expectation], timeout: 10.0) } - private func uploadData(account: SimpleAccount, password: String) -> (name: String, fileName: String, content: String) { + @discardableResult + private func uploadData(to folder: String = "", account: SimpleAccount, password: String) -> (name: String, fileName: String, content: String) { let expectation = XCTestExpectation(description: "Upload data") let name = UUID().uuidString - let fileName = name + ".txt" + let fileName: String + if folder.isEmpty { + fileName = name + ".txt" + } else { + fileName = "\(folder)/\(name).txt" + } let content = UUID().uuidString let data = content.data(using: .utf8)! @@ -763,6 +838,21 @@ final class WebDAVTests: XCTestCase { return folder } + private func downloadData(path file: String, account: SimpleAccount, password: String) { + let expectation = XCTestExpectation(description: "Download file from WebDAV") + + try? webDAV.deleteCachedData(forItemAtPath: file, account: account) + + webDAV.download(fileAtPath: file, account: account, password: password) { image, error in + XCTAssertNil(error) + XCTAssertNotNil(image) + + expectation.fulfill() + } + + wait(for: [expectation], timeout: 10.0) + } + private func downloadImage(imagePath: String, account: SimpleAccount, password: String) { let expectation = XCTestExpectation(description: "Download image from WebDAV") @@ -816,7 +906,7 @@ final class WebDAVTests: XCTestCase { ("testCleanupFilesCacheRoot", testCleanupFilesCacheRoot), ("testCleanupFilesCacheSubdirectory", testCleanupFilesCacheSubdirectory), // Disk Cache Cleanup - ("testCleanupDiskCacheFile", testCleanupDiskCacheFile), + ("testCleanupDiskCacheFileRoot", testCleanupDiskCacheFileRoot), ("testCleanupDiskCacheFolder", testCleanupDiskCacheFolder), ("testCleanupDiskCacheWithGoodFile", testCleanupDiskCacheWithGoodFile), // Image Cache