240 lines
8.2 KiB
Swift
240 lines
8.2 KiB
Swift
//
|
|
// KingfisherImagePrefetcherTests.swift
|
|
// readeckTests
|
|
//
|
|
// Created by Claude on 30.11.25.
|
|
//
|
|
|
|
import Testing
|
|
import Foundation
|
|
import Kingfisher
|
|
@testable import readeck
|
|
import UIKit
|
|
|
|
@Suite("KingfisherImagePrefetcher Tests")
|
|
struct KingfisherImagePrefetcherTests {
|
|
|
|
// MARK: - Test Setup & Helpers
|
|
|
|
/// Mock server URL for test images
|
|
private let testImageURL1 = URL(string: "https://via.placeholder.com/150/FF0000/FFFFFF?text=Test1")!
|
|
private let testImageURL2 = URL(string: "https://via.placeholder.com/150/00FF00/FFFFFF?text=Test2")!
|
|
private let testImageURL3 = URL(string: "https://via.placeholder.com/150/0000FF/FFFFFF?text=Test3")!
|
|
|
|
/// Creates a simple test image for caching
|
|
private func createTestImage() -> KFCrossPlatformImage {
|
|
#if os(iOS)
|
|
let size = CGSize(width: 10, height: 10)
|
|
UIGraphicsBeginImageContextWithOptions(size, false, 1.0)
|
|
UIColor.blue.setFill()
|
|
UIRectFill(CGRect(origin: .zero, size: size))
|
|
let image = UIGraphicsGetImageFromCurrentImageContext()!
|
|
UIGraphicsEndImageContext()
|
|
return image
|
|
#elseif os(macOS)
|
|
let size = NSSize(width: 10, height: 10)
|
|
let image = NSImage(size: size)
|
|
image.lockFocus()
|
|
NSColor.blue.setFill()
|
|
NSBezierPath.fill(NSRect(origin: .zero, size: size))
|
|
image.unlockFocus()
|
|
return image
|
|
#endif
|
|
}
|
|
|
|
/// Clears Kingfisher cache after tests
|
|
private func clearCache() async {
|
|
await ImageCache.default.clearCache()
|
|
}
|
|
|
|
/// Checks if an image is cached
|
|
private func isImageCached(forKey key: String) async -> Bool {
|
|
await withCheckedContinuation { continuation in
|
|
ImageCache.default.retrieveImage(forKey: key) { result in
|
|
switch result {
|
|
case .success(let cacheResult):
|
|
continuation.resume(returning: cacheResult.image != nil)
|
|
case .failure:
|
|
continuation.resume(returning: false)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Prefetch Tests
|
|
|
|
@Test("Prefetch images handles empty URL array")
|
|
func testPrefetchImagesHandlesEmptyArray() async {
|
|
let prefetcher = KingfisherImagePrefetcher()
|
|
let emptyURLs: [URL] = []
|
|
|
|
// Should complete without errors
|
|
await prefetcher.prefetchImages(urls: emptyURLs)
|
|
|
|
// No assertions needed - just verify it doesn't crash
|
|
#expect(emptyURLs.isEmpty)
|
|
}
|
|
|
|
@Test("Prefetch images uses never expiration for disk cache")
|
|
func testPrefetchImagesUsesNeverExpiration() async {
|
|
// This test verifies the configuration is set correctly
|
|
// The actual implementation uses .diskCacheExpiration(.never)
|
|
let prefetcher = KingfisherImagePrefetcher()
|
|
|
|
// Pre-cache a test image to verify it persists
|
|
let testURL = URL(string: "https://example.com/test.jpg")!
|
|
let testImage = createTestImage()
|
|
|
|
try? await ImageCache.default.store(
|
|
testImage,
|
|
forKey: testURL.cacheKey,
|
|
options: KingfisherParsedOptionsInfo([.diskCacheExpiration(.never)])
|
|
)
|
|
|
|
let isCached = await isImageCached(forKey: testURL.cacheKey)
|
|
#expect(isCached == true)
|
|
|
|
await clearCache()
|
|
}
|
|
|
|
@Test("Verify prefetched images confirms cache status")
|
|
func testVerifyPrefetchedImagesConfirmsCacheStatus() async {
|
|
let prefetcher = KingfisherImagePrefetcher()
|
|
|
|
// Manually cache some test images
|
|
let url1 = URL(string: "https://example.com/cached1.jpg")!
|
|
let url2 = URL(string: "https://example.com/cached2.jpg")!
|
|
let url3 = URL(string: "https://example.com/not-cached.jpg")!
|
|
|
|
let testImage = createTestImage()
|
|
try? await ImageCache.default.store(testImage, forKey: url1.cacheKey)
|
|
try? await ImageCache.default.store(testImage, forKey: url2.cacheKey)
|
|
|
|
// Verify the cached ones
|
|
await prefetcher.verifyPrefetchedImages([url1, url2, url3])
|
|
|
|
// Check that first two are cached
|
|
let isCached1 = await isImageCached(forKey: url1.cacheKey)
|
|
let isCached2 = await isImageCached(forKey: url2.cacheKey)
|
|
let isCached3 = await isImageCached(forKey: url3.cacheKey)
|
|
|
|
#expect(isCached1 == true)
|
|
#expect(isCached2 == true)
|
|
#expect(isCached3 == false)
|
|
|
|
await clearCache()
|
|
}
|
|
|
|
// MARK: - Custom Cache Key Tests
|
|
|
|
@Test("Cache image with custom key stores correctly")
|
|
func testCacheImageWithCustomKeyStoresCorrectly() async {
|
|
let prefetcher = KingfisherImagePrefetcher()
|
|
let customKey = "bookmark-123-hero"
|
|
|
|
// Pre-cache a test image with URL key so it can be "downloaded"
|
|
let sourceURL = URL(string: "https://example.com/hero.jpg")!
|
|
let testImage = createTestImage()
|
|
try? await ImageCache.default.store(testImage, forKey: sourceURL.cacheKey)
|
|
|
|
// Now use the prefetcher to cache with custom key
|
|
await prefetcher.cacheImageWithCustomKey(url: sourceURL, key: customKey)
|
|
|
|
// Verify it's cached with custom key
|
|
let isCached = await isImageCached(forKey: customKey)
|
|
#expect(isCached == true)
|
|
|
|
await clearCache()
|
|
}
|
|
|
|
@Test("Cache image with custom key skips if already cached")
|
|
func testCacheImageWithCustomKeySkipsIfAlreadyCached() async {
|
|
let prefetcher = KingfisherImagePrefetcher()
|
|
let customKey = "bookmark-456-hero"
|
|
let sourceURL = URL(string: "https://example.com/hero2.jpg")!
|
|
|
|
// Pre-cache with custom key
|
|
let testImage = createTestImage()
|
|
try? await ImageCache.default.store(testImage, forKey: customKey)
|
|
|
|
// Call again - should skip (verify by checking it doesn't fail)
|
|
await prefetcher.cacheImageWithCustomKey(url: sourceURL, key: customKey)
|
|
|
|
// Should still be cached
|
|
let isCached = await isImageCached(forKey: customKey)
|
|
#expect(isCached == true)
|
|
|
|
await clearCache()
|
|
}
|
|
|
|
// MARK: - Clear Cache Tests
|
|
|
|
@Test("Clear cached images removes all specified URLs")
|
|
func testClearCachedImagesRemovesAllURLs() async {
|
|
let prefetcher = KingfisherImagePrefetcher()
|
|
|
|
// Cache some test images
|
|
let url1 = URL(string: "https://example.com/clear1.jpg")!
|
|
let url2 = URL(string: "https://example.com/clear2.jpg")!
|
|
let testImage = createTestImage()
|
|
|
|
try? await ImageCache.default.store(testImage, forKey: url1.cacheKey)
|
|
try? await ImageCache.default.store(testImage, forKey: url2.cacheKey)
|
|
|
|
// Verify they are cached
|
|
var isCached1 = await isImageCached(forKey: url1.cacheKey)
|
|
var isCached2 = await isImageCached(forKey: url2.cacheKey)
|
|
#expect(isCached1 == true)
|
|
#expect(isCached2 == true)
|
|
|
|
// Clear them
|
|
await prefetcher.clearCachedImages(urls: [url1, url2])
|
|
|
|
// Verify they are removed
|
|
isCached1 = await isImageCached(forKey: url1.cacheKey)
|
|
isCached2 = await isImageCached(forKey: url2.cacheKey)
|
|
#expect(isCached1 == false)
|
|
#expect(isCached2 == false)
|
|
}
|
|
|
|
@Test("Clear cached images handles empty array")
|
|
func testClearCachedImagesHandlesEmptyArray() async {
|
|
let prefetcher = KingfisherImagePrefetcher()
|
|
let emptyURLs: [URL] = []
|
|
|
|
// Should complete without errors
|
|
await prefetcher.clearCachedImages(urls: emptyURLs)
|
|
|
|
// No assertions needed - just verify it doesn't crash
|
|
#expect(emptyURLs.isEmpty)
|
|
}
|
|
|
|
// MARK: - Integration Tests
|
|
|
|
@Test("Prefetch and verify workflow")
|
|
func testPrefetchAndVerifyWorkflow() async {
|
|
let prefetcher = KingfisherImagePrefetcher()
|
|
|
|
// Pre-populate cache with test images
|
|
let urls = [
|
|
URL(string: "https://example.com/workflow1.jpg")!,
|
|
URL(string: "https://example.com/workflow2.jpg")!
|
|
]
|
|
|
|
let testImage = createTestImage()
|
|
for url in urls {
|
|
try? await ImageCache.default.store(testImage, forKey: url.cacheKey)
|
|
}
|
|
|
|
// Verify they were cached
|
|
await prefetcher.verifyPrefetchedImages(urls)
|
|
|
|
for url in urls {
|
|
let isCached = await isImageCached(forKey: url.cacheKey)
|
|
#expect(isCached == true)
|
|
}
|
|
|
|
await clearCache()
|
|
}
|
|
}
|