5
5
using System ;
6
6
using System . Collections . Generic ;
7
7
using System . Linq ;
8
+ using System . Threading ;
8
9
using System . Threading . Tasks ;
9
10
using Xunit ;
10
11
@@ -162,6 +163,128 @@ public async Task Union_ToList()
162
163
Assert . Equal ( new [ ] { 1 , 2 , 3 , 4 , 5 } , ( await res . ToListAsync ( ) ) . OrderBy ( x => x ) ) ;
163
164
}
164
165
166
+
167
+ [ Fact ]
168
+ public async Task Union_DisposesNotEmpty ( )
169
+ {
170
+ var e1 = new DisposalDetectingEnumerable ( 10 , 2 ) ;
171
+ var e2 = new DisposalDetectingEnumerable ( 20 , 2 ) ;
172
+ var res = e1 . Union ( e2 ) . OrderBy ( x => x ) ;
173
+
174
+ var e = res . GetAsyncEnumerator ( ) ;
175
+ await HasNextAsync ( e , 10 ) ;
176
+ await HasNextAsync ( e , 11 ) ;
177
+ await HasNextAsync ( e , 20 ) ;
178
+ await HasNextAsync ( e , 21 ) ;
179
+ await NoNextAsync ( e ) ;
180
+
181
+ Assert . Single ( e1 . Enumerators ) ;
182
+ Assert . Single ( e2 . Enumerators ) ;
183
+ Assert . Equal ( [ true , true ] , [ e1 . Enumerators [ 0 ] . Disposed , e2 . Enumerators [ 0 ] . Disposed ] ) ;
184
+ }
185
+
186
+ [ Fact ]
187
+ public async Task Union_DisposesFirstEmpty ( )
188
+ {
189
+ var e1 = new DisposalDetectingEnumerable ( 0 , 0 ) ;
190
+ var e2 = new DisposalDetectingEnumerable ( 1 , 1 ) ;
191
+ var res = e1 . Union ( e2 ) ;
192
+
193
+ var e = res . GetAsyncEnumerator ( ) ;
194
+ await HasNextAsync ( e , 1 ) ;
195
+ await NoNextAsync ( e ) ;
196
+
197
+ Assert . Single ( e1 . Enumerators ) ;
198
+ Assert . Single ( e2 . Enumerators ) ;
199
+ Assert . Equal ( [ true , true ] , [ e1 . Enumerators [ 0 ] . Disposed , e2 . Enumerators [ 0 ] . Disposed ] ) ;
200
+ }
201
+
202
+ [ Fact ]
203
+ public async Task Union_DisposesSecondOfTwoEmpty ( )
204
+ {
205
+ var e1 = new DisposalDetectingEnumerable ( 1 , 1 ) ;
206
+ var e2 = new DisposalDetectingEnumerable ( 0 , 0 ) ;
207
+ var res = e1 . Union ( e2 ) ;
208
+
209
+ var e = res . GetAsyncEnumerator ( ) ;
210
+ await HasNextAsync ( e , 1 ) ;
211
+ await NoNextAsync ( e ) ;
212
+
213
+ Assert . Single ( e1 . Enumerators ) ;
214
+ Assert . Single ( e2 . Enumerators ) ;
215
+ Assert . Equal ( [ true , true ] , [ e1 . Enumerators [ 0 ] . Disposed , e2 . Enumerators [ 0 ] . Disposed ] ) ;
216
+ }
217
+
218
+ [ Fact ]
219
+ public async Task Union_DisposesSecondOfThreeEmpty ( )
220
+ {
221
+ var e1 = new DisposalDetectingEnumerable ( 10 , 1 ) ;
222
+ var e2 = new DisposalDetectingEnumerable ( 0 , 0 ) ;
223
+ var e3 = new DisposalDetectingEnumerable ( 30 , 1 ) ;
224
+ var res = e1 . Union ( e2 ) . Union ( e3 ) ;
225
+
226
+ var e = res . GetAsyncEnumerator ( ) ;
227
+ await HasNextAsync ( e , 10 ) ;
228
+ await HasNextAsync ( e , 30 ) ;
229
+ await NoNextAsync ( e ) ;
230
+
231
+ Assert . Single ( e1 . Enumerators ) ;
232
+ Assert . Single ( e2 . Enumerators ) ;
233
+ Assert . Single ( e3 . Enumerators ) ;
234
+ Assert . Equal ( [ true , true , true ] , [ e1 . Enumerators [ 0 ] . Disposed , e2 . Enumerators [ 0 ] . Disposed , e3 . Enumerators [ 0 ] . Disposed ] ) ;
235
+ }
236
+
237
+ [ Fact ]
238
+ public async Task Union_DisposesThirdOfThreeEmpty ( )
239
+ {
240
+ var e1 = new DisposalDetectingEnumerable ( 10 , 1 ) ;
241
+ var e2 = new DisposalDetectingEnumerable ( 20 , 1 ) ;
242
+ var e3 = new DisposalDetectingEnumerable ( 0 , 0 ) ;
243
+ var res = e1 . Union ( e2 ) . Union ( e3 ) ;
244
+
245
+ var e = res . GetAsyncEnumerator ( ) ;
246
+ await HasNextAsync ( e , 10 ) ;
247
+ await HasNextAsync ( e , 20 ) ;
248
+ await NoNextAsync ( e ) ;
249
+
250
+ Assert . Single ( e1 . Enumerators ) ;
251
+ Assert . Single ( e2 . Enumerators ) ;
252
+ Assert . Single ( e3 . Enumerators ) ;
253
+ Assert . Equal ( [ true , true , true ] , [ e1 . Enumerators [ 0 ] . Disposed , e2 . Enumerators [ 0 ] . Disposed , e3 . Enumerators [ 0 ] . Disposed ] ) ;
254
+ }
255
+
256
+ [ Fact ]
257
+ public async Task Union_DisposesAllOfTwoEmpty ( )
258
+ {
259
+ var e1 = new DisposalDetectingEnumerable ( 0 , 0 ) ;
260
+ var e2 = new DisposalDetectingEnumerable ( 0 , 0 ) ;
261
+ var res = e1 . Union ( e2 ) ;
262
+
263
+ var e = res . GetAsyncEnumerator ( ) ;
264
+ await NoNextAsync ( e ) ;
265
+
266
+ Assert . Single ( e1 . Enumerators ) ;
267
+ Assert . Single ( e2 . Enumerators ) ;
268
+ Assert . Equal ( [ true , true ] , [ e1 . Enumerators [ 0 ] . Disposed , e2 . Enumerators [ 0 ] . Disposed ] ) ;
269
+ }
270
+
271
+ [ Fact ]
272
+ public async Task Union_DisposesAllOfThreeEmpty ( )
273
+ {
274
+ var e1 = new DisposalDetectingEnumerable ( 0 , 0 ) ;
275
+ var e2 = new DisposalDetectingEnumerable ( 0 , 0 ) ;
276
+ var e3 = new DisposalDetectingEnumerable ( 0 , 0 ) ;
277
+ var res = e1 . Union ( e2 ) . Union ( e3 ) ;
278
+
279
+ var e = res . GetAsyncEnumerator ( ) ;
280
+ await NoNextAsync ( e ) ;
281
+
282
+ Assert . Single ( e1 . Enumerators ) ;
283
+ Assert . Single ( e2 . Enumerators ) ;
284
+ Assert . Single ( e3 . Enumerators ) ;
285
+ Assert . Equal ( [ true , true , true ] , [ e1 . Enumerators [ 0 ] . Disposed , e2 . Enumerators [ 0 ] . Disposed , e3 . Enumerators [ 0 ] . Disposed ] ) ;
286
+ }
287
+
165
288
private sealed class Eq : IEqualityComparer < int >
166
289
{
167
290
public bool Equals ( int x , int y )
@@ -174,5 +297,61 @@ public int GetHashCode(int obj)
174
297
return EqualityComparer < int > . Default . GetHashCode ( Math . Abs ( obj ) ) ;
175
298
}
176
299
}
300
+
301
+ private class DisposalDetectingEnumerable : IAsyncEnumerable < int >
302
+ {
303
+ private readonly int _start ;
304
+ private readonly int _count ;
305
+
306
+ public DisposalDetectingEnumerable ( int start , int count )
307
+ {
308
+ _start = start ;
309
+ _count = count ;
310
+ }
311
+
312
+ public List < Enumerator > Enumerators { get ; } = new List < Enumerator > ( ) ;
313
+
314
+ public IAsyncEnumerator < int > GetAsyncEnumerator ( CancellationToken cancellationToken = default )
315
+ {
316
+ Enumerator r = new ( _start , _count ) ;
317
+ Enumerators . Add ( r ) ;
318
+ return r ;
319
+ }
320
+
321
+ public class Enumerator : IAsyncEnumerator < int >
322
+ {
323
+ private readonly int _max ;
324
+
325
+ public Enumerator ( int start , int count )
326
+ {
327
+ Current = start - 1 ;
328
+ _max = start + count ;
329
+ }
330
+
331
+ public int Current { get ; private set ; }
332
+
333
+ public bool Disposed { get ; private set ; }
334
+
335
+ public void Dispose ( )
336
+ {
337
+ Disposed = true ;
338
+ }
339
+
340
+ public ValueTask DisposeAsync ( )
341
+ {
342
+ Disposed = true ;
343
+ return new ValueTask ( ) ;
344
+ }
345
+ public ValueTask < bool > MoveNextAsync ( )
346
+ {
347
+ if ( ++ Current < _max )
348
+ {
349
+ return new ValueTask < bool > ( true ) ;
350
+ }
351
+
352
+ return new ValueTask < bool > ( false ) ;
353
+ }
354
+ }
355
+ }
177
356
}
178
357
}
0 commit comments