mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-30 21:33:36 +00:00
Fix macOS menu bar status-item storms during rapid gateway connection churn by removing stale SwiftUI-vended status items before adopting replacements and debouncing transient control-channel states. Surface: macOS menu bar app, `MenuBarExtra` status item ownership, `ControlChannel` UI-observed connection state. Proof: - `git diff --check origin/main...pr/82739` - `swift test --package-path apps/macos --filter ControlChannelStateDebouncerTests` - PR CI: preflight, security-fast, macos-node, macos-swift, dependency-guard, changed-path scan, real behavior proof, Socket checks Co-authored-by: Alexander Falk <al@falk.us>
61 lines
2.3 KiB
Swift
61 lines
2.3 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import OpenClaw
|
|
|
|
struct ControlChannelStateDebouncerTests {
|
|
@Test func `terminal states apply immediately`() {
|
|
let start = Date(timeIntervalSince1970: 1_000)
|
|
var debouncer = ControlChannelStateDebouncer(interval: 0.5, lastAppliedAt: start)
|
|
|
|
let degradedDelay = debouncer.delayBeforeApplying(
|
|
currentState: .connecting,
|
|
newState: .degraded("gateway unavailable"),
|
|
now: start.addingTimeInterval(0.1))
|
|
#expect(degradedDelay != nil)
|
|
|
|
let connectedDelay = debouncer.delayBeforeApplying(
|
|
currentState: .connecting,
|
|
newState: .connected,
|
|
now: start.addingTimeInterval(0.2))
|
|
#expect(connectedDelay == nil)
|
|
|
|
let afterTerminalDelay = debouncer.delayBeforeApplying(
|
|
currentState: .connected,
|
|
newState: .connecting,
|
|
now: start.addingTimeInterval(0.3))
|
|
#expect(afterTerminalDelay == nil)
|
|
}
|
|
|
|
@Test func `nonterminal states are debounced within interval`() {
|
|
let start = Date(timeIntervalSince1970: 1_000)
|
|
var debouncer = ControlChannelStateDebouncer(interval: 0.5, lastAppliedAt: start)
|
|
|
|
let soonDelay = debouncer.delayBeforeApplying(
|
|
currentState: .connecting,
|
|
newState: .degraded("gateway unavailable"),
|
|
now: start.addingTimeInterval(0.1))
|
|
#expect(soonDelay != nil)
|
|
#expect(abs((soonDelay ?? 0) - 0.4) < 0.001)
|
|
|
|
let afterWindowDelay = debouncer.delayBeforeApplying(
|
|
currentState: .connecting,
|
|
newState: .degraded("gateway unavailable"),
|
|
now: start.addingTimeInterval(0.6))
|
|
#expect(afterWindowDelay == nil)
|
|
}
|
|
|
|
@Test func `deferred apply resets debounce window`() {
|
|
let start = Date(timeIntervalSince1970: 1_000)
|
|
var debouncer = ControlChannelStateDebouncer(interval: 0.5, lastAppliedAt: start)
|
|
|
|
debouncer.recordDeferredApply(at: start.addingTimeInterval(0.5))
|
|
|
|
let delayAfterDeferredUpdate = debouncer.delayBeforeApplying(
|
|
currentState: .degraded("gateway unavailable"),
|
|
newState: .connecting,
|
|
now: start.addingTimeInterval(0.7))
|
|
#expect(delayAfterDeferredUpdate != nil)
|
|
#expect(abs((delayAfterDeferredUpdate ?? 0) - 0.3) < 0.001)
|
|
}
|
|
}
|