summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2023-09-19 13:24:01 +0200
committerBug Magnet <marco.nikic@mullvad.net>2023-09-19 13:24:01 +0200
commitb84180ce3f4a9e42beb4e27fa8b2332d0744a7c8 (patch)
tree11838781ecbe079c8c185613f62d3de572396d31
parent34874c9dd637899fc1abeb50d4d93b80219ec1fa (diff)
parent54a114593bd09c4c0841d111d8164784f0b057f9 (diff)
downloadmullvadvpn-b84180ce3f4a9e42beb4e27fa8b2332d0744a7c8.tar.xz
mullvadvpn-b84180ce3f4a9e42beb4e27fa8b2332d0744a7c8.zip
Merge branch 'pinger-tests-fail-intermittently-ios-302'
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj14
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider.swift3
-rw-r--r--ios/PacketTunnelCore/TunnelMonitor/TunnelMonitor.swift76
-rw-r--r--ios/PacketTunnelCore/TunnelMonitor/TunnelMonitorTimings.swift69
-rw-r--r--ios/PacketTunnelCoreTests/TunnelMonitorTests.swift81
5 files changed, 142 insertions, 101 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index f65ffd2851..16fa53b829 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -393,16 +393,17 @@
7A11DD0B2A9495D400098CD8 /* AppRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5802EBC42A8E44AC00E5CE4C /* AppRoutes.swift */; };
7A1A26432A2612AE00B978AA /* PaymentAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A26422A2612AE00B978AA /* PaymentAlertPresenter.swift */; };
7A21DACF2A30AA3700A787A9 /* UITextField+Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A21DACE2A30AA3700A787A9 /* UITextField+Appearance.swift */; };
+ 7A2960F62A963F7500389B82 /* AlertCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2960F52A963F7500389B82 /* AlertCoordinator.swift */; };
+ 7A2960FD2A964BB700389B82 /* AlertPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2960FC2A964BB700389B82 /* AlertPresentation.swift */; };
7A307AD92A8CD8DA0017618B /* Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A307AD82A8CD8DA0017618B /* Duration.swift */; };
7A307ADB2A8F56DF0017618B /* Duration+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A307ADA2A8F56DF0017618B /* Duration+Extensions.swift */; };
7A33538F2AA9FF1600F0A71C /* SimulatorTunnelProviderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A33538E2AA9FF1600F0A71C /* SimulatorTunnelProviderManager.swift */; };
7A3353912AAA014400F0A71C /* SimulatorVPNConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3353902AAA014400F0A71C /* SimulatorVPNConnection.swift */; };
7A3353932AAA089000F0A71C /* SimulatorTunnelInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3353922AAA089000F0A71C /* SimulatorTunnelInfo.swift */; };
7A3353972AAA0F8600F0A71C /* OperationBlockObserverSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3353962AAA0F8600F0A71C /* OperationBlockObserverSupport.swift */; };
- 7A2960F62A963F7500389B82 /* AlertCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2960F52A963F7500389B82 /* AlertCoordinator.swift */; };
- 7A2960FD2A964BB700389B82 /* AlertPresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2960FC2A964BB700389B82 /* AlertPresentation.swift */; };
7A42DEC92A05164100B209BE /* SettingsInputCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A42DEC82A05164100B209BE /* SettingsInputCell.swift */; };
7A42DECD2A09064C00B209BE /* SelectableSettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A42DECC2A09064C00B209BE /* SelectableSettingsCell.swift */; };
+ 7A6B4F592AB8412E00123853 /* TunnelMonitorTimings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6B4F582AB8412E00123853 /* TunnelMonitorTimings.swift */; };
7A7AD28D29DC677800480EF1 /* FirstTimeLaunch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7AD28C29DC677800480EF1 /* FirstTimeLaunch.swift */; };
7A7AD28F29DEDB1C00480EF1 /* SettingsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7AD28E29DEDB1C00480EF1 /* SettingsHeaderView.swift */; };
7A818F1F29F0305800C7F0F4 /* RootConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A818F1E29F0305800C7F0F4 /* RootConfiguration.swift */; };
@@ -1318,16 +1319,17 @@
7A0C0F622A979C4A0058EFCE /* Coordinator+Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Coordinator+Router.swift"; sourceTree = "<group>"; };
7A1A26422A2612AE00B978AA /* PaymentAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentAlertPresenter.swift; sourceTree = "<group>"; };
7A21DACE2A30AA3700A787A9 /* UITextField+Appearance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Appearance.swift"; sourceTree = "<group>"; };
+ 7A2960F52A963F7500389B82 /* AlertCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertCoordinator.swift; sourceTree = "<group>"; };
+ 7A2960FC2A964BB700389B82 /* AlertPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertPresentation.swift; sourceTree = "<group>"; };
7A307AD82A8CD8DA0017618B /* Duration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Duration.swift; sourceTree = "<group>"; };
7A307ADA2A8F56DF0017618B /* Duration+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Duration+Extensions.swift"; sourceTree = "<group>"; };
7A33538E2AA9FF1600F0A71C /* SimulatorTunnelProviderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatorTunnelProviderManager.swift; sourceTree = "<group>"; };
7A3353902AAA014400F0A71C /* SimulatorVPNConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatorVPNConnection.swift; sourceTree = "<group>"; };
7A3353922AAA089000F0A71C /* SimulatorTunnelInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatorTunnelInfo.swift; sourceTree = "<group>"; };
7A3353962AAA0F8600F0A71C /* OperationBlockObserverSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationBlockObserverSupport.swift; sourceTree = "<group>"; };
- 7A2960F52A963F7500389B82 /* AlertCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertCoordinator.swift; sourceTree = "<group>"; };
- 7A2960FC2A964BB700389B82 /* AlertPresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertPresentation.swift; sourceTree = "<group>"; };
7A42DEC82A05164100B209BE /* SettingsInputCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsInputCell.swift; sourceTree = "<group>"; };
7A42DECC2A09064C00B209BE /* SelectableSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableSettingsCell.swift; sourceTree = "<group>"; };
+ 7A6B4F582AB8412E00123853 /* TunnelMonitorTimings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitorTimings.swift; sourceTree = "<group>"; };
7A7AD28C29DC677800480EF1 /* FirstTimeLaunch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstTimeLaunch.swift; sourceTree = "<group>"; };
7A7AD28E29DEDB1C00480EF1 /* SettingsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsHeaderView.swift; sourceTree = "<group>"; };
7A818F1E29F0305800C7F0F4 /* RootConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootConfiguration.swift; sourceTree = "<group>"; };
@@ -2458,9 +2460,9 @@
58D1560C29C0B27600749324 /* Root */ = {
isa = PBXGroup;
children = (
- 587425C02299833500CA2045 /* RootContainerViewController.swift */,
58F3C0A3249CB069003E76BE /* HeaderBarView.swift */,
7A818F1E29F0305800C7F0F4 /* RootConfiguration.swift */,
+ 587425C02299833500CA2045 /* RootContainerViewController.swift */,
);
path = Root;
sourceTree = "<group>";
@@ -2520,6 +2522,7 @@
582403162A821FD700163DE8 /* TunnelDeviceInfoProtocol.swift */,
58FC040927B3EE03001C21F0 /* TunnelMonitor.swift */,
58C7A42C2A85067A0060C66F /* TunnelMonitorProtocol.swift */,
+ 7A6B4F582AB8412E00123853 /* TunnelMonitorTimings.swift */,
58A3BDAF28A1821A00C8C2C6 /* WgStats.swift */,
);
path = TunnelMonitor;
@@ -3735,6 +3738,7 @@
58C7A4552A863FB90060C66F /* TunnelMonitor.swift in Sources */,
58C7A4512A863FB50060C66F /* PingerProtocol.swift in Sources */,
58C7A4592A863FB90060C66F /* WgStats.swift in Sources */,
+ 7A6B4F592AB8412E00123853 /* TunnelMonitorTimings.swift in Sources */,
58C7A4582A863FB90060C66F /* TunnelMonitorProtocol.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
diff --git a/ios/PacketTunnel/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider.swift
index 61e308d13b..d9438deb12 100644
--- a/ios/PacketTunnel/PacketTunnelProvider.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider.swift
@@ -182,7 +182,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
eventQueue: dispatchQueue,
pinger: Pinger(replyQueue: dispatchQueue),
tunnelDeviceInfo: WgAdapterDeviceInfo(adapter: adapter),
- defaultPathObserver: PacketTunnelPathObserver(packetTunnelProvider: self)
+ defaultPathObserver: PacketTunnelPathObserver(packetTunnelProvider: self),
+ timings: TunnelMonitorTimings()
)
tunnelMonitor.onEvent = { [weak self] event in
self?.handleTunnelMonitorEvent(event)
diff --git a/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitor.swift b/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitor.swift
index 54cbb73532..576a29533e 100644
--- a/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitor.swift
+++ b/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitor.swift
@@ -12,42 +12,6 @@ import MullvadTypes
import protocol Network.IPAddress
import struct Network.IPv4Address
-/// Interval for periodic heartbeat ping issued when traffic is flowing.
-/// Should help to detect connectivity issues on networks that drop traffic in one of directions,
-/// regardless if tx/rx counters are being updated.
-private let heartbeatPingInterval: Duration = .seconds(10)
-
-/// Heartbeat timeout that once exceeded triggers next heartbeat to be sent.
-private let heartbeatReplyTimeout: Duration = .seconds(3)
-
-/// Timeout used to determine if there was a network activity lately.
-private let trafficFlowTimeout: Duration = heartbeatPingInterval * 0.5
-
-/// Ping timeout.
-private let pingTimeout: Duration = .seconds(15)
-
-/// Interval to wait before sending next ping.
-private let pingDelay: Duration = .seconds(3)
-
-/// Initial timeout when establishing connection.
-private let initialEstablishTimeout: Duration = .seconds(4)
-
-/// Multiplier applied to `establishTimeout` on each failed connection attempt.
-private let establishTimeoutMultiplier: UInt32 = 2
-
-/// Maximum timeout when establishing connection.
-private let maxEstablishTimeout = pingTimeout
-
-/// Connectivity check periodicity.
-private let connectivityCheckInterval: Duration = .seconds(1)
-
-/// Inbound traffic timeout used when outbound traffic was registered prior to inbound traffic.
-private let inboundTrafficTimeout: Duration = .seconds(5)
-
-/// Traffic timeout applied when both tx/rx counters remain stale, i.e no traffic flowing.
-/// Ping is issued after that timeout is exceeded.s
-private let trafficTimeout: Duration = .minutes(2)
-
public final class TunnelMonitor: TunnelMonitorProtocol {
/// Connection state.
private enum ConnectionState {
@@ -98,6 +62,9 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
/// Retry attempt.
var retryAttempt: UInt32 = 0
+ // Timings and timeouts.
+ let timings: TunnelMonitorTimings
+
func evaluateConnection(now: Date, pingTimeout: Duration) -> ConnectionEvaluation {
switch connectionState {
case .connecting:
@@ -112,17 +79,17 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
func getPingTimeout() -> Duration {
switch connectionState {
case .connecting:
- let multiplier = establishTimeoutMultiplier.saturatingPow(retryAttempt)
- let nextTimeout = initialEstablishTimeout * Double(multiplier)
+ let multiplier = timings.establishTimeoutMultiplier.saturatingPow(retryAttempt)
+ let nextTimeout = timings.initialEstablishTimeout * Double(multiplier)
- if nextTimeout.isFinite, nextTimeout < maxEstablishTimeout {
+ if nextTimeout.isFinite, nextTimeout < timings.maxEstablishTimeout {
return nextTimeout
} else {
- return maxEstablishTimeout
+ return timings.maxEstablishTimeout
}
case .pendingStart, .connected, .waitingConnectivity, .stopped, .recovering:
- return pingTimeout
+ return timings.pingTimeout
}
}
@@ -163,7 +130,7 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
return .sendInitialPing
}
- if now.timeIntervalSince(lastRequestDate) >= pingDelay {
+ if now.timeIntervalSince(lastRequestDate) >= timings.pingDelay {
return .sendNextPing
}
@@ -181,8 +148,8 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
let timeSinceLastPing = now.timeIntervalSince(lastRequestDate)
if let lastReplyDate = pingStats.lastReplyDate,
- lastRequestDate.timeIntervalSince(lastReplyDate) >= heartbeatReplyTimeout,
- timeSinceLastPing >= pingDelay, !isHeartbeatSuspended {
+ lastRequestDate.timeIntervalSince(lastReplyDate) >= timings.heartbeatReplyTimeout,
+ timeSinceLastPing >= timings.pingDelay, !isHeartbeatSuspended {
return .retryHeartbeatPing
}
@@ -191,9 +158,9 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
let rxTimeElapsed = now.timeIntervalSince(lastSeenRx)
let txTimeElapsed = now.timeIntervalSince(lastSeenTx)
- if timeSinceLastPing >= heartbeatPingInterval {
+ if timeSinceLastPing >= timings.heartbeatPingInterval {
// Send heartbeat if traffic is flowing.
- if rxTimeElapsed <= trafficFlowTimeout || txTimeElapsed <= trafficFlowTimeout {
+ if rxTimeElapsed <= timings.trafficFlowTimeout || txTimeElapsed <= timings.trafficFlowTimeout {
return .sendHeartbeatPing
}
@@ -202,12 +169,12 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
}
}
- if timeSinceLastPing >= pingDelay {
- if txTimeElapsed >= trafficTimeout || rxTimeElapsed >= trafficTimeout {
+ if timeSinceLastPing >= timings.pingDelay {
+ if txTimeElapsed >= timings.trafficTimeout || rxTimeElapsed >= timings.trafficTimeout {
return .trafficTimeout
}
- if lastSeenTx > lastSeenRx, rxTimeElapsed >= inboundTrafficTimeout {
+ if lastSeenTx > lastSeenRx, rxTimeElapsed >= timings.inboundTrafficTimeout {
return .inboundTrafficTimeout
}
}
@@ -233,13 +200,14 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
private let nslock = NSLock()
private let timerQueue = DispatchQueue(label: "TunnelMonitor-timerQueue")
private let eventQueue: DispatchQueue
+ private let timings: TunnelMonitorTimings
private var pinger: PingerProtocol
private var defaultPathObserver: DefaultPathObserverProtocol
private var isObservingDefaultPath = false
private var timer: DispatchSourceTimer?
- private var state = State()
+ private var state: State
private var probeAddress: IPv4Address?
private let logger = Logger(label: "TunnelMonitor")
@@ -262,12 +230,16 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
eventQueue: DispatchQueue,
pinger: PingerProtocol,
tunnelDeviceInfo: TunnelDeviceInfoProtocol,
- defaultPathObserver: DefaultPathObserverProtocol
+ defaultPathObserver: DefaultPathObserverProtocol,
+ timings: TunnelMonitorTimings
) {
self.eventQueue = eventQueue
self.tunnelDeviceInfo = tunnelDeviceInfo
self.defaultPathObserver = defaultPathObserver
+ self.timings = timings
+ state = State(timings: timings)
+
self.pinger = pinger
self.pinger.onReply = { [weak self] reply in
guard let self else { return }
@@ -584,7 +556,7 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
timer.setEventHandler { [weak self] in
self?.checkConnectivity()
}
- timer.schedule(wallDeadline: .now(), repeating: connectivityCheckInterval.timeInterval)
+ timer.schedule(wallDeadline: .now(), repeating: timings.connectivityCheckInterval.timeInterval)
timer.activate()
self.timer?.cancel()
diff --git a/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitorTimings.swift b/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitorTimings.swift
new file mode 100644
index 0000000000..3bb689a473
--- /dev/null
+++ b/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitorTimings.swift
@@ -0,0 +1,69 @@
+//
+// TunnelMonitorTimings.swift
+// PacketTunnelCore
+//
+// Created by Jon Petersson on 2023-09-18.
+// Copyright © 2023 Mullvad VPN AB. All rights reserved.
+//
+
+import MullvadTypes
+
+public struct TunnelMonitorTimings {
+ /// Interval for periodic heartbeat ping issued when traffic is flowing.
+ /// Should help to detect connectivity issues on networks that drop traffic in one of directions,
+ /// regardless if tx/rx counters are being updated.
+ let heartbeatPingInterval: Duration
+
+ /// Heartbeat timeout that once exceeded triggers next heartbeat to be sent.
+ let heartbeatReplyTimeout: Duration
+
+ /// Timeout used to determine if there was a network activity lately.
+ var trafficFlowTimeout: Duration { heartbeatPingInterval * 0.5 }
+
+ /// Ping timeout.
+ let pingTimeout: Duration
+
+ /// Interval to wait before sending next ping.
+ let pingDelay: Duration
+
+ /// Initial timeout when establishing connection.
+ let initialEstablishTimeout: Duration
+
+ /// Multiplier applied to `establishTimeout` on each failed connection attempt.
+ let establishTimeoutMultiplier: UInt32
+
+ /// Maximum timeout when establishing connection.
+ var maxEstablishTimeout: Duration { pingTimeout }
+
+ /// Connectivity check periodicity.
+ let connectivityCheckInterval: Duration
+
+ /// Inbound traffic timeout used when outbound traffic was registered prior to inbound traffic.
+ let inboundTrafficTimeout: Duration
+
+ /// Traffic timeout applied when both tx/rx counters remain stale, i.e no traffic flowing.
+ /// Ping is issued after that timeout is exceeded.s
+ let trafficTimeout: Duration
+
+ public init(
+ heartbeatPingInterval: Duration = .seconds(10),
+ heartbeatReplyTimeout: Duration = .seconds(3),
+ pingTimeout: Duration = .seconds(15),
+ pingDelay: Duration = .seconds(3),
+ initialEstablishTimeout: Duration = .seconds(4),
+ establishTimeoutMultiplier: UInt32 = 2,
+ connectivityCheckInterval: Duration = .seconds(1),
+ inboundTrafficTimeout: Duration = .seconds(5),
+ trafficTimeout: Duration = .minutes(2)
+ ) {
+ self.heartbeatPingInterval = heartbeatPingInterval
+ self.heartbeatReplyTimeout = heartbeatReplyTimeout
+ self.pingTimeout = pingTimeout
+ self.pingDelay = pingDelay
+ self.initialEstablishTimeout = initialEstablishTimeout
+ self.establishTimeoutMultiplier = establishTimeoutMultiplier
+ self.connectivityCheckInterval = connectivityCheckInterval
+ self.inboundTrafficTimeout = inboundTrafficTimeout
+ self.trafficTimeout = trafficTimeout
+ }
+}
diff --git a/ios/PacketTunnelCoreTests/TunnelMonitorTests.swift b/ios/PacketTunnelCoreTests/TunnelMonitorTests.swift
index 4677b042ad..a2d2ac8a24 100644
--- a/ios/PacketTunnelCoreTests/TunnelMonitorTests.swift
+++ b/ios/PacketTunnelCoreTests/TunnelMonitorTests.swift
@@ -6,6 +6,7 @@
// Copyright © 2023 Mullvad VPN AB. All rights reserved.
//
+import MullvadTypes
import Network
@testable import PacketTunnelCore
import XCTest
@@ -22,12 +23,7 @@ final class TunnelMonitorTests: XCTestCase {
return .sendReply()
}
- let tunnelMonitor = TunnelMonitor(
- eventQueue: .main,
- pinger: pinger,
- tunnelDeviceInfo: MockTunnelDeviceInfo(networkStatsProviding: networkCounters),
- defaultPathObserver: MockDefaultPathObserver()
- )
+ let tunnelMonitor = createTunnelMonitor(pinger: pinger, timings: TunnelMonitorTimings())
tunnelMonitor.onEvent = { event in
switch event {
@@ -49,39 +45,34 @@ final class TunnelMonitorTests: XCTestCase {
func testInitialConnectionTimings() {
// Setup pinger so that it never receives any replies.
- let pinger = MockPinger(networkStatsReporting: networkCounters) { _, _ in
- return .ignore
- }
+ let pinger = MockPinger(networkStatsReporting: networkCounters) { _, _ in .ignore }
- let tunnelMonitor = TunnelMonitor(
- eventQueue: .main,
- pinger: pinger,
- tunnelDeviceInfo: MockTunnelDeviceInfo(networkStatsProviding: networkCounters),
- defaultPathObserver: MockDefaultPathObserver()
+ let timings = TunnelMonitorTimings(
+ pingTimeout: .milliseconds(300),
+ initialEstablishTimeout: .milliseconds(100),
+ connectivityCheckInterval: .milliseconds(100)
)
- /*
- Tunnel monitor uses shorter timeout intervals during the initial connection sequence and picks next relay more
- aggressively in order to reduce connection time.
+ let tunnelMonitor = createTunnelMonitor(pinger: pinger, timings: timings)
- First connection attempt starts at 4 second timeout, then doubles with each subsequent attempt, while being
- capped at 15s max.
- */
- var expectedTimings = [4, 8, 15, 15]
- let totalAttemptCount = expectedTimings.count
+ var expectedTimings = [
+ timings.initialEstablishTimeout.milliseconds,
+ timings.initialEstablishTimeout.milliseconds * 2,
+ timings.pingTimeout.milliseconds,
+ timings.pingTimeout.milliseconds,
+ ]
- // Calculate the amount of time necessary to perform the test adding some leeway.
- let timeout = expectedTimings.reduce(1, +)
+ // Calculate the amount of time necessary to perform the test.
+ var timeout = expectedTimings.reduce(0, +)
+ // Add leeway into the total amount of expected wait time.
+ timeout += timeout / 2
- let expectation = self.expectation(description: "Should respect all timings.")
+ let expectation = expectation(description: "Should respect all timings.")
expectation.expectedFulfillmentCount = expectedTimings.count
// This date will be used to measure the amount of time elapsed between `.connectionLost` events.
var startDate = Date()
- // Reconnection attempt counter.
- var currentAttempt = 0
-
tunnelMonitor.onEvent = { [weak tunnelMonitor] event in
guard case .connectionLost = event else { return }
@@ -90,29 +81,23 @@ final class TunnelMonitorTests: XCTestCase {
XCTAssertFalse(expectedTimings.isEmpty)
let expectedDuration = expectedTimings.removeFirst()
+ let leeway = expectedDuration / 2
- // Compute amount of time elapsed between `.connectionLost` events rounding it down towards zero.
- let timeElapsed = Int(Date().timeIntervalSince(startDate).rounded(.down))
-
- currentAttempt += 1
-
- print("[\(currentAttempt)/\(totalAttemptCount)] \(event), time elapsed: \(timeElapsed)s")
+ // Compute amount of time elapsed between `.connectionLost` events.
+ let timeElapsed = Int(Date().timeIntervalSince(startDate) * 1000)
XCTAssertEqual(
timeElapsed,
expectedDuration,
- "Expected to report connection loss after \(expectedDuration)s, instead reported it after \(timeElapsed)s."
+ accuracy: leeway,
+ "Expected to report connection loss after \(expectedDuration)-\(expectedDuration + leeway) ms, instead reported it after \(timeElapsed) ms."
)
expectation.fulfill()
- if expectedTimings.isEmpty {
- print("Finished.")
- } else {
+ if !expectedTimings.isEmpty {
startDate = Date()
- print("Continue monitoring.")
-
// Continue monitoring by calling start() again.
tunnelMonitor?.start(probeAddress: .loopback)
}
@@ -125,11 +110,21 @@ final class TunnelMonitorTests: XCTestCase {
}
}
- print("Start monitoring.")
-
// Start monitoring.
tunnelMonitor.start(probeAddress: .loopback)
- waitForExpectations(timeout: TimeInterval(timeout))
+ waitForExpectations(timeout: TimeInterval(timeout) / 1000)
+ }
+}
+
+extension TunnelMonitorTests {
+ private func createTunnelMonitor(pinger: PingerProtocol, timings: TunnelMonitorTimings) -> TunnelMonitor {
+ return TunnelMonitor(
+ eventQueue: .main,
+ pinger: pinger,
+ tunnelDeviceInfo: MockTunnelDeviceInfo(networkStatsProviding: networkCounters),
+ defaultPathObserver: MockDefaultPathObserver(),
+ timings: timings
+ )
}
}