Publish socket health into notify focus UI test

This commit is contained in:
Lawrence Chen 2026-03-05 17:35:57 -08:00
parent 69cfce9596
commit ee899042ca
2 changed files with 94 additions and 5 deletions

View file

@ -5725,10 +5725,60 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
"expectedLatestWindowId": window1.windowId.uuidString,
"expectedLatestTabId": tabId1.uuidString,
], at: path)
self.publishMultiWindowNotificationSocketStateIfNeeded(at: path)
}
}
}
private func publishMultiWindowNotificationSocketStateIfNeeded(at path: String) {
let env = ProcessInfo.processInfo.environment
guard env["CMUX_UI_TEST_SOCKET_SANITY"] == "1" else { return }
guard let config = socketListenerConfigurationIfEnabled() else {
writeMultiWindowNotificationTestData([
"socketExpectedPath": env["CMUX_SOCKET_PATH"] ?? "",
"socketMode": "off",
"socketReady": "0",
"socketIsRunning": "0",
"socketAcceptLoopAlive": "0",
"socketPathMatches": "0",
"socketPathExists": "0",
"socketFailureSignals": "socket_disabled",
], at: path)
return
}
writeMultiWindowNotificationTestData([
"socketExpectedPath": config.path,
"socketMode": config.mode.rawValue,
"socketReady": "pending",
], at: path)
restartSocketListenerIfEnabled(source: "uiTest.multiWindowNotifications.setup")
let deadline = Date().addingTimeInterval(12.0)
func publish() {
let health = TerminalController.shared.socketListenerHealth(expectedSocketPath: config.path)
let isTimedOut = Date() >= deadline
writeMultiWindowNotificationTestData([
"socketExpectedPath": config.path,
"socketMode": config.mode.rawValue,
"socketReady": health.isHealthy ? "1" : (isTimedOut ? "0" : "pending"),
"socketIsRunning": health.isRunning ? "1" : "0",
"socketAcceptLoopAlive": health.acceptLoopAlive ? "1" : "0",
"socketPathMatches": health.socketPathMatches ? "1" : "0",
"socketPathExists": health.socketPathExists ? "1" : "0",
"socketFailureSignals": health.failureSignals.joined(separator: ","),
], at: path)
guard !health.isHealthy, !isTimedOut else { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
publish()
}
}
publish()
}
private func writeMultiWindowNotificationTestData(_ updates: [String: String], at path: String) {
var payload = loadMultiWindowNotificationTestData(at: path)
for (key, value) in updates {

View file

@ -207,19 +207,44 @@ final class MultiWindowNotificationsUITests: XCTestCase {
"Expected app to launch for notify focus regression test. state=\(app.state.rawValue)"
)
XCTAssertTrue(
waitForData(keys: ["tabId2"], timeout: 15.0),
"Expected multi-window notification setup data"
waitForDataMatch(timeout: 20.0) { data in
let tabId2 = data["tabId2"] ?? ""
let socketReady = data["socketReady"] ?? ""
return !tabId2.isEmpty && !socketReady.isEmpty && socketReady != "pending"
},
"Expected multi-window notification setup data and socket readiness"
)
guard let tabId2 = loadData()?["tabId2"], !tabId2.isEmpty else {
guard let setup = loadData() else {
XCTFail("Missing setup data")
return
}
guard let tabId2 = setup["tabId2"], !tabId2.isEmpty else {
XCTFail("Missing setup workspace id")
return
}
if let expectedSocketPath = setup["socketExpectedPath"], !expectedSocketPath.isEmpty {
socketPath = expectedSocketPath
}
if setup["socketReady"] != "1" {
XCTFail(
"Control socket unavailable in this test environment. expected=\(socketPath) " +
"mode=\(setup["socketMode"] ?? "") running=\(setup["socketIsRunning"] ?? "") " +
"acceptLoopAlive=\(setup["socketAcceptLoopAlive"] ?? "") pathMatches=\(setup["socketPathMatches"] ?? "") " +
"pathExists=\(setup["socketPathExists"] ?? "") signals=\(setup["socketFailureSignals"] ?? "")"
)
return
}
XCTAssertTrue(waitForWindowCount(atLeast: 2, app: app, timeout: 6.0))
guard let resolvedPath = resolveSocketPath(timeout: 20.0, requiredWorkspaceId: tabId2) else {
XCTFail("Control socket unavailable in this test environment. requested=\(socketPath)")
guard let resolvedPath = resolveSocketPath(timeout: 5.0, requiredWorkspaceId: tabId2) else {
XCTFail(
"Control socket unavailable in this test environment. requested=\(socketPath) " +
"mode=\(setup["socketMode"] ?? "") running=\(setup["socketIsRunning"] ?? "") " +
"acceptLoopAlive=\(setup["socketAcceptLoopAlive"] ?? "") pathMatches=\(setup["socketPathMatches"] ?? "") " +
"pathExists=\(setup["socketPathExists"] ?? "") signals=\(setup["socketFailureSignals"] ?? "")"
)
return
}
socketPath = resolvedPath
@ -342,6 +367,20 @@ final class MultiWindowNotificationsUITests: XCTestCase {
return false
}
private func waitForDataMatch(timeout: TimeInterval, predicate: ([String: String]) -> Bool) -> Bool {
let deadline = Date().addingTimeInterval(timeout)
while Date() < deadline {
if let data = loadData(), predicate(data) {
return true
}
RunLoop.current.run(until: Date().addingTimeInterval(0.05))
}
if let data = loadData(), predicate(data) {
return true
}
return false
}
private func waitForSocketPong(timeout: TimeInterval) -> String? {
let deadline = Date().addingTimeInterval(timeout)
var lastResponse: String?