From d64b9604e89df2a5e8911940bf711468f1d749fa Mon Sep 17 00:00:00 2001 From: Christoph Sturm Date: Sun, 31 May 2026 11:18:05 +0200 Subject: [PATCH 1/4] Add failing speech recognition usage description test --- cmux.xcodeproj/project.pbxproj | 4 ++++ .../PrivacyUsageDescriptionBundleTests.swift | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 cmuxTests/PrivacyUsageDescriptionBundleTests.swift diff --git a/cmux.xcodeproj/project.pbxproj b/cmux.xcodeproj/project.pbxproj index 29e65084c7..23240a64a3 100644 --- a/cmux.xcodeproj/project.pbxproj +++ b/cmux.xcodeproj/project.pbxproj @@ -341,6 +341,7 @@ F4100000A1B2C3D4E5F60718 /* PortScannerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4100001A1B2C3D4E5F60718 /* PortScannerTests.swift */; }; A5001270 /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = A5001271 /* PostHog */; }; A5001521 /* PostHogAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001520 /* PostHogAnalytics.swift */; }; + C0DEF5000000000000000001 /* PrivacyUsageDescriptionBundleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DEF5000000000000000002 /* PrivacyUsageDescriptionBundleTests.swift */; }; C47110010000000000000001 /* ProcessPipeReadCrashRegressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47110010000000000000002 /* ProcessPipeReadCrashRegressionTests.swift */; }; C47110020000000000000001 /* ProcessPipeReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47110020000000000000002 /* ProcessPipeReader.swift */; }; C47110020000000000000003 /* ProcessPipeReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47110020000000000000002 /* ProcessPipeReader.swift */; }; @@ -977,6 +978,7 @@ A5001541 /* PortScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortScanner.swift; sourceTree = ""; }; F4100001A1B2C3D4E5F60718 /* PortScannerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortScannerTests.swift; sourceTree = ""; }; A5001520 /* PostHogAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogAnalytics.swift; sourceTree = ""; }; + C0DEF5000000000000000002 /* PrivacyUsageDescriptionBundleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyUsageDescriptionBundleTests.swift; sourceTree = ""; }; C47110010000000000000002 /* ProcessPipeReadCrashRegressionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessPipeReadCrashRegressionTests.swift; sourceTree = ""; }; C47110020000000000000002 /* ProcessPipeReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessPipeReader.swift; sourceTree = ""; }; A5C0DE0000000000000000B9 /* ProjectBuildSettingsTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Panels/ProjectBuildSettingsTabView.swift; sourceTree = ""; }; @@ -1815,6 +1817,7 @@ D7AB00000000000000000012 /* WorkspaceAdjacentPaneMoveTests.swift */, D7AB3605C10DEF0000000002 /* WorkspaceCloseTabsContextMenuTests.swift */, D0B10009A1B2C3D4E5F60001 /* PortalTabDragRoutingTests.swift */, + C0DEF5000000000000000002 /* PrivacyUsageDescriptionBundleTests.swift */, 71F8ED91A4B55D34BE6A0668 /* WorkspaceUnitTests.swift */, 71F8ED92A4B55D34BE6A0668 /* WorkspaceSplitStartupCommandTests.swift */, EF7347934AB1BD387686EE43 /* WorkspaceActionDispatcherTests.swift */, @@ -2699,6 +2702,7 @@ B3575000000000000000000A /* PiVaultAgentPersistenceTests.swift in Sources */, D0B10008A1B2C3D4E5F60001 /* PortalTabDragRoutingTests.swift in Sources */, F4100000A1B2C3D4E5F60718 /* PortScannerTests.swift in Sources */, + C0DEF5000000000000000001 /* PrivacyUsageDescriptionBundleTests.swift in Sources */, C47110010000000000000001 /* ProcessPipeReadCrashRegressionTests.swift in Sources */, F5410004A1B2C3D4E5F60718 /* RestorableAgentHookProviderHermesTests.swift in Sources */, F5410000A1B2C3D4E5F60718 /* RestorableAgentHookProviderResumeTests.swift in Sources */, diff --git a/cmuxTests/PrivacyUsageDescriptionBundleTests.swift b/cmuxTests/PrivacyUsageDescriptionBundleTests.swift new file mode 100644 index 0000000000..e5d9c49810 --- /dev/null +++ b/cmuxTests/PrivacyUsageDescriptionBundleTests.swift @@ -0,0 +1,20 @@ +import Testing + +#if canImport(cmux_DEV) +@testable import cmux_DEV +#elseif canImport(cmux) +@testable import cmux +#endif + +@MainActor +@Suite +struct PrivacyUsageDescriptionBundleTests { + @Test + func appBundleDeclaresSpeechRecognitionUsageDescription() throws { + let usageDescription = try #require(Bundle(for: AppDelegate.self) + .object(forInfoDictionaryKey: "NSSpeechRecognitionUsageDescription") as? String + ) + + #expect(usageDescription == "A program running within cmux would like to use speech recognition.") + } +} From aa4dbba036ad6c2ee44f5d5c608982f01724423b Mon Sep 17 00:00:00 2001 From: Christoph Sturm Date: Sun, 31 May 2026 11:20:07 +0200 Subject: [PATCH 2/4] Declare speech recognition usage description --- Resources/Info.plist | 2 + Resources/InfoPlist.xcstrings | 113 ++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/Resources/Info.plist b/Resources/Info.plist index 3f0cc6b569..6f92332f82 100644 --- a/Resources/Info.plist +++ b/Resources/Info.plist @@ -69,6 +69,8 @@ NSMicrophoneUsageDescription A program running within cmux would like to use your microphone. + NSSpeechRecognitionUsageDescription + A program running within cmux would like to use speech recognition. NSCameraUsageDescription A program running within cmux would like to use your camera. NSBluetoothAlwaysUsageDescription diff --git a/Resources/InfoPlist.xcstrings b/Resources/InfoPlist.xcstrings index 9e50f7d4b9..937eb953db 100644 --- a/Resources/InfoPlist.xcstrings +++ b/Resources/InfoPlist.xcstrings @@ -149,6 +149,119 @@ } } }, + "NSSpeechRecognitionUsageDescription": { + "extractionState": "manual", + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "A program running within cmux would like to use speech recognition." + } + }, + "ja": { + "stringUnit": { + "state": "translated", + "value": "cmux 内で実行中のプログラムが音声認識の使用を求めています。" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "在 cmux 中运行的程序想要使用语音识别。" + } + }, + "zh-Hant": { + "stringUnit": { + "state": "translated", + "value": "在 cmux 中執行的程式想要使用語音辨識。" + } + }, + "ko": { + "stringUnit": { + "state": "translated", + "value": "cmux 내에서 실행 중인 프로그램이 음성 인식을 사용하려고 합니다." + } + }, + "de": { + "stringUnit": { + "state": "translated", + "value": "Ein in cmux ausgeführtes Programm möchte Spracherkennung verwenden." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Un programa en ejecución dentro de cmux desea usar el reconocimiento de voz." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Un programme s'exécutant dans cmux souhaite utiliser la reconnaissance vocale." + } + }, + "it": { + "stringUnit": { + "state": "translated", + "value": "Un programma in esecuzione in cmux desidera utilizzare il riconoscimento vocale." + } + }, + "da": { + "stringUnit": { + "state": "translated", + "value": "Et program, der kører i cmux, vil gerne bruge talegenkendelse." + } + }, + "pl": { + "stringUnit": { + "state": "translated", + "value": "Program działający w cmux chciałby użyć rozpoznawania mowy." + } + }, + "ru": { + "stringUnit": { + "state": "translated", + "value": "Программа, запущенная в cmux, хотела бы использовать распознавание речи." + } + }, + "bs": { + "stringUnit": { + "state": "translated", + "value": "Program koji se izvršava unutar cmux želi koristiti prepoznavanje govora." + } + }, + "ar": { + "stringUnit": { + "state": "translated", + "value": "يرغب برنامج يعمل داخل cmux في استخدام التعرف على الكلام." + } + }, + "nb": { + "stringUnit": { + "state": "translated", + "value": "Et program som kjører i cmux ønsker å bruke talegjenkjenning." + } + }, + "pt-BR": { + "stringUnit": { + "state": "translated", + "value": "Um programa em execução no cmux gostaria de usar reconhecimento de fala." + } + }, + "th": { + "stringUnit": { + "state": "translated", + "value": "โปรแกรมที่ทำงานภายใน cmux ต้องการใช้การรู้จำเสียงพูด" + } + }, + "tr": { + "stringUnit": { + "state": "translated", + "value": "cmux içinde çalışan bir program konuşma tanımayı kullanmak istiyor." + } + } + } + }, "New $(PRODUCT_NAME) Workspace Here": { "extractionState": "manual", "localizations": { From 0618e6574abaae1997836bcc56a67772cc599517 Mon Sep 17 00:00:00 2001 From: Christoph Sturm Date: Sun, 31 May 2026 17:18:03 +0200 Subject: [PATCH 3/4] Make privacy usage description test locale-independent --- cmuxTests/PrivacyUsageDescriptionBundleTests.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmuxTests/PrivacyUsageDescriptionBundleTests.swift b/cmuxTests/PrivacyUsageDescriptionBundleTests.swift index e5d9c49810..3ea72892e3 100644 --- a/cmuxTests/PrivacyUsageDescriptionBundleTests.swift +++ b/cmuxTests/PrivacyUsageDescriptionBundleTests.swift @@ -1,3 +1,4 @@ +import Foundation import Testing #if canImport(cmux_DEV) @@ -12,7 +13,7 @@ struct PrivacyUsageDescriptionBundleTests { @Test func appBundleDeclaresSpeechRecognitionUsageDescription() throws { let usageDescription = try #require(Bundle(for: AppDelegate.self) - .object(forInfoDictionaryKey: "NSSpeechRecognitionUsageDescription") as? String + .infoDictionary?["NSSpeechRecognitionUsageDescription"] as? String ) #expect(usageDescription == "A program running within cmux would like to use speech recognition.") From bd1223e5323ec3d80dbcbdba57a086d09b922f1d Mon Sep 17 00:00:00 2001 From: Christoph Sturm Date: Sun, 31 May 2026 17:22:27 +0200 Subject: [PATCH 4/4] Normalize privacy usage test project indentation --- cmux.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmux.xcodeproj/project.pbxproj b/cmux.xcodeproj/project.pbxproj index 23240a64a3..c0e19b6162 100644 --- a/cmux.xcodeproj/project.pbxproj +++ b/cmux.xcodeproj/project.pbxproj @@ -2702,7 +2702,7 @@ B3575000000000000000000A /* PiVaultAgentPersistenceTests.swift in Sources */, D0B10008A1B2C3D4E5F60001 /* PortalTabDragRoutingTests.swift in Sources */, F4100000A1B2C3D4E5F60718 /* PortScannerTests.swift in Sources */, - C0DEF5000000000000000001 /* PrivacyUsageDescriptionBundleTests.swift in Sources */, + C0DEF5000000000000000001 /* PrivacyUsageDescriptionBundleTests.swift in Sources */, C47110010000000000000001 /* ProcessPipeReadCrashRegressionTests.swift in Sources */, F5410004A1B2C3D4E5F60718 /* RestorableAgentHookProviderHermesTests.swift in Sources */, F5410000A1B2C3D4E5F60718 /* RestorableAgentHookProviderResumeTests.swift in Sources */,