From 929a86ce2ba882d59209783335291b832e5dd153 Mon Sep 17 00:00:00 2001 From: krishrathi1 Date: Sat, 29 Aug 2026 23:01:53 +0530 Subject: [PATCH] Refactor BLEConnectTimeoutPolicy to use primitive attributes and eliminate CBPeripheral test mock crash --- bitchat/Services/BLE/BLELinkStateStore.swift | 11 +-- bitchat/Services/BLE/BLERadioController.swift | 13 ++-- .../BLEConnectTimeoutPolicyTests.swift | 71 ++++++------------- 3 files changed, 34 insertions(+), 61 deletions(-) diff --git a/bitchat/Services/BLE/BLELinkStateStore.swift b/bitchat/Services/BLE/BLELinkStateStore.swift index 7bed020b..c286ffcf 100644 --- a/bitchat/Services/BLE/BLELinkStateStore.swift +++ b/bitchat/Services/BLE/BLELinkStateStore.swift @@ -19,17 +19,18 @@ struct BLEPeripheralLinkState { } struct BLEConnectTimeoutPolicy { - /// Pure decision: given a captured attempt token and current link state, + /// Pure decision: given a captured attempt token and current link state attributes, /// returns true if the timeout closure should act on this peripheral attempt. static func shouldExecuteConnectTimeout( capturedAttemptToken: UInt64, - state: BLEPeripheralLinkState?, + isConnecting: Bool, + isConnected: Bool, + currentAttemptToken: UInt64, isPeripheralConnected: Bool ) -> Bool { - guard let state = state else { return false } - guard state.isConnecting && !state.isConnected else { return false } + guard isConnecting && !isConnected else { return false } guard !isPeripheralConnected else { return false } - guard state.attemptToken == capturedAttemptToken else { return false } + guard currentAttemptToken == capturedAttemptToken else { return false } return true } } diff --git a/bitchat/Services/BLE/BLERadioController.swift b/bitchat/Services/BLE/BLERadioController.swift index 82026493..880622fe 100644 --- a/bitchat/Services/BLE/BLERadioController.swift +++ b/bitchat/Services/BLE/BLERadioController.swift @@ -253,11 +253,14 @@ final class BLERadioController { // this check, this now-stale timeout would cancel that newer, // still-live attempt out from under it and apply a // connection-timeout penalty to a peer that never actually timed out. - guard BLEConnectTimeoutPolicy.shouldExecuteConnectTimeout( - capturedAttemptToken: attemptToken, - state: state, - isPeripheralConnected: peripheral.state == .connected - ) else { + guard let state, + BLEConnectTimeoutPolicy.shouldExecuteConnectTimeout( + capturedAttemptToken: attemptToken, + isConnecting: state.isConnecting, + isConnected: state.isConnected, + currentAttemptToken: state.attemptToken, + isPeripheralConnected: peripheral.state == .connected + ) else { if let state, state.attemptToken != attemptToken { SecureLogger.debug("⏱️ Timeout fired for a superseded connection attempt, ignoring: \(candidate.name)", category: .session) } diff --git a/bitchatTests/Services/BLEConnectTimeoutPolicyTests.swift b/bitchatTests/Services/BLEConnectTimeoutPolicyTests.swift index 10bc3145..50f0a8dd 100644 --- a/bitchatTests/Services/BLEConnectTimeoutPolicyTests.swift +++ b/bitchatTests/Services/BLEConnectTimeoutPolicyTests.swift @@ -1,39 +1,18 @@ import Foundation -import CoreBluetooth import Testing @testable import bitchat struct BLEConnectTimeoutPolicyTests { - @Test - func validConnectingAttemptTriggersTimeout() { - let token: UInt64 = 42 - - // When state is nil -> false - #expect(!BLEConnectTimeoutPolicy.shouldExecuteConnectTimeout( - capturedAttemptToken: token, - state: nil, - isPeripheralConnected: false - )) - } - @Test func supersededAttemptTokenIgnoresTimeout() { let capturedToken: UInt64 = 1 let currentToken: UInt64 = 2 - let mockState = BLEPeripheralLinkState( - peripheral: CBPeripheralMock.create(), - characteristic: nil, - isConnecting: true, - isConnected: false, - lastConnectionAttempt: Date(), - attemptToken: currentToken, - assembler: NotificationStreamAssembler() - ) - let result = BLEConnectTimeoutPolicy.shouldExecuteConnectTimeout( capturedAttemptToken: capturedToken, - state: mockState, + isConnecting: true, + isConnected: false, + currentAttemptToken: currentToken, isPeripheralConnected: false ) @@ -44,19 +23,11 @@ struct BLEConnectTimeoutPolicyTests { func matchingAttemptTokenExecutesTimeout() { let token: UInt64 = 5 - let mockState = BLEPeripheralLinkState( - peripheral: CBPeripheralMock.create(), - characteristic: nil, - isConnecting: true, - isConnected: false, - lastConnectionAttempt: Date(), - attemptToken: token, - assembler: NotificationStreamAssembler() - ) - let result = BLEConnectTimeoutPolicy.shouldExecuteConnectTimeout( capturedAttemptToken: token, - state: mockState, + isConnecting: true, + isConnected: false, + currentAttemptToken: token, isPeripheralConnected: false ) @@ -67,31 +38,29 @@ struct BLEConnectTimeoutPolicyTests { func connectedStateIgnoresTimeout() { let token: UInt64 = 5 - let mockState = BLEPeripheralLinkState( - peripheral: CBPeripheralMock.create(), - characteristic: nil, + let result = BLEConnectTimeoutPolicy.shouldExecuteConnectTimeout( + capturedAttemptToken: token, isConnecting: false, isConnected: true, - lastConnectionAttempt: Date(), - attemptToken: token, - assembler: NotificationStreamAssembler() + currentAttemptToken: token, + isPeripheralConnected: true ) + #expect(!result) + } + + @Test + func peripheralConnectedStateIgnoresTimeout() { + let token: UInt64 = 5 + let result = BLEConnectTimeoutPolicy.shouldExecuteConnectTimeout( capturedAttemptToken: token, - state: mockState, + isConnecting: true, + isConnected: false, + currentAttemptToken: token, isPeripheralConnected: true ) #expect(!result) } } - -// Helper mock CBPeripheral for tests -private final class CBPeripheralMock { - static func create() -> CBPeripheral { - let dummy = UnsafeMutableRawPointer.allocate(byteCount: 256, alignment: 16) - dummy.initializeMemory(as: UInt8.self, repeating: 0, count: 256) - return Unmanaged.fromOpaque(dummy).takeUnretainedValue() - } -}