77using System . Net ;
88using System . Net . Sockets ;
99using System . Runtime . InteropServices ;
10+ using System . Threading ;
1011using System . Threading . Tasks ;
1112
1213using NUnit . Framework ;
@@ -161,6 +162,24 @@ public async Task Start()
161162#pragma warning restore CS0618
162163 }
163164
165+ [ Test ]
166+ public async Task Start_Restart ( )
167+ {
168+ await using var node = CreateNode ( ) ;
169+
170+ #pragma warning disable CS0618
171+ Assert . DoesNotThrow ( node . Start ) ;
172+ Assert . Throws < InvalidOperationException > ( node . Start , "already started" ) ;
173+ #pragma warning restore CS0618
174+
175+ Assert . That ( async ( ) => await node . StopAsync ( default ) , Throws . Nothing ) ;
176+
177+ #pragma warning disable CS0618
178+ Assert . DoesNotThrow ( node . Start , "restart" ) ;
179+ Assert . Throws < InvalidOperationException > ( node . Start , "already restarted" ) ;
180+ #pragma warning restore CS0618
181+ }
182+
164183 [ Test ]
165184 public async Task StartAsync ( )
166185 {
@@ -179,4 +198,104 @@ public async Task StartAsync()
179198 Assert . That ( ( ) => _ = node . LocalEndPoint , Throws . Nothing , $ "{ nameof ( node . LocalEndPoint ) } after start") ;
180199#pragma warning restore CS0618
181200 }
201+
202+ [ Test ]
203+ public async Task StartAsync_Restart ( )
204+ {
205+ await using var node = CreateNode ( ) ;
206+
207+ #pragma warning disable CS0618
208+ Assert . That ( async ( ) => await node . StartAsync ( default ) , Throws . Nothing ) ;
209+ Assert . That ( async ( ) => await node . StartAsync ( default ) , Throws . InvalidOperationException , "already started" ) ;
210+ #pragma warning restore CS0618
211+
212+ Assert . That ( async ( ) => await node . StopAsync ( default ) , Throws . Nothing ) ;
213+
214+ #pragma warning disable CS0618
215+ Assert . That ( async ( ) => await node . StartAsync ( default ) , Throws . Nothing , "restart" ) ;
216+ Assert . That ( async ( ) => await node . StartAsync ( default ) , Throws . InvalidOperationException , "already restarted" ) ;
217+ #pragma warning restore CS0618
218+ }
219+
220+ [ Test ]
221+ public async Task StopAsync_StartedByStartAsync ( )
222+ {
223+ await using var node = CreateNode ( ) ;
224+
225+ Assert . That ( async ( ) => await node . StartAsync ( default ) , Throws . Nothing ) ;
226+
227+ Assert . That ( async ( ) => await node . StopAsync ( default ) , Throws . Nothing ) ;
228+ Assert . That ( async ( ) => await node . StopAsync ( default ) , Throws . Nothing , "stop again" ) ;
229+ }
230+
231+ [ Test ]
232+ public async Task StopAsync_StartedByStart ( )
233+ {
234+ await using var node = CreateNode ( ) ;
235+
236+ #pragma warning disable CS0618
237+ Assert . That ( ( ) => node . Start ( ) , Throws . Nothing ) ;
238+ #pragma warning restore CS0618
239+
240+ Assert . That ( async ( ) => await node . StopAsync ( default ) , Throws . Nothing ) ;
241+ Assert . That ( async ( ) => await node . StopAsync ( default ) , Throws . Nothing , "stop again" ) ;
242+ }
243+
244+ [ Test ]
245+ public async Task StopAsync_NotStarted ( )
246+ {
247+ await using var node = CreateNode ( ) ;
248+
249+ Assert . That ( async ( ) => await node . StopAsync ( default ) , Throws . Nothing ) ;
250+ Assert . That ( async ( ) => await node . StopAsync ( default ) , Throws . Nothing , "stop again" ) ;
251+ }
252+
253+ [ Test ]
254+ public async Task StopAsync_WhileProcessingClient ( )
255+ {
256+ await using var node = CreateNode ( ) ;
257+
258+ await node . StartAsync ( ) ;
259+
260+ using var cts = new CancellationTokenSource ( ) ;
261+
262+ var taskAccept = Task . Run ( async ( ) => await node . AcceptAsync ( throwIfCancellationRequested : false , cts . Token ) ) ;
263+
264+ using var client = CreateClient ( ( IPEndPoint ) node . EndPoint , out var writer , out var reader ) ;
265+
266+ reader . ReadLine ( ) ; // banner
267+
268+ // attempt stop while processing client
269+ const int MaxAttempt = 10 ;
270+
271+ for ( var n = 0 ; n < MaxAttempt ; n ++ ) {
272+ using var ctsStopWhileProcessingClientTimeout = new CancellationTokenSource ( TimeSpan . FromSeconds ( 0.1 ) ) ;
273+
274+ Assert . That (
275+ async ( ) => await node . StopAsync ( ctsStopWhileProcessingClientTimeout . Token ) ,
276+ Throws
277+ . InstanceOf < OperationCanceledException > ( )
278+ . With
279+ . Property ( nameof ( OperationCanceledException . CancellationToken ) )
280+ . EqualTo ( ctsStopWhileProcessingClientTimeout . Token )
281+ ) ;
282+ }
283+
284+ // close session, disconnect client
285+ writer . WriteLine ( "." ) ;
286+ writer . Close ( ) ;
287+
288+ // stop accepting task
289+ cts . Cancel ( ) ;
290+
291+ Assert . DoesNotThrowAsync ( async ( ) => await taskAccept ) ;
292+
293+ // attempt stop after processing client
294+ using var ctsStopTimeout = new CancellationTokenSource ( TimeSpan . FromSeconds ( 1 ) ) ;
295+
296+ Assert . That (
297+ async ( ) => await node . StopAsync ( ctsStopTimeout . Token ) ,
298+ Throws . Nothing
299+ ) ;
300+ }
182301}
0 commit comments