1+ // Licensed to the .NET Foundation under one or more agreements.
2+ // The .NET Foundation licenses this file to you under the MIT license.
3+ // See the LICENSE file in the project root for more information.
4+
5+ using System ;
6+ using System . Diagnostics ;
7+ using System . Threading ;
8+ using System . Threading . Tasks ;
9+ using Microsoft . VisualStudio . TestTools . UnitTesting ;
10+ using MQTTnet . Internal ;
11+
12+ namespace MQTTnet . Tests . Internal
13+ {
14+ [ TestClass ]
15+ public sealed class AsyncSignal_Tests
16+ {
17+ [ TestMethod ]
18+ [ ExpectedException ( typeof ( TaskCanceledException ) ) ]
19+ public async Task Cancel_If_No_Signal ( )
20+ {
21+ var asyncSignal = new AsyncSignal ( ) ;
22+
23+ using ( var timeout = new CancellationTokenSource ( TimeSpan . FromSeconds ( 2 ) ) )
24+ {
25+ await asyncSignal . WaitAsync ( timeout . Token ) ;
26+
27+ Assert . Fail ( "There is no signal. So we must fail here!" ) ;
28+ }
29+ }
30+
31+ [ TestMethod ]
32+ [ ExpectedException ( typeof ( ObjectDisposedException ) ) ]
33+ public async Task Dispose_Properly ( )
34+ {
35+ var asyncSignal = new AsyncSignal ( ) ;
36+
37+ // The timeout will not be reached but another task will kill the async signal.
38+ using ( var timeout = new CancellationTokenSource ( TimeSpan . FromSeconds ( 999 ) ) )
39+ {
40+ _ = Task . Run (
41+ async ( ) =>
42+ {
43+ await Task . Delay ( 2000 , CancellationToken . None ) ;
44+ asyncSignal . Dispose ( ) ;
45+ } ,
46+ CancellationToken . None ) ;
47+
48+ await asyncSignal . WaitAsync ( timeout . Token ) ;
49+
50+ Assert . Fail ( "There is no signal. So we must fail here!" ) ;
51+ }
52+ }
53+
54+ [ TestMethod ]
55+ public async Task Loop_Signal ( )
56+ {
57+ var asyncSignal = new AsyncSignal ( ) ;
58+
59+ for ( var i = 0 ; i < 10 ; i ++ )
60+ {
61+ asyncSignal . Set ( ) ;
62+
63+ // WaitAsync should return directly because the signal is available.
64+ await asyncSignal . WaitAsync ( ) ;
65+ }
66+ }
67+
68+ [ TestMethod ]
69+ public async Task Signal ( )
70+ {
71+ var asyncSignal = new AsyncSignal ( ) ;
72+
73+ // The timeout will not be reached but another task will kill the async signal.
74+ using ( var timeout = new CancellationTokenSource ( TimeSpan . FromSeconds ( 3 ) ) )
75+ {
76+ var stopwatch = Stopwatch . StartNew ( ) ;
77+
78+ _ = Task . Run (
79+ async ( ) =>
80+ {
81+ await Task . Delay ( 1000 , CancellationToken . None ) ;
82+ asyncSignal . Set ( ) ;
83+ } ,
84+ CancellationToken . None ) ;
85+
86+ await asyncSignal . WaitAsync ( timeout . Token ) ;
87+
88+ stopwatch . Stop ( ) ;
89+
90+ Assert . IsTrue ( stopwatch . ElapsedMilliseconds > 900 ) ;
91+ }
92+ }
93+
94+ [ TestMethod ]
95+ [ ExpectedException ( typeof ( InvalidOperationException ) ) ]
96+ public async Task Fail_For_Two_Waiters ( )
97+ {
98+ var asyncSignal = new AsyncSignal ( ) ;
99+
100+ // This thread will wait properly because it is the first waiter.
101+ _ = Task . Run (
102+ async ( ) =>
103+ {
104+ await asyncSignal . WaitAsync ( ) ;
105+ } ,
106+ CancellationToken . None ) ;
107+
108+ await Task . Delay ( 1000 ) ;
109+
110+ // Now the current thread must fail because there is already a waiter.
111+ await asyncSignal . WaitAsync ( ) ;
112+ }
113+ }
114+ }
0 commit comments