Skip to content

Commit

Permalink
fix(AsyncOperation): cancel() and isCancelled behavior (#801)
Browse files Browse the repository at this point in the history
  • Loading branch information
KaiOelfke committed Aug 15, 2023
1 parent 71e4e1c commit c060289
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 5 deletions.
5 changes: 0 additions & 5 deletions Sources/AlgoliaSearchClient/Async/AsyncOperation.swift
Expand Up @@ -54,9 +54,4 @@ open class AsyncOperation: Operation {
main()
state = .executing
}

open override func cancel() {
state = .finished
}

}
Expand Up @@ -70,6 +70,7 @@ class HTTPRequest<ResponseType: Decodable, Output>: AsyncOperation, ResultContai

guard !isCancelled else {
Logger.loggingService.log(level: .debug, message: "Request was cancelled")
result = .failure(SyncOperationError.cancelled)
return
}

Expand Down
133 changes: 133 additions & 0 deletions Tests/AlgoliaSearchClientTests/Unit/AsyncOperationTests.swift
@@ -0,0 +1,133 @@
import XCTest
@testable import AlgoliaSearchClient

final class AsyncOperationTests: XCTestCase {

class TestOperation: AsyncOperation {
override func main() {
work()
}

private func work() {
DispatchQueue.global().asyncAfter(deadline: .now() + DispatchTimeInterval.milliseconds(200)) {
self.state = .finished
}
}
}

func testCancellation_withoutQueue() {
let operation = TestOperation()
XCTAssertTrue(operation.isReady)
operation.cancel()
XCTAssertTrue(operation.isCancelled)
operation.start()
XCTAssertTrue(operation.isFinished)
}

func testCancellation_updatesIsCancelled_whenCancelledBeforeEnqueuing() {
let operation = TestOperation()
let queue = OperationQueue()
operation.cancel()
queue.addOperation(operation)
queue.waitUntilAllOperationsAreFinished()
XCTAssertTrue(operation.isCancelled)
XCTAssertTrue(operation.isFinished)
}

func testCancellation_updatesIsCancelled_whenCancelledAfterEnqueuing() {
let operation = TestOperation()
let queue = OperationQueue()
queue.addOperation(operation)
operation.cancel()
queue.waitUntilAllOperationsAreFinished()
XCTAssertTrue(operation.isCancelled)
XCTAssertTrue(operation.isFinished)
}

func testCancellation_updatesIsCancelled_whenOperationIsRunning() {
let queue = OperationQueue()
let operation = TestOperation()
queue.addOperation(operation)
Thread.sleep(forTimeInterval: 0.1)
XCTAssertTrue(operation.isExecuting)
operation.cancel()
XCTAssertFalse(operation.isFinished)
queue.waitUntilAllOperationsAreFinished()
XCTAssertTrue(operation.isCancelled)
XCTAssertTrue(operation.isFinished)
}

func testCancellation_ignoresCancellation_whenOperationIsFinished() {
let queue = OperationQueue()
let operation = TestOperation()
queue.addOperation(operation)
queue.waitUntilAllOperationsAreFinished()
XCTAssertTrue(operation.isFinished)
operation.cancel()
XCTAssertFalse(operation.isCancelled)
}

// Behavior of standard Operation without subclassing.
func testCancellation_ofBlockOperation_withoutQueue() {
let operation = BlockOperation {
Thread.sleep(forTimeInterval: 0.1)
}
XCTAssertTrue(operation.isReady)
operation.cancel()
XCTAssertTrue(operation.isCancelled)
operation.start()
XCTAssertTrue(operation.isFinished)
}

func testCancellation_ofBlockOperation_updatesIsCancelled_whenCancelledBeforeEnqueuing() {
let operation = BlockOperation {
Thread.sleep(forTimeInterval: 0.1)
}
let queue = OperationQueue()
operation.cancel()
queue.addOperation(operation)
queue.waitUntilAllOperationsAreFinished()
XCTAssertTrue(operation.isCancelled)
XCTAssertTrue(operation.isFinished)
}

func testCancellation_ofBlockOperation_updatesIsCancelled_whenCancelledAfterEnqueuing() {
let operation = BlockOperation {
Thread.sleep(forTimeInterval: 0.1)
}
let queue = OperationQueue()
queue.addOperation(operation)
operation.cancel()
queue.waitUntilAllOperationsAreFinished()
XCTAssertTrue(operation.isCancelled)
XCTAssertTrue(operation.isFinished)
}

func testCancellation_ofBlockOperation_updatesIsCancelled_whenOperationIsRunning() {
let queue = OperationQueue()
let operation = BlockOperation {
Thread.sleep(forTimeInterval: 0.2)
}
queue.addOperation(operation)
Thread.sleep(forTimeInterval: 0.1)
XCTAssertTrue(operation.isExecuting)
operation.cancel()
XCTAssertFalse(operation.isFinished)
queue.waitUntilAllOperationsAreFinished()
XCTAssertTrue(operation.isCancelled)
XCTAssertTrue(operation.isFinished)
}

func testCancellation_ofBlockOperation_ignoresCancellation_whenOperationIsFinished() {
let queue = OperationQueue()
let operation = BlockOperation {
Thread.sleep(forTimeInterval: 0.1)
}
queue.addOperation(operation)
queue.waitUntilAllOperationsAreFinished()
XCTAssertTrue(operation.isFinished)
operation.cancel()
XCTAssertFalse(operation.isCancelled)
}

}

0 comments on commit c060289

Please sign in to comment.