Implement WatchConnectivity relay bridge
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
import RemoteCore
|
||||
import WatchBridgeCommon
|
||||
import WatchBridgePhone
|
||||
|
||||
@MainActor
|
||||
private final class FakePhoneSession: RelaySessionSending {
|
||||
var activationState: RelayActivationState = .notActivated
|
||||
var isReachable = true
|
||||
var sent: [[String: Any]] = []
|
||||
var reply: (([String: Any]) -> Void)?
|
||||
var error: ((Error) -> Void)?
|
||||
func activate() { activationState = .activated }
|
||||
func sendMessage(_ message: [String: Any],
|
||||
replyHandler: @escaping ([String: Any]) -> Void,
|
||||
errorHandler: @escaping (Error) -> Void) {
|
||||
sent.append(message)
|
||||
reply = replyHandler
|
||||
error = errorHandler
|
||||
}
|
||||
func updateApplicationContext(_ context: [String: Any]) throws {}
|
||||
func answer(_ value: [String: Any]) { reply?(value) }
|
||||
}
|
||||
|
||||
private actor RecordingPhoneCoordinator: PhoneCommandCoordinating {
|
||||
var activated: [WatchPeerGeneration] = []
|
||||
var deactivated: [WatchPeerGeneration] = []
|
||||
var commands: [CommandEnvelope] = []
|
||||
let sessionID = UUID()
|
||||
let televisionID = UUID()
|
||||
|
||||
func watchBridgeDidActivate(peer: WatchPeerGeneration) { activated.append(peer) }
|
||||
func watchBridgeDidDeactivate(peer: WatchPeerGeneration) { deactivated.append(peer) }
|
||||
func beginWatchSession(_ request: BeginRemoteSessionRequest, peer: WatchPeerGeneration)
|
||||
-> Result<RemoteSessionGrant, RelayFailure> {
|
||||
.success(RemoteSessionGrant(sessionID: sessionID, televisionID: televisionID,
|
||||
issuedAt: Date(timeIntervalSince1970: 1_700_000_000)))
|
||||
}
|
||||
func endWatchSession(_ request: EndRemoteSessionRequest, peer: WatchPeerGeneration)
|
||||
-> Result<RemoteSessionEnded, RelayFailure> {
|
||||
.success(RemoteSessionEnded(sessionID: request.sessionID))
|
||||
}
|
||||
func submitWatchCommand(_ envelope: CommandEnvelope, peer: WatchPeerGeneration) -> CommandReceipt {
|
||||
commands.append(envelope)
|
||||
return CommandReceipt(id: envelope.id, status: .sentToTransport)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Test func phoneWaitsForActorActivationAndForwardsCommandWithoutAdmission() async throws {
|
||||
let session = FakePhoneSession()
|
||||
let actor = RecordingPhoneCoordinator()
|
||||
let bridge = PhoneRelayBridge(session: session, dependency: PhoneRelayDependency(coordinator: actor))
|
||||
bridge.activate()
|
||||
bridge.activationDidComplete(error: nil)
|
||||
await Task.yield()
|
||||
#expect({
|
||||
if case .ready = bridge.state { return true }
|
||||
return false
|
||||
}())
|
||||
|
||||
let begin = try RelayContainer(kind: .beginSession, value: BeginRemoteSessionRequest()).dictionary()
|
||||
bridge.receive(message: begin) { _ in }
|
||||
await Task.yield()
|
||||
#expect(session.sent.isEmpty)
|
||||
|
||||
let peerActivated = await actor.activated
|
||||
#expect(peerActivated.count == 1)
|
||||
bridge.receive(message: begin) { _ in }
|
||||
await Task.yield()
|
||||
#expect(session.sent.isEmpty)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Test func phoneRejectsMalformedAndWrongDirectionRequests() async throws {
|
||||
let session = FakePhoneSession()
|
||||
let actor = RecordingPhoneCoordinator()
|
||||
let bridge = PhoneRelayBridge(session: session, dependency: PhoneRelayDependency(coordinator: actor))
|
||||
bridge.activate(); bridge.activationDidComplete(error: nil)
|
||||
await Task.yield()
|
||||
var replies = 0
|
||||
bridge.receive(message: ["schemaVersion": true, "kind": "command", "payload": Data()]) { _ in
|
||||
replies += 1
|
||||
}
|
||||
await Task.yield()
|
||||
#expect(replies == 1)
|
||||
}
|
||||
Reference in New Issue
Block a user