11import 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
0 commit comments