diff --git a/apps/ios/Tests/Logic/TalkConfigParsingTests.swift b/apps/ios/Tests/Logic/TalkConfigParsingTests.swift new file mode 100644 index 00000000000..6c99b85dea2 --- /dev/null +++ b/apps/ios/Tests/Logic/TalkConfigParsingTests.swift @@ -0,0 +1,76 @@ +import Foundation +import OpenClawKit +import Testing + +private let iOSSilenceTimeoutMs = 900 + +@Suite struct TalkConfigParsingTests { + @Test func prefersNormalizedTalkProviderPayload() { + let talk: [String: Any] = [ + "provider": "elevenlabs", + "providers": [ + "elevenlabs": [ + "voiceId": "voice-normalized", + ], + ], + "voiceId": "voice-legacy", + ] + + let selection = TalkConfigParsing.selectProviderConfig( + TalkConfigParsing.bridgeFoundationDictionary(talk), + defaultProvider: "elevenlabs", + allowLegacyFallback: false) + #expect(selection?.provider == "elevenlabs") + #expect(selection?.config["voiceId"]?.stringValue == "voice-normalized") + } + + @Test func ignoresLegacyTalkFieldsWhenNormalizedPayloadMissing() { + let talk: [String: Any] = [ + "voiceId": "voice-legacy", + "apiKey": "legacy-key", // pragma: allowlist secret + ] + + let selection = TalkConfigParsing.selectProviderConfig( + TalkConfigParsing.bridgeFoundationDictionary(talk), + defaultProvider: "elevenlabs", + allowLegacyFallback: false) + #expect(selection == nil) + } + + @Test func readsConfiguredSilenceTimeoutMs() { + let talk: [String: Any] = [ + "silenceTimeoutMs": 1500, + ] + + #expect( + TalkConfigParsing.resolvedSilenceTimeoutMs( + TalkConfigParsing.bridgeFoundationDictionary(talk), + fallback: iOSSilenceTimeoutMs) == 1500) + } + + @Test func defaultsSilenceTimeoutMsWhenMissing() { + #expect(TalkConfigParsing.resolvedSilenceTimeoutMs(nil, fallback: iOSSilenceTimeoutMs) == iOSSilenceTimeoutMs) + } + + @Test func defaultsSilenceTimeoutMsWhenInvalid() { + let talk: [String: Any] = [ + "silenceTimeoutMs": 0, + ] + + #expect( + TalkConfigParsing.resolvedSilenceTimeoutMs( + TalkConfigParsing.bridgeFoundationDictionary(talk), + fallback: iOSSilenceTimeoutMs) == iOSSilenceTimeoutMs) + } + + @Test func defaultsSilenceTimeoutMsWhenBool() { + let talk: [String: Any] = [ + "silenceTimeoutMs": true, + ] + + #expect( + TalkConfigParsing.resolvedSilenceTimeoutMs( + TalkConfigParsing.bridgeFoundationDictionary(talk), + fallback: iOSSilenceTimeoutMs) == iOSSilenceTimeoutMs) + } +} diff --git a/apps/ios/Tests/TalkModeConfigParsingTests.swift b/apps/ios/Tests/TalkModeConfigParsingTests.swift index 6fad939cc7a..f27ae08bdcf 100644 --- a/apps/ios/Tests/TalkModeConfigParsingTests.swift +++ b/apps/ios/Tests/TalkModeConfigParsingTests.swift @@ -1,38 +1,9 @@ import Foundation -import OpenClawKit import Testing @testable import OpenClaw @MainActor -@Suite struct TalkModeConfigParsingTests { - @Test func prefersNormalizedTalkProviderPayload() { - let talk: [String: Any] = [ - "provider": "elevenlabs", - "providers": [ - "elevenlabs": [ - "voiceId": "voice-normalized", - ], - ], - "voiceId": "voice-legacy", - ] - - let selection = TalkModeManager.selectTalkProviderConfig( - TalkConfigParsing.bridgeFoundationDictionary(talk)) - #expect(selection?.provider == "elevenlabs") - #expect(selection?.config["voiceId"]?.stringValue == "voice-normalized") - } - - @Test func ignoresLegacyTalkFieldsWhenNormalizedPayloadMissing() { - let talk: [String: Any] = [ - "voiceId": "voice-legacy", - "apiKey": "legacy-key", // pragma: allowlist secret - ] - - let selection = TalkModeManager.selectTalkProviderConfig( - TalkConfigParsing.bridgeFoundationDictionary(talk)) - #expect(selection == nil) - } - +@Suite struct TalkModeManagerTests { @Test func detectsPCMFormatRejectionFromElevenLabsError() { let error = NSError( domain: "ElevenLabsTTS", @@ -50,32 +21,4 @@ import Testing userInfo: [NSLocalizedDescriptionKey: "queue enqueue failed"]) #expect(TalkModeManager._test_isPCMFormatRejectedByAPI(error) == false) } - - @Test func readsConfiguredSilenceTimeoutMs() { - let talk: [String: Any] = [ - "silenceTimeoutMs": 1500, - ] - - #expect(TalkModeManager.resolvedSilenceTimeoutMs(TalkConfigParsing.bridgeFoundationDictionary(talk)) == 1500) - } - - @Test func defaultsSilenceTimeoutMsWhenMissing() { - #expect(TalkModeManager.resolvedSilenceTimeoutMs(nil) == TalkDefaults.silenceTimeoutMs) - } - - @Test func defaultsSilenceTimeoutMsWhenInvalid() { - let talk: [String: Any] = [ - "silenceTimeoutMs": 0, - ] - - #expect(TalkModeManager.resolvedSilenceTimeoutMs(TalkConfigParsing.bridgeFoundationDictionary(talk)) == TalkDefaults.silenceTimeoutMs) - } - - @Test func defaultsSilenceTimeoutMsWhenBool() { - let talk: [String: Any] = [ - "silenceTimeoutMs": true, - ] - - #expect(TalkModeManager.resolvedSilenceTimeoutMs(TalkConfigParsing.bridgeFoundationDictionary(talk)) == TalkDefaults.silenceTimeoutMs) - } } diff --git a/apps/ios/project.yml b/apps/ios/project.yml index f0b6011eeaf..3d2bc93af80 100644 --- a/apps/ios/project.yml +++ b/apps/ios/project.yml @@ -25,6 +25,15 @@ schemes: test: targets: - OpenClawTests + - OpenClawLogicTests + OpenClawLogicTests: + shared: true + build: + targets: + OpenClawLogicTests: all + test: + targets: + - OpenClawLogicTests targets: OpenClaw: @@ -117,8 +126,11 @@ targets: NSLocationWhenInUseUsageDescription: OpenClaw uses your location when you allow location sharing. NSLocationAlwaysAndWhenInUseUsageDescription: OpenClaw can share your location in the background when you enable Always. NSMicrophoneUsageDescription: OpenClaw needs microphone access for voice wake. + NSMotionUsageDescription: OpenClaw may use motion data to support device-aware interactions and automations. + NSPhotoLibraryUsageDescription: OpenClaw needs photo library access when you choose existing photos to share with your assistant. NSSpeechRecognitionUsageDescription: OpenClaw uses on-device speech recognition for voice wake. NSSupportsLiveActivities: true + ITSAppUsesNonExemptEncryption: false UISupportedInterfaceOrientations: - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown @@ -259,6 +271,8 @@ targets: Release: Signing.xcconfig sources: - path: Tests + excludes: + - Logic dependencies: - target: OpenClaw - package: Swabble @@ -281,3 +295,29 @@ targets: CFBundleDisplayName: OpenClawTests CFBundleShortVersionString: "2026.3.8" CFBundleVersion: "20260308" + + OpenClawLogicTests: + type: bundle.unit-test + platform: iOS + configFiles: + Debug: Signing.xcconfig + Release: Signing.xcconfig + sources: + - path: Tests/Logic + dependencies: + - package: OpenClawKit + settings: + base: + CODE_SIGN_IDENTITY: "Apple Development" + CODE_SIGN_STYLE: "$(OPENCLAW_CODE_SIGN_STYLE)" + DEVELOPMENT_TEAM: "$(OPENCLAW_DEVELOPMENT_TEAM)" + PRODUCT_BUNDLE_IDENTIFIER: ai.openclaw.ios.logic-tests + ENABLE_APP_INTENTS_METADATA_GENERATION: NO + SWIFT_VERSION: "6.0" + SWIFT_STRICT_CONCURRENCY: complete + info: + path: Tests/Info.plist + properties: + CFBundleDisplayName: OpenClawLogicTests + CFBundleShortVersionString: "2026.3.8" + CFBundleVersion: "20260308"