From bd8231252fb43a6b99b0fa592856e13af9e4c2a2 Mon Sep 17 00:00:00 2001 From: Isvvc Date: Mon, 10 May 2021 19:26:42 -0600 Subject: [PATCH 1/4] Fix issue where directories would always be removed from filesCache Improve files cache cleanup test --- Sources/WebDAV/WebDAV.swift | 2 +- Tests/WebDAVTests/WebDAVTests.swift | 43 ++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 11 deletions(-) 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..5b21987 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() @@ -719,11 +736,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)! From 1c604afe9c38a8751558da2c0863733d96bc3eb6 Mon Sep 17 00:00:00 2001 From: Isvvc Date: Mon, 10 May 2021 19:46:26 -0600 Subject: [PATCH 2/4] Fix issue where all files would be deleted from root on cleanup Improve disk cache cleanup file test --- Sources/WebDAV/WebDAV+DiskCache.swift | 16 +++++++++++---- Tests/WebDAVTests/WebDAVTests.swift | 28 ++++++++++++++++++++------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Sources/WebDAV/WebDAV+DiskCache.swift b/Sources/WebDAV/WebDAV+DiskCache.swift index 6fd6a90..5eb63f7 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,7 +185,8 @@ 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 }) diff --git a/Tests/WebDAVTests/WebDAVTests.swift b/Tests/WebDAVTests/WebDAVTests.swift index 5b21987..bb01e46 100644 --- a/Tests/WebDAVTests/WebDAVTests.swift +++ b/Tests/WebDAVTests/WebDAVTests.swift @@ -433,20 +433,31 @@ final class WebDAVTests: XCTestCase { func testCleanupDiskCacheFile() { guard let (account, password) = getAccount() else { return XCTFail() } + let realFileExpectation = XCTestExpectation(description: "Download real file") 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)! + webDAV.download(fileAtPath: realFile.fileName, account: account, password: password, caching: .doNotReturnCachedResult) { _, _ in + realFileExpectation.fulfill() + } + + wait(for: [realFileExpectation], timeout: 10.0) + + 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() @@ -456,7 +467,10 @@ final class WebDAVTests: XCTestCase { // Check that the fake cached file was cleaned up - XCTAssertFalse(FileManager.default.fileExists(atPath: tempFileURL.path)) + XCTAssertFalse(FileManager.default.fileExists(atPath: dummyFileURL.path)) + XCTAssert(FileManager.default.fileExists(atPath: realFileURL.path)) + + deleteFile(path: realFile.fileName, account: account, password: password) } func testCleanupDiskCacheFolder() { From 4f31371f203fcf7eaefe0f84cf80ed7172384612 Mon Sep 17 00:00:00 2001 From: Isvvc Date: Mon, 10 May 2021 19:59:31 -0600 Subject: [PATCH 3/4] Improve testCleanupDiskCacheFolder to include a real folder --- Tests/WebDAVTests/WebDAVTests.swift | 51 +++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/Tests/WebDAVTests/WebDAVTests.swift b/Tests/WebDAVTests/WebDAVTests.swift index bb01e46..695575a 100644 --- a/Tests/WebDAVTests/WebDAVTests.swift +++ b/Tests/WebDAVTests/WebDAVTests.swift @@ -430,13 +430,13 @@ final class WebDAVTests: XCTestCase { //MARK: Disk Cache Cleanup - func testCleanupDiskCacheFile() { + func testCleanupDiskCacheFileRoot() { guard let (account, password) = getAccount() else { return XCTFail() } - let realFileExpectation = XCTestExpectation(description: "Download real file") let expectation = XCTestExpectation(description: "List files from WebDAV") // Add dummy file to disk cache + let dummyPath = UUID().uuidString + ".txt" let data = UUID().uuidString.data(using: .utf8)! let dummyFileURL = webDAV.cachedDataURL(forItemAtPath: dummyPath, account: account)! @@ -445,13 +445,10 @@ final class WebDAVTests: XCTestCase { 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)! - webDAV.download(fileAtPath: realFile.fileName, account: account, password: password, caching: .doNotReturnCachedResult) { _, _ in - realFileExpectation.fulfill() - } - - wait(for: [realFileExpectation], timeout: 10.0) + downloadData(path: realFile.fileName, account: account, password: password) XCTAssert(FileManager.default.fileExists(atPath: realFileURL.path)) @@ -466,8 +463,8 @@ 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)) deleteFile(path: realFile.fileName, account: account, password: password) @@ -481,9 +478,19 @@ 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 @@ -496,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() { @@ -800,6 +810,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") @@ -853,7 +878,7 @@ final class WebDAVTests: XCTestCase { ("testCleanupFilesCacheRoot", testCleanupFilesCacheRoot), ("testCleanupFilesCacheSubdirectory", testCleanupFilesCacheSubdirectory), // Disk Cache Cleanup - ("testCleanupDiskCacheFile", testCleanupDiskCacheFile), + ("testCleanupDiskCacheFileRoot", testCleanupDiskCacheFileRoot), ("testCleanupDiskCacheFolder", testCleanupDiskCacheFolder), ("testCleanupDiskCacheWithGoodFile", testCleanupDiskCacheWithGoodFile), // Image Cache From 838d43e66d03ac550d0319a067050a98942bb20c Mon Sep 17 00:00:00 2001 From: Isvvc Date: Mon, 10 May 2021 20:10:58 -0600 Subject: [PATCH 4/4] Fix issue where cached thumbnails would be deleted on cleanup --- Sources/WebDAV/WebDAV+DiskCache.swift | 4 ++-- Tests/WebDAVTests/WebDAVTests.swift | 32 +++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Sources/WebDAV/WebDAV+DiskCache.swift b/Sources/WebDAV/WebDAV+DiskCache.swift index 5eb63f7..6114620 100644 --- a/Sources/WebDAV/WebDAV+DiskCache.swift +++ b/Sources/WebDAV/WebDAV+DiskCache.swift @@ -188,11 +188,11 @@ extension WebDAV { 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/Tests/WebDAVTests/WebDAVTests.swift b/Tests/WebDAVTests/WebDAVTests.swift index 695575a..2f0063e 100644 --- a/Tests/WebDAVTests/WebDAVTests.swift +++ b/Tests/WebDAVTests/WebDAVTests.swift @@ -494,7 +494,7 @@ final class WebDAVTests: XCTestCase { // 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() @@ -537,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() @@ -552,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() {