diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2023-04-26 18:07:58 +0200 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2023-05-03 16:41:55 +0200 |
| commit | f3760caf18da794e3aaa95a2f40a492d04a3f5f8 (patch) | |
| tree | 5db7476a37518bd1bfc99208098d4fbd3409980d | |
| parent | 3d4e85a949c2d92a9484e354072df221c477bd63 (diff) | |
| download | mullvadvpn-f3760caf18da794e3aaa95a2f40a492d04a3f5f8.tar.xz mullvadvpn-f3760caf18da794e3aaa95a2f40a492d04a3f5f8.zip | |
Operations: add new tests
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 16 | ||||
| -rw-r--r-- | ios/Operations/AsyncOperation.swift | 5 | ||||
| -rw-r--r-- | ios/OperationsTests/AsyncBlockOperationTests.swift | 100 | ||||
| -rw-r--r-- | ios/OperationsTests/AsyncResultBlockOperationTests.swift | 75 | ||||
| -rw-r--r-- | ios/OperationsTests/OperationCancellationTests.swift | 35 | ||||
| -rw-r--r-- | ios/OperationsTests/OperationObserverTests.swift | 4 | ||||
| -rw-r--r-- | ios/OperationsTests/TransformOperationTests.swift | 93 |
7 files changed, 287 insertions, 41 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 08a215b1c9..9e3cabb024 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -116,7 +116,6 @@ 585E820327F3285E00939F0E /* SendStoreReceiptOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585E820227F3285E00939F0E /* SendStoreReceiptOperation.swift */; }; 58607A4D2947287800BC467D /* AccountExpiryInAppNotificationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58607A4C2947287800BC467D /* AccountExpiryInAppNotificationProvider.swift */; }; 586168692976F6BD00EF8598 /* DisplayError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586168682976F6BD00EF8598 /* DisplayError.swift */; }; - 586250BB29E6F8F300F4B521 /* OperationCancellationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586250BA29E6F8F300F4B521 /* OperationCancellationTests.swift */; }; 5862805422428EF100F5A6E1 /* TranslucentButtonBlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5862805322428EF100F5A6E1 /* TranslucentButtonBlurView.swift */; }; 5864211F29F04CED00822139 /* UIBarButtonItem+Blocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5864211E29F04CED00822139 /* UIBarButtonItem+Blocks.swift */; }; 5864859929A0D028006C5743 /* FormsheetPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5864859829A0D028006C5743 /* FormsheetPresentationController.swift */; }; @@ -221,6 +220,9 @@ 58ACF64B26553C3F00ACE4B7 /* SettingsSwitchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58ACF64A26553C3F00ACE4B7 /* SettingsSwitchCell.swift */; }; 58ACF64D26567A5000ACE4B7 /* CustomSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58ACF64C26567A4F00ACE4B7 /* CustomSwitch.swift */; }; 58ACF64F26567A7100ACE4B7 /* CustomSwitchContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58ACF64E26567A7100ACE4B7 /* CustomSwitchContainer.swift */; }; + 58AFC99529F96F7B000829DE /* AsyncBlockOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58AFC99429F96F7B000829DE /* AsyncBlockOperationTests.swift */; }; + 58AFC99729F9753D000829DE /* AsyncResultBlockOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58AFC99629F9753D000829DE /* AsyncResultBlockOperationTests.swift */; }; + 58AFC99929F97856000829DE /* TransformOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58AFC99829F97856000829DE /* TransformOperationTests.swift */; }; 58B0A2A8238EE68200BC001D /* RelaySelectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 584B26F3237434D00073B10E /* RelaySelectorTests.swift */; }; 58B26E1E2943514300D5980C /* InAppNotificationDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B26E1D2943514300D5980C /* InAppNotificationDescriptor.swift */; }; 58B26E22294351EA00D5980C /* InAppNotificationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B26E21294351EA00D5980C /* InAppNotificationProvider.swift */; }; @@ -748,7 +750,6 @@ 585E820227F3285E00939F0E /* SendStoreReceiptOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendStoreReceiptOperation.swift; sourceTree = "<group>"; }; 58607A4C2947287800BC467D /* AccountExpiryInAppNotificationProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountExpiryInAppNotificationProvider.swift; sourceTree = "<group>"; }; 586168682976F6BD00EF8598 /* DisplayError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayError.swift; sourceTree = "<group>"; }; - 586250BA29E6F8F300F4B521 /* OperationCancellationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationCancellationTests.swift; sourceTree = "<group>"; }; 5862805322428EF100F5A6E1 /* TranslucentButtonBlurView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslucentButtonBlurView.swift; sourceTree = "<group>"; }; 5864211E29F04CED00822139 /* UIBarButtonItem+Blocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Blocks.swift"; sourceTree = "<group>"; }; 5864859829A0D028006C5743 /* FormsheetPresentationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormsheetPresentationController.swift; sourceTree = "<group>"; }; @@ -851,6 +852,9 @@ 58ACF64C26567A4F00ACE4B7 /* CustomSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSwitch.swift; sourceTree = "<group>"; }; 58ACF64E26567A7100ACE4B7 /* CustomSwitchContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSwitchContainer.swift; sourceTree = "<group>"; }; 58AEEF642344A36000C9BBD5 /* KeychainError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainError.swift; sourceTree = "<group>"; }; + 58AFC99429F96F7B000829DE /* AsyncBlockOperationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncBlockOperationTests.swift; sourceTree = "<group>"; }; + 58AFC99629F9753D000829DE /* AsyncResultBlockOperationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncResultBlockOperationTests.swift; sourceTree = "<group>"; }; + 58AFC99829F97856000829DE /* TransformOperationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransformOperationTests.swift; sourceTree = "<group>"; }; 58B0A2A0238EE67E00BC001D /* MullvadVPNTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MullvadVPNTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 58B0A2A4238EE67E00BC001D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 58B26E1D2943514300D5980C /* InAppNotificationDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppNotificationDescriptor.swift; sourceTree = "<group>"; }; @@ -1646,7 +1650,9 @@ 58DF5B772852178600E92647 /* OperationInputInjectionTests.swift */, 583E1E292848DF67004838B3 /* OperationObserverTests.swift */, 580CBFB72848D503007878F0 /* OperationConditionTests.swift */, - 586250BA29E6F8F300F4B521 /* OperationCancellationTests.swift */, + 58AFC99429F96F7B000829DE /* AsyncBlockOperationTests.swift */, + 58AFC99629F9753D000829DE /* AsyncResultBlockOperationTests.swift */, + 58AFC99829F97856000829DE /* TransformOperationTests.swift */, ); path = OperationsTests; sourceTree = "<group>"; @@ -2567,9 +2573,11 @@ files = ( 589A455F28E094BF00565204 /* OperationConditionTests.swift in Sources */, 589A455E28E094BF00565204 /* OperationInputInjectionTests.swift in Sources */, - 586250BB29E6F8F300F4B521 /* OperationCancellationTests.swift in Sources */, + 58AFC99529F96F7B000829DE /* AsyncBlockOperationTests.swift in Sources */, + 58AFC99729F9753D000829DE /* AsyncResultBlockOperationTests.swift in Sources */, 589A455C28E094BF00565204 /* OperationSmokeTests.swift in Sources */, 589A455D28E094BF00565204 /* OperationObserverTests.swift in Sources */, + 58AFC99929F97856000829DE /* TransformOperationTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/Operations/AsyncOperation.swift b/ios/Operations/AsyncOperation.swift index 2ca049030c..ca11e86d7e 100644 --- a/ios/Operations/AsyncOperation.swift +++ b/ios/Operations/AsyncOperation.swift @@ -436,6 +436,11 @@ extension OperationBlockObserverSupport where Self: AsyncOperation { addBlockObserver(OperationBlockObserver(didFinish: fn)) } + /// Add observer responding to start event. + public func onStart(_ fn: @escaping (Self) -> Void) { + addBlockObserver(OperationBlockObserver(didStart: fn)) + } + /// Add block-based observer. public func addBlockObserver(_ observer: OperationBlockObserver<Self>) { addObserver(observer) diff --git a/ios/OperationsTests/AsyncBlockOperationTests.swift b/ios/OperationsTests/AsyncBlockOperationTests.swift new file mode 100644 index 0000000000..03373a57bf --- /dev/null +++ b/ios/OperationsTests/AsyncBlockOperationTests.swift @@ -0,0 +1,100 @@ +// +// AsyncBlockOperationTests.swift +// OperationsTests +// +// Created by pronebird on 26/04/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import MullvadTypes +import Operations +import XCTest + +final class AsyncBlockOperationTests: XCTestCase { + let operationQueue = AsyncOperationQueue() + + func testBlockOperation() { + let executionExpectation = expectation(description: "Should execute") + let finishExpectation = expectation(description: "Should finish") + + let operation = AsyncBlockOperation(block: { op in + executionExpectation.fulfill() + op.finish() + }) + + operation.completionBlock = { + finishExpectation.fulfill() + } + + operationQueue.addOperation(operation) + + waitForExpectations(timeout: 1) + } + + func testSynchronousBlockOperation() { + let executionExpectation = expectation(description: "Should execute") + let finishExpectation = expectation(description: "Should finish") + + let operation = AsyncBlockOperation { + executionExpectation.fulfill() + } + + operation.completionBlock = { + finishExpectation.fulfill() + } + + operationQueue.addOperation(operation) + + waitForExpectations(timeout: 1) + } + + func testCancellableTaskBlockOperation() { + let executionExpectation = expectation(description: "Should execute") + let cancelExpectation = expectation(description: "Should cancel") + let finishExpectation = expectation(description: "Should finish") + + let operation = AsyncBlockOperation(cancellableTask: { op in + executionExpectation.fulfill() + + return AnyCancellable { + cancelExpectation.fulfill() + + op.finish() + } + }) + + operation.completionBlock = { + finishExpectation.fulfill() + } + + operation.onStart { op in + op.cancel() + } + + operationQueue.addOperation(operation) + + waitForExpectations(timeout: 100) + } + + func testCancellationShouldNotFireBeforeOperationIsEnqueued() throws { + let expect = expectation(description: "Cancellation should not fire.") + expect.isInverted = true + + let operation = AsyncBlockOperation {} + operation.onCancel { _ in expect.fulfill() } + operation.cancel() + + waitForExpectations(timeout: 1) + } + + func testCancellationShouldFireAfterCancelledOperationIsEnqueued() throws { + let expect = expectation(description: "Cancellation should fire.") + + let operation = AsyncBlockOperation {} + operation.onCancel { _ in expect.fulfill() } + operation.cancel() + operationQueue.addOperation(operation) + + waitForExpectations(timeout: 1) + } +} diff --git a/ios/OperationsTests/AsyncResultBlockOperationTests.swift b/ios/OperationsTests/AsyncResultBlockOperationTests.swift new file mode 100644 index 0000000000..160e7b2580 --- /dev/null +++ b/ios/OperationsTests/AsyncResultBlockOperationTests.swift @@ -0,0 +1,75 @@ +// +// AsyncResultBlockOperationTests.swift +// OperationsTests +// +// Created by pronebird on 26/04/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import MullvadTypes +import Operations +import XCTest + +final class AsyncResultBlockOperationTests: XCTestCase { + let operationQueue = AsyncOperationQueue() + + func testBlockOperation() { + let expectation = expectation(description: "Should finish") + + let operation = ResultBlockOperation<Bool> { op in + op.finish(result: .success(true)) + } + + operation.onFinish { op, error in + XCTAssertEqual(op.result?.value, true) + expectation.fulfill() + } + + operationQueue.addOperation(operation) + + waitForExpectations(timeout: 1) + } + + func testThrowingBlockOperation() { + let expectation = expectation(description: "Should finish") + + let operation = ResultBlockOperation { + throw URLError(.badURL) + } + + operation.onFinish { op, error in + XCTAssertEqual(op.result?.error as? URLError, URLError(.badURL)) + XCTAssertEqual(error as? URLError, URLError(.badURL)) + + expectation.fulfill() + } + + operationQueue.addOperation(operation) + + waitForExpectations(timeout: 1) + } + + func testCancellableTaskOperation() { + let expectation = expectation(description: "Should finish") + + let operation = ResultBlockOperation<Bool>(cancellableTask: { op in + return AnyCancellable { + op.finish(result: .failure(URLError(.cancelled))) + } + }) + + operation.onStart { op in + op.cancel() + } + + operation.onFinish { op, error in + XCTAssertEqual(op.result?.error as? URLError, URLError(.cancelled)) + XCTAssertEqual(error as? URLError, URLError(.cancelled)) + expectation.fulfill() + } + + operationQueue.addOperation(operation) + + waitForExpectations(timeout: 1) + } +} diff --git a/ios/OperationsTests/OperationCancellationTests.swift b/ios/OperationsTests/OperationCancellationTests.swift deleted file mode 100644 index 7313bc83b2..0000000000 --- a/ios/OperationsTests/OperationCancellationTests.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// OperationCancellationTests.swift -// OperationsTests -// -// Created by pronebird on 12/04/2023. -// Copyright © 2023 Mullvad VPN AB. All rights reserved. -// - -import Operations -import XCTest - -final class OperationCancellationTests: XCTestCase { - func testCancellationShouldNotFireBeforeOperationIsEnqueued() throws { - let expect = expectation(description: "Cancellation should not fire.") - expect.isInverted = true - - let operation = AsyncBlockOperation {} - operation.onCancel { _ in expect.fulfill() } - operation.cancel() - - waitForExpectations(timeout: 1) - } - - func testCancellationShouldFireAfterCancelledOperationIsEnqueued() throws { - let expect = expectation(description: "Cancellation should fire.") - - let operationQueue = AsyncOperationQueue() - let operation = AsyncBlockOperation {} - operation.onCancel { _ in expect.fulfill() } - operation.cancel() - operationQueue.addOperation(operation) - - waitForExpectations(timeout: 1) - } -} diff --git a/ios/OperationsTests/OperationObserverTests.swift b/ios/OperationsTests/OperationObserverTests.swift index c246cdc526..de334be21a 100644 --- a/ios/OperationsTests/OperationObserverTests.swift +++ b/ios/OperationsTests/OperationObserverTests.swift @@ -17,7 +17,7 @@ class OperationObserverTests: XCTestCase { expectDidCancel.isInverted = true let expectDidFinish = expectation(description: "didAttach handler") - let operation = AsyncBlockOperation() + let operation = AsyncBlockOperation {} operation.addBlockObserver(OperationBlockObserver( didAttach: { op in expectDidAttach.fulfill() @@ -44,7 +44,7 @@ class OperationObserverTests: XCTestCase { let expectDidCancel = expectation(description: "didCancel handler") let expectDidFinish = expectation(description: "didAttach handler") - let operation = AsyncBlockOperation() + let operation = AsyncBlockOperation {} operation.addBlockObserver(OperationBlockObserver( didAttach: { op in expectDidAttach.fulfill() diff --git a/ios/OperationsTests/TransformOperationTests.swift b/ios/OperationsTests/TransformOperationTests.swift new file mode 100644 index 0000000000..20eabc77e6 --- /dev/null +++ b/ios/OperationsTests/TransformOperationTests.swift @@ -0,0 +1,93 @@ +// +// TransformOperationTests.swift +// OperationsTests +// +// Created by pronebird on 26/04/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import MullvadTypes +import Operations +import XCTest + +final class TransformOperationTests: XCTestCase { + let operationQueue = AsyncOperationQueue() + + func testBlockTransformOperation() { + let finishExpectation = expectation(description: "Should finish") + + let transform = TransformOperation(input: Int.zero) { input, op in + op.finish(result: .success(input + 1)) + } + + transform.onFinish { op, error in + XCTAssertEqual(op.result?.value, 1) + + finishExpectation.fulfill() + } + + operationQueue.addOperation(transform) + + waitForExpectations(timeout: 1) + } + + func testThrowingBlockTransformOperation() { + let finishExpectation = expectation(description: "Should finish") + + let transform = TransformOperation(input: Int.zero) { value in + throw URLError(.badURL) + } + + transform.onFinish { op, error in + XCTAssertEqual(error as? URLError, URLError(.badURL)) + + finishExpectation.fulfill() + } + + operationQueue.addOperation(transform) + + waitForExpectations(timeout: 1) + } + + func testCancellableTaskBlockTranasformOperation() { + let finishExpectation = expectation(description: "Should finish") + + let transform = TransformOperation<Int, Int>(input: Int.zero, cancellableTask: { _, op in + return AnyCancellable { + op.finish(result: .failure(URLError(.cancelled))) + } + }) + + transform.onStart { op in + op.cancel() + } + + transform.onFinish { op, error in + XCTAssertEqual(error as? URLError, URLError(.cancelled)) + + finishExpectation.fulfill() + } + + operationQueue.addOperation(transform) + + waitForExpectations(timeout: 1) + } + + func testShouldFailWithUnsatisfiedRequirement() { + let finishExpectation = expectation(description: "Should finish") + + let transform = TransformOperation<Int, Int> { input, op in + op.finish(result: .success(input)) + } + + transform.onFinish { _, error in + XCTAssertEqual(error as? OperationError, .unsatisfiedRequirement) + + finishExpectation.fulfill() + } + + operationQueue.addOperation(transform) + + waitForExpectations(timeout: 1) + } +} |
