Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(AsyncOperation): cancel() and isCancelled behavior #801

Merged
merged 9 commits into from
Aug 15, 2023
5 changes: 0 additions & 5 deletions Sources/AlgoliaSearchClient/Async/AsyncOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,4 @@ open class AsyncOperation: Operation {
main()
state = .executing
}

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

}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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)
}

}
Loading