Skip to content

Commit c060289

Browse files
authored
fix(AsyncOperation): cancel() and isCancelled behavior (#801)
1 parent 71e4e1c commit c060289

File tree

3 files changed

+134
-5
lines changed

3 files changed

+134
-5
lines changed

Sources/AlgoliaSearchClient/Async/AsyncOperation.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,4 @@ open class AsyncOperation: Operation {
5454
main()
5555
state = .executing
5656
}
57-
58-
open override func cancel() {
59-
state = .finished
60-
}
61-
6257
}

Sources/AlgoliaSearchClient/Transport/HTTP/HTTPRequest.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class HTTPRequest<ResponseType: Decodable, Output>: AsyncOperation, ResultContai
7070

7171
guard !isCancelled else {
7272
Logger.loggingService.log(level: .debug, message: "Request was cancelled")
73+
result = .failure(SyncOperationError.cancelled)
7374
return
7475
}
7576

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import XCTest
2+
@testable import AlgoliaSearchClient
3+
4+
final class AsyncOperationTests: XCTestCase {
5+
6+
class TestOperation: AsyncOperation {
7+
override func main() {
8+
work()
9+
}
10+
11+
private func work() {
12+
DispatchQueue.global().asyncAfter(deadline: .now() + DispatchTimeInterval.milliseconds(200)) {
13+
self.state = .finished
14+
}
15+
}
16+
}
17+
18+
func testCancellation_withoutQueue() {
19+
let operation = TestOperation()
20+
XCTAssertTrue(operation.isReady)
21+
operation.cancel()
22+
XCTAssertTrue(operation.isCancelled)
23+
operation.start()
24+
XCTAssertTrue(operation.isFinished)
25+
}
26+
27+
func testCancellation_updatesIsCancelled_whenCancelledBeforeEnqueuing() {
28+
let operation = TestOperation()
29+
let queue = OperationQueue()
30+
operation.cancel()
31+
queue.addOperation(operation)
32+
queue.waitUntilAllOperationsAreFinished()
33+
XCTAssertTrue(operation.isCancelled)
34+
XCTAssertTrue(operation.isFinished)
35+
}
36+
37+
func testCancellation_updatesIsCancelled_whenCancelledAfterEnqueuing() {
38+
let operation = TestOperation()
39+
let queue = OperationQueue()
40+
queue.addOperation(operation)
41+
operation.cancel()
42+
queue.waitUntilAllOperationsAreFinished()
43+
XCTAssertTrue(operation.isCancelled)
44+
XCTAssertTrue(operation.isFinished)
45+
}
46+
47+
func testCancellation_updatesIsCancelled_whenOperationIsRunning() {
48+
let queue = OperationQueue()
49+
let operation = TestOperation()
50+
queue.addOperation(operation)
51+
Thread.sleep(forTimeInterval: 0.1)
52+
XCTAssertTrue(operation.isExecuting)
53+
operation.cancel()
54+
XCTAssertFalse(operation.isFinished)
55+
queue.waitUntilAllOperationsAreFinished()
56+
XCTAssertTrue(operation.isCancelled)
57+
XCTAssertTrue(operation.isFinished)
58+
}
59+
60+
func testCancellation_ignoresCancellation_whenOperationIsFinished() {
61+
let queue = OperationQueue()
62+
let operation = TestOperation()
63+
queue.addOperation(operation)
64+
queue.waitUntilAllOperationsAreFinished()
65+
XCTAssertTrue(operation.isFinished)
66+
operation.cancel()
67+
XCTAssertFalse(operation.isCancelled)
68+
}
69+
70+
// Behavior of standard Operation without subclassing.
71+
func testCancellation_ofBlockOperation_withoutQueue() {
72+
let operation = BlockOperation {
73+
Thread.sleep(forTimeInterval: 0.1)
74+
}
75+
XCTAssertTrue(operation.isReady)
76+
operation.cancel()
77+
XCTAssertTrue(operation.isCancelled)
78+
operation.start()
79+
XCTAssertTrue(operation.isFinished)
80+
}
81+
82+
func testCancellation_ofBlockOperation_updatesIsCancelled_whenCancelledBeforeEnqueuing() {
83+
let operation = BlockOperation {
84+
Thread.sleep(forTimeInterval: 0.1)
85+
}
86+
let queue = OperationQueue()
87+
operation.cancel()
88+
queue.addOperation(operation)
89+
queue.waitUntilAllOperationsAreFinished()
90+
XCTAssertTrue(operation.isCancelled)
91+
XCTAssertTrue(operation.isFinished)
92+
}
93+
94+
func testCancellation_ofBlockOperation_updatesIsCancelled_whenCancelledAfterEnqueuing() {
95+
let operation = BlockOperation {
96+
Thread.sleep(forTimeInterval: 0.1)
97+
}
98+
let queue = OperationQueue()
99+
queue.addOperation(operation)
100+
operation.cancel()
101+
queue.waitUntilAllOperationsAreFinished()
102+
XCTAssertTrue(operation.isCancelled)
103+
XCTAssertTrue(operation.isFinished)
104+
}
105+
106+
func testCancellation_ofBlockOperation_updatesIsCancelled_whenOperationIsRunning() {
107+
let queue = OperationQueue()
108+
let operation = BlockOperation {
109+
Thread.sleep(forTimeInterval: 0.2)
110+
}
111+
queue.addOperation(operation)
112+
Thread.sleep(forTimeInterval: 0.1)
113+
XCTAssertTrue(operation.isExecuting)
114+
operation.cancel()
115+
XCTAssertFalse(operation.isFinished)
116+
queue.waitUntilAllOperationsAreFinished()
117+
XCTAssertTrue(operation.isCancelled)
118+
XCTAssertTrue(operation.isFinished)
119+
}
120+
121+
func testCancellation_ofBlockOperation_ignoresCancellation_whenOperationIsFinished() {
122+
let queue = OperationQueue()
123+
let operation = BlockOperation {
124+
Thread.sleep(forTimeInterval: 0.1)
125+
}
126+
queue.addOperation(operation)
127+
queue.waitUntilAllOperationsAreFinished()
128+
XCTAssertTrue(operation.isFinished)
129+
operation.cancel()
130+
XCTAssertFalse(operation.isCancelled)
131+
}
132+
133+
}

0 commit comments

Comments
 (0)