10
10
using System . Threading ;
11
11
using System . Threading . Tasks ;
12
12
using Akka . Pattern ;
13
- using Akka . Routing ;
14
13
using Akka . Streams . Dsl ;
15
14
using Akka . Streams . TestKit ;
16
15
using Akka . TestKit ;
17
16
using FluentAssertions ;
18
- using Nito . AsyncEx . Synchronous ;
19
17
using Xunit ;
20
18
using Xunit . Abstractions ;
21
19
using System . Collections . Generic ;
24
22
using Akka . Streams . TestKit . Tests ;
25
23
using Akka . Streams . Tests . Actor ;
26
24
using Reactive . Streams ;
25
+ using System . Runtime . CompilerServices ;
26
+ using Akka . Util ;
27
+ using FluentAssertions . Extensions ;
27
28
28
29
namespace Akka . Streams . Tests . Dsl
29
30
{
@@ -33,16 +34,16 @@ public class AsyncEnumerableSpec : AkkaSpec
33
34
private ActorMaterializer Materializer { get ; }
34
35
private ITestOutputHelper _helper ;
35
36
public AsyncEnumerableSpec ( ITestOutputHelper helper ) : base (
36
- AkkaSpecConfig . WithFallback ( StreamTestDefaultMailbox . DefaultConfig ) ,
37
- helper )
37
+ AkkaSpecConfig . WithFallback ( StreamTestDefaultMailbox . DefaultConfig ) ,
38
+ helper )
38
39
{
39
40
_helper = helper ;
40
41
var settings = ActorMaterializerSettings . Create ( Sys ) . WithInputBuffer ( 2 , 16 ) ;
41
42
Materializer = ActorMaterializer . Create ( Sys , settings ) ;
42
43
}
43
44
44
45
45
- [ Fact ]
46
+ [ Fact ]
46
47
public async Task RunAsAsyncEnumerable_Uses_CancellationToken ( )
47
48
{
48
49
var input = Enumerable . Range ( 1 , 6 ) . ToList ( ) ;
@@ -67,7 +68,7 @@ public async Task RunAsAsyncEnumerable_Uses_CancellationToken()
67
68
68
69
caught . ShouldBeTrue ( ) ;
69
70
}
70
-
71
+
71
72
[ Fact ]
72
73
public async Task RunAsAsyncEnumerable_must_return_an_IAsyncEnumerableT_from_a_Source ( )
73
74
{
@@ -79,7 +80,7 @@ public async Task RunAsAsyncEnumerable_must_return_an_IAsyncEnumerableT_from_a_S
79
80
( output [ 0 ] == a ) . ShouldBeTrue ( "Did not get elements in order!" ) ;
80
81
output = output . Skip ( 1 ) . ToArray ( ) ;
81
82
}
82
- output . Length . ShouldBe ( 0 , "Did not receive all elements!" ) ;
83
+ output . Length . ShouldBe ( 0 , "Did not receive all elements!" ) ;
83
84
}
84
85
85
86
[ Fact ]
@@ -93,15 +94,15 @@ public async Task RunAsAsyncEnumerable_must_allow_multiple_enumerations()
93
94
( output [ 0 ] == a ) . ShouldBeTrue ( "Did not get elements in order!" ) ;
94
95
output = output . Skip ( 1 ) . ToArray ( ) ;
95
96
}
96
- output . Length . ShouldBe ( 0 , "Did not receive all elements!" ) ;
97
+ output . Length . ShouldBe ( 0 , "Did not receive all elements!" ) ;
97
98
98
99
output = input . ToArray ( ) ;
99
100
await foreach ( var a in asyncEnumerable )
100
101
{
101
102
( output [ 0 ] == a ) . ShouldBeTrue ( "Did not get elements in order!" ) ;
102
103
output = output . Skip ( 1 ) . ToArray ( ) ;
103
104
}
104
- output . Length . ShouldBe ( 0 , "Did not receive all elements in second enumeration!!" ) ;
105
+ output . Length . ShouldBe ( 0 , "Did not receive all elements in second enumeration!!" ) ;
105
106
}
106
107
107
108
@@ -112,7 +113,7 @@ public async Task RunAsAsyncEnumerable_Throws_on_Abrupt_Stream_termination()
112
113
var probe = this . CreatePublisherProbe < int > ( ) ;
113
114
var task = Source . FromPublisher ( probe ) . RunAsAsyncEnumerable ( materializer ) ;
114
115
115
- var a = Task . Run ( async ( ) =>
116
+ var a = Task . Run ( async ( ) =>
116
117
{
117
118
await foreach ( var notused in task )
118
119
{
@@ -123,19 +124,20 @@ public async Task RunAsAsyncEnumerable_Throws_on_Abrupt_Stream_termination()
123
124
//we want to send messages so we aren't just waiting forever.
124
125
probe . SendNext ( 1 ) ;
125
126
probe . SendNext ( 2 ) ;
126
- bool thrown = false ;
127
+ var thrown = false ;
127
128
try
128
129
{
129
130
await a ;
130
131
}
131
- catch ( StreamDetachedException e )
132
- {
133
- thrown = true ;
134
- }
132
+ catch ( StreamDetachedException e )
133
+ {
134
+ thrown = true ;
135
+ }
135
136
catch ( AbruptTerminationException e )
136
137
{
137
138
thrown = true ;
138
139
}
140
+
139
141
thrown . ShouldBeTrue ( ) ;
140
142
}
141
143
@@ -151,47 +153,128 @@ async Task ShouldThrow()
151
153
{
152
154
await foreach ( var a in task )
153
155
{
154
-
155
156
}
156
157
}
157
158
158
159
await Assert . ThrowsAsync < IllegalStateException > ( ShouldThrow ) ;
159
160
}
160
161
161
- [ Fact ]
162
- public void AsyncEnumerableSource_Must_Complete_Immediately_With_No_elements_When_An_Empty_IAsyncEnumerable_Is_Passed_In ( )
162
+ [ Fact ]
163
+ public void
164
+ AsyncEnumerableSource_Must_Complete_Immediately_With_No_elements_When_An_Empty_IAsyncEnumerable_Is_Passed_In ( )
163
165
{
164
- Func < IAsyncEnumerable < int > > range = ( ) =>
165
- {
166
- return RangeAsync ( 1 , 100 ) ;
167
- } ;
166
+ IAsyncEnumerable < int > Range ( ) => RangeAsync ( 0 , 0 ) ;
168
167
var subscriber = this . CreateManualSubscriberProbe < int > ( ) ;
169
168
170
- Source . From ( range )
169
+ Source . From ( Range )
171
170
. RunWith ( Sink . FromSubscriber ( subscriber ) , Materializer ) ;
172
171
173
172
var subscription = subscriber . ExpectSubscription ( ) ;
174
173
subscription . Request ( 100 ) ;
175
- for ( int i = 1 ; i <= 20 ; i ++ )
174
+ subscriber . ExpectComplete ( ) ;
175
+ }
176
+
177
+ [ Fact ]
178
+ public void AsyncEnumerableSource_Must_Process_All_Elements ( )
179
+ {
180
+ IAsyncEnumerable < int > Range ( ) => RangeAsync ( 0 , 100 ) ;
181
+ var subscriber = this . CreateManualSubscriberProbe < int > ( ) ;
182
+
183
+ Source . From ( Range )
184
+ . RunWith ( Sink . FromSubscriber ( subscriber ) , Materializer ) ;
185
+
186
+ var subscription = subscriber . ExpectSubscription ( ) ;
187
+ subscription . Request ( 101 ) ;
188
+
189
+ subscriber . ExpectNextN ( Enumerable . Range ( 0 , 100 ) ) ;
190
+
191
+ subscriber . ExpectComplete ( ) ;
192
+ }
193
+
194
+ [ Fact ]
195
+ public void AsyncEnumerableSource_Must_Process_Source_That_Immediately_Throws ( )
196
+ {
197
+ IAsyncEnumerable < int > Range ( ) => ThrowingRangeAsync ( 0 , 100 , 50 ) ;
198
+ var subscriber = this . CreateManualSubscriberProbe < int > ( ) ;
199
+
200
+ Source . From ( Range )
201
+ . RunWith ( Sink . FromSubscriber ( subscriber ) , Materializer ) ;
202
+
203
+ var subscription = subscriber . ExpectSubscription ( ) ;
204
+ subscription . Request ( 101 ) ;
205
+
206
+ subscriber . ExpectNextN ( Enumerable . Range ( 0 , 50 ) ) ;
207
+
208
+ var exception = subscriber . ExpectError ( ) ;
209
+
210
+ // Exception should be automatically unrolled, this SHOULD NOT be AggregateException
211
+ exception . Should ( ) . BeOfType < TestException > ( ) ;
212
+ exception . Message . Should ( ) . Be ( "BOOM!" ) ;
213
+ }
214
+
215
+ [ Fact ]
216
+ public async Task AsyncEnumerableSource_Must_Cancel_Running_Source_If_Downstream_Completes ( )
217
+ {
218
+ var latch = new AtomicBoolean ( ) ;
219
+ IAsyncEnumerable < int > Range ( ) => ProbeableRangeAsync ( 0 , 100 , latch ) ;
220
+ var subscriber = this . CreateManualSubscriberProbe < int > ( ) ;
221
+
222
+ Source . From ( Range )
223
+ . RunWith ( Sink . FromSubscriber ( subscriber ) , Materializer ) ;
224
+
225
+ var subscription = subscriber . ExpectSubscription ( ) ;
226
+ subscription . Request ( 50 ) ;
227
+ subscriber . ExpectNextN ( Enumerable . Range ( 0 , 50 ) ) ;
228
+ subscription . Cancel ( ) ;
229
+
230
+ // The cancellation token inside the IAsyncEnumerable should be cancelled
231
+ await WithinAsync ( 3 . Seconds ( ) , async ( ) => latch . Value ) ;
232
+ }
233
+
234
+ private static async IAsyncEnumerable < int > RangeAsync ( int start , int count ,
235
+ [ EnumeratorCancellation ] CancellationToken token = default )
236
+ {
237
+ foreach ( var i in Enumerable . Range ( start , count ) )
176
238
{
177
- var next = subscriber . ExpectNext ( i ) ;
178
- _helper . WriteLine ( i . ToString ( ) ) ;
239
+ await Task . Delay ( 10 , token ) ;
240
+ if ( token . IsCancellationRequested )
241
+ yield break ;
242
+ yield return i ;
179
243
}
244
+ }
180
245
181
- //subscriber.ExpectComplete();
246
+ private static async IAsyncEnumerable < int > ThrowingRangeAsync ( int start , int count , int throwAt ,
247
+ [ EnumeratorCancellation ] CancellationToken token = default )
248
+ {
249
+ foreach ( var i in Enumerable . Range ( start , count ) )
250
+ {
251
+ if ( token . IsCancellationRequested )
252
+ yield break ;
253
+
254
+ if ( i == throwAt )
255
+ throw new TestException ( "BOOM!" ) ;
256
+
257
+ yield return i ;
258
+ }
182
259
}
183
260
184
- static async IAsyncEnumerable < int > RangeAsync ( int start , int count )
261
+ private static async IAsyncEnumerable < int > ProbeableRangeAsync ( int start , int count , AtomicBoolean latch ,
262
+ [ EnumeratorCancellation ] CancellationToken token = default )
185
263
{
186
- for ( var i = 0 ; i < count ; i ++ )
264
+ token . Register ( ( ) =>
265
+ {
266
+ latch . GetAndSet ( true ) ;
267
+ } ) ;
268
+ foreach ( var i in Enumerable . Range ( start , count ) )
187
269
{
188
- await Task . Delay ( i ) ;
189
- yield return start + i ;
270
+ if ( token . IsCancellationRequested )
271
+ yield break ;
272
+
273
+ yield return i ;
190
274
}
191
275
}
192
276
193
277
}
194
278
#else
195
279
#endif
196
-
197
- }
280
+ }
0 commit comments