Skip to content

Commit 9f6f54a

Browse files
Update test deployment target to macOS 15.6 and refactor test setup/teardown for async compatibility
1 parent 8a92d71 commit 9f6f54a

7 files changed

Lines changed: 208 additions & 184 deletions

File tree

MiddleDrag.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,7 @@
829829
GCC_C_LANGUAGE_STANDARD = gnu17;
830830
GENERATE_INFOPLIST_FILE = YES;
831831
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
832-
MACOSX_DEPLOYMENT_TARGET = 11.5;
832+
MACOSX_DEPLOYMENT_TARGET = 15.6;
833833
MARKETING_VERSION = 1.0;
834834
PRODUCT_BUNDLE_IDENTIFIER = NullPointer.MiddleDragCoreTests;
835835
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -856,7 +856,7 @@
856856
GCC_C_LANGUAGE_STANDARD = gnu17;
857857
GENERATE_INFOPLIST_FILE = YES;
858858
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
859-
MACOSX_DEPLOYMENT_TARGET = 11.5;
859+
MACOSX_DEPLOYMENT_TARGET = 15.6;
860860
MARKETING_VERSION = 1.0;
861861
PRODUCT_BUNDLE_IDENTIFIER = NullPointer.MiddleDragCoreTests;
862862
PRODUCT_NAME = "$(TARGET_NAME)";

MiddleDrag/MiddleDragTests/AlertHelperTests.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ final class AlertHelperTests: XCTestCase {
3838
var mockSettingsProvider: MockTrackpadSettingsProvider!
3939
var originalSettingsProvider: TrackpadSettingsProvider!
4040

41-
override func setUp() {
42-
super.setUp()
41+
@MainActor
42+
override func setUp() async throws {
43+
try await super.setUp()
4344
// Save and replace AlertHelper presenter
4445
originalPresenter = AlertHelper.presenter
4546
mockPresenter = MockAlertPresenter()
@@ -51,13 +52,14 @@ final class AlertHelperTests: XCTestCase {
5152
unsafe SystemGestureHelper.settingsProvider = mockSettingsProvider
5253
}
5354

54-
override func tearDown() {
55+
@MainActor
56+
override func tearDown() async throws {
5557
// Restore originals
5658
AlertHelper.presenter = originalPresenter
5759
unsafe SystemGestureHelper.settingsProvider = originalSettingsProvider
5860
mockPresenter = nil
5961
mockSettingsProvider = nil
60-
super.tearDown()
62+
try await super.tearDown()
6163
}
6264

6365
// MARK: - Existence Tests

MiddleDrag/MiddleDragTests/DeviceMonitorTests.swift

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import XCTest
2+
import Synchronization
23

34
@testable import MiddleDragCore
45

@@ -24,7 +25,7 @@ import XCTest
2425
///
2526
/// The critical race condition fix (gCallbackEnabled flag + os_unfair_lock) is validated
2627
/// by crash-safety tests that would fail if the synchronization was broken.
27-
@unsafe final class DeviceMonitorTests: XCTestCase {
28+
@unsafe final class DeviceMonitorTests: XCTestCase, @unchecked Sendable {
2829

2930
// Note: DeviceMonitor uses a global variable (gDeviceMonitor) for C callback compatibility
3031
// This limits testing options since only one instance can be active at a time
@@ -325,13 +326,13 @@ import XCTest
325326
// This tests crash-safety of the synchronization, not correctness of final state.
326327
let expectation = XCTestExpectation(description: "Concurrent operations complete")
327328
expectation.expectedFulfillmentCount = 2
328-
var startCount = 0
329-
var stopCount = 0
329+
let startCount = Mutex(0)
330+
let stopCount = Mutex(0)
330331

331332
DispatchQueue.global().async {
332333
for _ in 0..<5 {
333334
unsafe self.monitor.start()
334-
startCount += 1
335+
startCount.withLock { $0 += 1 }
335336
Thread.sleep(forTimeInterval: 0.01)
336337
}
337338
expectation.fulfill()
@@ -340,16 +341,20 @@ import XCTest
340341
DispatchQueue.global().async {
341342
for _ in 0..<5 {
342343
unsafe self.monitor.stop()
343-
stopCount += 1
344+
stopCount.withLock { $0 += 1 }
344345
Thread.sleep(forTimeInterval: 0.01)
345346
}
346347
expectation.fulfill()
347348
}
348349

349350
unsafe wait(for: [expectation], timeout: 5.0)
351+
352+
let finalStart = startCount.withLock { $0 }
353+
let finalStop = stopCount.withLock { $0 }
354+
350355
// Verify operations completed (crash-safety check)
351-
XCTAssertEqual(startCount, 5, "All start operations should complete")
352-
XCTAssertEqual(stopCount, 5, "All stop operations should complete")
356+
XCTAssertEqual(finalStart, 5, "All start operations should complete")
357+
XCTAssertEqual(finalStop, 5, "All stop operations should complete")
353358
}
354359

355360
func testMultipleMonitorCreationDuringCleanup() throws {
@@ -517,7 +522,7 @@ import XCTest
517522

518523
func testStartCreatesRefreshTimer() {
519524
let enumerator = unsafe MockDeviceEnumerator()
520-
let device = makeFakeDevice()
525+
let device = unsafe makeFakeDevice()
521526
defer { unsafe device.deallocate() }
522527
unsafe enumerator.devices = [device]
523528
unsafe enumerator.defaultDevice = device
@@ -531,7 +536,7 @@ import XCTest
531536

532537
func testStopCancelsRefreshTimer() {
533538
let enumerator = unsafe MockDeviceEnumerator()
534-
let device = makeFakeDevice()
539+
let device = unsafe makeFakeDevice()
535540
defer { unsafe device.deallocate() }
536541
unsafe enumerator.devices = [device]
537542
unsafe enumerator.defaultDevice = device
@@ -555,7 +560,7 @@ import XCTest
555560

556561
func testRefreshSkipsWhenDeviceCountUnchanged() {
557562
let enumerator = unsafe MockDeviceEnumerator()
558-
let device = makeFakeDevice()
563+
let device = unsafe makeFakeDevice()
559564
defer { unsafe device.deallocate() }
560565
unsafe enumerator.devices = [device]
561566
unsafe enumerator.defaultDevice = device
@@ -574,8 +579,8 @@ import XCTest
574579

575580
func testRefreshRegistersWhenDeviceCountIncreases() {
576581
let enumerator = unsafe MockDeviceEnumerator()
577-
let device1 = makeFakeDevice()
578-
let device2 = makeFakeDevice()
582+
let device1 = unsafe makeFakeDevice()
583+
let device2 = unsafe makeFakeDevice()
579584
defer { unsafe device1.deallocate(); unsafe device2.deallocate() }
580585
unsafe enumerator.devices = [device1]
581586
unsafe enumerator.defaultDevice = device1
@@ -596,8 +601,8 @@ import XCTest
596601

597602
func testRefreshHandlesDeviceDisconnect() {
598603
let enumerator = unsafe MockDeviceEnumerator()
599-
let device1 = makeFakeDevice()
600-
let device2 = makeFakeDevice()
604+
let device1 = unsafe makeFakeDevice()
605+
let device2 = unsafe makeFakeDevice()
601606
defer { unsafe device1.deallocate(); unsafe device2.deallocate() }
602607
unsafe enumerator.devices = [device1, device2]
603608
unsafe enumerator.defaultDevice = device1
@@ -617,8 +622,8 @@ import XCTest
617622

618623
func testRefreshUpdatesLastKnownDeviceCount() {
619624
let enumerator = unsafe MockDeviceEnumerator()
620-
let device1 = makeFakeDevice()
621-
let device2 = makeFakeDevice()
625+
let device1 = unsafe makeFakeDevice()
626+
let device2 = unsafe makeFakeDevice()
622627
defer { unsafe device1.deallocate(); unsafe device2.deallocate() }
623628
unsafe enumerator.devices = [device1]
624629
unsafe enumerator.defaultDevice = device1
@@ -636,7 +641,7 @@ import XCTest
636641

637642
func testRefreshAfterStopIsNoop() {
638643
let enumerator = unsafe MockDeviceEnumerator()
639-
let device = makeFakeDevice()
644+
let device = unsafe makeFakeDevice()
640645
defer { unsafe device.deallocate() }
641646
unsafe enumerator.devices = [device]
642647
unsafe enumerator.defaultDevice = device
@@ -657,7 +662,7 @@ import XCTest
657662

658663
func testRegisterCallsCallbackAndStartsDevice() {
659664
let enumerator = unsafe MockDeviceEnumerator()
660-
let device = makeFakeDevice()
665+
let device = unsafe makeFakeDevice()
661666
defer { unsafe device.deallocate() }
662667
unsafe enumerator.devices = [device]
663668
unsafe enumerator.defaultDevice = device
@@ -674,7 +679,7 @@ import XCTest
674679

675680
func testRegisterSkipsStartForAlreadyRunningDevice() {
676681
let enumerator = unsafe MockDeviceEnumerator()
677-
let device = makeFakeDevice()
682+
let device = unsafe makeFakeDevice()
678683
defer { unsafe device.deallocate() }
679684
unsafe enumerator.devices = [device]
680685
unsafe enumerator.defaultDevice = device
@@ -692,7 +697,7 @@ import XCTest
692697

693698
func testStopUnregistersAndStopsDevices() {
694699
let enumerator = unsafe MockDeviceEnumerator()
695-
let device = makeFakeDevice()
700+
let device = unsafe makeFakeDevice()
696701
defer { unsafe device.deallocate() }
697702
unsafe enumerator.devices = [device]
698703
unsafe enumerator.defaultDevice = device
@@ -711,7 +716,7 @@ import XCTest
711716

712717
func testStartStopCycleWithMockDoesNotCrash() {
713718
let enumerator = unsafe MockDeviceEnumerator()
714-
let device = makeFakeDevice()
719+
let device = unsafe makeFakeDevice()
715720
defer { unsafe device.deallocate() }
716721
unsafe enumerator.devices = [device]
717722
unsafe enumerator.defaultDevice = device
@@ -725,7 +730,7 @@ import XCTest
725730

726731
func testSecondInstanceStartsAfterFirstStopsWithMock() {
727732
let enumerator = unsafe MockDeviceEnumerator()
728-
let device = makeFakeDevice()
733+
let device = unsafe makeFakeDevice()
729734
defer { unsafe device.deallocate() }
730735
unsafe enumerator.devices = [device]
731736
unsafe enumerator.defaultDevice = device
@@ -766,18 +771,18 @@ class MockDeviceMonitorDelegate: DeviceMonitorDelegate {
766771
/// without the private MultitouchSupport framework.
767772
@unsafe final class MockDeviceEnumerator: DeviceEnumerating {
768773
/// Devices returned by `enumerateDevices()`.
769-
var devices: [MTDeviceRef] = []
774+
var devices: [MTDeviceRef] = unsafe []
770775
/// Device returned by `getDefaultDevice()`.
771776
var defaultDevice: MTDeviceRef?
772777
/// Set of devices that report as running.
773778
var runningDevices: Set<UnsafeMutableRawPointer> = unsafe []
774779

775780
// Call tracking
776781
var enumerateCallCount = 0
777-
var registeredCallbackDevices: [MTDeviceRef] = []
778-
var unregisteredCallbackDevices: [MTDeviceRef] = []
779-
var startedDevices: [MTDeviceRef] = []
780-
var stoppedDevices: [MTDeviceRef] = []
782+
var registeredCallbackDevices: [MTDeviceRef] = unsafe []
783+
var unregisteredCallbackDevices: [MTDeviceRef] = unsafe []
784+
var startedDevices: [MTDeviceRef] = unsafe []
785+
var stoppedDevices: [MTDeviceRef] = unsafe []
781786

782787
func enumerateDevices() -> [MTDeviceRef] {
783788
unsafe enumerateCallCount += 1

MiddleDrag/MiddleDragTests/LaunchAtLoginManagerTests.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,14 @@ final class LaunchAtLoginManagerTests: XCTestCase {
118118
let expectation = XCTestExpectation(description: "Concurrent operations completed")
119119
expectation.expectedFulfillmentCount = 10
120120

121+
guard let manager = self.manager else {
122+
XCTFail("Manager was nil")
123+
return
124+
}
125+
121126
for i in 0..<10 {
122-
DispatchQueue.global().async {
123-
self.manager.setLaunchAtLogin(i % 2 == 0)
127+
DispatchQueue.global().async { [manager] in
128+
manager.setLaunchAtLogin(i % 2 == 0)
124129
expectation.fulfill()
125130
}
126131
}

MiddleDrag/MiddleDragTests/MenuBarControllerTests.swift

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,25 @@ import XCTest
1111
var notificationExpectation: XCTestExpectation?
1212
var receivedNotificationObject: Any?
1313

14-
override func setUpWithError() throws {
15-
try super.setUpWithError()
14+
@MainActor
15+
override func setUp() async throws {
16+
try await super.setUp()
1617
unsafe mockDevice = unsafe MockDeviceMonitor()
1718
unsafe manager = MultitouchManager(
1819
deviceProviderFactory: { unsafe self.mockDevice }, eventTapSetup: { true })
1920
unsafe preferences = UserPreferences()
2021
unsafe controller = unsafe MenuBarController(multitouchManager: manager, preferences: preferences)
2122
}
2223

23-
override func tearDown() {
24+
@MainActor
25+
override func tearDown() async throws {
2426
unsafe NotificationCenter.default.removeObserver(self)
2527
unsafe manager.stop()
2628
unsafe controller = nil
2729
unsafe manager = nil
2830
unsafe mockDevice = nil
2931
unsafe preferences = nil
30-
super.tearDown()
32+
try await super.tearDown()
3133
}
3234

3335
// MARK: - Initialization Tests
@@ -578,34 +580,34 @@ import XCTest
578580

579581
unsafe manager.stop()
580582
}
581-
583+
582584
// MARK: - Force Release Stuck Drag Tests
583-
585+
584586
func testForceReleaseStuckDragDoesNotCrash() {
585587
unsafe manager.start()
586-
588+
587589
// Calling force release when not dragging should be safe
588590
unsafe XCTAssertNoThrow(controller.forceReleaseStuckDrag())
589-
591+
590592
unsafe manager.stop()
591593
}
592-
594+
593595
func testForceReleaseStuckDragMultipleTimes() {
594596
unsafe manager.start()
595-
597+
596598
// Calling force release multiple times rapidly should be safe
597599
for _ in 0..<5 {
598600
unsafe XCTAssertNoThrow(controller.forceReleaseStuckDrag())
599601
}
600-
602+
601603
unsafe manager.stop()
602604
}
603-
605+
604606
func testForceReleaseStuckDragWhenStopped() {
605607
// Controller should handle force release even when manager is stopped
606608
unsafe XCTAssertNoThrow(controller.forceReleaseStuckDrag())
607609
}
608-
610+
609611
func testForceReleaseStuckDragSelectorExists() {
610612
let selector = #selector(MenuBarController.forceReleaseStuckDrag)
611613
unsafe XCTAssertTrue(controller.responds(to: selector))

0 commit comments

Comments
 (0)