RadrootsAppleBackgroundTaskSchedulerTests.swift (6430B)
1 import Foundation 2 import Testing 3 @testable import RadrootsKit 4 5 @Test func appleBackgroundTaskSchedulerRegistersAndSubmitsThroughAdapters() async throws { 6 let probe = RadrootsAppleBackgroundTaskSchedulerProbe(now: Date(timeIntervalSince1970: 100)) 7 let scheduler = RadrootsAppleBackgroundTaskScheduler(adapters: probe.adapters()) 8 let identifier = try RadrootsBackgroundTaskIdentifier("org.radroots.field-ios.background.refresh") 9 let registration = RadrootsAppleBackgroundTaskRegistration( 10 identifier: identifier, 11 kind: .appRefresh, 12 handler: { true } 13 ) 14 let request = try RadrootsBackgroundTaskRequest( 15 identifier: identifier, 16 kind: .appRefresh, 17 earliestBeginDate: Date(timeIntervalSince1970: 120) 18 ) 19 20 #expect(try await scheduler.register(registration)) 21 let snapshot = try await scheduler.submit(request) 22 23 #expect(snapshot.identifier == identifier) 24 #expect(snapshot.submittedAt == Date(timeIntervalSince1970: 100)) 25 #expect(await probe.registeredIdentifiers == [identifier]) 26 #expect(await probe.submittedRequests == [request]) 27 } 28 29 @Test func appleBackgroundTaskSchedulerCancelsAndListsPendingTasksThroughAdapters() async throws { 30 let identifier = try RadrootsBackgroundTaskIdentifier("org.radroots.field-ios.background.processing") 31 let request = try RadrootsBackgroundTaskRequest( 32 identifier: identifier, 33 kind: .processing, 34 requiresNetworkConnectivity: true 35 ) 36 let pendingSnapshot = try RadrootsBackgroundTaskSnapshot( 37 request: request, 38 submittedAt: Date(timeIntervalSince1970: 5) 39 ) 40 let probe = RadrootsAppleBackgroundTaskSchedulerProbe(pendingSnapshots: [pendingSnapshot]) 41 let scheduler = RadrootsAppleBackgroundTaskScheduler(adapters: probe.adapters()) 42 43 try await scheduler.cancel(identifier) 44 try await scheduler.cancelAll() 45 46 #expect(await probe.cancelledIdentifiers == [identifier]) 47 #expect(await probe.cancelAllCount == 1) 48 #expect(try await scheduler.pendingTasks() == [pendingSnapshot]) 49 } 50 51 @Test func appleBackgroundTaskSchedulerMapsAdapterSubmitFailures() async throws { 52 let probe = RadrootsAppleBackgroundTaskSchedulerProbe( 53 submitOutcome: .failure(.schedulerFailure("submit rejected")) 54 ) 55 let scheduler = RadrootsAppleBackgroundTaskScheduler(adapters: probe.adapters()) 56 let request = try RadrootsBackgroundTaskRequest( 57 identifier: "org.radroots.field-ios.background.refresh", 58 kind: .appRefresh 59 ) 60 61 await #expect(throws: RadrootsBackgroundTaskError.schedulerFailure("submit rejected")) { 62 _ = try await scheduler.submit(request) 63 } 64 } 65 66 @Test func appleBackgroundTaskSchedulerMapsRegistrationRejectionToTypedFailure() async throws { 67 let probe = RadrootsAppleBackgroundTaskSchedulerProbe(registerResult: false) 68 let scheduler = RadrootsAppleBackgroundTaskScheduler(adapters: probe.adapters()) 69 let identifier = try RadrootsBackgroundTaskIdentifier("org.radroots.field-ios.background.refresh") 70 let registration = RadrootsAppleBackgroundTaskRegistration( 71 identifier: identifier, 72 kind: .appRefresh, 73 handler: { true } 74 ) 75 76 await #expect(throws: RadrootsBackgroundTaskError.schedulerFailure("background task registration was rejected")) { 77 _ = try await scheduler.register(registration) 78 } 79 #expect(await probe.registeredIdentifiers == [identifier]) 80 } 81 82 private actor RadrootsAppleBackgroundTaskSchedulerProbe { 83 private let nowValue: Date 84 private let registerResult: Bool 85 private let submitOutcome: Result<Void, RadrootsBackgroundTaskError> 86 private var pendingSnapshotsValue: [RadrootsBackgroundTaskSnapshot] 87 private var registeredIdentifiersValue: [RadrootsBackgroundTaskIdentifier] 88 private var submittedRequestsValue: [RadrootsBackgroundTaskRequest] 89 private var cancelledIdentifiersValue: [RadrootsBackgroundTaskIdentifier] 90 private var cancelAllCountValue: Int 91 92 init( 93 now: Date = Date(timeIntervalSince1970: 0), 94 registerResult: Bool = true, 95 submitOutcome: Result<Void, RadrootsBackgroundTaskError> = .success(()), 96 pendingSnapshots: [RadrootsBackgroundTaskSnapshot] = [] 97 ) { 98 self.nowValue = now 99 self.registerResult = registerResult 100 self.submitOutcome = submitOutcome 101 self.pendingSnapshotsValue = pendingSnapshots 102 self.registeredIdentifiersValue = [] 103 self.submittedRequestsValue = [] 104 self.cancelledIdentifiersValue = [] 105 self.cancelAllCountValue = 0 106 } 107 108 nonisolated func adapters() -> RadrootsAppleBackgroundTaskSchedulerAdapters { 109 RadrootsAppleBackgroundTaskSchedulerAdapters( 110 now: { 111 self.nowValue 112 }, 113 register: { registration in 114 await self.register(registration) 115 }, 116 submit: { request in 117 try await self.submit(request) 118 }, 119 cancel: { identifier in 120 await self.cancel(identifier) 121 }, 122 cancelAll: { 123 await self.cancelAll() 124 }, 125 pendingTasks: { 126 await self.pendingSnapshots() 127 } 128 ) 129 } 130 131 private func register(_ registration: RadrootsAppleBackgroundTaskRegistration) -> Bool { 132 registeredIdentifiersValue.append(registration.identifier) 133 return registerResult 134 } 135 136 private func submit(_ request: RadrootsBackgroundTaskRequest) throws { 137 submittedRequestsValue.append(request) 138 switch submitOutcome { 139 case .success: 140 return 141 case .failure(let error): 142 throw error 143 } 144 } 145 146 private func cancel(_ identifier: RadrootsBackgroundTaskIdentifier) { 147 cancelledIdentifiersValue.append(identifier) 148 } 149 150 private func cancelAll() { 151 cancelAllCountValue += 1 152 } 153 154 private func pendingSnapshots() -> [RadrootsBackgroundTaskSnapshot] { 155 pendingSnapshotsValue 156 } 157 158 var registeredIdentifiers: [RadrootsBackgroundTaskIdentifier] { 159 registeredIdentifiersValue 160 } 161 162 var submittedRequests: [RadrootsBackgroundTaskRequest] { 163 submittedRequestsValue 164 } 165 166 var cancelledIdentifiers: [RadrootsBackgroundTaskIdentifier] { 167 cancelledIdentifiersValue 168 } 169 170 var cancelAllCount: Int { 171 cancelAllCountValue 172 } 173 }