| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- //
- // ImageDownloaderTests.swift
- // Kingfisher
- //
- // Created by Wei Wang on 15/4/10.
- //
- // Copyright (c) 2019 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
- @testable import Kingfisher
- class ImageDownloaderTests: XCTestCase {
- var downloader: ImageDownloader!
- var modifier = URLModifier()
- override class func setUp() {
- super.setUp()
- LSNocilla.sharedInstance().start()
- }
-
- override class func tearDown() {
- LSNocilla.sharedInstance().stop()
- super.tearDown()
- }
-
- override func setUp() {
- super.setUp()
- downloader = ImageDownloader(name: "test")
- }
-
- override func tearDown() {
- LSNocilla.sharedInstance().clearStubs()
- downloader = nil
- super.tearDown()
- }
-
- func testDownloadAnImage() {
- let exp = expectation(description: #function)
-
- let url = testURLs[0]
- stub(url, data: testImageData)
- downloader.downloadImage(with: url) { result in
- XCTAssertNotNil(result.value)
- exp.fulfill()
- }
- waitForExpectations(timeout: 3, handler: nil)
- }
-
- func testDownloadMultipleImages() {
- let exp = expectation(description: #function)
- let group = DispatchGroup()
-
- for url in testURLs {
- group.enter()
- stub(url, data: testImageData)
- downloader.downloadImage(with: url) { result in
- XCTAssertNotNil(result.value)
- group.leave()
- }
- }
-
- group.notify(queue: .main, execute: exp.fulfill)
- waitForExpectations(timeout: 3, handler: nil)
- }
-
- func testDownloadAnImageWithMultipleCallback() {
- let exp = expectation(description: #function)
-
- let group = DispatchGroup()
- let url = testURLs[0]
- stub(url, data: testImageData)
- for _ in 0...5 {
- group.enter()
- downloader.downloadImage(with: url) { result in
- XCTAssertNotNil(result.value)
- group.leave()
- }
- }
- group.notify(queue: .main, execute: exp.fulfill)
- waitForExpectations(timeout: 5, handler: nil)
- }
-
- func testDownloadWithModifyingRequest() {
- let exp = expectation(description: #function)
- let url = testURLs[0]
- stub(url, data: testImageData)
-
- modifier.url = url
-
- let someURL = URL(string: "some_strange_url")!
- downloader.downloadImage(with: someURL, options: [.requestModifier(modifier)]) { result in
- XCTAssertNotNil(result.value)
- XCTAssertEqual(result.value?.url, url)
- exp.fulfill()
- }
- waitForExpectations(timeout: 3, handler: nil)
- }
- func testDownloadWithModifyingRequestToNil() {
- let nilModifier = AnyModifier { _ in
- return nil
- }
- let exp = expectation(description: #function)
- let someURL = URL(string: "some_strange_url")!
- downloader.downloadImage(with: someURL, options: [.requestModifier(nilModifier)]) { result in
- XCTAssertNotNil(result.error)
- guard case .requestError(reason: .emptyRequest) = result.error! else {
- XCTFail()
- fatalError()
- }
- exp.fulfill()
- }
- waitForExpectations(timeout: 3, handler: nil)
- }
-
- func testServerInvalidStatusCode() {
- let exp = expectation(description: #function)
-
- let url = testURLs[0]
- stub(url, data: testImageData, statusCode: 404)
-
- downloader.downloadImage(with: url) { result in
- XCTAssertNotNil(result.error)
- XCTAssertTrue(result.error!.isInvalidResponseStatusCode(404))
- exp.fulfill()
- }
-
- waitForExpectations(timeout: 3, handler: nil)
- }
-
- // Since we could not receive one challage, no test for trusted hosts currently.
- // See http://stackoverflow.com/questions/27065372/ for more.
- func testSSLCertificateValidation() {
- LSNocilla.sharedInstance().stop()
- let exp = expectation(description: #function)
- let downloader = ImageDownloader(name: "ssl.test")
- let url = URL(string: "https://testssl-expire.disig.sk/Expired.png")!
-
- downloader.downloadImage(with: url) { result in
- XCTAssertNotNil(result.error)
- if case .responseError(reason: .URLSessionError(let error)) = result.error! {
- let nsError = error as NSError
- XCTAssert(nsError.code == NSURLErrorServerCertificateUntrusted ||
- nsError.code == NSURLErrorSecureConnectionFailed,
- "Error should be NSURLErrorServerCertificateUntrusted, but \(String(describing: error))")
- } else {
- XCTFail()
- }
- exp.fulfill()
- LSNocilla.sharedInstance().start()
- }
-
- waitForExpectations(timeout: 20) { error in
- XCTAssertNil(error, "\(String(describing: error))")
- LSNocilla.sharedInstance().start()
- }
- }
-
-
- func testDownloadResultErrorAndRetry() {
- let exp = expectation(description: #function)
-
- let url = testURLs[0]
- stub(url, errorCode: -1)
- downloader.downloadImage(with: url) { result in
- XCTAssertNotNil(result.error)
-
- LSNocilla.sharedInstance().clearStubs()
- stub(url, data: testImageData)
- // Retry the download
- self.downloader.downloadImage(with: url) { result in
- XCTAssertNil(result.error)
- exp.fulfill()
- }
- }
-
- waitForExpectations(timeout: 3, handler: nil)
- }
-
- func testDownloadEmptyURL() {
- let exp = expectation(description: #function)
-
- modifier.url = nil
-
- let url = URL(string: "http://onevcat.com")!
- downloader.downloadImage(
- with: url,
- options: [.requestModifier(modifier)],
- progressBlock: { received, totalSize in XCTFail("The progress block should not be called.") })
- {
- result in
- XCTAssertNotNil(result.error)
- if case .requestError(reason: .invalidURL(let request)) = result.error! {
- XCTAssertNil(request.url)
- } else {
- XCTFail()
- }
- exp.fulfill()
- }
-
- waitForExpectations(timeout: 3, handler: nil)
- }
-
- func testDownloadTaskProperty() {
- let task = downloader.downloadImage(with: URL(string: "1234")!)
- XCTAssertNotNil(task, "The task should exist.")
- }
-
- func testCancelDownloadTask() {
- let exp = expectation(description: #function)
- let url = testURLs[0]
- let stub = delayedStub(url, data: testImageData, length: 123)
-
- let task = downloader.downloadImage(
- with: url,
- progressBlock: { _, _ in XCTFail() })
- {
- result in
- XCTAssertNotNil(result.error)
- XCTAssertTrue(result.error!.isTaskCancelled)
- delay(0.1) { exp.fulfill() }
- }
-
- XCTAssertNotNil(task)
- task!.cancel()
- _ = stub.go()
-
- waitForExpectations(timeout: 3, handler: nil)
- }
- func testCancelOneDownloadTask() {
- let exp = expectation(description: #function)
- let url = testURLs[0]
- let stub = delayedStub(url, data: testImageData)
- let group = DispatchGroup()
- group.enter()
- let task1 = downloader.downloadImage(with: url) { result in
- XCTAssertNotNil(result.error)
- group.leave()
- }
- group.enter()
- _ = downloader.downloadImage(with: url) { result in
- XCTAssertNotNil(result.value?.image)
- group.leave()
- }
- task1?.cancel()
- delay(0.1) { _ = stub.go() }
- group.notify(queue: .main) {
- delay(0.1) { exp.fulfill() }
- }
- waitForExpectations(timeout: 3, handler: nil)
- }
-
- func testCancelAllDownloadTasks() {
- let exp = expectation(description: #function)
- let url1 = testURLs[0]
- let stub1 = delayedStub(url1, data: testImageData)
- let url2 = testURLs[1]
- let stub2 = delayedStub(url2, data: testImageData)
- let group = DispatchGroup()
- let urls = [url1, url1, url2]
- urls.forEach {
- group.enter()
- downloader.downloadImage(with: $0) { result in
- XCTAssertNotNil(result.error)
- XCTAssertTrue(result.error!.isTaskCancelled)
- group.leave()
- }
- }
- delay(0.1) {
- self.downloader.cancelAll()
- _ = stub1.go()
- _ = stub2.go()
- }
- group.notify(queue: .main) {
- delay(0.1) { exp.fulfill() }
- }
- waitForExpectations(timeout: 3, handler: nil)
- }
-
- func testCancelDownloadTaskForURL() {
- let exp = expectation(description: #function)
-
- let url1 = testURLs[0]
- let stub1 = delayedStub(url1, data: testImageData)
-
- let url2 = testURLs[1]
- let stub2 = delayedStub(url2, data: testImageData)
-
- let group = DispatchGroup()
-
- group.enter()
- downloader.downloadImage(with: url1) { result in
- XCTAssertNotNil(result.error)
- XCTAssertTrue(result.error!.isTaskCancelled)
- group.leave()
- }
-
- group.enter()
- downloader.downloadImage(with: url1) { result in
- XCTAssertNotNil(result.error)
- XCTAssertTrue(result.error!.isTaskCancelled)
- group.leave()
- }
-
- group.enter()
- downloader.downloadImage(with: url2) { result in
- XCTAssertNotNil(result.value)
- group.leave()
- }
-
- delay(0.1) {
- self.downloader.cancel(url: url1)
- _ = stub1.go()
- _ = stub2.go()
- }
-
- group.notify(queue: .main) {
- delay(0.1) { exp.fulfill() }
- }
- waitForExpectations(timeout: 3, handler: nil)
- }
-
- // Issue 532 https://github.com/onevcat/Kingfisher/issues/532#issuecomment-305644311
- func testCancelThenRestartSameDownload() {
- let exp = expectation(description: #function)
-
- let url = testURLs[0]
- let stub = delayedStub(url, data: testImageData, length: 123)
- let group = DispatchGroup()
-
- group.enter()
- let downloadTask = downloader.downloadImage(
- with: url,
- progressBlock: { _, _ in XCTFail()})
- {
- result in
- XCTAssertNotNil(result.error)
- XCTAssertTrue(result.error!.isTaskCancelled)
- group.leave()
- }
-
- XCTAssertNotNil(downloadTask)
-
- downloadTask!.cancel()
- _ = stub.go()
-
- group.enter()
- downloader.downloadImage(with: url) {
- result in
- XCTAssertNotNil(result.value)
- if let error = result.error {
- print(error)
- }
- group.leave()
- }
-
- group.notify(queue: .main) {
- delay(0.1) { exp.fulfill() }
- }
- waitForExpectations(timeout: 3, handler: nil)
- }
-
- func testDownloadTaskNil() {
- modifier.url = nil
- let downloadTask = downloader.downloadImage(with: URL(string: "url")!, options: [.requestModifier(modifier)])
- XCTAssertNil(downloadTask)
- }
-
- func testDownloadWithProcessor() {
- let exp = expectation(description: #function)
-
- let url = testURLs[0]
- stub(url, data: testImageData)
- let p = RoundCornerImageProcessor(cornerRadius: 40)
- let roundcornered = testImage.kf.image(withRoundRadius: 40, fit: testImage.kf.size)
-
- downloader.downloadImage(with: url, options: [.processor(p)]) { result in
- XCTAssertNotNil(result.value)
- let image = result.value!.image
- XCTAssertFalse(image.renderEqual(to: testImage))
- XCTAssertTrue(image.renderEqual(to: roundcornered))
- XCTAssertEqual(result.value!.originalData, testImageData)
- exp.fulfill()
- }
-
- waitForExpectations(timeout: 3, handler: nil)
- }
-
- func testDownloadWithDifferentProcessors() {
- let exp = expectation(description: #function)
- let url = testURLs[0]
- let stub = delayedStub(url, data: testImageData)
- let p1 = RoundCornerImageProcessor(cornerRadius: 40)
- let roundcornered = testImage.kf.image(withRoundRadius: 40, fit: testImage.kf.size)
- let p2 = BlurImageProcessor(blurRadius: 3.0)
- let blurred = testImage.kf.blurred(withRadius: 3.0)
-
- let group = DispatchGroup()
- group.enter()
- let task1 = downloader.downloadImage(with: url, options: [.processor(p1)]) { result in
- XCTAssertTrue(result.value!.image.renderEqual(to: roundcornered))
- group.leave()
- }
- group.enter()
- let task2 = downloader.downloadImage(with: url, options: [.processor(p2)]) { result in
- XCTAssertTrue(result.value!.image.renderEqual(to: blurred))
- group.leave()
- }
- XCTAssertNotNil(task1)
- XCTAssertEqual(task1?.sessionTask.task, task2?.sessionTask.task)
- _ = stub.go()
-
- group.notify(queue: .main, execute: exp.fulfill)
- waitForExpectations(timeout: 3, handler: nil)
- }
-
- func testDownloadedDataCouldBeModified() {
- let exp = expectation(description: #function)
-
- let url = testURLs[0]
- stub(url, data: testImageData)
- downloader.delegate = self
- downloader.downloadImage(with: url) { result in
- XCTAssertNil(result.value)
- XCTAssertNotNil(result.error)
- if case .responseError(reason: .dataModifyingFailed) = result.error! {
- } else {
- XCTFail()
- }
- self.downloader.delegate = nil
- exp.fulfill()
- }
- waitForExpectations(timeout: 3, handler: nil)
- }
- #if os(iOS) || os(tvOS) || os(watchOS)
- func testDownloadedImageCouldBeModified() {
- let exp = expectation(description: #function)
- let url = testURLs[0]
- stub(url, data: testImageData)
- var modifierCalled = false
- let modifier = AnyImageModifier { image in
- modifierCalled = true
- return image.withRenderingMode(.alwaysTemplate)
- }
- downloader.downloadImage(with: url, options: [.imageModifier(modifier)]) { result in
- XCTAssertTrue(modifierCalled)
- XCTAssertEqual(result.value?.image.renderingMode, .alwaysTemplate)
- exp.fulfill()
- }
- waitForExpectations(timeout: 3, handler: nil)
- }
- #endif
-
- func testDownloadTaskTakePriorityOption() {
- let exp = expectation(description: #function)
-
- let url = testURLs[0]
- stub(url, data: testImageData)
- let task = downloader.downloadImage(with: url, options: [.downloadPriority(URLSessionTask.highPriority)])
- {
- _ in
- exp.fulfill()
- }
- XCTAssertEqual(task?.sessionTask.task.priority, URLSessionTask.highPriority)
- waitForExpectations(timeout: 3, handler: nil)
- }
- }
- extension ImageDownloaderTests: ImageDownloaderDelegate {
- func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data? {
- return nil
- }
- }
- class URLModifier: ImageDownloadRequestModifier {
- var url: URL? = nil
- func modified(for request: URLRequest) -> URLRequest? {
- var r = request
- r.url = url
- return r
- }
- }
|