Pārlūkot izejas kodu

Add tests to source protocol

onevcat 7 gadi atpakaļ
vecāks
revīzija
00c8b4f5f9

+ 29 - 3
Kingfisher.xcodeproj/project.pbxproj

@@ -166,6 +166,13 @@
 		D186696D21834261002B502E /* ImageDrawingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D186696C21834261002B502E /* ImageDrawingTests.swift */; };
 		D186696E21834261002B502E /* ImageDrawingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D186696C21834261002B502E /* ImageDrawingTests.swift */; };
 		D186696F21834261002B502E /* ImageDrawingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D186696C21834261002B502E /* ImageDrawingTests.swift */; };
+		D1A1CC9A219FAB4B00263AD8 /* Source.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A1CC99219FAB4B00263AD8 /* Source.swift */; };
+		D1A1CC9B219FAB4B00263AD8 /* Source.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A1CC99219FAB4B00263AD8 /* Source.swift */; };
+		D1A1CC9C219FAB4B00263AD8 /* Source.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A1CC99219FAB4B00263AD8 /* Source.swift */; };
+		D1A1CC9D219FAB4B00263AD8 /* Source.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A1CC99219FAB4B00263AD8 /* Source.swift */; };
+		D1A1CC9F21A0F98600263AD8 /* ImageDataProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A1CC9E21A0F98600263AD8 /* ImageDataProviderTests.swift */; };
+		D1A1CCA021A0F98600263AD8 /* ImageDataProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A1CC9E21A0F98600263AD8 /* ImageDataProviderTests.swift */; };
+		D1A1CCA121A0F98600263AD8 /* ImageDataProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A1CC9E21A0F98600263AD8 /* ImageDataProviderTests.swift */; };
 		D1A37BC6215D2DBA009B39B7 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12AB6B6215D2BB50013BA68 /* ImageCache.swift */; };
 		D1A37BC7215D2DBA009B39B7 /* CacheSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12AB6B7215D2BB50013BA68 /* CacheSerializer.swift */; };
 		D1A37BC8215D2DBA009B39B7 /* FormatIndicatedCacheSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12AB6B8215D2BB50013BA68 /* FormatIndicatedCacheSerializer.swift */; };
@@ -331,6 +338,8 @@
 		D16799EB1C4E74460020FD12 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		D1839844216E333E003927D3 /* Delegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delegate.swift; sourceTree = "<group>"; };
 		D186696C21834261002B502E /* ImageDrawingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDrawingTests.swift; sourceTree = "<group>"; };
+		D1A1CC99219FAB4B00263AD8 /* Source.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Source.swift; sourceTree = "<group>"; };
+		D1A1CC9E21A0F98600263AD8 /* ImageDataProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDataProviderTests.swift; sourceTree = "<group>"; };
 		D1A37BDD215D34E8009B39B7 /* ImageDrawing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDrawing.swift; sourceTree = "<group>"; };
 		D1A37BE2215D359F009B39B7 /* ImageFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageFormat.swift; sourceTree = "<group>"; };
 		D1A37BE7215D365A009B39B7 /* ExtenionHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtenionHelpers.swift; sourceTree = "<group>"; };
@@ -340,7 +349,7 @@
 		D1D2C3291C70A3230018F2F9 /* single-frame.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = "single-frame.gif"; sourceTree = "<group>"; };
 		D1DC4B401D60996D00DFDFAA /* StringExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtensionTests.swift; sourceTree = "<group>"; };
 		D1E564402199C21E0057AAE3 /* StorageExpirationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageExpirationTests.swift; sourceTree = "<group>"; };
-		D1E56444219B16330057AAE3 /* ImageDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ImageDataProvider.swift; path = Sources/General/ImageDataProvider.swift; sourceTree = SOURCE_ROOT; };
+		D1E56444219B16330057AAE3 /* ImageDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ImageDataProvider.swift; path = Sources/General/ImageSource/ImageDataProvider.swift; sourceTree = SOURCE_ROOT; };
 		D1ED2D351AD2D09F00CFC3EB /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		D1ED2D3F1AD2D09F00CFC3EB /* KingfisherTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KingfisherTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		D7B91E45CD834BE64717E77F /* Pods-KingfisherTests-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KingfisherTests-tvOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-KingfisherTests-tvOS/Pods-KingfisherTests-tvOS.debug.xcconfig"; sourceTree = "<group>"; };
@@ -509,12 +518,11 @@
 		D12AB6B0215D2BB50013BA68 /* General */ = {
 			isa = PBXGroup;
 			children = (
+				D1A1CC98219FAB3500263AD8 /* ImageSource */,
 				D12AB6B1215D2BB50013BA68 /* Kingfisher.swift */,
 				D12AB6B2215D2BB50013BA68 /* KingfisherError.swift */,
 				D12AB6B3215D2BB50013BA68 /* KingfisherManager.swift */,
 				D12AB6B4215D2BB50013BA68 /* KingfisherOptionsInfo.swift */,
-				D12AB69E215D2BB50013BA68 /* Resource.swift */,
-				D1E56444219B16330057AAE3 /* ImageDataProvider.swift */,
 				D1A37BEC215D375F009B39B7 /* Deprecated.swift */,
 			);
 			path = General;
@@ -581,6 +589,7 @@
 				4BCFF7A521990DB60055AAC4 /* MemoryStorageTests.swift */,
 				4BCFF7A9219932390055AAC4 /* DiskStorageTests.swift */,
 				D1E564402199C21E0057AAE3 /* StorageExpirationTests.swift */,
+				D1A1CC9E21A0F98600263AD8 /* ImageDataProviderTests.swift */,
 			);
 			name = KingfisherTests;
 			path = Tests/KingfisherTests;
@@ -595,6 +604,16 @@
 			path = "Tests/KingfisherTests-tvOS";
 			sourceTree = "<group>";
 		};
+		D1A1CC98219FAB3500263AD8 /* ImageSource */ = {
+			isa = PBXGroup;
+			children = (
+				D1A1CC99219FAB4B00263AD8 /* Source.swift */,
+				D12AB69E215D2BB50013BA68 /* Resource.swift */,
+				D1E56444219B16330057AAE3 /* ImageDataProvider.swift */,
+			);
+			path = ImageSource;
+			sourceTree = "<group>";
+		};
 		D1ED2D021AD2CFA600CFC3EB = {
 			isa = PBXGroup;
 			children = (
@@ -1069,6 +1088,7 @@
 				D1A37BD8215D2DBA009B39B7 /* ImageModifier.swift in Sources */,
 				D1A37BD9215D2DBA009B39B7 /* ImagePrefetcher.swift in Sources */,
 				D1A37BDB215D2DBA009B39B7 /* Box.swift in Sources */,
+				D1A1CC9C219FAB4B00263AD8 /* Source.swift in Sources */,
 				D1A37BDC215D2DBA009B39B7 /* Indicator.swift in Sources */,
 				4B8E2919216F3F7F0095FAD1 /* ImageDownloaderDelegate.swift in Sources */,
 				D114F36E215D2D0B00A01349 /* String+MD5.swift in Sources */,
@@ -1092,6 +1112,7 @@
 				D12E0C731C47F6FE00AC98AD /* KingfisherOptionsInfoTests.swift in Sources */,
 				D186696E21834261002B502E /* ImageDrawingTests.swift in Sources */,
 				4BCFF7AB219932390055AAC4 /* DiskStorageTests.swift in Sources */,
+				D1A1CCA021A0F98600263AD8 /* ImageDataProviderTests.swift in Sources */,
 				4B8351C9217066580081EED8 /* StubHelpers.swift in Sources */,
 				D1DC4B421D60996D00DFDFAA /* StringExtensionTests.swift in Sources */,
 				D12E0C741C47F6FE00AC98AD /* UIButtonExtensionTests.swift in Sources */,
@@ -1115,6 +1136,7 @@
 				D12E0C861C47F7AF00AC98AD /* KingfisherManagerTests.swift in Sources */,
 				D186696F21834261002B502E /* ImageDrawingTests.swift in Sources */,
 				4BCFF7AC219932390055AAC4 /* DiskStorageTests.swift in Sources */,
+				D1A1CCA121A0F98600263AD8 /* ImageDataProviderTests.swift in Sources */,
 				4B8351CA217066580081EED8 /* StubHelpers.swift in Sources */,
 				D1DC4B431D60996D00DFDFAA /* StringExtensionTests.swift in Sources */,
 				D12E0C871C47F7AF00AC98AD /* KingfisherOptionsInfoTests.swift in Sources */,
@@ -1157,6 +1179,7 @@
 				D12AB6FD215D2BB50013BA68 /* UIButton+Kingfisher.swift in Sources */,
 				D12AB6E9215D2BB50013BA68 /* GIFAnimatedImage.swift in Sources */,
 				D13646752165A1A100A33652 /* Result.swift in Sources */,
+				D1A1CC9B219FAB4B00263AD8 /* Source.swift in Sources */,
 				4BD821632189FC0C0084CC21 /* SessionDelegate.swift in Sources */,
 				D12AB6E1215D2BB50013BA68 /* Filter.swift in Sources */,
 				D12AB6C5215D2BB50013BA68 /* Resource.swift in Sources */,
@@ -1174,6 +1197,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				D1A1CC9D219FAB4B00263AD8 /* Source.swift in Sources */,
 				D1A37BE1215D34E8009B39B7 /* ImageDrawing.swift in Sources */,
 				D12AB6CF215D2BB50013BA68 /* ImageModifier.swift in Sources */,
 				D1A37BF5215D3850009B39B7 /* SizeExtensions.swift in Sources */,
@@ -1250,6 +1274,7 @@
 				D12AB6FC215D2BB50013BA68 /* UIButton+Kingfisher.swift in Sources */,
 				D12AB6E8215D2BB50013BA68 /* GIFAnimatedImage.swift in Sources */,
 				D13646742165A1A100A33652 /* Result.swift in Sources */,
+				D1A1CC9A219FAB4B00263AD8 /* Source.swift in Sources */,
 				4BD821622189FC0C0084CC21 /* SessionDelegate.swift in Sources */,
 				D12AB6E0215D2BB50013BA68 /* Filter.swift in Sources */,
 				D12AB6C4215D2BB50013BA68 /* Resource.swift in Sources */,
@@ -1280,6 +1305,7 @@
 				D12E0C531C47F23500AC98AD /* ImageViewExtensionTests.swift in Sources */,
 				D186696D21834261002B502E /* ImageDrawingTests.swift in Sources */,
 				4BCFF7AA219932390055AAC4 /* DiskStorageTests.swift in Sources */,
+				D1A1CC9F21A0F98600263AD8 /* ImageDataProviderTests.swift in Sources */,
 				4B8351C8217066580081EED8 /* StubHelpers.swift in Sources */,
 				D1DC4B411D60996D00DFDFAA /* StringExtensionTests.swift in Sources */,
 				D12E0C501C47F23500AC98AD /* ImageCacheTests.swift in Sources */,

+ 0 - 73
Sources/General/ImageDataProvider.swift

@@ -1,73 +0,0 @@
-//
-//  ImageDataProvider.swift
-//  Kingfisher
-//
-//  Created by onevcat on 2018/11/13.
-//
-//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
-//
-//  Permission is hereby granted, free of charge, to any person obtaining a copy
-//  of this software and associated documentation files (the "Software"), to deal
-//  in the Software without restriction, including without limitation the rights
-//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-//  copies of the Software, and to permit persons to whom the Software is
-//  furnished to do so, subject to the following conditions:
-//
-//  The above copyright notice and this permission notice shall be included in
-//  all copies or substantial portions of the Software.
-//
-//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-//  THE SOFTWARE.
-
-import Foundation
-
-public protocol ImageDataProvider {
-    var cacheKey: String { get }
-    func data(handler: (Result<Data, Error>) -> Void)
-    var identifier: String {get }
-}
-
-public struct LocalFileImageDataProvider: ImageDataProvider {
-    public var cacheKey: String
-
-    public func data(handler: (Result<Data, Error>) -> Void) {
-        do {
-            let data = try Data(contentsOf: fileURL)
-            handler(.success(data))
-        } catch {
-            handler(
-                .failure(error)
-            )
-        }
-    }
-
-    public var identifier: String { return fileURL.absoluteString }
-
-    public let fileURL: URL
-    public init(fileURL: URL, cacheKey: String? = nil) {
-        self.fileURL = fileURL
-        self.cacheKey = cacheKey ?? fileURL.absoluteString
-    }
-}
-
-public struct Base64ImageDataProvider: ImageDataProvider {
-    public var cacheKey: String
-    public let base64String: String
-    public var identifier: String { return cacheKey }
-
-    public init(base64String: String, cacheKey: String) {
-        self.base64String = base64String
-        self.cacheKey = cacheKey
-    }
-
-    public func data(handler: (Result<Data, Error>) -> Void) {
-        let data = Data(base64Encoded: base64String)!
-        handler(.success(data))
-    }
-
-}

+ 110 - 0
Sources/General/ImageSource/ImageDataProvider.swift

@@ -0,0 +1,110 @@
+//
+//  ImageDataProvider.swift
+//  Kingfisher
+//
+//  Created by onevcat on 2018/11/13.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+/// Represents a data provider to provide image data to Kingfisher when setting with
+/// `Source.provider` source. Compared to `Source.network` memeber, it gives a chance
+/// to load some image data in your own way, as long as you can provide the data
+/// representation for the image.
+public protocol ImageDataProvider {
+    
+    /// The key used in cache.
+    var cacheKey: String { get }
+    
+    /// The image identifier of this provider instance. For different image loading, you
+    /// should provide a different `identifier` value. For example, when you want to load
+    /// an image from a certain URL, use the `absoluteString` property of that URL is
+    /// generally a good idea.
+    ///
+    /// Kingfisher uses this value to identify whether a finished task is the running and
+    /// being expected one for a view when you use Kingfisher's view extensions methods.
+    var identifier: String {get }
+    
+    /// Provides the data which represents image. Kingfisher uses the data you pass in the
+    /// handler to process images and caches it for later use.
+    ///
+    /// - Parameter handler: The handler you should call when you prepared your data.
+    ///                      If the data is loaded successfully, call the handler with
+    ///                      a `.success` with the data associated. Otherwise, call it
+    ///                      with a `.failure` and pass the error.
+    ///
+    /// - Note:
+    /// If the `handler` is called with a `.failure` and error, a `dataProviderError` of
+    /// `ImageSettingErrorReason` will be finally thrown out to you as the `KingfisherError`
+    /// accrossing the framework.
+    func data(handler: @escaping (Result<Data, Error>) -> Void)
+}
+
+/// Represents an image data provider for loading from a local file URL on disk.
+/// Uses this type for adding a disk image to Kingfisher. Compared to loading it
+/// directly, you can get benefit of using Kingfisher's extension methods, as well
+/// as applying `ImageProcessor`s and storing the image to `ImageCache` of Kingfisher.
+public struct LocalFileImageDataProvider: ImageDataProvider {
+
+    /// The file URL from which the image be loaded.
+    public let fileURL: URL
+    
+    /// Creates an image data provider by supplying the target local file URL.
+    ///
+    /// - Parameters:
+    ///   - fileURL: The file URL from which the image be loaded.
+    ///   - cacheKey: The key is used for caching the image data. By default,
+    ///               the `absoluteString` of `fileURL` is used.
+    public init(fileURL: URL, cacheKey: String? = nil) {
+        self.fileURL = fileURL
+        self.cacheKey = cacheKey ?? fileURL.absoluteString
+    }
+    
+    public var cacheKey: String
+    public func data(handler: (Result<Data, Error>) -> Void) {
+        handler( Result { try Data(contentsOf: fileURL) } )
+    }
+    public var identifier: String { return fileURL.absoluteString }
+}
+
+/// Represents an image data provider for loading image from a given Base64 encoded string.
+public struct Base64ImageDataProvider: ImageDataProvider {
+    
+    let base64String: String
+    
+    /// Creates an umage data provider by supplying the Base64 encoded string.
+    ///
+    /// - Parameters:
+    ///   - base64String: The Base64 encoded string for an image.
+    ///   - cacheKey: The key is used for caching the image data. You need a different key for any different image.
+    public init(base64String: String, cacheKey: String) {
+        self.base64String = base64String
+        self.cacheKey = cacheKey
+    }
+
+    public var cacheKey: String
+    public var identifier: String { return cacheKey }
+    public func data(handler: (Result<Data, Error>) -> Void) {
+        let data = Data(base64Encoded: base64String)!
+        handler(.success(data))
+    }
+}

+ 2 - 27
Sources/General/Resource.swift → Sources/General/ImageSource/Resource.swift

@@ -26,35 +26,10 @@
 
 import Foundation
 
-public enum Source {
-    case network(Resource)
-    case provider(ImageDataProvider)
-
-    var identifier: String {
-        switch self {
-        case .network(let resource): return resource.identifier
-        case .provider(let provider): return provider.identifier
-        }
-    }
-
-    var cacheKey: String {
-        switch self {
-        case .network(let resource): return resource.cacheKey
-        case .provider(let provider): return provider.cacheKey
-        }
-    }
-
-    var url: URL? {
-        switch self {
-        case .network(let resource): return resource.downloadURL
-        case .provider(_): return nil
-        }
-    }
-}
-
 /// Represents an image resource at a certain url and a given cache key.
-/// Kingfisher will use a `Resource` to download a resource from network and cache it with the cache key.
+/// Kingfisher will use a `Resource` to download a resource from network and cache it with the cache key when using `Source.network` source.
 public protocol Resource {
+    
     /// The key used in cache.
     var cacheKey: String { get }
     

+ 53 - 0
Sources/General/ImageSource/Source.swift

@@ -0,0 +1,53 @@
+//
+//  Source.swift
+//  Kingfisher
+//
+//  Created by onevcat on 2018/11/17.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+public enum Source {
+    case network(Resource)
+    case provider(ImageDataProvider)
+    
+    var identifier: String {
+        switch self {
+        case .network(let resource): return resource.identifier
+        case .provider(let provider): return provider.identifier
+        }
+    }
+    
+    var cacheKey: String {
+        switch self {
+        case .network(let resource): return resource.cacheKey
+        case .provider(let provider): return provider.cacheKey
+        }
+    }
+    
+    var url: URL? {
+        switch self {
+        case .network(let resource): return resource.downloadURL
+        case .provider(_): return nil
+        }
+    }
+}

+ 65 - 0
Tests/KingfisherTests/ImageDataProviderTests.swift

@@ -0,0 +1,65 @@
+//
+//  ImageDataProviderTests.swift
+//  Kingfisher
+//
+//  Created by onevcat on 2018/11/18.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import XCTest
+import Kingfisher
+
+class ImageDataProviderTests: XCTestCase {
+    
+    func testLocalFileImageDataProvider() {
+        let fm = FileManager.default
+        let document = try! fm.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
+        let fileURL = document.appendingPathComponent("test")
+        try! testImageData.write(to: fileURL)
+        
+        let provider = LocalFileImageDataProvider(fileURL: fileURL)
+        XCTAssertEqual(provider.cacheKey, fileURL.absoluteString)
+        XCTAssertEqual(provider.fileURL, fileURL)
+        
+        var syncCalled = false
+        provider.data { result in
+            XCTAssertEqual(result.value, testImageData)
+            syncCalled = true
+        }
+        
+        XCTAssertTrue(syncCalled)
+        try! fm.removeItem(at: fileURL)
+    }
+    
+    func testBase64ImageDataProvider() {
+        let base64String = testImageData.base64EncodedString()
+        let provider = Base64ImageDataProvider(base64String: base64String, cacheKey: "123")
+        XCTAssertEqual(provider.cacheKey, "123")
+        var syncCalled = false
+        provider.data { result in
+            XCTAssertEqual(result.value, testImageData)
+            syncCalled = true
+        }
+        
+        XCTAssertTrue(syncCalled)
+    }
+    
+}

+ 40 - 0
Tests/KingfisherTests/KingfisherManagerTests.swift

@@ -655,6 +655,33 @@ class KingfisherManagerTests: XCTestCase {
         waitForExpectations(timeout: 1, handler: nil)
     }
 #endif
+    
+    func testRetrieveWithImageProvider() {
+        let provider = SimpleImageDataProvider { .success(testImageData) }
+        var called = false
+        _ = manager.retrieveImage(with: .provider(provider)) { result in
+            called = true
+            XCTAssertNotNil(result.value)
+            XCTAssertTrue(result.value!.image.renderEqual(to: testImage))
+        }
+        XCTAssertTrue(called)
+    }
+    
+    func testRetrieveWithImageProviderFail() {
+        let provider = SimpleImageDataProvider { .failure(SimpleImageDataProvider.E()) }
+        var called = false
+        _ = manager.retrieveImage(with: .provider(provider)) { result in
+            called = true
+            XCTAssertNotNil(result.error)
+            if case .imageSettingError(reason: .dataProviderError(let p, let error)) = result.error! {
+                XCTAssertEqual(provider.identifier, p.identifier)
+                XCTAssertTrue(error is SimpleImageDataProvider.E)
+            } else {
+                XCTFail()
+            }
+        }
+        XCTAssertTrue(called)
+    }
 }
 
 class SimpleProcessor: ImageProcessor {
@@ -691,3 +718,16 @@ class FailingProcessor: ImageProcessor {
         return nil
     }
 }
+
+struct SimpleImageDataProvider: ImageDataProvider {
+    let cacheKey = "simple_image"
+    let identifier = "simple_image"
+    
+    let provider: () -> (Result<Data, Error>)
+    
+    func data(handler: @escaping (Result<Data, Error>) -> Void) {
+        handler(provider())
+    }
+    
+    struct E: Error {}
+}