Mission Control Turbo: macOS multitasking turbocharged
at main 187 lines 7.2 kB view raw
1import XCTest 2@testable import MCT 3 4final class LayoutEngineTests: XCTestCase { 5 let screenBounds = CGRect(x: 0, y: 0, width: 1920, height: 1080) 6 7 // MARK: - Helpers 8 9 private func makeWindow( 10 id: CGWindowID, 11 title: String, 12 appName: String, 13 bundleID: String 14 ) -> WindowInfo { 15 WindowInfo( 16 windowID: id, 17 title: title, 18 appName: appName, 19 appBundleID: bundleID, 20 appIcon: nil, 21 frame: CGRect(x: 0, y: 0, width: 800, height: 600), 22 pid: pid_t(id), 23 isOnScreen: true, 24 thumbnail: nil 25 ) 26 } 27 28 private func makeSnapshot(windows: [WindowInfo]) -> WindowSnapshot { 29 let groups = WindowEnumerator.groupWindows(windows) 30 return WindowSnapshot(groups: groups) 31 } 32 33 // MARK: - Grouping Tests 34 35 func testGroupingByApp() { 36 let windows = [ 37 makeWindow(id: 1, title: "Doc 1", appName: "Safari", bundleID: "com.apple.Safari"), 38 makeWindow(id: 2, title: "Doc 2", appName: "Safari", bundleID: "com.apple.Safari"), 39 makeWindow(id: 3, title: "Main", appName: "Xcode", bundleID: "com.apple.dt.Xcode"), 40 ] 41 42 let groups = WindowEnumerator.groupWindows(windows) 43 XCTAssertEqual(groups.count, 2) 44 // Safari has frontmost window (index 0), so it should be first 45 XCTAssertEqual(groups[0].appBundleID, "com.apple.Safari") 46 XCTAssertEqual(groups[0].windows.count, 2) 47 XCTAssertEqual(groups[1].appBundleID, "com.apple.dt.Xcode") 48 XCTAssertEqual(groups[1].windows.count, 1) 49 } 50 51 func testGroupOrderingFrontmostFirst() { 52 let windows = [ 53 makeWindow(id: 1, title: "Main", appName: "Xcode", bundleID: "com.apple.dt.Xcode"), 54 makeWindow(id: 2, title: "Tab 1", appName: "Safari", bundleID: "com.apple.Safari"), 55 makeWindow(id: 3, title: "Tab 2", appName: "Safari", bundleID: "com.apple.Safari"), 56 ] 57 58 let groups = WindowEnumerator.groupWindows(windows) 59 // Xcode has the frontmost window (index 0), so it should be first 60 XCTAssertEqual(groups[0].appBundleID, "com.apple.dt.Xcode") 61 XCTAssertEqual(groups[1].appBundleID, "com.apple.Safari") 62 } 63 64 // MARK: - Layout Tests 65 66 func testLayoutProducesItemsForAllWindows() { 67 let windows = [ 68 makeWindow(id: 1, title: "W1", appName: "App", bundleID: "com.test.app"), 69 makeWindow(id: 2, title: "W2", appName: "App", bundleID: "com.test.app"), 70 makeWindow(id: 3, title: "W3", appName: "Other", bundleID: "com.test.other"), 71 ] 72 73 let engine = LayoutEngine() 74 let snapshot = makeSnapshot(windows: windows) 75 let result = engine.layout(snapshot: snapshot, screenBounds: screenBounds) 76 77 XCTAssertEqual(result.items.count, 3) 78 XCTAssertEqual(result.headers.count, 2) 79 } 80 81 func testLayoutItemsWithinScreenBounds() { 82 let windows = (1...6).map { i in 83 makeWindow(id: CGWindowID(i), title: "Win \(i)", appName: "App", bundleID: "com.test.app") 84 } 85 86 let engine = LayoutEngine() 87 let snapshot = makeSnapshot(windows: windows) 88 let result = engine.layout(snapshot: snapshot, screenBounds: screenBounds) 89 90 for item in result.items { 91 XCTAssertTrue( 92 screenBounds.contains(CGPoint(x: item.frame.midX, y: item.frame.midY)), 93 "Item center \(item.frame) should be within screen bounds \(screenBounds)" 94 ) 95 } 96 } 97 98 // MARK: - Fingerprint / Stability Tests 99 100 func testSnapshotFingerprint() { 101 let windows = [ 102 makeWindow(id: 1, title: "Doc", appName: "Safari", bundleID: "com.apple.Safari"), 103 makeWindow(id: 2, title: "Main", appName: "Xcode", bundleID: "com.apple.dt.Xcode"), 104 ] 105 106 let snapshot = makeSnapshot(windows: windows) 107 XCTAssertEqual(snapshot.fingerprint.count, 2) 108 XCTAssertTrue(snapshot.fingerprint.contains("com.apple.Safari|Doc")) 109 XCTAssertTrue(snapshot.fingerprint.contains("com.apple.dt.Xcode|Main")) 110 } 111 112 func testOverlapRatioIdentical() { 113 let windows = [ 114 makeWindow(id: 1, title: "Doc", appName: "Safari", bundleID: "com.apple.Safari"), 115 ] 116 117 let s1 = makeSnapshot(windows: windows) 118 let s2 = makeSnapshot(windows: windows) 119 XCTAssertEqual(s1.overlapRatio(with: s2), 1.0) 120 } 121 122 func testOverlapRatioDisjoint() { 123 let w1 = [makeWindow(id: 1, title: "A", appName: "App1", bundleID: "com.a")] 124 let w2 = [makeWindow(id: 2, title: "B", appName: "App2", bundleID: "com.b")] 125 126 let s1 = makeSnapshot(windows: w1) 127 let s2 = makeSnapshot(windows: w2) 128 XCTAssertEqual(s1.overlapRatio(with: s2), 0.0) 129 } 130 131 func testOverlapRatioPartial() { 132 let shared = makeWindow(id: 1, title: "Shared", appName: "App", bundleID: "com.app") 133 let w1 = [shared, makeWindow(id: 2, title: "Only1", appName: "X", bundleID: "com.x")] 134 let w2 = [shared, makeWindow(id: 3, title: "Only2", appName: "Y", bundleID: "com.y")] 135 136 let s1 = makeSnapshot(windows: w1) 137 let s2 = makeSnapshot(windows: w2) 138 // intersection = 1 (shared), union = 3 (shared, Only1, Only2) 139 XCTAssertEqual(s1.overlapRatio(with: s2), 1.0 / 3.0, accuracy: 0.001) 140 } 141 142 // MARK: - Stability Tests 143 144 func testStableLayoutSameWindows() { 145 let windows = [ 146 makeWindow(id: 1, title: "W1", appName: "App", bundleID: "com.test"), 147 makeWindow(id: 2, title: "W2", appName: "App", bundleID: "com.test"), 148 ] 149 150 let engine = LayoutEngine() 151 let snapshot = makeSnapshot(windows: windows) 152 153 let result1 = engine.layout(snapshot: snapshot, screenBounds: screenBounds) 154 let result2 = engine.layout(snapshot: snapshot, screenBounds: screenBounds) 155 156 // Same windows should produce same positions 157 for (item1, item2) in zip(result1.items, result2.items) { 158 XCTAssertEqual(item1.frame.origin.x, item2.frame.origin.x, accuracy: 1.0) 159 XCTAssertEqual(item1.frame.origin.y, item2.frame.origin.y, accuracy: 1.0) 160 } 161 } 162 163 // MARK: - Geometry Tests 164 165 func testThumbnailSizeFitsOnScreen() { 166 let (size, cols) = LayoutGeometry.uniformThumbnailSize( 167 groupWindowCounts: [4, 4, 4, 4, 4], 168 availableWidth: 1920 - LayoutGeometry.margin * 2, 169 availableHeight: 1080 - LayoutGeometry.margin * 2 170 ) 171 XCTAssertGreaterThan(size.width, 0) 172 XCTAssertGreaterThan(cols, 0) 173 } 174 175 func testAspectFit() { 176 let fitted = LayoutGeometry.aspectFit( 177 source: CGSize(width: 1600, height: 900), 178 target: CGSize(width: 320, height: 200) 179 ) 180 XCTAssertLessThanOrEqual(fitted.width, 320) 181 XCTAssertLessThanOrEqual(fitted.height, 200) 182 // Aspect ratio should be preserved 183 let sourceRatio = 1600.0 / 900.0 184 let fittedRatio = fitted.width / fitted.height 185 XCTAssertEqual(sourceRatio, fittedRatio, accuracy: 0.01) 186 } 187}