apple_kit

Apple-native services for Radroots iOS and macOS apps
git clone https://radroots.dev/git/apple_kit.git
Log | Files | Refs | README

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 }