Skip to content

Commit 464ee61

Browse files
committed
Cancellation support
1 parent e39a8de commit 464ee61

6 files changed

Lines changed: 126 additions & 11 deletions

File tree

TaxonomyKit/Taxonomy.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public final class Taxonomy {
9999
default:
100100
callback(.failure(.unexpectedResponseError(code: response.statusCode)))
101101
}
102-
} else if let rootError = error {
102+
} else if let rootError = error as? NSError, rootError.code != NSURLErrorCancelled {
103103
callback(.failure(.networkError(underlyingError: rootError)))
104104
}
105105
}
@@ -152,7 +152,7 @@ public final class Taxonomy {
152152
default:
153153
callback(.failure(.unexpectedResponseError(code: response.statusCode)))
154154
}
155-
} else if let rootError = error {
155+
} else if let rootError = error as? NSError, rootError.code != NSURLErrorCancelled {
156156
callback(.failure(.networkError(underlyingError: rootError)))
157157
}
158158
}
@@ -239,7 +239,7 @@ public final class Taxonomy {
239239
default:
240240
callback(.failure(.unexpectedResponseError(code: response.statusCode)))
241241
}
242-
} else if let rootError = error {
242+
} else if let rootError = error as? NSError, rootError.code != NSURLErrorCancelled {
243243
callback(.failure(.networkError(underlyingError: rootError)))
244244
}
245245
}
@@ -319,7 +319,7 @@ public final class Taxonomy {
319319
} else {
320320
callback(.failure(.unexpectedResponseError(code: response.statusCode)))
321321
}
322-
} else if let rootError = error {
322+
} else if let rootError = error as? NSError, rootError.code != NSURLErrorCancelled {
323323
callback(.failure(.networkError(underlyingError: rootError)))
324324
}
325325
}

TaxonomyKitTests/DownloadTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,26 @@ final class DownloadTests: XCTestCase {
201201
}
202202
waitForExpectations(timeout: 10)
203203
}
204+
205+
func testCancellation() {
206+
Taxonomy._urlSession = MockSession()
207+
(Taxonomy._urlSession as! MockSession).wait = 5
208+
let response =
209+
HTTPURLResponse(url: URL(string: "https://gservera.com")!,
210+
statusCode: 200,
211+
httpVersion: "1.1",
212+
headerFields: [:])! as URLResponse
213+
let data = try! JSONSerialization.data(withJSONObject: ["Any JSON"])
214+
MockSession.mockResponse = (data, response, nil)
215+
let condition = expectation(description: "Finished")
216+
let dataTask = Taxonomy.downloadTaxon(withIdentifier: "anything") { result in
217+
XCTFail("Should have been canceled")
218+
219+
} as! MockSession.MockTask
220+
dataTask.cancel()
221+
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 7.0) {
222+
condition.fulfill()
223+
}
224+
waitForExpectations(timeout: 10)
225+
}
204226
}

TaxonomyKitTests/FindIdentifiersTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,26 @@ final class FindIdentifiersTests: XCTestCase {
138138
}
139139
waitForExpectations(timeout: 1000)
140140
}
141+
142+
func testCancellation() {
143+
Taxonomy._urlSession = MockSession()
144+
(Taxonomy._urlSession as! MockSession).wait = 5
145+
let response =
146+
HTTPURLResponse(url: URL(string: "https://gservera.com")!,
147+
statusCode: 200,
148+
httpVersion: "1.1",
149+
headerFields: [:])! as URLResponse
150+
let data = try! JSONSerialization.data(withJSONObject: ["Any JSON"])
151+
MockSession.mockResponse = (data, response, nil)
152+
let condition = expectation(description: "Finished")
153+
let dataTask = Taxonomy.findIdentifiers(for: "anything") { result in
154+
XCTFail("Should have been canceled")
155+
156+
} as! MockSession.MockTask
157+
dataTask.cancel()
158+
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 7.0) {
159+
condition.fulfill()
160+
}
161+
waitForExpectations(timeout: 10)
162+
}
141163
}

TaxonomyKitTests/LinkTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,26 @@ final class LinkTests: XCTestCase {
183183
}
184184
waitForExpectations(timeout: 10)
185185
}
186+
187+
func testCancellation() {
188+
Taxonomy._urlSession = MockSession()
189+
(Taxonomy._urlSession as! MockSession).wait = 5
190+
let response =
191+
HTTPURLResponse(url: URL(string: "https://gservera.com")!,
192+
statusCode: 200,
193+
httpVersion: "1.1",
194+
headerFields: [:])! as URLResponse
195+
let data = try! JSONSerialization.data(withJSONObject: ["Any JSON"])
196+
MockSession.mockResponse = (data, response, nil)
197+
let condition = expectation(description: "Finished")
198+
let dataTask = Taxonomy.findLinkedResources(for: "anything") { result in
199+
XCTFail("Should have been canceled")
200+
201+
} as! MockSession.MockTask
202+
dataTask.cancel()
203+
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 7.0) {
204+
condition.fulfill()
205+
}
206+
waitForExpectations(timeout: 10)
207+
}
186208
}

TaxonomyKitTests/MockURLSession.swift

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,30 @@ import Foundation
2828
import XCTest
2929

3030
/// A class used to mock URLSession objects for testing purposes.
31-
final class MockSession: URLSession {
31+
@objc public class MockSession: URLSession {
32+
33+
var wait: UInt32 = 0
3234

3335
var completionHandler:((Data?, URLResponse?, Error?) -> Void)?
3436

3537
static var mockResponse: (data: Data?, urlResponse: URLResponse?, error: Error?)
3638

37-
override class var shared: URLSession {
39+
override public class var shared: URLSession {
3840
return MockSession()
3941

4042
}
4143

42-
override func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
44+
override public func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
4345
self.completionHandler = completionHandler
44-
return MockTask(response: MockSession.mockResponse, completionHandler: completionHandler)
46+
let task = MockTask(response: MockSession.mockResponse, completionHandler: completionHandler)
47+
task.wait = wait
48+
return task
4549
}
4650

47-
final class MockTask: URLSessionDataTask {
51+
@objc public class MockTask: URLSessionDataTask {
52+
53+
var wait: UInt32 = 0
54+
var canceled = false
4855

4956
typealias Response = (data: Data?, urlResponse: URLResponse?, error: Error?)
5057
var mockResponse: Response
@@ -55,8 +62,27 @@ final class MockSession: URLSession {
5562
self.completionHandler = completionHandler
5663
}
5764

58-
override func resume() {
59-
completionHandler!(mockResponse.data, mockResponse.urlResponse, mockResponse.error)
65+
override public func resume() {
66+
DispatchQueue.global(qos: .background).async {
67+
let wait = self.wait
68+
if wait > 0 {
69+
sleep(wait)
70+
}
71+
DispatchQueue.main.async {
72+
if self.canceled {
73+
let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled, userInfo: nil)
74+
self.completionHandler!(nil, nil, error)
75+
} else {
76+
self.completionHandler!(self.mockResponse.data, self.mockResponse.urlResponse, self.mockResponse.error)
77+
}
78+
79+
}
80+
}
81+
82+
}
83+
84+
public override func cancel() {
85+
canceled = true
6086
}
6187

6288
}
@@ -66,6 +92,7 @@ final class MockSessionTests: XCTestCase {
6692

6793
func testMockSessionCreation() {
6894
let mockSession = MockSession.shared
95+
(mockSession as! MockSession).wait = 0
6996
XCTAssertTrue(mockSession is MockSession, "Failed")
7097
}
7198

TaxonomyKitTests/SpellingTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,26 @@ final class SpellingTests: XCTestCase {
166166
}
167167
waitForExpectations(timeout: 1000)
168168
}
169+
170+
func testCancellation() {
171+
Taxonomy._urlSession = MockSession()
172+
(Taxonomy._urlSession as! MockSession).wait = 5
173+
let response =
174+
HTTPURLResponse(url: URL(string: "https://gservera.com")!,
175+
statusCode: 200,
176+
httpVersion: "1.1",
177+
headerFields: [:])! as URLResponse
178+
let data = try! JSONSerialization.data(withJSONObject: ["Any JSON"])
179+
MockSession.mockResponse = (data, response, nil)
180+
let condition = expectation(description: "Finished")
181+
let dataTask = Taxonomy.findSimilarSpelledCandidates(for: "anything") { result in
182+
XCTFail("Should have been canceled")
183+
184+
} as! MockSession.MockTask
185+
dataTask.cancel()
186+
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 7.0) {
187+
condition.fulfill()
188+
}
189+
waitForExpectations(timeout: 10)
190+
}
169191
}

0 commit comments

Comments
 (0)