Skip to content

Commit

Permalink
Merge pull request #32 from Isvvc/cache-cleanup
Browse files Browse the repository at this point in the history
Cache cleanup
  • Loading branch information
skjiisa authored Apr 20, 2021
2 parents ce121ed + d005b99 commit afcd830
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 5 deletions.
18 changes: 16 additions & 2 deletions Sources/WebDAV/WebDAV+DiskCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ public extension WebDAV {

let filename = url.lastPathComponent
let directory = url.deletingLastPathComponent()
guard fm.fileExists(atPath: url.deletingLastPathComponent().path) else { return }
guard fm.fileExists(atPath: directory.path) else { return }

for item in try fm.contentsOfDirectory(atPath: directory.path) where item != filename && item.contains(filename) {
for item in try fm.contentsOfDirectory(atPath: directory.path) where item != filename && item.starts(with: filename) {
try fm.removeItem(at: directory.appendingPathComponent(item))
}
}
Expand Down Expand Up @@ -125,6 +125,20 @@ extension WebDAV {
try saveDataToDiskCache(data, url: url)
}

func cleanupDiskCache<A: WebDAVAccount>(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 }

let goodFilePaths = Set(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)
&& path != infoPlist {
try fm.removeItem(atPath: path)
}
}

//MARK: Thumbnail Cache

func loadCachedThumbnailFromDisk<A: WebDAVAccount>(forItemAtPath path: String, account: A, with properties: ThumbnailProperties) -> UIImage? {
Expand Down
36 changes: 33 additions & 3 deletions Sources/WebDAV/WebDAV.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,15 @@ public extension WebDAV {
request.httpBody = body.data(using: .utf8)

let task = URLSession(configuration: .ephemeral, delegate: self, delegateQueue: nil).dataTask(with: request) { [weak self] data, response, error in
var error = WebDAVError.getError(response: response, error: error)

// Check the response
let response = response as? HTTPURLResponse

guard 200...299 ~= response?.statusCode ?? 0,
let data = data,
let string = String(data: data, encoding: .utf8) else {
let webDAVError = WebDAVError.getError(statusCode: response?.statusCode, error: error)
return completion(nil, webDAVError)
return completion(nil, error)
}

// Create WebDAVFiles from the XML response
Expand All @@ -136,10 +136,16 @@ public extension WebDAV {
self?.saveFilesCacheToDisk()
}

do {
try self?.cleanupCache(at: path, account: account, files: Array(files.dropFirst()))
} catch let cachingError {
error = .nsError(cachingError)
}

let sortedFiles = WebDAV.sortedFiles(files, foldersFirst: foldersFirst, includeSelf: includeSelf)
// Don't send a duplicate completion if the results are the same.
if sortedFiles != cachedResponse {
completion(sortedFiles, nil)
completion(sortedFiles, error)
}
}

Expand Down Expand Up @@ -501,4 +507,28 @@ extension WebDAV {
return previewURL
}

//MARK: Cache

func cleanupFilesCache<A: WebDAVAccount>(at path: String, account: A, files: [WebDAVFile]) {
let directory = path.trimmingCharacters(in: AccountPath.slash)
var changed = false
// Remove from cache if the the parent directory no longer exists
for (key, _) in filesCache
where key.path != directory
&& key.path.starts(with: directory)
&& !files.contains(where: { key.path.starts(with: $0.path) }) {
filesCache.removeValue(forKey: key)
changed = true
}

if changed {
saveFilesCacheToDisk()
}
}

func cleanupCache<A: WebDAVAccount>(at path: String, account: A, files: [WebDAVFile]) throws {
cleanupFilesCache(at: path, account: account, files: files)
try cleanupDiskCache(at: path, account: account, files: files)
}

}
167 changes: 167 additions & 0 deletions Tests/WebDAVTests/WebDAVTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,166 @@ final class WebDAVTests: XCTestCase {
wait(for: [expectation], timeout: 10.0)
}

//MARK: Files Cache Cleanup

func testCleanupFilesCacheRoot() {
guard let (account, password) = getAccount() else { return XCTFail() }

let expectation = XCTestExpectation(description: "List files from WebDAV")

let path = UUID().uuidString
let accountPath = AccountPath(account: account, path: path)

// 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])

// List files

webDAV.listFiles(atPath: "/", account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in
XCTAssertNotNil(files)
XCTAssertNil(error)
expectation.fulfill()
}

wait(for: [expectation], timeout: 10.0)

// Check that the fake cached file was cleaned up

XCTAssertNil(webDAV.filesCache[accountPath])
}

func testCleanupFilesCacheSubdirectory() {
guard let (account, password) = getAccount() else { return XCTFail() }

let expectation = XCTestExpectation(description: "List files from WebDAV")

let folder = createFolder(account: account, password: password)
let path = folder + UUID().uuidString
let accountPath = AccountPath(account: account, path: path)

// 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])

// List files

webDAV.listFiles(atPath: folder, account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in
XCTAssertNotNil(files)
XCTAssertNil(error)
expectation.fulfill()
}

wait(for: [expectation], timeout: 10.0)

// Check that the fake cached file was cleaned up

deleteFile(path: folder, account: account, password: password)
XCTAssertNil(webDAV.filesCache[accountPath])
}

//MARK: Disk Cache Cleanup

func testCleanupDiskCacheFile() {
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 data = UUID().uuidString.data(using: .utf8)!
let tempFileURL = webDAV.cachedDataURL(forItemAtPath: path, account: account)!
XCTAssertNoThrow(try webDAV.saveDataToDiskCache(data, url: tempFileURL))

XCTAssert(FileManager.default.fileExists(atPath: tempFileURL.path))

// List files

webDAV.listFiles(atPath: "/", account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in
XCTAssertNotNil(files)
XCTAssertNil(error)
expectation.fulfill()
}

wait(for: [expectation], timeout: 10.0)

// Check that the fake cached file was cleaned up

XCTAssertFalse(FileManager.default.fileExists(atPath: tempFileURL.path))
}

func testCleanupDiskCacheFolder() {
guard let (account, password) = getAccount() else { return XCTFail() }

let expectation = XCTestExpectation(description: "List files from WebDAV")

// 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))

// List files

webDAV.listFiles(atPath: "/", account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in
XCTAssertNotNil(files)
XCTAssertNil(error)
expectation.fulfill()
}

wait(for: [expectation], timeout: 10.0)

// Check that the fake cached folder was cleaned up

XCTAssertFalse(FileManager.default.fileExists(atPath: tempFileURL.path))
}

func testCleanupDiskCacheWithGoodFile() {
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 image

let cachedImageURL = webDAV.cachedDataURL(forItemAtPath: imagePath, account: account)!
downloadImage(imagePath: imagePath, account: account, password: password)
XCTAssert(FileManager.default.fileExists(atPath: cachedImageURL.path))

// Add dummy file to same directory as image in disk cache

let imageURL = URL(fileURLWithPath: imagePath, isDirectory: false)
let directory = imageURL.deletingLastPathComponent()
let url = directory.appendingPathComponent(UUID().uuidString)
let data = UUID().uuidString.data(using: .utf8)!
let tempFileURL = webDAV.cachedDataURL(forItemAtPath: url.path, account: account)!
XCTAssertNoThrow(try webDAV.saveDataToDiskCache(data, url: tempFileURL))

XCTAssert(FileManager.default.fileExists(atPath: tempFileURL.path))

// List files

webDAV.listFiles(atPath: directory.path, account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in
XCTAssertNotNil(files)
XCTAssertNil(error)
expectation.fulfill()
}

wait(for: [expectation], timeout: 10.0)

// Check that the fake cached file was cleaned up and the valid cached file still exists

XCTAssert(FileManager.default.fileExists(atPath: cachedImageURL.path))
XCTAssertFalse(FileManager.default.fileExists(atPath: tempFileURL.path))
XCTAssertNoThrow(try webDAV.deleteCachedData(forItemAtPath: imagePath, account: account))
}

//MARK: Image Cache

func testDownloadImage() {
Expand Down Expand Up @@ -615,6 +775,13 @@ final class WebDAVTests: XCTestCase {
("testFilesReadFromMemoryCache", testFilesReadFromMemoryCache),
("testDiskCache", testDiskCache),
("testFilesCacheDoubleRequest", testFilesCacheDoubleRequest),
// Files Cache Cleanup
("testCleanupFilesCacheRoot", testCleanupFilesCacheRoot),
("testCleanupFilesCacheSubdirectory", testCleanupFilesCacheSubdirectory),
// Disk Cache Cleanup
("testCleanupDiskCacheFile", testCleanupDiskCacheFile),
("testCleanupDiskCacheFolder", testCleanupDiskCacheFolder),
("testCleanupDiskCacheWithGoodFile", testCleanupDiskCacheWithGoodFile),
// Image Cache
("testDownloadImage", testDownloadImage),
("testImageCache", testImageCache),
Expand Down

0 comments on commit afcd830

Please sign in to comment.