Fix LiveLocationManager flaky tests (#5389)

This commit is contained in:
Mauro
2026-04-13 18:05:47 +02:00
committed by GitHub
parent 252e2f75df
commit df7f8e0510
6 changed files with 120 additions and 9 deletions

View File

@@ -192,6 +192,7 @@
1C8BC70A18060677E295A846 /* ShareToMapsAppActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4481799F455B3DA243BDA2AC /* ShareToMapsAppActivity.swift */; };
1C9BB74711E5F24C77B7FED0 /* RoomMembersListScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AEA0B743847CFA5B3C38EE4 /* RoomMembersListScreenCoordinator.swift */; };
1CA094038D4D036A6F0A1314 /* VoiceMessageTrashButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF84AA68B2B7584D9275769 /* VoiceMessageTrashButton.swift */; };
1D2D713A5A269072D103AE37 /* CLLocationManagerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA46D6DD4B4AB17E1D45092E /* CLLocationManagerProtocol.swift */; };
1D5DC685CED904386C89B7DA /* NSRegularExpresion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BAC0F6C9644336E9567EE6 /* NSRegularExpresion.swift */; };
1D623953F970D11F6F38499C /* AppLockService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851B95BB98649B8E773D6790 /* AppLockService.swift */; };
1D69E31913DF66426985909B /* EmojiPickerScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11151E78D6BB2B04A8FBD389 /* EmojiPickerScreenViewModelProtocol.swift */; };
@@ -624,6 +625,7 @@
69DE29C3E3180BB17D840690 /* ProgressCursorModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97C8E13A1FBA717B0C277ECC /* ProgressCursorModifier.swift */; };
6A38D0A2BC3943A92D82576E /* EditRoomAddressScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B18089ED50324583BB2FB7 /* EditRoomAddressScreenViewModelProtocol.swift */; };
6A54F52443EC52AC5CD772C0 /* JoinRoomScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 869A8A4632E511351BFE2EC4 /* JoinRoomScreen.swift */; };
6A5FDF9306CBD62C7EDDB552 /* CLLocationManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C723327DC4A3093CD9675B27 /* CLLocationManagerMock.swift */; };
6A64546ABE648ED9E6DBB459 /* RemoteSettingsHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D186A6DB8FAC5C9D0E4D61 /* RemoteSettingsHook.swift */; };
6AB306367E56A6F6DFA0E2FF /* RoomSummaryProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46E441BA50705E6CEC89FE0 /* RoomSummaryProviderTests.swift */; };
6AD722DD92E465E56D2885AB /* BugReportScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA919F521E9F0EE3638AFC85 /* BugReportScreen.swift */; };
@@ -2706,6 +2708,7 @@
C6A9F49B3EE59147AF2F70BB /* SeparatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorRoomTimelineItem.swift; sourceTree = "<group>"; };
C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderAvatarImage.swift; sourceTree = "<group>"; };
C715CFE00686DACA59D836EA /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = fa.lproj/SAS.strings; sourceTree = "<group>"; };
C723327DC4A3093CD9675B27 /* CLLocationManagerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLLocationManagerMock.swift; sourceTree = "<group>"; };
C729D95CB4588D4D9AAC3DFA /* RoomChangePermissionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenModels.swift; sourceTree = "<group>"; };
C75EF87651B00A176AB08E97 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
C75FE3F524B575D53787868C /* TimelineMediaPreviewRedactConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMediaPreviewRedactConfirmationView.swift; sourceTree = "<group>"; };
@@ -2810,6 +2813,7 @@
D9C5AA3EF7EC67C01C75CEDD /* LabsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabsScreen.swift; sourceTree = "<group>"; };
DA14564EE143F73F7E4D1F79 /* RoomNotificationSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenModels.swift; sourceTree = "<group>"; };
DA3D82522494E78746B2214E /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/SAS.strings; sourceTree = "<group>"; };
DA46D6DD4B4AB17E1D45092E /* CLLocationManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLLocationManagerProtocol.swift; sourceTree = "<group>"; };
DAB8D7926A5684E18196B538 /* VoiceMessageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageCache.swift; sourceTree = "<group>"; };
DADECBBB672497BCD4822468 /* Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
DB06F22CFA34885B40976061 /* RoomDetailsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreen.swift; sourceTree = "<group>"; };
@@ -3730,6 +3734,7 @@
8F7FC9580CABF797A2E6213A /* BugReportServiceMock.swift */,
633BAD3D9BB44B2AED7CBB93 /* ClassicAppManagerMock.swift */,
E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */,
C723327DC4A3093CD9675B27 /* CLLocationManagerMock.swift */,
4E600B315B920B9687F8EE1B /* ComposerDraftServiceMock.swift */,
86E1BAA7232081635662A83F /* CXProviderMock.swift */,
E321E840DCC63790049984F4 /* ElementCallServiceMock.swift */,
@@ -5454,6 +5459,7 @@
90FD376E9373246E4925CE95 /* Location */ = {
isa = PBXGroup;
children = (
DA46D6DD4B4AB17E1D45092E /* CLLocationManagerProtocol.swift */,
D17F49E39CC38DAB7B305701 /* LiveLocationManager.swift */,
33752AE856E93CE62412B7A1 /* LiveLocationManagerProtocol.swift */,
);
@@ -8120,6 +8126,8 @@
9A8E6FCD86B89970EC72EFD8 /* BugReportServiceMock.swift in Sources */,
172E6E9A612ADCF10A62CF13 /* BugReportServiceProtocol.swift in Sources */,
E1DF24D085572A55C9758A2D /* Bundle.swift in Sources */,
6A5FDF9306CBD62C7EDDB552 /* CLLocationManagerMock.swift in Sources */,
1D2D713A5A269072D103AE37 /* CLLocationManagerProtocol.swift in Sources */,
5470E62F65AE1803BBF3D528 /* CXProviderMock.swift in Sources */,
3D0DAED550E967AB49F1758C /* CXProviderProtocol.swift in Sources */,
01B63F1A04A276B39AC17014 /* CallInviteRoomTimelineItem.swift in Sources */,

View File

@@ -0,0 +1,20 @@
//
// Copyright 2026 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import CoreLocation
extension CLLocationManagerMock {
struct Configuration {
var authorizationStatus: CLAuthorizationStatus = .authorizedAlways
}
convenience init(_ configuration: Configuration) {
self.init()
underlyingAuthorizationStatus = configuration.authorizationStatus
}
}

View File

@@ -2187,6 +2187,65 @@ class BugReportServiceMock: BugReportServiceProtocol, @unchecked Sendable {
}
}
}
class CLLocationManagerMock: CLLocationManagerProtocol, @unchecked Sendable {
weak var delegate: CLLocationManagerDelegate?
var allowsBackgroundLocationUpdates: Bool {
get { return underlyingAllowsBackgroundLocationUpdates }
set(value) { underlyingAllowsBackgroundLocationUpdates = value }
}
var underlyingAllowsBackgroundLocationUpdates: Bool!
var desiredAccuracy: CLLocationAccuracy {
get { return underlyingDesiredAccuracy }
set(value) { underlyingDesiredAccuracy = value }
}
var underlyingDesiredAccuracy: CLLocationAccuracy!
var pausesLocationUpdatesAutomatically: Bool {
get { return underlyingPausesLocationUpdatesAutomatically }
set(value) { underlyingPausesLocationUpdatesAutomatically = value }
}
var underlyingPausesLocationUpdatesAutomatically: Bool!
var authorizationStatus: CLAuthorizationStatus {
get { return underlyingAuthorizationStatus }
set(value) { underlyingAuthorizationStatus = value }
}
var underlyingAuthorizationStatus: CLAuthorizationStatus!
//MARK: - requestAlwaysAuthorization
var requestAlwaysAuthorizationUnderlyingCallsCount = 0
var requestAlwaysAuthorizationCallsCount: Int {
get {
if Thread.isMainThread {
return requestAlwaysAuthorizationUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = requestAlwaysAuthorizationUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
requestAlwaysAuthorizationUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
requestAlwaysAuthorizationUnderlyingCallsCount = newValue
}
}
}
}
var requestAlwaysAuthorizationCalled: Bool {
return requestAlwaysAuthorizationCallsCount > 0
}
var requestAlwaysAuthorizationClosure: (() -> Void)?
func requestAlwaysAuthorization() {
requestAlwaysAuthorizationCallsCount += 1
requestAlwaysAuthorizationClosure?()
}
}
class CXProviderMock: CXProviderProtocol, @unchecked Sendable {
//MARK: - setDelegate

View File

@@ -0,0 +1,21 @@
//
// Copyright 2026 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import CoreLocation
// sourcery: AutoMockable
protocol CLLocationManagerProtocol: AnyObject {
var delegate: CLLocationManagerDelegate? { get set }
var allowsBackgroundLocationUpdates: Bool { get set }
var desiredAccuracy: CLLocationAccuracy { get set }
var pausesLocationUpdatesAutomatically: Bool { get set }
var authorizationStatus: CLAuthorizationStatus { get }
func requestAlwaysAuthorization()
}
extension CLLocationManager: CLLocationManagerProtocol { }

View File

@@ -10,7 +10,7 @@ import CoreLocation
class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationManagerDelegate {
private let clientProxy: ClientProxyProtocol
private let locationManager: CLLocationManager
private let locationManager: CLLocationManagerProtocol
private let appSettings: AppSettings
private let authorizationStatusSubject: CurrentValueSubject<CLAuthorizationStatus, Never>
@@ -30,21 +30,22 @@ class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationMana
@MainActor
init(clientProxy: ClientProxyProtocol,
appSettings: AppSettings) {
appSettings: AppSettings,
locationManager: @autoclosure @MainActor () -> CLLocationManagerProtocol = CLLocationManager()) {
self.clientProxy = clientProxy
self.appSettings = appSettings
// Very important, the CLLocationManager needs to be initialised on the main thread
// or the delegate functions won't be handled!
// https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate
locationManager = CLLocationManager()
authorizationStatusSubject = CurrentValueSubject(locationManager.authorizationStatus)
self.locationManager = locationManager()
authorizationStatusSubject = CurrentValueSubject(self.locationManager.authorizationStatus)
super.init()
locationManager.delegate = self
locationManager.allowsBackgroundLocationUpdates = true
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.pausesLocationUpdatesAutomatically = false
self.locationManager.delegate = self
self.locationManager.allowsBackgroundLocationUpdates = true
self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.locationManager.pausesLocationUpdatesAutomatically = false
setupSubscriptions()
}

View File

@@ -12,6 +12,7 @@ import Testing
@MainActor
final class LiveLocationManagerTests {
private var clientProxy: ClientProxyMock!
private var locationManagerMock: CLLocationManagerMock!
private var manager: LiveLocationManager!
private let appSettings: AppSettings
@@ -20,7 +21,8 @@ final class LiveLocationManagerTests {
AppSettings.resetAllSettings()
appSettings = AppSettings()
clientProxy = ClientProxyMock(.init())
manager = LiveLocationManager(clientProxy: clientProxy, appSettings: appSettings)
locationManagerMock = CLLocationManagerMock(.init())
manager = LiveLocationManager(clientProxy: clientProxy, appSettings: appSettings, locationManager: locationManagerMock)
}
deinit {