summaryrefslogtreecommitdiffhomepage
path: root/ios/PacketTunnelCoreTests
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2023-10-11 16:19:25 +0200
committerBug Magnet <marco.nikic@mullvad.net>2023-10-13 14:49:41 +0200
commit40f7b0d6fc509e3ecde4bc61ae80eec69d77eeeb (patch)
tree57914c93f42f1dba0f8dd05eccde74c77e8cbed5 /ios/PacketTunnelCoreTests
parent127bd501fbcc7d1eb27f8f39e603bb7bf9e9b472 (diff)
downloadmullvadvpn-40f7b0d6fc509e3ecde4bc61ae80eec69d77eeeb.tar.xz
mullvadvpn-40f7b0d6fc509e3ecde4bc61ae80eec69d77eeeb.zip
Ensure atomicity between (re)connection attempts
Diffstat (limited to 'ios/PacketTunnelCoreTests')
-rw-r--r--ios/PacketTunnelCoreTests/ActorTests.swift94
-rw-r--r--ios/PacketTunnelCoreTests/Mocks/TunnelMonitorStub.swift2
2 files changed, 95 insertions, 1 deletions
diff --git a/ios/PacketTunnelCoreTests/ActorTests.swift b/ios/PacketTunnelCoreTests/ActorTests.swift
index 7b8cd69538..c82119759b 100644
--- a/ios/PacketTunnelCoreTests/ActorTests.swift
+++ b/ios/PacketTunnelCoreTests/ActorTests.swift
@@ -99,4 +99,98 @@ final class ActorTests: XCTestCase {
await fulfillment(of: allExpectations, timeout: 1, enforceOrder: true)
}
+
+ /**
+ Each subsequent connection attempt should produce a single change to `state` containing the incremented attempt counter and new relay.
+
+ .connecting (attempt: 0) → .connecting (attempt: 1) → .connecting (attempt: 2) → ...
+ */
+ func testConnectionAttemptTransition() async throws {
+ let tunnelMonitor = TunnelMonitorStub { _, _ in }
+ let actor = PacketTunnelActor.mock(tunnelMonitor: tunnelMonitor)
+ let connectingStateExpectation = expectation(description: "Expect connecting state")
+ connectingStateExpectation.expectedFulfillmentCount = 5
+
+ var nextAttemptCount: UInt = 0
+ stateSink = await actor.$state
+ .receive(on: DispatchQueue.main)
+ .sink { newState in
+ switch newState {
+ case .initial:
+ break
+
+ case let .connecting(connState):
+ XCTAssertEqual(connState.connectionAttemptCount, nextAttemptCount)
+ nextAttemptCount += 1
+ connectingStateExpectation.fulfill()
+
+ if nextAttemptCount < connectingStateExpectation.expectedFulfillmentCount {
+ tunnelMonitor.dispatch(.connectionLost, after: .milliseconds(10))
+ }
+
+ default:
+ XCTFail("Received invalid state: \(newState.name).")
+ }
+ }
+
+ self.actor = actor
+
+ actor.start(options: StartOptions(launchSource: .app))
+
+ await fulfillment(of: [connectingStateExpectation], timeout: 1)
+ }
+
+ /**
+ Each subsequent re-connection attempt should produce a single change to `state` containing the incremented attempt counter and new relay.
+
+ .reconnecting (attempt: 0) → .reconnecting (attempt: 1) → .reconnecting (attempt: 2) → ...
+ */
+ func testReconnectionAttemptTransition() async throws {
+ let tunnelMonitor = TunnelMonitorStub { _, _ in }
+ let actor = PacketTunnelActor.mock(tunnelMonitor: tunnelMonitor)
+ let connectingStateExpectation = expectation(description: "Expect connecting state")
+ let connectedStateExpectation = expectation(description: "Expect connected state")
+ let reconnectingStateExpectation = expectation(description: "Expect reconnecting state")
+ reconnectingStateExpectation.expectedFulfillmentCount = 5
+
+ var nextAttemptCount: UInt = 0
+ stateSink = await actor.$state
+ .receive(on: DispatchQueue.main)
+ .sink { newState in
+ switch newState {
+ case .initial:
+ break
+
+ case .connecting:
+ connectingStateExpectation.fulfill()
+ tunnelMonitor.dispatch(.connectionEstablished, after: .milliseconds(10))
+
+ case .connected:
+ connectedStateExpectation.fulfill()
+ tunnelMonitor.dispatch(.connectionLost, after: .milliseconds(10))
+
+ case let .reconnecting(connState):
+ XCTAssertEqual(connState.connectionAttemptCount, nextAttemptCount)
+ nextAttemptCount += 1
+ reconnectingStateExpectation.fulfill()
+
+ if nextAttemptCount < reconnectingStateExpectation.expectedFulfillmentCount {
+ tunnelMonitor.dispatch(.connectionLost, after: .milliseconds(10))
+ }
+
+ default:
+ XCTFail("Received invalid state: \(newState.name).")
+ }
+ }
+
+ self.actor = actor
+
+ actor.start(options: StartOptions(launchSource: .app))
+
+ await fulfillment(
+ of: [connectingStateExpectation, connectedStateExpectation, reconnectingStateExpectation],
+ timeout: 1,
+ enforceOrder: true
+ )
+ }
}
diff --git a/ios/PacketTunnelCoreTests/Mocks/TunnelMonitorStub.swift b/ios/PacketTunnelCoreTests/Mocks/TunnelMonitorStub.swift
index 3b045540b2..4857b03ae2 100644
--- a/ios/PacketTunnelCoreTests/Mocks/TunnelMonitorStub.swift
+++ b/ios/PacketTunnelCoreTests/Mocks/TunnelMonitorStub.swift
@@ -64,7 +64,7 @@ class TunnelMonitorStub: TunnelMonitorProtocol {
func onSleep() {}
- private func dispatch(_ event: TunnelMonitorEvent, after delay: DispatchTimeInterval = .never) {
+ func dispatch(_ event: TunnelMonitorEvent, after delay: DispatchTimeInterval = .never) {
if case .never = delay {
onEvent?(event)
} else {