| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 | // Licensed to the .NET Foundation under one or more agreements.// The .NET Foundation licenses this file to you under the Apache 2.0 License.// See the LICENSE file in the project root for more information. using System;using System.Collections;using System.Collections.Generic;using System.Linq;using System.Threading;using System.Threading.Tasks;using Xunit;namespace Tests{    public partial class AsyncTests    {        public AsyncTests()        {            TaskScheduler.UnobservedTaskException += (o, e) =>            {            };        }        [Fact]        public async void CorrectDispose()        {            var disposed = new TaskCompletionSource<bool>();            var xs = new[] { 1, 2, 3 }.WithDispose(() =>            {                disposed.TrySetResult(true);            }).ToAsyncEnumerable();            var ys = xs.Select(x => x + 1);            var e = ys.GetAsyncEnumerator();            // We have to call move next because otherwise the internal enumerator is never allocated            await e.MoveNextAsync();            await e.DisposeAsync();            await disposed.Task;            Assert.True(disposed.Task.Result);            Assert.False(e.MoveNextAsync().Result);            var next = await e.MoveNextAsync();            Assert.False(next);        }        [Fact]        public async Task DisposesUponError()        {            var disposed = new TaskCompletionSource<bool>();            var xs = new[] { 1, 2, 3 }.WithDispose(() =>            {                disposed.SetResult(true);            }).ToAsyncEnumerable();            var ex = new Exception("Bang!");            var ys = xs.Select(x => { if (x == 1) throw ex; return x; });            var e = ys.GetAsyncEnumerator();            await AssertX.ThrowsAsync<Exception>(() => e.MoveNextAsync());            var result = await disposed.Task;            Assert.True(result);        }        private static IEnumerable<int> Blocking(ManualResetEvent evt, ManualResetEvent blockingStarted)        {            blockingStarted.Set();            evt.WaitOne();            yield return 42;        }        [Fact]        public async Task TakeOneFromSelectMany()        {            var ret0 = new[] { 0 }.ToAsyncEnumerable();            var retCheck = new[] { "Check" }.ToAsyncEnumerable();            var enumerable =                ret0                .SelectMany(_ => retCheck)                .Take(1)                .Do(_ => { });            Assert.Equal("Check", await enumerable.First());        }        [Fact]        public void SelectManyDisposeInvokedOnlyOnce()        {            var disposeCounter = new DisposeCounter();            var result = new[] { 1 }.ToAsyncEnumerable().SelectMany(i => disposeCounter).Select(i => i).ToList().Result;            Assert.Empty(result);            Assert.Equal(1, disposeCounter.DisposeCount);        }        [Fact]        public void SelectManyInnerDispose()        {            var disposes = Enumerable.Range(0, 10).Select(_ => new DisposeCounter()).ToList();            var result = AsyncEnumerable.Range(0, 10).SelectMany(i => disposes[i]).Select(i => i).ToList().Result;            Assert.Empty(result);            Assert.True(disposes.All(d => d.DisposeCount == 1));        }        [Fact]        public void DisposeAfterCreation()        {            var enumerable = new[] { 1 }.ToAsyncEnumerable() as IDisposable;            enumerable?.Dispose();        }        private class DisposeCounter : IAsyncEnumerable<object>        {            public int DisposeCount { get; private set; }            public IAsyncEnumerator<object> GetAsyncEnumerator(CancellationToken cancellationToken)            {                return new Enumerator(this);            }            private class Enumerator : IAsyncEnumerator<object>            {                private readonly DisposeCounter _disposeCounter;                public Enumerator(DisposeCounter disposeCounter)                {                    _disposeCounter = disposeCounter;                }                public ValueTask DisposeAsync()                {                    _disposeCounter.DisposeCount++;                    return new ValueTask(Task.FromResult(true));                }                public ValueTask<bool> MoveNextAsync()                {                    return new ValueTask<bool>(Task.Factory.StartNew(() => false));                }                public object Current { get; private set; }            }        }    }    internal static class MyExt    {        public static IEnumerable<T> WithDispose<T>(this IEnumerable<T> source, Action a)        {            return new Enumerable<T>(() =>            {                var e = source.GetEnumerator();                return new Enumerator<T>(e.MoveNext, () => e.Current, () => { e.Dispose(); a(); });            });        }        private sealed class Enumerable<T> : IEnumerable<T>        {            private readonly Func<IEnumerator<T>> _getEnumerator;            public Enumerable(Func<IEnumerator<T>> getEnumerator)            {                _getEnumerator = getEnumerator;            }            public IEnumerator<T> GetEnumerator() => _getEnumerator();            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();        }        private sealed class Enumerator<T> : IEnumerator<T>        {            private readonly Func<bool> _moveNext;            private readonly Func<T> _current;            private readonly Action _dispose;            public Enumerator(Func<bool> moveNext, Func<T> current, Action dispose)            {                _moveNext = moveNext;                _current = current;                _dispose = dispose;            }            public T Current => _current();            public void Dispose() => _dispose();            object IEnumerator.Current => Current;            public bool MoveNext() => _moveNext();            public void Reset()            {                throw new NotImplementedException();            }        }    }}
 |