Skip to content

Commit c9f3980

Browse files
committed
fix: Tests.
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
1 parent 8cb6161 commit c9f3980

24 files changed

Lines changed: 663 additions & 567 deletions

Sources/NextcloudFileProviderKit/Enumeration/RemoteChangeObserver.swift

Lines changed: 56 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ import NextcloudKit
99

1010
public let NotifyPushAuthenticatedNotificationName = Notification.Name("NotifyPushAuthenticated")
1111

12-
public actor RemoteChangeObserver: NSObject, Sendable {
12+
public final class RemoteChangeObserver: NSObject, @unchecked Sendable {
1313
// @unchecked Sendable is used because 'account' is mutable, but mutation is controlled and safe in this context.
1414
public let remoteInterface: RemoteInterface
1515
public let changeNotificationInterface: ChangeNotificationInterface
1616
public let domain: NSFileProviderDomain?
1717
public let dbManager: FilesDatabaseManager
18-
public let account: Account
18+
public var account: Account
1919
public var accountId: String { account.ncKitAccount }
2020

21-
public let webSocketPingIntervalNanoseconds: UInt64 = 3 * 1_000_000_000
21+
public var webSocketPingIntervalNanoseconds: UInt64 = 3 * 1_000_000_000
2222
public let webSocketReconfigureIntervalNanoseconds: UInt64 = 1 * 1_000_000_000
2323
public let webSocketPingFailLimit = 8
2424
public let webSocketAuthenticationFailLimit = 3
@@ -40,14 +40,8 @@ public actor RemoteChangeObserver: NSObject, Sendable {
4040
private(set) var webSocketAuthenticationFailCount = 0
4141

4242
private(set) var pollingTimer: Timer?
43-
public var pollInterval: TimeInterval = 60 {
44-
didSet {
45-
if pollingActive {
46-
stopPollingTimer()
47-
startPollingTimer()
48-
}
49-
}
50-
}
43+
44+
let pollInterval: TimeInterval
5145

5246
public var pollingActive: Bool {
5347
pollingTimer != nil
@@ -73,13 +67,15 @@ public actor RemoteChangeObserver: NSObject, Sendable {
7367
changeNotificationInterface: ChangeNotificationInterface,
7468
domain: NSFileProviderDomain?,
7569
dbManager: FilesDatabaseManager,
70+
pollInterval: TimeInterval = 60,
7671
log: any FileProviderLogging
7772
) {
7873
self.account = account
7974
self.remoteInterface = remoteInterface
8075
self.changeNotificationInterface = changeNotificationInterface
8176
self.domain = domain
8277
self.dbManager = dbManager
78+
self.pollInterval = pollInterval
8379
logger = FileProviderLogger(category: "RemoteChangeObserver", log: log)
8480
super.init()
8581

@@ -89,22 +85,22 @@ public actor RemoteChangeObserver: NSObject, Sendable {
8985
webSocketAuthenticationFailCount = 0
9086

9187
Task {
92-
await reconnectWebSocket()
88+
reconnectWebSocket()
9389
}
9490
}
9591

9692
private func startPollingTimer() {
97-
guard !invalidated else { return }
98-
Task {
99-
pollingTimer = Timer.scheduledTimer(
100-
withTimeInterval: pollInterval, repeats: true
101-
) { [weak self] _ in
102-
self?.logger.info("Polling timer timeout, notifying change.")
93+
guard !invalidated else {
94+
self.logger.error("Starting polling timer while the current one is not invalidated yet!")
95+
return
96+
}
10397

104-
Task {
105-
await self?.startWorkingSetCheck()
106-
}
98+
Task { @MainActor in
99+
pollingTimer = Timer.scheduledTimer(withTimeInterval: pollInterval, repeats: true) { [weak self] _ in
100+
self?.logger.info("Polling timer timeout, notifying change.")
101+
self?.startWorkingSetCheck()
107102
}
103+
108104
logger.info("Starting polling timer.")
109105
}
110106
}
@@ -118,29 +114,35 @@ public actor RemoteChangeObserver: NSObject, Sendable {
118114
}
119115

120116
public func invalidate() {
117+
logger.debug("Invalidating.")
121118
invalidated = true
122119
resetWebSocket()
123120
}
124121

125122
private func reconnectWebSocket() {
123+
logger.debug("Reconnecting web socket...")
126124
stopPollingTimer()
127125
resetWebSocket()
126+
128127
guard networkReachability != .notReachable else {
129128
logger.error("Network unreachable, will retry when reconnected.")
130129
return
131130
}
131+
132132
guard webSocketAuthenticationFailCount < webSocketAuthenticationFailLimit else {
133133
logger.error("Exceeded authentication failures for notify push websocket \(account.ncKitAccount), will poll instead.", [.account: account.ncKitAccount])
134134
startPollingTimer()
135135
return
136136
}
137+
137138
Task { [weak self] in
138139
try await Task.sleep(nanoseconds: self?.webSocketReconfigureIntervalNanoseconds ?? 0)
139140
await self?.configureNotifyPush()
140141
}
141142
}
142143

143144
public func resetWebSocket() {
145+
logger.debug("Resetting web socket...")
144146
webSocketTask?.cancel()
145147
webSocketUrlSession = nil
146148
webSocketTask = nil
@@ -152,7 +154,10 @@ public actor RemoteChangeObserver: NSObject, Sendable {
152154
}
153155

154156
private func configureNotifyPush() async {
157+
logger.debug("Configuring notify push...")
158+
155159
guard !invalidated else {
160+
logger.error("Attempt to configure notify push while being invalidated!")
156161
return
157162
}
158163

@@ -246,7 +251,7 @@ public actor RemoteChangeObserver: NSObject, Sendable {
246251
}
247252

248253
Task {
249-
await self.pingWebSocket()
254+
self.pingWebSocket()
250255
}
251256
}
252257
}
@@ -273,20 +278,20 @@ public actor RemoteChangeObserver: NSObject, Sendable {
273278

274279
guard error == nil else {
275280
self.logger.error("Websocket ping failed.", [.error: error])
276-
await self.incrementWebSocketPingFailCount()
281+
self.incrementWebSocketPingFailCount()
277282

278-
if await self.webSocketPingFailCount > self.webSocketPingFailLimit {
283+
if self.webSocketPingFailCount > self.webSocketPingFailLimit {
279284
Task.detached(priority: .medium) {
280-
await self.reconnectWebSocket()
285+
self.reconnectWebSocket()
281286
}
282287
} else {
283-
await startNewWebSocketPingTask()
288+
startNewWebSocketPingTask()
284289
}
285290

286291
return
287292
}
288293

289-
await startNewWebSocketPingTask()
294+
startNewWebSocketPingTask()
290295
}
291296
}
292297
}
@@ -304,20 +309,20 @@ public actor RemoteChangeObserver: NSObject, Sendable {
304309

305310
switch result {
306311
case .failure:
307-
let accountId = await self.accountId
312+
let accountId = self.accountId
308313
self.logger.debug("Failed to read websocket.", [.account: accountId])
309314
// Do not reconnect here, delegate methods will handle reconnecting
310315
case let .success(message):
311316
switch message {
312317
case let .data(data):
313-
await self.processWebsocket(data: data)
318+
self.processWebsocket(data: data)
314319
case let .string(string):
315-
await self.processWebsocket(string: string)
320+
self.processWebsocket(string: string)
316321
@unknown default:
317322
self.logger.error("Unknown case encountered while reading websocket!")
318323
}
319324

320-
await self.readWebSocket()
325+
self.readWebSocket()
321326
}
322327
}
323328
}
@@ -360,37 +365,45 @@ public actor RemoteChangeObserver: NSObject, Sendable {
360365
logger.error("Received unknown string from websocket \(account.ncKitAccount): \(string)", [.account: account.ncKitAccount])
361366
}
362367
}
368+
369+
func replaceAccount(with account: Account) {
370+
self.account = account
371+
}
372+
373+
func setWebSocketPingInterval(to nanoseconds: UInt64) {
374+
self.webSocketPingIntervalNanoseconds = nanoseconds
375+
}
363376
}
364377

365378
// MARK: - URLSessionWebSocketDelegate
366379

367380
extension RemoteChangeObserver: URLSessionWebSocketDelegate {
368381
nonisolated public func urlSession(_: URLSession, webSocketTask _: URLSessionWebSocketTask, didOpenWithProtocol _: String?) {
369382
Task {
370-
guard await invalidated == false else {
383+
guard invalidated == false else {
371384
return
372385
}
373386

374-
logger.debug("Websocket connected sending auth details", [.account: await accountId])
387+
logger.debug("Websocket connected sending auth details", [.account: accountId])
375388
await authenticateWebSocket()
376389
}
377390
}
378391

379392
nonisolated public func urlSession(_: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith _: URLSessionWebSocketTask.CloseCode, reason: Data?) {
380393
Task {
381-
guard await invalidated == false else {
394+
guard invalidated == false else {
382395
return
383396
}
384397

385398
// If the task that closed is not the current active task, it means we have
386399
// already initiated a reset and this is a stale callback. Ignore it.
387-
guard await webSocketTask === self.webSocketTask else {
400+
guard webSocketTask === self.webSocketTask else {
388401
logger.debug("An old websocket task closed, ignoring.")
389402
return
390403
}
391404

392-
logger.debug("Socket connection closed: \(String(data: reason ?? Data(), encoding: .utf8) ?? "unknown reason"). Retrying websocket connection.", [.account: await accountId])
393-
await reconnectWebSocket()
405+
logger.debug("Socket connection closed: \(String(data: reason ?? Data(), encoding: .utf8) ?? "unknown reason"). Retrying websocket connection.", [.account: accountId])
406+
reconnectWebSocket()
394407
}
395408
}
396409

@@ -406,7 +419,7 @@ extension RemoteChangeObserver: NextcloudKitDelegate {
406419
return
407420
}
408421

409-
guard await !invalidated else {
422+
guard !invalidated else {
410423
return
411424
}
412425

@@ -440,7 +453,7 @@ extension RemoteChangeObserver: NextcloudKitDelegate {
440453
return
441454
}
442455

443-
await self.setNetworkReachability(typeReachability)
456+
self.setNetworkReachability(typeReachability)
444457
}
445458
}
446459

@@ -500,8 +513,9 @@ extension RemoteChangeObserver: NextcloudKitDelegate {
500513
/// - Parameters:
501514
/// - completionHandler: An optional closure to call after the working set check completed.
502515
///
503-
func startWorkingSetCheck(completionHandler: (() -> Void)? = nil) {
516+
func startWorkingSetCheck(completionHandler: (@Sendable () -> Void)? = nil) {
504517
guard !workingSetCheckOngoing, !invalidated else {
518+
logger.error("Cancelling dispatch of working set check because it either is already ongoing or this is invalidated!")
505519
return
506520
}
507521

@@ -512,9 +526,11 @@ extension RemoteChangeObserver: NextcloudKitDelegate {
512526
}
513527

514528
private func checkWorkingSet() async {
529+
logger.debug("Checking working set...")
515530
workingSetCheckOngoing = true
516531

517532
defer {
533+
logger.debug("Working set check no longer ongoing.")
518534
workingSetCheckOngoing = false
519535
}
520536

Sources/NextcloudFileProviderKit/Utilities/RetrievedCapabilitiesActor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import NextcloudCapabilitiesKit
66

77
let CapabilitiesFetchInterval: TimeInterval = 30 * 60 // 30mins
88

9-
actor RetrievedCapabilitiesActor {
9+
actor RetrievedCapabilitiesActor: Sendable {
1010
static let shared = RetrievedCapabilitiesActor()
1111

1212
var ongoingFetches: Set<String> = []

Sources/NextcloudFileProviderKitMocks/FileProviderLogMock.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public actor FileProviderLogMock: FileProviderLogging {
1212
logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "FileProviderLogMock")
1313
}
1414

15-
public func write(category _: String, level _: OSLogType, message: String, details _: [FileProviderLogDetailKey: Any?]) {
15+
public func write(category _: String, level _: OSLogType, message: String, details _: [FileProviderLogDetailKey: (any Sendable)?]) {
1616
logger.debug("\(message, privacy: .public)")
1717
}
1818
}

0 commit comments

Comments
 (0)