Files
ggbond268--mactools/Tests/Core/Plugins/PluginHostFailureIsolationTests.swift
wehub-resource-sync 1d1286fadb
Build / Build and test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:18:38 +08:00

103 lines
3.2 KiB
Swift

import AppKit
import SwiftUI
import XCTest
import MacToolsPluginKit
@testable import MacTools
@MainActor
final class PluginHostFailureIsolationTests: XCTestCase {
func testPanelStateExceptionIsolatesFailingPluginAndKeepsHealthyPlugin() {
let failingPlugin = ExceptionPrimaryPlugin(id: "bad", failurePoint: .panelState)
let healthyPlugin = ExceptionPrimaryPlugin(id: "good", order: 2)
let host = makePluginHostForTests(
plugins: [failingPlugin, healthyPlugin],
suiteName: "PluginHostFailureIsolationTests-state"
)
XCTAssertEqual(host.panelItems.map(\.id), ["good"])
XCTAssertEqual(host.featureManagementItems.map(\.id), ["good"])
XCTAssertEqual(failingPlugin.deactivationReasons, [.disabled])
}
func testActionExceptionIsolatesPluginAndStopsFutureCalls() {
let plugin = ExceptionPrimaryPlugin(id: "bad", failurePoint: .action)
let host = makePluginHostForTests(
plugins: [plugin],
suiteName: "PluginHostFailureIsolationTests-action"
)
XCTAssertEqual(host.panelItems.map(\.id), ["bad"])
host.setSwitchValue(true, for: "bad")
XCTAssertTrue(host.panelItems.isEmpty)
XCTAssertTrue(host.featureManagementItems.isEmpty)
XCTAssertEqual(plugin.handleActionCallCount, 1)
XCTAssertEqual(plugin.deactivationReasons, [.disabled])
host.setSwitchValue(false, for: "bad")
XCTAssertEqual(plugin.handleActionCallCount, 1)
}
}
@MainActor
private final class ExceptionPrimaryPlugin: MacToolsPlugin, PluginPrimaryPanel {
enum FailurePoint {
case action
case panelState
}
let metadata: PluginMetadata
let primaryPanelDescriptor = PluginPrimaryPanelDescriptor(
controlStyle: .switch,
menuActionBehavior: .keepPresented
)
var onStateChange: (() -> Void)?
var requestPermissionGuidance: ((String) -> Void)?
var shortcutBindingResolver: ((String) -> ShortcutBinding?)?
private let failurePoint: FailurePoint?
private(set) var handleActionCallCount = 0
private(set) var deactivationReasons: [PluginDeactivationReason] = []
init(id: String, order: Int = 1, failurePoint: FailurePoint? = nil) {
self.metadata = PluginMetadata(
id: id,
title: id,
iconName: "sparkles",
iconTint: Color(nsColor: .systemRed),
order: order,
defaultDescription: "Failure isolation \(id)"
)
self.failurePoint = failurePoint
}
var primaryPanelState: PluginPanelState {
if failurePoint == .panelState {
raiseTestPluginException(reason: "panel state failed")
}
return PluginPanelState(
subtitle: "Ready",
isOn: false,
isExpanded: false,
isEnabled: true,
isVisible: true,
detail: nil,
errorMessage: nil
)
}
func handleAction(_ action: PluginPanelAction) {
handleActionCallCount += 1
if failurePoint == .action {
raiseTestPluginException(reason: "action failed")
}
}
func deactivate(reason: PluginDeactivationReason) {
deactivationReasons.append(reason)
}
}