@@ -77,6 +77,140 @@ final class NIOAsyncTestingEventLoopScheduledCallbackTests: _BaseScheduledCallba
7777 }
7878}
7979
80+ final class IsolatedEventLoopScheduledCallbackTests : XCTestCase {
81+ struct Requirements : ScheduledCallbackTestRequirements {
82+ let _loop = EmbeddedEventLoop ( )
83+ var loop : ( any EventLoop ) { self . _loop }
84+
85+ func advanceTime( by amount: TimeAmount ) {
86+ self . _loop. advanceTime ( by: amount)
87+ }
88+
89+ func shutdownEventLoop( ) {
90+ try ! self . _loop. syncShutdownGracefully ( )
91+ }
92+
93+ func waitForLoopTick( ) { }
94+ }
95+
96+ var requirements : Requirements ! = nil
97+ var loop : ( any EventLoop ) { self . requirements. loop }
98+
99+ func advanceTime( by amount: TimeAmount ) {
100+ self . requirements. advanceTime ( by: amount)
101+ }
102+
103+ func shutdownEventLoop( ) {
104+ self . requirements. shutdownEventLoop ( )
105+ }
106+
107+ override func setUp( ) {
108+ self . requirements = Requirements ( )
109+ }
110+
111+ func testScheduledCallbackNotExecutedBeforeDeadline( ) throws {
112+ let handler = NonSendableMockScheduledCallbackHandler ( )
113+
114+ _ = try self . loop. assumeIsolated ( ) . scheduleCallback ( in: . milliseconds( 1 ) , handler: handler)
115+ handler. assert ( callbackCount: 0 , cancelCount: 0 )
116+
117+ self . advanceTime ( by: . microseconds( 1 ) )
118+ handler. assert ( callbackCount: 0 , cancelCount: 0 )
119+ }
120+
121+ func testScheduledCallbackExecutedAtDeadline( ) throws {
122+ let handler = NonSendableMockScheduledCallbackHandler ( )
123+
124+ _ = try self . loop. assumeIsolated ( ) . scheduleCallback ( in: . milliseconds( 1 ) , handler: handler)
125+ self . advanceTime ( by: . milliseconds( 1 ) )
126+ handler. assert ( callbackCount: 1 , cancelCount: 0 )
127+ }
128+
129+ func testMultipleScheduledCallbacksUsingSameHandler( ) throws {
130+ let handler = NonSendableMockScheduledCallbackHandler ( )
131+
132+ _ = try self . loop. assumeIsolated ( ) . scheduleCallback ( in: . milliseconds( 1 ) , handler: handler)
133+ _ = try self . loop. assumeIsolated ( ) . scheduleCallback ( in: . milliseconds( 1 ) , handler: handler)
134+
135+ self . advanceTime ( by: . milliseconds( 1 ) )
136+ handler. assert ( callbackCount: 2 , cancelCount: 0 )
137+
138+ _ = try self . loop. assumeIsolated ( ) . scheduleCallback ( in: . milliseconds( 2 ) , handler: handler)
139+ _ = try self . loop. assumeIsolated ( ) . scheduleCallback ( in: . milliseconds( 3 ) , handler: handler)
140+
141+ self . advanceTime ( by: . milliseconds( 3 ) )
142+ handler. assert ( callbackCount: 4 , cancelCount: 0 )
143+ }
144+
145+ func testCancelExecutesCancellationCallback( ) throws {
146+ let handler = NonSendableMockScheduledCallbackHandler ( )
147+
148+ let scheduledCallback = try self . loop. assumeIsolated ( ) . scheduleCallback ( in: . milliseconds( 1 ) , handler: handler)
149+ scheduledCallback. cancel ( )
150+ handler. assert ( callbackCount: 0 , cancelCount: 1 )
151+ }
152+
153+ func testCancelAfterDeadlineDoesNotExecutesCancellationCallback( ) throws {
154+ let handler = NonSendableMockScheduledCallbackHandler ( )
155+
156+ let scheduledCallback = try self . loop. assumeIsolated ( ) . scheduleCallback ( in: . milliseconds( 1 ) , handler: handler)
157+ self . advanceTime ( by: . milliseconds( 1 ) )
158+ scheduledCallback. cancel ( )
159+ self . requirements. waitForLoopTick ( )
160+ handler. assert ( callbackCount: 1 , cancelCount: 0 )
161+ }
162+
163+ func testCancelAfterCancelDoesNotCallCancellationCallbackAgain( ) throws {
164+ let handler = NonSendableMockScheduledCallbackHandler ( )
165+
166+ let scheduledCallback = try self . loop. assumeIsolated ( ) . scheduleCallback ( in: . milliseconds( 1 ) , handler: handler)
167+ scheduledCallback. cancel ( )
168+ scheduledCallback. cancel ( )
169+ handler. assert ( callbackCount: 0 , cancelCount: 1 )
170+ }
171+
172+ func testCancelAfterShutdownDoesNotCallCancellationCallbackAgain( ) throws {
173+ let handler = NonSendableMockScheduledCallbackHandler ( )
174+
175+ let scheduledCallback = try self . loop. assumeIsolated ( ) . scheduleCallback ( in: . milliseconds( 1 ) , handler: handler)
176+ self . shutdownEventLoop ( )
177+ handler. assert ( callbackCount: 0 , cancelCount: 1 )
178+
179+ scheduledCallback. cancel ( )
180+ handler. assert ( callbackCount: 0 , cancelCount: 1 )
181+ }
182+
183+ func testShutdownCancelsOutstandingScheduledCallbacks( ) throws {
184+ let handler = NonSendableMockScheduledCallbackHandler ( )
185+
186+ _ = try self . loop. assumeIsolated ( ) . scheduleCallback ( in: . milliseconds( 1 ) , handler: handler)
187+ self . shutdownEventLoop ( )
188+ handler. assert ( callbackCount: 0 , cancelCount: 1 )
189+ }
190+
191+ func testShutdownDoesNotCancelCancelledCallbacksAgain( ) throws {
192+ let handler = NonSendableMockScheduledCallbackHandler ( )
193+
194+ let handle = try self . loop. assumeIsolated ( ) . scheduleCallback ( in: . milliseconds( 1 ) , handler: handler)
195+ handle. cancel ( )
196+ handler. assert ( callbackCount: 0 , cancelCount: 1 )
197+
198+ self . shutdownEventLoop ( )
199+ handler. assert ( callbackCount: 0 , cancelCount: 1 )
200+ }
201+
202+ func testShutdownDoesNotCancelPastCallbacks( ) throws {
203+ let handler = NonSendableMockScheduledCallbackHandler ( )
204+
205+ _ = try self . loop. assumeIsolated ( ) . scheduleCallback ( in: . milliseconds( 1 ) , handler: handler)
206+ self . advanceTime ( by: . milliseconds( 1 ) )
207+ handler. assert ( callbackCount: 1 , cancelCount: 0 )
208+
209+ self . shutdownEventLoop ( )
210+ handler. assert ( callbackCount: 1 , cancelCount: 0 )
211+ }
212+ }
213+
80214class _BaseScheduledCallbackTests : XCTestCase {
81215 // EL-specific test requirements.
82216 var requirements : ( any ScheduledCallbackTestRequirements ) ! = nil
@@ -114,7 +248,7 @@ extension _BaseScheduledCallbackTests {
114248 handler. assert ( callbackCount: 0 , cancelCount: 0 )
115249 }
116250
117- func testSheduledCallbackExecutedAtDeadline ( ) async throws {
251+ func testScheduledCallbackExecutedAtDeadline ( ) async throws {
118252 let handler = MockScheduledCallbackHandler ( )
119253
120254 _ = try self . loop. scheduleCallback ( in: . milliseconds( 1 ) , handler: handler)
@@ -123,7 +257,7 @@ extension _BaseScheduledCallbackTests {
123257 handler. assert ( callbackCount: 1 , cancelCount: 0 )
124258 }
125259
126- func testMultipleSheduledCallbacksUsingSameHandler ( ) async throws {
260+ func testMultipleScheduledCallbacksUsingSameHandler ( ) async throws {
127261 let handler = MockScheduledCallbackHandler ( )
128262
129263 _ = try self . loop. scheduleCallback ( in: . milliseconds( 1 ) , handler: handler)
@@ -143,7 +277,7 @@ extension _BaseScheduledCallbackTests {
143277 handler. assert ( callbackCount: 4 , cancelCount: 0 )
144278 }
145279
146- func testMultipleSheduledCallbacksUsingDifferentHandlers ( ) async throws {
280+ func testMultipleScheduledCallbacksUsingDifferentHandlers ( ) async throws {
147281 let handlerA = MockScheduledCallbackHandler ( )
148282 let handlerB = MockScheduledCallbackHandler ( )
149283
@@ -278,6 +412,36 @@ private final class MockScheduledCallbackHandler: NIOScheduledCallbackHandler, S
278412 }
279413}
280414
415+ private final class NonSendableMockScheduledCallbackHandler : NIOScheduledCallbackHandler {
416+ private( set) var callbackCount = 0
417+ private( set) var cancelCount = 0
418+
419+ func handleScheduledCallback( eventLoop: some EventLoop ) {
420+ self . callbackCount += 1
421+ }
422+
423+ func didCancelScheduledCallback( eventLoop: some EventLoop ) {
424+ self . cancelCount += 1
425+ }
426+
427+ func assert( callbackCount: Int , cancelCount: Int , file: StaticString = #file, line: UInt = #line) {
428+ XCTAssertEqual (
429+ self . callbackCount,
430+ callbackCount,
431+ " Unexpected callback count " ,
432+ file: file,
433+ line: line
434+ )
435+ XCTAssertEqual (
436+ self . cancelCount,
437+ cancelCount,
438+ " Unexpected cancel count " ,
439+ file: file,
440+ line: line
441+ )
442+ }
443+ }
444+
281445/// This function exists because there's no nice way of waiting in tests for something to happen in the handler
282446/// without an arbitrary sleep.
283447///
0 commit comments