Skip to content

Commit

Permalink
Merge branch 'test-connection-retry-logic-ios-435'
Browse files Browse the repository at this point in the history
  • Loading branch information
buggmagnet committed Apr 8, 2024
2 parents ede40a2 + 23136e9 commit 42183bb
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 28 deletions.
4 changes: 3 additions & 1 deletion ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public enum AccessibilityIdentifier: String {
case cancelButton
case connectionPanelButton
case collapseButton
case expandButton
case createAccountButton
case deleteButton
case disconnectButton
Expand Down Expand Up @@ -57,7 +58,8 @@ public enum AccessibilityIdentifier: String {

// Labels
case headerDeviceNameLabel
case connectionStatusLabel
case connectionStatusConnectedLabel
case connectionStatusNotConnectedLabel
case welcomeAccountNumberLabel
case connectionPanelDetailLabel

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ class LocationCell: UITableViewCell {

private let collapseButton: UIButton = {
let button = UIButton(type: .custom)
button.accessibilityIdentifier = .collapseButton
button.isAccessibilityElement = false
button.tintColor = .white
return button
Expand Down Expand Up @@ -267,6 +266,7 @@ class LocationCell: UITableViewCell {
private func updateCollapseImage() {
let image = isExpanded ? chevronUp : chevronDown

collapseButton.accessibilityIdentifier = isExpanded ? .collapseButton : .expandButton
collapseButton.setImage(image, for: .normal)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ final class LocationViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()

view.accessibilityIdentifier = .selectLocationView
view.backgroundColor = .secondaryColor

navigationItem.title = NSLocalizedString(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,6 @@ final class TunnelControlView: UIView {
accessibilityContainerType = .semanticGroup
accessibilityIdentifier = .tunnelControlView

secureLabel.accessibilityIdentifier = .connectionStatusLabel

addSubviews()
addButtonHandlers()
}
Expand Down Expand Up @@ -187,6 +185,13 @@ final class TunnelControlView: UIView {
private func updateSecureLabel(tunnelState: TunnelState) {
secureLabel.text = tunnelState.localizedTitleForSecureLabel.uppercased()
secureLabel.textColor = tunnelState.textColorForSecureLabel

switch tunnelState {
case .connected:
secureLabel.accessibilityIdentifier = .connectionStatusConnectedLabel
default:
secureLabel.accessibilityIdentifier = .connectionStatusNotConnectedLabel
}
}

private func updateButtonTitles(tunnelState: TunnelState) {
Expand Down
10 changes: 10 additions & 0 deletions ios/MullvadVPNUITests/Networking/FirewallRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ struct FirewallRule {
)
}

public static func makeBlockAllTrafficRule(toIPAddress: String) throws -> FirewallRule {
let deviceIPAddress = try Networking.getIPAddress()

return FirewallRule(
fromIPAddress: deviceIPAddress,
toIPAddress: toIPAddress,
protocols: [.ICMP, .TCP, .UDP]
)
}

public static func makeBlockUDPTrafficRule(toIPAddress: String) throws -> FirewallRule {
let deviceIPAddress = try Networking.getIPAddress()

Expand Down
9 changes: 7 additions & 2 deletions ios/MullvadVPNUITests/Pages/SelectLocationPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,19 @@ class SelectLocationPage: Page {
return self
}

@discardableResult func tapLocationCellExpandCollapseButton(withName name: String) -> Self {
@discardableResult func tapLocationCellExpandButton(withName name: String) -> Self {
let table = app.tables[AccessibilityIdentifier.selectLocationTableView]
let matchingCells = table.cells.containing(.any, identifier: name)
let buttons = matchingCells.buttons
let expandButton = buttons[AccessibilityIdentifier.collapseButton]
let expandButton = buttons[AccessibilityIdentifier.expandButton]

expandButton.tap()

return self
}

func locationCellIsExpanded(_ name: String) -> Bool {
let matchingCells = app.cells.containing(.any, identifier: name)
return matchingCells.buttons[AccessibilityIdentifier.expandButton].exists ? false : true
}
}
50 changes: 47 additions & 3 deletions ios/MullvadVPNUITests/Pages/TunnelControlPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ class TunnelControlPage: Page {
/// Poll the "in address row" label for its updated values and output an array of ConnectionAttempt objects representing the connection attempts that have been communicated through the UI.
/// - Parameters:
/// - attemptsCount: number of connection attempts to look for
/// - timeout: timeout after this many seconds if attemptsCount haven't been reached yet
/// - timeout: return the attemps found so far after this many seconds if `attemptsCount` haven't been reached yet
private func waitForConnectionAttempts(_ attemptsCount: Int, timeout: TimeInterval) -> [ConnectionAttempt] {
var connectionAttempts: [ConnectionAttempt] = []
var lastConnectionAttempt: ConnectionAttempt?
let startTime = Date()
let pollingInterval = TimeInterval(0.5) // How often to check for changes

Expand Down Expand Up @@ -52,8 +53,9 @@ class TunnelControlPage: Page {
protocolName: protocolName
)

if connectionAttempts.contains(connectionAttempt) == false {
if connectionAttempt != lastConnectionAttempt {
connectionAttempts.append(connectionAttempt)
lastConnectionAttempt = connectionAttempt

if connectionAttempts.count == attemptsCount {
break
Expand Down Expand Up @@ -87,8 +89,13 @@ class TunnelControlPage: Page {
return self
}

@discardableResult func tapCancelButton() -> Self {
app.buttons[AccessibilityIdentifier.cancelButton].tap()
return self
}

@discardableResult func waitForSecureConnectionLabel() -> Self {
_ = app.staticTexts[AccessibilityIdentifier.connectionStatusLabel]
_ = app.staticTexts[AccessibilityIdentifier.connectionStatusConnectedLabel]
.waitForExistence(timeout: BaseUITestCase.defaultTimeout)
return self
}
Expand Down Expand Up @@ -130,6 +137,43 @@ class TunnelControlPage: Page {
return self
}

/// Verify that connection attempts are made in the correct order
@discardableResult func verifyConnectionAttemptsOrder() -> Self {
let connectionAttempts = waitForConnectionAttempts(4, timeout: 50)
XCTAssertEqual(connectionAttempts.count, 4)

if connectionAttempts.last?.protocolName == "UDP" {
// If last attempt is over UDP it means we have encountered the UI bug where only one UDP attempt is shown and then the two TCP attempts
for (attemptIndex, attempt) in connectionAttempts.enumerated() {
if attemptIndex == 0 {
XCTAssertEqual(attempt.protocolName, "UDP")
} else if attemptIndex == 1 {
XCTAssertEqual(attempt.protocolName, "TCP")
XCTAssertEqual(attempt.port, "80")
} else if attemptIndex == 2 {
XCTAssertEqual(attempt.protocolName, "TCP")
XCTAssertEqual(attempt.port, "5001")
} // Ignore the 4th attempt which is the first attempt of new attempt cycle
}
} else {
for (attemptIndex, attempt) in connectionAttempts.enumerated() {
if attemptIndex == 0 {
XCTAssertEqual(attempt.protocolName, "UDP")
} else if attemptIndex == 1 {
XCTAssertEqual(attempt.protocolName, "UDP")
} else if attemptIndex == 2 {
XCTAssertEqual(attempt.protocolName, "TCP")
XCTAssertEqual(attempt.port, "80")
} else if attemptIndex == 3 {
XCTAssertEqual(attempt.protocolName, "TCP")
XCTAssertEqual(attempt.port, "5001")
}
}
}

return self
}

@discardableResult func verifyConnectingToPort(_ port: String) -> Self {
let connectionAttempts = waitForConnectionAttempts(1, timeout: 10)
XCTAssertEqual(connectionAttempts.count, 1)
Expand Down
72 changes: 53 additions & 19 deletions ios/MullvadVPNUITests/RelayTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,27 @@ class RelayTests: LoggedInWithTimeUITestCase {
.tapDisconnectButton()
}

func testConnectionRetryLogic() throws {
FirewallAPIClient().removeRules()
removeFirewallRulesInTearDown = true

// First get relay IP address
let relayIPAddress = getGot001WireGuardRelayIPAddress()

// Run actual test
try FirewallAPIClient().createRule(
FirewallRule.makeBlockAllTrafficRule(toIPAddress: relayIPAddress)
)

TunnelControlPage(app)
.tapSecureConnectionButton()

// Should be two UDP connection attempts but sometimes only one is shown in the UI
TunnelControlPage(app)
.verifyConnectionAttemptsOrder()
.tapCancelButton()
}

func testWireGuardOverTCPManually() throws {
HeaderBar(app)
.tapSettingsButton()
Expand Down Expand Up @@ -86,29 +107,11 @@ class RelayTests: LoggedInWithTimeUITestCase {

/// Test automatic switching to TCP is functioning when UDP traffic to relay is blocked. This test first connects to a realy to get the IP address of it, in order to block UDP traffic to this relay.
func testWireGuardOverTCPAutomatically() throws {
let wireGuardGot001RelayName = "se-got-wg-001"

FirewallAPIClient().removeRules()
removeFirewallRulesInTearDown = true

// First get relay IP address
TunnelControlPage(app)
.tapSelectLocationButton()

SelectLocationPage(app)
.tapLocationCellExpandCollapseButton(withName: "Sweden")
.tapLocationCellExpandCollapseButton(withName: "Gothenburg")
.tapLocationCell(withName: wireGuardGot001RelayName)

allowAddVPNConfigurationsIfAsked()

let relayIPAddress = TunnelControlPage(app)
.waitForSecureConnectionLabel()
.tapRelayStatusExpandCollapseButton()
.getInIPAddressFromConnectionStatus()

TunnelControlPage(app)
.tapDisconnectButton()
let relayIPAddress = getGot001WireGuardRelayIPAddress()

// Run actual test
try FirewallAPIClient().createRule(
Expand Down Expand Up @@ -163,4 +166,35 @@ class RelayTests: LoggedInWithTimeUITestCase {
.verifyConnectingToPort("4001")
.tapDisconnectButton()
}

/// Get got001 WireGuard relay IP address by connecting to it and checking which IP address the app connects to. Assumes user is logged on and at tunnel control page.
private func getGot001WireGuardRelayIPAddress() -> String {
let wireGuardGot001RelayName = "se-got-wg-001"

TunnelControlPage(app)
.tapSelectLocationButton()

if SelectLocationPage(app).locationCellIsExpanded("Sweden") {
// Already expanded - just make sure correct relay is selected
SelectLocationPage(app)
.tapLocationCell(withName: wireGuardGot001RelayName)
} else {
SelectLocationPage(app)
.tapLocationCellExpandButton(withName: "Sweden")
.tapLocationCellExpandButton(withName: "Gothenburg")
.tapLocationCell(withName: wireGuardGot001RelayName)
}

allowAddVPNConfigurationsIfAsked()

let relayIPAddress = TunnelControlPage(app)
.waitForSecureConnectionLabel()
.tapRelayStatusExpandCollapseButton()
.getInIPAddressFromConnectionStatus()

TunnelControlPage(app)
.tapDisconnectButton()

return relayIPAddress
}
}

0 comments on commit 42183bb

Please sign in to comment.