commit 974e7375deb4b519dc7fc931aa7d731a179adff5
parent dac6052952c6fd09b33371adb2018fbd03df9b23
Author: triesap <tyson@radroots.org>
Date: Sun, 14 Jun 2026 04:04:13 -0700
app: add location check-in test probes
- add deterministic UI-test location service modes
- route AppState through the configured check-in runtime
- keep production location behavior on AppleKit services
- support success denied unavailable and timeout UI proofs
Diffstat:
2 files changed, 89 insertions(+), 7 deletions(-)
diff --git a/Radroots/App/AppState.swift b/Radroots/App/AppState.swift
@@ -76,7 +76,7 @@ public final class AppState: ObservableObject {
private var statusTask: Task<Void, Never>?
private var secureIdentityStore: FieldSecureIdentityStore?
private var identityMetadataStore: FieldIdentityPublicMetadataStore?
- private let locationCheckIn = FieldLocationCheckIn()
+ private let locationCheckIn = FieldLocationCheckIn.configured()
public init(radroots: Radroots = Radroots()) {
self.radroots = radroots
diff --git a/Radroots/Runtime/FieldLocationCheckIn.swift b/Radroots/Runtime/FieldLocationCheckIn.swift
@@ -49,14 +49,17 @@ public struct FieldLocationCheckIn: Sendable {
public init(
locationServices: any RadrootsLocationServices = RadrootsAppleLocationServices(),
- request: RadrootsCurrentLocationRequest = try! RadrootsCurrentLocationRequest(
- timeoutSeconds: 10,
- desiredAccuracyMeters: 100,
- maximumCachedReadingAgeSeconds: 30
- )
+ request: RadrootsCurrentLocationRequest? = nil
) {
self.locationServices = locationServices
- self.request = request
+ self.request = request ?? Self.defaultRequest()
+ }
+
+ static func configured() -> Self {
+ guard let mode = FieldLocationCheckInUITestMode.current else {
+ return Self()
+ }
+ return Self(locationServices: FieldLocationCheckInUITestLocationServices(mode: mode))
}
public func status() async -> FieldLocationCheckInState {
@@ -95,4 +98,83 @@ public struct FieldLocationCheckIn: Sendable {
return .failed(availability, error.localizedDescription)
}
}
+
+ private static func defaultRequest() -> RadrootsCurrentLocationRequest {
+ do {
+ return try RadrootsCurrentLocationRequest(
+ timeoutSeconds: 10,
+ desiredAccuracyMeters: 100,
+ maximumCachedReadingAgeSeconds: 30
+ )
+ } catch {
+ preconditionFailure("invalid default location check-in request")
+ }
+ }
+}
+
+private enum FieldLocationCheckInUITestMode: String {
+ case success
+ case denied
+ case unavailable
+ case timeout
+
+ static var current: Self? {
+ guard ProcessInfo.processInfo.environment["RADROOTS_FIELD_IOS_UI_TEST"] == "true" else {
+ return nil
+ }
+ guard let raw = ProcessInfo.processInfo.environment["RADROOTS_FIELD_IOS_UI_TEST_LOCATION_MODE"] else {
+ return nil
+ }
+ return Self(rawValue: raw)
+ }
+}
+
+private actor FieldLocationCheckInUITestLocationServices: RadrootsLocationServices {
+ private let mode: FieldLocationCheckInUITestMode
+
+ init(mode: FieldLocationCheckInUITestMode) {
+ self.mode = mode
+ }
+
+ func currentAvailability() async -> RadrootsLocationServicesAvailability {
+ switch mode {
+ case .success:
+ RadrootsLocationServicesAvailability(locationServicesEnabled: true, authorization: .authorizedWhenInUse)
+ case .denied:
+ RadrootsLocationServicesAvailability(locationServicesEnabled: true, authorization: .denied)
+ case .unavailable:
+ RadrootsLocationServicesAvailability(locationServicesEnabled: false, authorization: .unavailable)
+ case .timeout:
+ RadrootsLocationServicesAvailability(locationServicesEnabled: true, authorization: .authorizedWhenInUse)
+ }
+ }
+
+ func requestWhenInUseAuthorization() async throws -> RadrootsLocationAuthorization {
+ switch mode {
+ case .success, .timeout:
+ .authorizedWhenInUse
+ case .denied:
+ throw RadrootsLocationServicesError.permissionDenied("location permission is denied")
+ case .unavailable:
+ throw RadrootsLocationServicesError.unavailable("location services are unavailable")
+ }
+ }
+
+ func currentLocation(_ request: RadrootsCurrentLocationRequest) async throws -> RadrootsCurrentLocationResult {
+ switch mode {
+ case .success:
+ let reading = try RadrootsLocationReading(
+ coordinate: RadrootsLocationCoordinate(latitude: 49.2827, longitude: -123.1207),
+ horizontalAccuracyMeters: 12,
+ capturedAt: Date()
+ )
+ return try RadrootsCurrentLocationResult(reading: reading, authorization: .authorizedWhenInUse)
+ case .denied:
+ throw RadrootsLocationServicesError.permissionDenied("location permission is denied")
+ case .unavailable:
+ throw RadrootsLocationServicesError.unavailable("location services are unavailable")
+ case .timeout:
+ throw RadrootsLocationServicesError.timeout("current location request timed out")
+ }
+ }
}