From 41c3bde3eac5dd3e73cfeb56c7ed332285101ea2 Mon Sep 17 00:00:00 2001 From: Micah Alpern Date: Mon, 1 Jun 2026 13:18:16 -0700 Subject: [PATCH] test: migrate 9 coordinator suites to KeyPathTestCase (#698) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move suites that construct RuntimeCoordinator/InstallerEngine/etc. onto the KeyPathTestCase base class so the testPIDProvider seam is installed and they no longer spawn real pgrep (the parallel-execution deadlock from #698). Removed each from the TestSeamLintTests allowlist. Migrated (each verified in isolation — passes, no deadlock): ContentViewDebounceTests, GrabRecoveryGateTests, InstallerEngineBrokerForwardingTests, KanataViewModelTests, KeyboardCaptureTests, LayerSelectorTests, OverlayHealthIndicatorObserverTests, RecordingCoordinatorTests, WizardRecipeParityTests (batch of 8 → 47 tests / 0 failures / 4.3s; KeyboardCaptureTests → 22 / 0 / 1.6s) Still allowlisted (intentionally not migrated): - ErrorHandlingTests, KeyPathTests, KanataManagerResetTests (RuntimeCoordinatorResetTests.swift): the seam fixes their pgrep hang but they also do real saveConfiguration/ updateStatus I/O that still hangs — needs deeper per-suite mocking. - VHIDDeviceManagerTests: the seam's OWN test suite (sets testPIDProvider per test and asserts the real-pgrep fallthrough); must not be force-seamed. - FacadeLintTests, PrivilegedOperationsCoordinatorTests, SystemRequirementsTests, WizardPureLogicTests: only reference the types, low priority. Co-Authored-By: Claude Opus 4.8 (1M context) --- Tests/KeyPathTests/ContentViewDebounceTests.swift | 2 +- .../InstallerEngineBrokerForwardingTests.swift | 2 +- .../InstallationWizard/WizardRecipeParityTests.swift | 2 +- Tests/KeyPathTests/KeyboardCaptureTests.swift | 2 +- Tests/KeyPathTests/Lint/TestSeamLintTests.swift | 11 +---------- Tests/KeyPathTests/RecordingCoordinatorTests.swift | 2 +- .../KeyPathTests/Services/GrabRecoveryGateTests.swift | 2 +- Tests/KeyPathTests/UI/KanataViewModelTests.swift | 2 +- Tests/KeyPathTests/UI/LayerSelectorTests.swift | 2 +- .../UI/OverlayHealthIndicatorObserverTests.swift | 2 +- 10 files changed, 10 insertions(+), 19 deletions(-) diff --git a/Tests/KeyPathTests/ContentViewDebounceTests.swift b/Tests/KeyPathTests/ContentViewDebounceTests.swift index ce9254c2d..735a245f6 100644 --- a/Tests/KeyPathTests/ContentViewDebounceTests.swift +++ b/Tests/KeyPathTests/ContentViewDebounceTests.swift @@ -6,7 +6,7 @@ import SwiftUI /// Phase 1 Unit Tests: ContentView Debouncing /// Tests the save operation debouncing added in Phase 1.3 to prevent rapid successive saves @MainActor -class ContentViewDebounceTests: XCTestCase { +class ContentViewDebounceTests: KeyPathTestCase { lazy var testManager: RuntimeCoordinator = .init() // MARK: - Debounce Logic Tests (Non-UI) diff --git a/Tests/KeyPathTests/InstallationEngine/InstallerEngineBrokerForwardingTests.swift b/Tests/KeyPathTests/InstallationEngine/InstallerEngineBrokerForwardingTests.swift index 369c7902b..0b5217abe 100644 --- a/Tests/KeyPathTests/InstallationEngine/InstallerEngineBrokerForwardingTests.swift +++ b/Tests/KeyPathTests/InstallationEngine/InstallerEngineBrokerForwardingTests.swift @@ -3,7 +3,7 @@ @preconcurrency import XCTest @MainActor -final class InstallerEngineBrokerForwardingTests: XCTestCase { +final class InstallerEngineBrokerForwardingTests: KeyPathTestCase { func testUninstallVirtualHIDDriversRoutesToBroker() async throws { let coordinator = PrivilegedCoordinatorStub() let broker = PrivilegeBroker(coordinator: coordinator) diff --git a/Tests/KeyPathTests/InstallationWizard/WizardRecipeParityTests.swift b/Tests/KeyPathTests/InstallationWizard/WizardRecipeParityTests.swift index 38cddd2b6..7d4686e55 100644 --- a/Tests/KeyPathTests/InstallationWizard/WizardRecipeParityTests.swift +++ b/Tests/KeyPathTests/InstallationWizard/WizardRecipeParityTests.swift @@ -7,7 +7,7 @@ import KeyPathWizardCore /// Characterization tests to ensure auto-fix actions map to the same recipes the planner uses. @MainActor -final class WizardRecipeParityTests: XCTestCase { +final class WizardRecipeParityTests: KeyPathTestCase { func testRecipeIDsMatchForCommonActions() { let engine = InstallerEngine() let context = SystemContextBuilder( diff --git a/Tests/KeyPathTests/KeyboardCaptureTests.swift b/Tests/KeyPathTests/KeyboardCaptureTests.swift index 664792d31..77b164b5d 100644 --- a/Tests/KeyPathTests/KeyboardCaptureTests.swift +++ b/Tests/KeyPathTests/KeyboardCaptureTests.swift @@ -5,7 +5,7 @@ import KeyPathCore @preconcurrency import XCTest @MainActor -final class KeyboardCaptureTests: XCTestCase { +final class KeyboardCaptureTests: KeyPathTestCase { private let defaultNotificationCenter = NotificationCenter .perform(NSSelectorFromString("defaultCenter"))? .takeUnretainedValue() as? NotificationCenter diff --git a/Tests/KeyPathTests/Lint/TestSeamLintTests.swift b/Tests/KeyPathTests/Lint/TestSeamLintTests.swift index 2057d1350..8f5643587 100644 --- a/Tests/KeyPathTests/Lint/TestSeamLintTests.swift +++ b/Tests/KeyPathTests/Lint/TestSeamLintTests.swift @@ -17,23 +17,14 @@ final class TestSeamLintTests: XCTestCase { /// Pre-existing suites that use a hazard type but still extend XCTestCase directly. /// Migrate these to KeyPathTestCase and remove them here. Never add new entries. private static let allowList: Set = [ - "ContentViewDebounceTests.swift", "ErrorHandlingTests.swift", "FacadeLintTests.swift", - "GrabRecoveryGateTests.swift", - "InstallerEngineBrokerForwardingTests.swift", - "KanataViewModelTests.swift", - "KeyboardCaptureTests.swift", "KeyPathTests.swift", - "LayerSelectorTests.swift", - "OverlayHealthIndicatorObserverTests.swift", "PrivilegedOperationsCoordinatorTests.swift", - "RecordingCoordinatorTests.swift", "RuntimeCoordinatorResetTests.swift", "SystemRequirementsTests.swift", "VHIDDeviceManagerTests.swift", - "WizardPureLogicTests.swift", - "WizardRecipeParityTests.swift" + "WizardPureLogicTests.swift" ] func testCoordinatorSuitesUseKeyPathTestCase() throws { diff --git a/Tests/KeyPathTests/RecordingCoordinatorTests.swift b/Tests/KeyPathTests/RecordingCoordinatorTests.swift index da0703290..774e02796 100644 --- a/Tests/KeyPathTests/RecordingCoordinatorTests.swift +++ b/Tests/KeyPathTests/RecordingCoordinatorTests.swift @@ -3,7 +3,7 @@ import KeyPathPermissions @preconcurrency import XCTest @MainActor -final class RecordingCoordinatorTests: XCTestCase { +final class RecordingCoordinatorTests: KeyPathTestCase { private var statusMessages: [String] = [] private lazy var permissionProvider = StubPermissionProvider( snapshot: RecordingCoordinatorTests.snapshot(accessibility: .unknown) diff --git a/Tests/KeyPathTests/Services/GrabRecoveryGateTests.swift b/Tests/KeyPathTests/Services/GrabRecoveryGateTests.swift index 715a49567..ac4f4fa5b 100644 --- a/Tests/KeyPathTests/Services/GrabRecoveryGateTests.swift +++ b/Tests/KeyPathTests/Services/GrabRecoveryGateTests.swift @@ -6,7 +6,7 @@ import XCTest /// recovery. The recover/give-up tail (the bounded budget) is covered separately by /// `ServiceHealthMonitorTests`; these tests cover only the pre-guard gate so they /// never touch the real recovery action (which would shell out to launchctl/pgrep). -final class GrabRecoveryGateTests: XCTestCase { +final class GrabRecoveryGateTests: KeyPathTestCase { typealias Gate = RuntimeCoordinator.GrabRecoveryGate func testActiveGrabRecordsSuccess() { diff --git a/Tests/KeyPathTests/UI/KanataViewModelTests.swift b/Tests/KeyPathTests/UI/KanataViewModelTests.swift index 71dca3f4a..b5074e30a 100644 --- a/Tests/KeyPathTests/UI/KanataViewModelTests.swift +++ b/Tests/KeyPathTests/UI/KanataViewModelTests.swift @@ -8,7 +8,7 @@ /// - ViewModel delegates actions to KanataManager /// - UI state updates correctly @MainActor -final class KanataViewModelTests: XCTestCase { +final class KanataViewModelTests: KeyPathTestCase { // Note: These tests are simple sanity checks to verify the MVVM architecture compiles and runs // Full integration testing is done at the UI level diff --git a/Tests/KeyPathTests/UI/LayerSelectorTests.swift b/Tests/KeyPathTests/UI/LayerSelectorTests.swift index b517eeeb2..f81a2ae81 100644 --- a/Tests/KeyPathTests/UI/LayerSelectorTests.swift +++ b/Tests/KeyPathTests/UI/LayerSelectorTests.swift @@ -3,7 +3,7 @@ import XCTest /// Tests for layer selector functionality in MapperViewModel @MainActor -final class LayerSelectorTests: XCTestCase { +final class LayerSelectorTests: KeyPathTestCase { // MARK: - getAvailableLayers Tests func testGetAvailableLayers_defaultsToBaseAndNav() { diff --git a/Tests/KeyPathTests/UI/OverlayHealthIndicatorObserverTests.swift b/Tests/KeyPathTests/UI/OverlayHealthIndicatorObserverTests.swift index fde7006dd..4825f59ea 100644 --- a/Tests/KeyPathTests/UI/OverlayHealthIndicatorObserverTests.swift +++ b/Tests/KeyPathTests/UI/OverlayHealthIndicatorObserverTests.swift @@ -4,7 +4,7 @@ import KeyPathInstallationWizard import KeyPathWizardCore import XCTest -final class OverlayHealthIndicatorObserverTests: XCTestCase { +final class OverlayHealthIndicatorObserverTests: KeyPathTestCase { @MainActor func testHealthyStateTriggersDismiss() async { var states: [HealthIndicatorState] = []