From 862e21212eaf0749cdc2171eca681b90d526fc74 Mon Sep 17 00:00:00 2001 From: Zhilong Zheng Date: Thu, 6 Aug 2026 22:51:52 -0700 Subject: [PATCH] test(macos): observe realtime recovery atomically Punchcard-Session: golden-meadow-cedar-dv --- .../OpenClaw/TalkModeRuntime+Realtime.swift | 15 +++++++++------ .../TalkModeRuntimeSpeechTests.swift | 14 +++++++------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/apps/macos/Sources/OpenClaw/TalkModeRuntime+Realtime.swift b/apps/macos/Sources/OpenClaw/TalkModeRuntime+Realtime.swift index 44c07a0059b5..31a174eeff64 100644 --- a/apps/macos/Sources/OpenClaw/TalkModeRuntime+Realtime.swift +++ b/apps/macos/Sources/OpenClaw/TalkModeRuntime+Realtime.swift @@ -366,16 +366,23 @@ extension TalkModeRuntime { func _test_handleRealtimeTermination( _ termination: RealtimeTalkRelayTermination, - relayGeneration: UInt64) async + relayGeneration: UInt64) async -> Bool { await self.handleRealtimeTermination(termination, relayGeneration: relayGeneration) + return self.realtimeRestartTask != nil } func _test_handleRealtimeInputRestartFailure( _ message: String, - relayGeneration: UInt64) async + relayGeneration: UInt64) async -> Bool { await self.handleRealtimeInputRestartFailure(message, relayGeneration: relayGeneration) + return self.realtimeRestartTask != nil + } + + func _test_setPausedAndHasPendingRealtimeRestart(_ paused: Bool) async -> Bool { + await self.setPaused(paused) + return self.realtimeRestartTask != nil } func _test_realtimeSessionIsActive() -> Bool { @@ -386,10 +393,6 @@ extension TalkModeRuntime { self.rapidRealtimeRestartCount } - func _test_hasPendingRealtimeRestart() -> Bool { - self.realtimeRestartTask != nil - } - func _test_cancelRealtimeRecovery() { self.isEnabled = false self.cancelScheduledRealtimeRecovery() diff --git a/apps/macos/Tests/OpenClawIPCTests/TalkModeRuntimeSpeechTests.swift b/apps/macos/Tests/OpenClawIPCTests/TalkModeRuntimeSpeechTests.swift index 46dcfae2fe2c..5427338e3c8a 100644 --- a/apps/macos/Tests/OpenClawIPCTests/TalkModeRuntimeSpeechTests.swift +++ b/apps/macos/Tests/OpenClawIPCTests/TalkModeRuntimeSpeechTests.swift @@ -164,7 +164,7 @@ struct TalkModeRuntimeSpeechTests { onSpeakingChanged: { _ in }) let relayGeneration = await runtime._test_prepareEnabledRealtimeSessionForClose(session) - await runtime._test_handleRealtimeTermination( + _ = await runtime._test_handleRealtimeTermination( .remoteClose(reason: "stale"), relayGeneration: relayGeneration &- 1) #expect(await runtime._test_realtimeSessionIsActive()) @@ -172,13 +172,13 @@ struct TalkModeRuntimeSpeechTests { await runtime._test_handleRealtimeStatus( "Listening (Realtime)", relayGeneration: relayGeneration) - await runtime._test_handleRealtimeTermination( + let recoveryScheduled = await runtime._test_handleRealtimeTermination( .audioCaptureFailed(message: "microphone unavailable"), relayGeneration: relayGeneration) #expect(await !(runtime._test_realtimeSessionIsActive())) #expect(await runtime._test_rapidRealtimeRestartCount() == 1) - #expect(await runtime._test_hasPendingRealtimeRestart()) + #expect(recoveryScheduled) await runtime._test_cancelRealtimeRecovery() session.stop() @@ -192,13 +192,13 @@ struct TalkModeRuntimeSpeechTests { audioCapture: RuntimeTestAudioCapture()) let relayGeneration = await runtime._test_prepareEnabledRealtimeSessionForClose(session) - await runtime._test_handleRealtimeInputRestartFailure( + let recoveryScheduled = await runtime._test_handleRealtimeInputRestartFailure( "selected microphone unavailable", relayGeneration: relayGeneration) #expect(await !(runtime._test_realtimeSessionIsActive())) #expect(await runtime._test_rapidRealtimeRestartCount() == 1) - #expect(await runtime._test_hasPendingRealtimeRestart()) + #expect(recoveryScheduled) // Ownership must not be dropped while the server relay stays live; recovery would then // run a second session against the same gateway lease. @@ -219,13 +219,13 @@ struct TalkModeRuntimeSpeechTests { await runtime.setPaused(true) audioCapture.startError = RuntimeTestAudioCaptureError.inputUnavailable - await runtime.setPaused(false) + let recoveryScheduled = await runtime._test_setPausedAndHasPendingRealtimeRestart(false) // Talk must never stay enabled with no microphone and no route back: the failed unpause // has to reach the same bounded recovery / native-speech fallback as any other capture loss. #expect(await !(runtime._test_realtimeSessionIsActive())) #expect(await runtime._test_rapidRealtimeRestartCount() == 1) - #expect(await runtime._test_hasPendingRealtimeRestart()) + #expect(recoveryScheduled) let recorded = await waitForRelayClose(requests) #expect(recorded == ["talk.session.close"])