Mission Control Turbo: macOS multitasking turbocharged
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}