// // MainThreadOperationTests.swift // RSCoreTests // // Created by Brent Simmons on 1/17/20. // Copyright © 2020 Ranchero Software, LLC. All rights reserved. // import XCTest @testable import RSCore class MainThreadOperationTests: XCTestCase { func testSingleOperation() { let queue = MainThreadOperationQueue() var operationDidRun = false let singleOperationDidRunExpectation = expectation(description: "singleOperationDidRun") let operation = MainThreadBlockOperation { operationDidRun = true XCTAssertTrue(operationDidRun) singleOperationDidRunExpectation.fulfill() } queue.add(operation) waitForExpectations(timeout: 1.0, handler: nil) XCTAssertTrue(queue.pendingOperationsCount == 0) } func testOperationAndDependency() { let queue = MainThreadOperationQueue() var operationIndex = 0 let parentOperationExpectation = expectation(description: "parentOperation") let parentOperation = MainThreadBlockOperation { XCTAssertTrue(operationIndex == 0) operationIndex += 1 parentOperationExpectation.fulfill() } let childOperationExpectation = expectation(description: "childOperation") let childOperation = MainThreadBlockOperation { XCTAssertTrue(operationIndex == 1) operationIndex += 1 childOperationExpectation.fulfill() } queue.make(childOperation, dependOn: parentOperation) queue.add(parentOperation) queue.add(childOperation) waitForExpectations(timeout: 1.0, handler: nil) XCTAssertTrue(queue.pendingOperationsCount == 0) } func testOperationAndDependencyAddedOutOfOrder() { let queue = MainThreadOperationQueue() var operationIndex = 0 let parentOperationExpectation = expectation(description: "parentOperation") let parentOperation = MainThreadBlockOperation { XCTAssertTrue(operationIndex == 0) operationIndex += 1 parentOperationExpectation.fulfill() } let childOperationExpectation = expectation(description: "childOperation") let childOperation = MainThreadBlockOperation { XCTAssertTrue(operationIndex == 1) operationIndex += 1 childOperationExpectation.fulfill() } queue.make(childOperation, dependOn: parentOperation) queue.add(childOperation) queue.add(parentOperation) waitForExpectations(timeout: 1.0, handler: nil) XCTAssertTrue(queue.pendingOperationsCount == 0) } func testOperationAndTwoDependenciesAddedOutOfOrder() { let queue = MainThreadOperationQueue() var operationIndex = 0 let parentOperationExpectation = expectation(description: "parentOperation") let parentOperation = MainThreadBlockOperation { XCTAssertTrue(operationIndex == 0) operationIndex += 1 parentOperationExpectation.fulfill() } let childOperationExpectation = expectation(description: "childOperation") let childOperation = MainThreadBlockOperation { XCTAssertTrue(operationIndex == 1) operationIndex += 1 childOperationExpectation.fulfill() } let childOperationExpectation2 = expectation(description: "childOperation2") let childOperation2 = MainThreadBlockOperation { XCTAssertTrue(operationIndex == 2) operationIndex += 1 childOperationExpectation2.fulfill() } queue.make(childOperation, dependOn: parentOperation) queue.make(childOperation2, dependOn: parentOperation) queue.add(childOperation) queue.add(childOperation2) queue.add(parentOperation) waitForExpectations(timeout: 1.0, handler: nil) XCTAssertTrue(queue.pendingOperationsCount == 0) } func testChildOperationWithTwoDependencies() { let queue = MainThreadOperationQueue() var operationIndex = 0 let parentOperationExpectation = expectation(description: "parentOperation") let parentOperation = MainThreadBlockOperation { XCTAssertTrue(operationIndex == 0) operationIndex += 1 parentOperationExpectation.fulfill() } let parentOperationExpectation2 = expectation(description: "parentOperation2") let parentOperation2 = MainThreadBlockOperation { XCTAssertTrue(operationIndex == 1) operationIndex += 1 parentOperationExpectation2.fulfill() } let childOperationExpectation = expectation(description: "childOperation") let childOperation = MainThreadBlockOperation { XCTAssertTrue(operationIndex == 2) operationIndex += 1 childOperationExpectation.fulfill() } queue.make(childOperation, dependOn: parentOperation) queue.make(childOperation, dependOn: parentOperation2) queue.add(childOperation) queue.add(parentOperation) queue.add(parentOperation2) waitForExpectations(timeout: 1.0, handler: nil) XCTAssertTrue(queue.pendingOperationsCount == 0) } func testAddingManyOperations() { let queue = MainThreadOperationQueue() let operationsCount = 1000 var operationIndex = 0 var operations = [MainThreadBlockOperation]() for i in 0.. ())? init(didCancelExpectation: XCTestExpectation) { self.didCancelExpectation = didCancelExpectation } func run() { guard let block = didStartRunBlock else { XCTFail("Unable to test cancelation of running operation.") return } block() DispatchQueue.main.async { [weak self] in if let self = self { XCTAssert(false, "This code should not be executed.") self.operationDelegate?.operationDidComplete(self) } } } } let queue = MainThreadOperationQueue() let didCancelExpectation = expectation(description: "Did Cancel Operation") let completionBlockDidRunExpectation = expectation(description: "Completion Block Did Run") // Using an Optional allows us to control this scope's ownership of the operation. var operation: SlowFinishingOperation? = { let operation = SlowFinishingOperation(didCancelExpectation: didCancelExpectation) operation.didStartRunBlock = { [weak operation] in guard let operation = operation else { XCTFail("Could not cancel slow finishing operation because it seems to be prematurely disowned.") return } queue.cancelOperation(operation) } operation.completionBlock = { _ in XCTAssertTrue(Thread.isMainThread) completionBlockDidRunExpectation.fulfill() } return operation }() // The queue should take ownership of the operation (asserted below). queue.add(operation!) // Verify something other than this scope has ownership of the operation. weak var addedOperation = operation! operation = nil XCTAssertNil(operation) XCTAssertNotNil(addedOperation, "Perhaps the queue did not take ownership of the operation?") let didDisownOperationExpectation = expectation(description: "Did Disown Operation") DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak addedOperation] in XCTAssertNil(addedOperation, "Perhaps the queue did not disown the operation?") didDisownOperationExpectation.fulfill() } // Wait for the operation to start running, cancel and complete. waitForExpectations(timeout: 1) } func testCancellingOperationsWithName() { let queue = MainThreadOperationQueue() queue.suspend() let operationsCount = 100 for i in 0..