Ariver
2026-08-14 86f0b4bd20cdec86dfb5bdb7978d1f6ff4af111a
C1.source/Tests/AlignerCoreTests/AlignerCoreTests.swift
@@ -6,7 +6,91 @@
    func testAppIdentity() {
        XCTAssertEqual(AlignerAppInfo.name, "Aligner")
        XCTAssertEqual(AlignerAppInfo.bundleIdentifier, "com.ar.Aligner")
        XCTAssertEqual(AlignerAppInfo.minimumMacOSVersion, "26.0")
        XCTAssertEqual(AlignerAppInfo.minimumMacOSVersion, "14.0")
    }
    func testPerformanceDiagnosticArchiveWriterCreatesInspectableArchive() throws {
        let fileManager = FileManager.default
        let rootURL = fileManager.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let logURL = rootURL.appendingPathComponent("events.log")
        let archiveURL = rootURL.appendingPathComponent("diagnostics.zip")
        let extractionURL = rootURL.appendingPathComponent("extracted", isDirectory: true)
        defer { try? fileManager.removeItem(at: rootURL) }
        try fileManager.createDirectory(at: rootURL, withIntermediateDirectories: true)
        try "event=quickSwitch.snapshot.success durationMilliseconds=42\n".write(to: logURL, atomically: true, encoding: .utf8)
        let summary = try JSONSerialization.data(withJSONObject: ["schemaVersion": 1], options: [])
        _ = try PerformanceDiagnosticArchiveWriter.write(
            to: archiveURL,
            summaryJSON: summary,
            eventLogURLs: [logURL]
        )
        XCTAssertTrue(fileManager.fileExists(atPath: archiveURL.path))
        let unzipProcess = Process()
        unzipProcess.executableURL = URL(fileURLWithPath: "/usr/bin/ditto")
        unzipProcess.arguments = ["-x", "-k", archiveURL.path, extractionURL.path]
        try unzipProcess.run()
        unzipProcess.waitUntilExit()
        XCTAssertEqual(unzipProcess.terminationStatus, 0)
        let contentsURL = extractionURL.appendingPathComponent("Aligner-Diagnostics", isDirectory: true)
        XCTAssertTrue(fileManager.fileExists(atPath: contentsURL.appendingPathComponent("summary.json").path))
        XCTAssertTrue(fileManager.fileExists(atPath: contentsURL.appendingPathComponent("performance-events.log").path))
        XCTAssertTrue(fileManager.fileExists(atPath: contentsURL.appendingPathComponent("README.txt").path))
    }
    func testPerformanceDiagnosticArchiveWriterDoesNotOverwriteExistingArchive() throws {
        let fileManager = FileManager.default
        let rootURL = fileManager.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let archiveURL = rootURL.appendingPathComponent("diagnostics.zip")
        defer { try? fileManager.removeItem(at: rootURL) }
        try fileManager.createDirectory(at: rootURL, withIntermediateDirectories: true)
        try Data("existing archive".utf8).write(to: archiveURL)
        let summary = try JSONSerialization.data(withJSONObject: ["schemaVersion": 1], options: [])
        XCTAssertThrowsError(
            try PerformanceDiagnosticArchiveWriter.write(
                to: archiveURL,
                summaryJSON: summary,
                eventLogURLs: []
            )
        )
        XCTAssertEqual(try Data(contentsOf: archiveURL), Data("existing archive".utf8))
    }
    func testPerformanceDiagnosticArchiveWriterFiltersSensitiveLogFields() throws {
        let fileManager = FileManager.default
        let rootURL = fileManager.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let sourceLogURL = rootURL.appendingPathComponent("events.log")
        let archiveURL = rootURL.appendingPathComponent("diagnostics.zip")
        let extractionURL = rootURL.appendingPathComponent("extracted", isDirectory: true)
        defer { try? fileManager.removeItem(at: rootURL) }
        try fileManager.createDirectory(at: rootURL, withIntermediateDirectories: true)
        try "event=quickSwitch.snapshot.success ts=2026-08-14T00:00:00Z durationMilliseconds=42 reportPathBasename=private-notes.txt title=secret\n".write(to: sourceLogURL, atomically: true, encoding: .utf8)
        let summary = try JSONSerialization.data(withJSONObject: ["schemaVersion": 1], options: [])
        _ = try PerformanceDiagnosticArchiveWriter.write(
            to: archiveURL,
            summaryJSON: summary,
            eventLogURLs: [sourceLogURL]
        )
        let unzipProcess = Process()
        unzipProcess.executableURL = URL(fileURLWithPath: "/usr/bin/ditto")
        unzipProcess.arguments = ["-x", "-k", archiveURL.path, extractionURL.path]
        try unzipProcess.run()
        unzipProcess.waitUntilExit()
        XCTAssertEqual(unzipProcess.terminationStatus, 0)
        let exportedLogURL = extractionURL
            .appendingPathComponent("Aligner-Diagnostics", isDirectory: true)
            .appendingPathComponent("performance-events.log")
        let exportedLog = try String(contentsOf: exportedLogURL, encoding: .utf8)
        XCTAssertTrue(exportedLog.contains("event=quickSwitch.snapshot.success"))
        XCTAssertTrue(exportedLog.contains("durationMilliseconds=42"))
        XCTAssertFalse(exportedLog.contains("private-notes.txt"))
        XCTAssertFalse(exportedLog.contains("title=secret"))
    }
    func testRound0OnlyDefinesQuickSwitchEntryPoint() {
@@ -173,6 +257,33 @@
        )
    }
    func testWindowEnumerationPolicyRejectsLowConfidenceAXOnlyRegularWindows() {
        XCTAssertFalse(WindowEnumerationPolicy.shouldIncludeAXOnlyRecord(
            identifierSource: .cgWindow,
            isMinimized: false,
            isFullscreen: false,
            spaceIDs: []
        ))
        XCTAssertTrue(WindowEnumerationPolicy.shouldIncludeAXOnlyRecord(
            identifierSource: .cgWindow,
            isMinimized: true,
            isFullscreen: false,
            spaceIDs: []
        ))
        XCTAssertTrue(WindowEnumerationPolicy.shouldIncludeAXOnlyRecord(
            identifierSource: .cgWindow,
            isMinimized: false,
            isFullscreen: true,
            spaceIDs: [42]
        ))
        XCTAssertFalse(WindowEnumerationPolicy.shouldIncludeAXOnlyRecord(
            identifierSource: .syntheticAX,
            isMinimized: false,
            isFullscreen: false,
            spaceIDs: []
        ))
    }
    func testMinimizedWindowFallbackPolicyAvoidsOccupiedSyntheticIDCollisions() {
        let windowID = MinimizedWindowFallbackPolicy.syntheticWindowID(
            processIdentifier: 42,
@@ -212,12 +323,25 @@
        let record = makeWindowRecord(
            title: "Closed Document.md",
            hasAXBacking: false,
            isOnscreen: false,
            spaceIDs: [5]
            isOnscreen: false
        )
        XCTAssertFalse(WindowEnumerationPolicy.shouldInclude(record))
        XCTAssertNil(WindowEnumerationPolicy.window(from: record))
    }
    func testWindowEnumerationPolicyKeepsCGOnlyOffscreenWindowsWithKnownSpaceIDs() {
        let record = makeWindowRecord(
            title: "Other Space Document.md",
            hasAXBacking: false,
            isOnscreen: false,
            spaceIDs: [5]
        )
        let window = WindowEnumerationPolicy.window(from: record)
        XCTAssertEqual(window?.title, "Other Space Document.md")
        XCTAssertEqual(window?.spaceIDs, [5])
    }
    func testWindowEnumerationPolicyKeepsAXBackedOffscreenWindows() {
@@ -283,13 +407,13 @@
        XCTAssertNil(WindowEnumerationPolicy.window(from: tinyRecord))
    }
    func testWindowEnumerationPolicyKeepsShottrCGOnlyAnnotationWindow() {
    func testWindowEnumerationPolicyExcludesClosedShottrCGPlaceholderButKeepsVisibleWindow() {
        let shottrApp = AlignerApp(
            bundleIdentifier: "cc.ffitch.shottr",
            name: "Shottr",
            category: .generic
        )
        let annotationRecord = WindowEnumerationRecord(
        let closedPlaceholderRecord = WindowEnumerationRecord(
            id: 78,
            app: shottrApp,
            activationPolicy: .accessory,
@@ -298,6 +422,16 @@
            subrole: .standard,
            hasAXBacking: false,
            isOnscreen: false
        )
        let visibleRecord = WindowEnumerationRecord(
            id: 79,
            app: shottrApp,
            activationPolicy: .accessory,
            title: "Shottr",
            size: CGSize(width: 1710, height: 997),
            subrole: .standard,
            hasAXBacking: false,
            isOnscreen: true
        )
        let utilityRecord = WindowEnumerationRecord(
            id: 102,
@@ -310,7 +444,8 @@
            isOnscreen: false
        )
        XCTAssertEqual(WindowEnumerationPolicy.window(from: annotationRecord)?.title, "Shottr")
        XCTAssertNil(WindowEnumerationPolicy.window(from: closedPlaceholderRecord))
        XCTAssertEqual(WindowEnumerationPolicy.window(from: visibleRecord)?.title, "Shottr")
        XCTAssertNil(WindowEnumerationPolicy.window(from: utilityRecord))
    }
@@ -351,6 +486,214 @@
        XCTAssertEqual(WindowEnumerationPolicy.window(from: record)?.title, "Screencapture")
        XCTAssertNil(WindowEnumerationPolicy.window(from: emptyTitleRecord))
        XCTAssertNil(WindowEnumerationPolicy.window(from: tinyRecord))
    }
    func testFinderTabSpaceAttributionInheritsFullscreenHostSpaceForMatchingFrame() {
        let finderApp = AlignerApp(
            bundleIdentifier: "com.apple.finder",
            name: "访达",
            category: .finder,
            processIdentifier: 74678
        )
        let frame = CGRect(x: 0, y: 0, width: 960, height: 1080)
        let host = WindowEnumerationRecord(
            id: 33112,
            app: finderApp,
            title: "下载",
            size: frame.size,
            frame: frame,
            subrole: .standard,
            isFullscreen: true,
            hasAXBacking: true,
            spaceIDs: [843]
        )
        let inactiveTab = WindowEnumerationRecord(
            id: 42286,
            app: finderApp,
            title: "Appcache",
            size: frame.size,
            frame: frame,
            subrole: .standard,
            hasAXBacking: false,
            isOnscreen: false
        )
        let attributed = FinderTabSpaceAttributionPolicy.attributedRecords([inactiveTab, host])
        XCTAssertEqual(attributed[0].spaceIDs, [843])
        XCTAssertTrue(attributed[0].isFullscreen)
    }
    func testFinderTabSpaceAttributionDoesNotGuessNoSpaceWindowsWithoutHostOverlap() {
        let finderApp = AlignerApp(
            bundleIdentifier: "com.apple.finder",
            name: "访达",
            category: .finder,
            processIdentifier: 74678
        )
        let otherApp = AlignerApp(
            bundleIdentifier: "com.example.Other",
            name: "Other",
            category: .generic,
            processIdentifier: 74678
        )
        let hostFrame = CGRect(x: 0, y: 0, width: 960, height: 1080)
        let farFrame = CGRect(x: 1200, y: 0, width: 500, height: 600)
        let host = WindowEnumerationRecord(
            id: 33112,
            app: finderApp,
            title: "下载",
            size: hostFrame.size,
            frame: hostFrame,
            subrole: .standard,
            isFullscreen: true,
            hasAXBacking: true,
            spaceIDs: [843]
        )
        let farFinderTab = WindowEnumerationRecord(
            id: 42286,
            app: finderApp,
            title: "Appcache",
            size: farFrame.size,
            frame: farFrame,
            subrole: .standard,
            hasAXBacking: false,
            isOnscreen: false
        )
        let nonFinderWindow = WindowEnumerationRecord(
            id: 50222,
            app: otherApp,
            title: "Appcache",
            size: hostFrame.size,
            frame: hostFrame,
            subrole: .standard,
            hasAXBacking: false,
            isOnscreen: false
        )
        let attributed = FinderTabSpaceAttributionPolicy.attributedRecords([farFinderTab, nonFinderWindow, host])
        XCTAssertEqual(attributed[0].spaceIDs, [])
        XCTAssertFalse(attributed[0].isFullscreen)
        XCTAssertEqual(attributed[1].spaceIDs, [])
        XCTAssertFalse(attributed[1].isFullscreen)
    }
    func testFinderTabSpaceAttributionHandlesFinderSplitViewHostsByFrame() {
        let finderApp = AlignerApp(
            bundleIdentifier: "com.apple.finder",
            name: "访达",
            category: .finder,
            processIdentifier: 74678
        )
        let leftFrame = CGRect(x: 0, y: 0, width: 960, height: 1080)
        let rightFrame = CGRect(x: 960, y: 0, width: 960, height: 1080)
        let leftHost = WindowEnumerationRecord(
            id: 33112,
            app: finderApp,
            title: "下载",
            size: leftFrame.size,
            frame: leftFrame,
            subrole: .standard,
            isFullscreen: true,
            hasAXBacking: true,
            spaceIDs: [843]
        )
        let rightHost = WindowEnumerationRecord(
            id: 41557,
            app: finderApp,
            title: "应用程序",
            size: rightFrame.size,
            frame: rightFrame,
            subrole: .standard,
            isFullscreen: true,
            hasAXBacking: true,
            spaceIDs: [843]
        )
        let leftInactiveTab = WindowEnumerationRecord(
            id: 42286,
            app: finderApp,
            title: "Appcache",
            size: leftFrame.size,
            frame: leftFrame,
            subrole: .standard,
            hasAXBacking: false,
            isOnscreen: false
        )
        let rightInactiveTab = WindowEnumerationRecord(
            id: 61385,
            app: finderApp,
            title: "build",
            size: rightFrame.size,
            frame: rightFrame,
            subrole: .standard,
            hasAXBacking: false,
            isOnscreen: false
        )
        let attributed = FinderTabSpaceAttributionPolicy.attributedRecords([
            leftInactiveTab,
            rightInactiveTab,
            leftHost,
            rightHost
        ])
        XCTAssertEqual(attributed[0].spaceIDs, [843])
        XCTAssertEqual(attributed[1].spaceIDs, [843])
        XCTAssertTrue(attributed[0].isFullscreen)
        XCTAssertTrue(attributed[1].isFullscreen)
    }
    func testFinderTabSpaceAttributionRejectsContainingHostsWithDifferentSize() {
        let finderApp = AlignerApp(
            bundleIdentifier: "com.apple.finder",
            name: "访达",
            category: .finder,
            processIdentifier: 74678
        )
        let splitRightFrame = CGRect(x: 960, y: 0, width: 960, height: 1080)
        let fullDisplayFrame = CGRect(x: 0, y: 0, width: 1920, height: 1080)
        let splitHost = WindowEnumerationRecord(
            id: 33112,
            app: finderApp,
            title: "下载",
            size: splitRightFrame.size,
            frame: splitRightFrame,
            subrole: .standard,
            isFullscreen: true,
            hasAXBacking: true,
            spaceIDs: [843]
        )
        let containingHost = WindowEnumerationRecord(
            id: 41557,
            app: finderApp,
            title: "应用程序",
            size: fullDisplayFrame.size,
            frame: fullDisplayFrame,
            subrole: .standard,
            isFullscreen: true,
            hasAXBacking: true,
            spaceIDs: [1112]
        )
        let inactiveTab = WindowEnumerationRecord(
            id: 42286,
            app: finderApp,
            title: "Appcache",
            size: splitRightFrame.size,
            frame: splitRightFrame,
            subrole: .standard,
            hasAXBacking: false,
            isOnscreen: false
        )
        let attributed = FinderTabSpaceAttributionPolicy.attributedRecords([
            inactiveTab,
            containingHost,
            splitHost
        ])
        XCTAssertEqual(attributed[0].spaceIDs, [843])
        XCTAssertTrue(attributed[0].isFullscreen)
    }
    func testWindowEnumerationPolicyKeepsMinimizedAndFullscreenCGOnlyOffscreenWindows() {
@@ -473,6 +816,13 @@
        XCTAssertEqual(templates.map(\.category), categories)
        XCTAssertTrue(templates.allSatisfy { $0.headerHeight > 0 })
        XCTAssertTrue(templates.allSatisfy { $0.contentRows > 0 })
    }
    func testSkeletonThumbnailPaletteStaysQuietForFallbackState() {
        XCTAssertLessThanOrEqual(SkeletonThumbnailPalette.headerAlpha, 0.10)
        XCTAssertLessThanOrEqual(SkeletonThumbnailPalette.contentRowAlpha, 0.08)
        XCTAssertLessThanOrEqual(SkeletonThumbnailPalette.accentStripeAlpha, 0.30)
        XCTAssertGreaterThanOrEqual(SkeletonThumbnailPalette.titleBandAlpha, 0.80)
    }
    @MainActor
@@ -849,6 +1199,7 @@
                "schemaVersion",
                "quickSwitch.showMinimizedWindows",
                "quickSwitch.showFullscreenWindows",
                "quickSwitch.waterfallViewMode",
                "appearance.theme",
                "appearance.language",
                "spaces.displayStrategy"
@@ -861,9 +1212,10 @@
    }
    func testDefaultPreferencesMatchRound0Decisions() {
        XCTAssertEqual(PreferenceSchema.version, 1)
        XCTAssertEqual(PreferenceSchema.version, 2)
        XCTAssertEqual(PreferenceSchema.defaults.showMinimizedWindows, true)
        XCTAssertEqual(PreferenceSchema.defaults.showFullscreenWindows, true)
        XCTAssertEqual(PreferenceSchema.defaults.quickSwitchWaterfallViewMode, .verticalColumns)
        XCTAssertEqual(PreferenceSchema.defaults.theme, .system)
        XCTAssertEqual(PreferenceSchema.defaults.language, .system)
        XCTAssertEqual(PreferenceSchema.defaults.spaceDisplayStrategy, .includeVisibleAndFullscreen)
@@ -884,6 +1236,7 @@
        let preferences = AlignerPreferences(
            showMinimizedWindows: false,
            showFullscreenWindows: false,
            quickSwitchWaterfallViewMode: .horizontalMasonry,
            theme: .dark,
            language: .simplifiedChinese,
            spaceDisplayStrategy: .currentSpaceOnly
@@ -921,12 +1274,14 @@
        userDefaults.set("not-a-theme", forKey: PreferenceKey.theme.storageKey)
        userDefaults.set("not-a-language", forKey: PreferenceKey.language.storageKey)
        userDefaults.set("not-a-space-strategy", forKey: PreferenceKey.spaceDisplayStrategy.storageKey)
        userDefaults.set("not-a-waterfall-mode", forKey: PreferenceKey.quickSwitchWaterfallViewMode.storageKey)
        let preferences = UserDefaultsPreferenceStore(userDefaults: userDefaults).read()
        XCTAssertEqual(preferences.theme, PreferenceSchema.defaults.theme)
        XCTAssertEqual(preferences.language, PreferenceSchema.defaults.language)
        XCTAssertEqual(preferences.spaceDisplayStrategy, PreferenceSchema.defaults.spaceDisplayStrategy)
        XCTAssertEqual(preferences.quickSwitchWaterfallViewMode, PreferenceSchema.defaults.quickSwitchWaterfallViewMode)
    }
    @MainActor
@@ -1150,6 +1505,50 @@
        XCTAssertEqual(snapshot.appGroups.single?.windows.map(\.primarySpaceID), [10, 11, 12, 12, nil])
    }
    func testWindowSpaceMappingFillsUnresolvedWindowsFromSpaceInverseMap() {
        let mapping = WindowSpaceMappingPolicy.mergedSpaceIDsByWindowID(
            requestedWindowIDs: [10, 20, 30],
            directSpaceIDsByWindowID: [10: [100]],
            windowIDsBySpaceID: [
                100: [10, 20],
                101: [30]
            ],
            orderedSpaceIDs: [100, 101]
        )
        XCTAssertEqual(mapping[10], [100])
        XCTAssertEqual(mapping[20], [100])
        XCTAssertEqual(mapping[30], [101])
    }
    func testWindowSpaceMappingDoesNotOverwriteDirectSpaceAssignments() {
        let mapping = WindowSpaceMappingPolicy.mergedSpaceIDsByWindowID(
            requestedWindowIDs: [10],
            directSpaceIDsByWindowID: [10: [101]],
            windowIDsBySpaceID: [
                100: [10]
            ],
            orderedSpaceIDs: [100, 101]
        )
        XCTAssertEqual(mapping[10], [101])
    }
    func testWindowSpaceMappingLeavesAmbiguousNoSpaceWindowsUnassigned() {
        let mapping = WindowSpaceMappingPolicy.mergedSpaceIDsByWindowID(
            requestedWindowIDs: [10, 20],
            directSpaceIDsByWindowID: [:],
            windowIDsBySpaceID: [
                100: [10, 99]
            ],
            orderedSpaceIDs: [100]
        )
        XCTAssertEqual(mapping[10], [100])
        XCTAssertNil(mapping[20])
        XCTAssertNil(mapping[99])
    }
    func testQuickSwitchSnapshotKeepsMinimizedAndFullscreenWindows() {
        let app = makeApp()
        let snapshot = QuickSwitchSnapshotBuilder.snapshot(
@@ -1211,6 +1610,244 @@
        XCTAssertEqual(viewModel.waterfallColumns.flatMap { $0.windows.map(\.primarySpaceLabel) }, ["01", "02"])
    }
    func testQuickSwitchSpaceFilterProjectsAppShelfAndWaterfallWithoutChangingSpaceLane() {
        let alpha = AlignerApp(bundleIdentifier: "com.example.Alpha", name: "Alpha", category: .generic)
        let beta = AlignerApp(bundleIdentifier: "com.example.Beta", name: "Beta", category: .generic)
        let snapshot = QuickSwitchSnapshotBuilder.snapshot(
            displays: [makeDisplay(spaceIDs: [10, 11])],
            windows: [
                AlignerWindow(id: 1, app: alpha, title: "A1", spaceIDs: [10]),
                AlignerWindow(id: 2, app: alpha, title: "A2", spaceIDs: [11]),
                AlignerWindow(id: 3, app: beta, title: "B1", spaceIDs: [11]),
                AlignerWindow(id: 4, app: beta, title: "No Space")
            ]
        )
        let viewModel = QuickSwitchViewModelBuilder.viewModel(from: snapshot)
        let filtered = QuickSwitchSpaceFilterPolicy.projectedViewModel(
            from: viewModel,
            lockedSpaceID: 11
        )
        XCTAssertEqual(filtered.lockedSpaceID, 11)
        XCTAssertEqual(filtered.displays.flatMap { $0.spaces.map(\.id) }, [10, 11])
        XCTAssertEqual(filtered.displays.flatMap { $0.spaces.map(\.windowCount) }, [1, 2])
        XCTAssertEqual(filtered.appShelf.map(\.app.name), ["Alpha", "Beta"])
        XCTAssertEqual(filtered.appShelf.map(\.windowCount), [1, 1])
        XCTAssertEqual(filtered.appShelf.flatMap(\.primarySpaceIDs), [11, 11])
        XCTAssertEqual(filtered.waterfallColumns.flatMap { $0.windows.map(\.window.id) }, [2, 3])
        XCTAssertEqual(filtered.waterfallColumns.flatMap { $0.windows.map(\.windowIndex) }, [0, 0])
        XCTAssertEqual(filtered.waterfallColumns.flatMap { $0.windows.map(\.globalIndex) }, [0, 1])
        XCTAssertEqual(filtered.initialSelection, QuickSwitchSelection(appGroupIndex: 0, windowIndex: 0, windowID: 2))
    }
    func testQuickSwitchSpaceFilterKeyboardNavigationStaysInsideFilteredCollection() {
        let alpha = AlignerApp(bundleIdentifier: "com.example.Alpha", name: "Alpha", category: .generic)
        let beta = AlignerApp(bundleIdentifier: "com.example.Beta", name: "Beta", category: .generic)
        let snapshot = QuickSwitchSnapshotBuilder.snapshot(
            displays: [makeDisplay(spaceIDs: [10, 11])],
            windows: [
                AlignerWindow(id: 1, app: alpha, title: "Alpha A1", spaceIDs: [10]),
                AlignerWindow(id: 2, app: alpha, title: "Alpha A2", spaceIDs: [11]),
                AlignerWindow(id: 3, app: beta, title: "Beta A2", spaceIDs: [11]),
                AlignerWindow(id: 4, app: beta, title: "Beta No Space")
            ]
        )
        let viewModel = QuickSwitchViewModelBuilder.viewModel(from: snapshot)
        let filtered = QuickSwitchSpaceFilterPolicy.projectedViewModel(
            from: viewModel,
            lockedSpaceID: 11
        )
        let filteredSnapshot = QuickSwitchSnapshot(
            displays: snapshot.displays,
            appGroups: filtered.waterfallColumns.map { column in
                QuickSwitchAppGroup(
                    app: column.app,
                    windows: column.windows.map {
                        QuickSwitchWindowItem(window: $0.window, primarySpaceID: $0.primarySpaceID)
                    }
                )
            }
        )
        XCTAssertEqual(filtered.initialSelection, QuickSwitchSelection(appGroupIndex: 0, windowIndex: 0, windowID: 2))
        XCTAssertEqual(
            QuickSwitchFocusPolicy.nextSelection(
                from: filtered.initialSelection!,
                in: filteredSnapshot,
                direction: .right
            ),
            QuickSwitchSelection(appGroupIndex: 1, windowIndex: 0, windowID: 3)
        )
        XCTAssertEqual(
            QuickSwitchFocusPolicy.nextSelection(
                from: QuickSwitchSelection(appGroupIndex: 1, windowIndex: 0, windowID: 3),
                in: filteredSnapshot,
                direction: .right
            ),
            QuickSwitchSelection(appGroupIndex: 1, windowIndex: 0, windowID: 3)
        )
        XCTAssertFalse(filteredSnapshot.appGroups.flatMap(\.windows).contains { $0.window.id == 1 || $0.window.id == 4 })
    }
    func testQuickSwitchSpaceFilterCanLockEmptySpaceWithoutChangingSpaceLane() {
        let app = AlignerApp(bundleIdentifier: "com.example.Alpha", name: "Alpha", category: .generic)
        let snapshot = QuickSwitchSnapshotBuilder.snapshot(
            displays: [makeDisplay(spaceIDs: [10, 11])],
            windows: [
                AlignerWindow(id: 1, app: app, title: "A1", spaceIDs: [10])
            ]
        )
        let viewModel = QuickSwitchViewModelBuilder.viewModel(from: snapshot)
        let filtered = QuickSwitchSpaceFilterPolicy.projectedViewModel(
            from: viewModel,
            lockedSpaceID: 11
        )
        XCTAssertEqual(filtered.lockedSpaceID, 11)
        XCTAssertEqual(filtered.displays.flatMap { $0.spaces.map(\.id) }, [10, 11])
        XCTAssertTrue(filtered.appShelf.isEmpty)
        XCTAssertTrue(filtered.waterfallColumns.isEmpty)
        XCTAssertNil(filtered.initialSelection)
    }
    func testQuickSwitchSpaceFilterRestoresPreferredSelectionWhenUnlocking() {
        let app = AlignerApp(bundleIdentifier: "com.example.Alpha", name: "Alpha", category: .generic)
        let snapshot = QuickSwitchSnapshotBuilder.snapshot(
            displays: [makeDisplay(spaceIDs: [10, 11])],
            windows: [
                AlignerWindow(id: 1, app: app, title: "A1", spaceIDs: [10]),
                AlignerWindow(id: 2, app: app, title: "A2", spaceIDs: [11])
            ]
        )
        let viewModel = QuickSwitchViewModelBuilder.viewModel(from: snapshot)
        let preferred = QuickSwitchSelection(appGroupIndex: 0, windowIndex: 1, windowID: 2)
        let unlocked = QuickSwitchSpaceFilterPolicy.projectedViewModel(
            from: viewModel,
            lockedSpaceID: nil,
            preferredSelection: preferred
        )
        XCTAssertNil(unlocked.lockedSpaceID)
        XCTAssertEqual(unlocked.initialSelection, preferred)
    }
    func testQuickSwitchSpaceFilterDetectsOnlySingleFullscreenWindowSpacesAsDirectActivation() {
        let alpha = AlignerApp(bundleIdentifier: "com.example.Alpha", name: "Alpha", category: .generic)
        let beta = AlignerApp(bundleIdentifier: "com.example.Beta", name: "Beta", category: .generic)
        let displayUUID = "display-a"
        let snapshot = QuickSwitchSnapshotBuilder.snapshot(
            displays: [
                AlignerDisplay(
                    uuid: displayUUID,
                    physical: true,
                    spaces: [
                        AlignerSpace(id: 10, type: .user, displayUUID: displayUUID, index: 1),
                        AlignerSpace(id: 11, type: .fullscreen, displayUUID: displayUUID, index: 2),
                        AlignerSpace(id: 12, type: .fullscreen, displayUUID: displayUUID, index: 3)
                    ]
                )
            ],
            windows: [
                AlignerWindow(id: 1, app: alpha, title: "Normal", spaceIDs: [10]),
                AlignerWindow(id: 2, app: alpha, title: "Fullscreen", isFullscreen: true, spaceIDs: [11]),
                AlignerWindow(id: 3, app: beta, title: "Split A", isFullscreen: true, spaceIDs: [12]),
                AlignerWindow(id: 4, app: beta, title: "Split B", isFullscreen: true, spaceIDs: [12])
            ]
        )
        let viewModel = QuickSwitchViewModelBuilder.viewModel(from: snapshot)
        XCTAssertEqual(
            QuickSwitchSpaceFilterPolicy.singleFullscreenSelection(inSpaceID: 11, viewModel: viewModel),
            QuickSwitchSelection(appGroupIndex: 0, windowIndex: 1, windowID: 2)
        )
        XCTAssertNil(QuickSwitchSpaceFilterPolicy.singleFullscreenSelection(inSpaceID: 10, viewModel: viewModel))
        XCTAssertNil(QuickSwitchSpaceFilterPolicy.singleFullscreenSelection(inSpaceID: 12, viewModel: viewModel))
    }
    func testQuickSwitchViewModelOrdersSplitViewAppNamesByWindowFrame() {
        let leftApp = AlignerApp(bundleIdentifier: "com.example.Left", name: "Left App", category: .generic)
        let rightApp = AlignerApp(bundleIdentifier: "com.example.Right", name: "Right App", category: .generic)
        let displayUUID = "display-a"
        let snapshot = QuickSwitchSnapshotBuilder.snapshot(
            displays: [
                AlignerDisplay(
                    uuid: displayUUID,
                    physical: true,
                    spaces: [
                        AlignerSpace(id: 20, type: .fullscreen, displayUUID: displayUUID, index: 1)
                    ]
                )
            ],
            windows: [
                AlignerWindow(
                    id: 2,
                    app: rightApp,
                    title: "Right",
                    isFullscreen: true,
                    frame: CGRect(x: 960, y: 0, width: 960, height: 1080),
                    spaceIDs: [20]
                ),
                AlignerWindow(
                    id: 1,
                    app: leftApp,
                    title: "Left",
                    isFullscreen: true,
                    frame: CGRect(x: 0, y: 0, width: 960, height: 1080),
                    spaceIDs: [20]
                )
            ]
        )
        let viewModel = QuickSwitchViewModelBuilder.viewModel(from: snapshot)
        let space = viewModel.displays.single?.spaces.single
        XCTAssertEqual(space?.splitViewAppNames, ["Left App", "Right App"])
        XCTAssertEqual(space?.appNames, ["Left App", "Right App"])
    }
    func testQuickSwitchViewModelKeepsDuplicateAppNamesForSameAppSplitView() {
        let finder = AlignerApp(bundleIdentifier: "com.apple.finder", name: "访达", category: .finder)
        let displayUUID = "display-a"
        let snapshot = QuickSwitchSnapshotBuilder.snapshot(
            displays: [
                AlignerDisplay(
                    uuid: displayUUID,
                    physical: true,
                    spaces: [
                        AlignerSpace(id: 21, type: .fullscreen, displayUUID: displayUUID, index: 1)
                    ]
                )
            ],
            windows: [
                AlignerWindow(
                    id: 12,
                    app: finder,
                    title: "右侧访达",
                    isFullscreen: true,
                    frame: CGRect(x: 900, y: 0, width: 900, height: 1000),
                    spaceIDs: [21]
                ),
                AlignerWindow(
                    id: 11,
                    app: finder,
                    title: "左侧访达",
                    isFullscreen: true,
                    frame: CGRect(x: 0, y: 0, width: 900, height: 1000),
                    spaceIDs: [21]
                )
            ]
        )
        let viewModel = QuickSwitchViewModelBuilder.viewModel(from: snapshot)
        let space = viewModel.displays.single?.spaces.single
        XCTAssertEqual(space?.splitViewAppNames, ["访达", "访达"])
        XCTAssertEqual(space?.appNames, ["访达"])
    }
    func testQuickSwitchFocusPolicySelectsFirstStableWindowInitially() {
        let app = makeApp()
        let snapshot = QuickSwitchSnapshotBuilder.snapshot(