public async Task MaxParallelismDefaultIsRespected()
        {
            ThreadPool.SetMinThreads(100, 100);
            var inputValues         = AsyncEnumerableProvider.GetInts(100);
            int parallelism         = 10;
            int maxSeenParallelism  = 0;
            int parallelCounter     = 0;
            Func <int, Task> action = async(int i) =>
            {
                Interlocked.Increment(ref parallelCounter);
                if (parallelCounter > maxSeenParallelism)
                {
                    // This is not threadsafe but should be good enough for this
                    maxSeenParallelism = parallelCounter;
                }

                await Task.Delay(200);  // Need substantially slower work here to see the parallelism

                parallelCounter.ShouldBeLessThanOrEqualTo(parallelism);
                Interlocked.Decrement(ref parallelCounter);
            };
            await inputValues.SafeParallelAsync(action, parallelism);

            maxSeenParallelism.ShouldBe(parallelism);
        }
        public async Task MaxParallelismDefaultIsRespected()
        {
            var inputValues                = AsyncEnumerableProvider.GetInts(100);
            int parallelism                = 10;
            int maxSeenParallelism         = 0;
            int parallelCounter            = 0;
            Func <int, Task <int> > action = async(int i) =>
            {
                Interlocked.Increment(ref parallelCounter);
                if (parallelCounter > maxSeenParallelism)
                {
                    // This is not threadsafe but should be good enough for this
                    maxSeenParallelism = parallelCounter;
                }

                await Task.Delay(200);

                parallelCounter.ShouldBeLessThanOrEqualTo(parallelism);
                Interlocked.Decrement(ref parallelCounter);
                return(i * 2);
            };

            await foreach (var result in inputValues.SafeParallelAsyncWithResult(action, parallelism))
            {
            }

            maxSeenParallelism.ShouldBe(parallelism);
        }
        public async Task ReportsExceptionsWithReferenceTypes()
        {
            var inputValues = AsyncEnumerableProvider.GetObjects(100);

            Func <DummyClass, Task <DummyClass> > action = async(DummyClass input) =>
            {
                await Task.Delay(10);

                if (input.MyInt % 10 == 0)
                {
                    throw new Exception("We don't like 10s");
                }

                return(new DummyClass(input.MyInt * 2));
            };

            var results = new ConcurrentBag <Result <DummyClass, DummyClass> >();

            await foreach (var result in inputValues.SafeParallelAsyncWithResult(action))
            {
                results.Add(result);
            }

            results.Count().ShouldBe(100);
            results.Count(r => r.Success).ShouldBe(90);
            results.Count(r => !r.Success).ShouldBe(10);
            results.First(r => !r.Success).Exception.ShouldNotBeNull();
            results.First(r => !r.Success).Exception.Message.ShouldBe("We don't like 10s");
            results.Select(r => r.Input.MyInt).ShouldBe(Enumerable.Range(1, 100), true);
            results.First(r => !r.Success).Output.ShouldBe(null);
        }
        public async Task ReportsExceptionsWithValueTypes()
        {
            var inputValues = AsyncEnumerableProvider.GetInts(100);

            Func <int, Task <int> > action = async(int i) =>
            {
                await Task.Delay(10);

                if (i % 10 == 0)
                {
                    throw new Exception("We don't like 10s");
                }

                return(i * 2);
            };

            ConcurrentBag <Result <int, int> > results = new ConcurrentBag <Result <int, int> >();

            await foreach (var result in inputValues.SafeParallelAsyncWithResult(action))
            {
                results.Add(result);
            }

            results.Count().ShouldBe(100);
            results.Count(r => r.Success).ShouldBe(90);
            results.Count(r => !r.Success).ShouldBe(10);
            results.First(r => !r.Success).Exception.ShouldNotBeNull();
            results.First(r => !r.Success).Exception.Message.ShouldBe("We don't like 10s");
            results.Select(r => r.Input).ShouldBe(Enumerable.Range(1, 100), true);
            results.First(r => !r.Success).Output.ShouldBe(default(int));
        }
        public async Task MaxParallelismDefaultIsRespectedWhenChangedGlobally()
        {
            var inputValues = AsyncEnumerableProvider.GetInts(100);

            Parallelizer.MaxParallelismDefault = 10;
            int maxSeenParallelism  = 0;
            int parallelCounter     = 0;
            Func <int, Task> action = async(int i) =>
            {
                Interlocked.Increment(ref parallelCounter);
                if (parallelCounter > maxSeenParallelism)
                {
                    // This is not threadsafe but should be good enough for this
                    maxSeenParallelism = parallelCounter;
                }

                await Task.Delay(200);

                parallelCounter.ShouldBeLessThanOrEqualTo(Parallelizer.MaxParallelismDefault);
                Interlocked.Decrement(ref parallelCounter);
            };

            await foreach (var result in inputValues.SafeParallelAsyncWithResult(action))
            {
            }

            maxSeenParallelism.ShouldBe(Parallelizer.MaxParallelismDefault);

            // Restore it's value to default
            Parallelizer.MaxParallelismDefault = 100;
        }
        public async Task EachItemIsProcessedOnce()
        {
            var inputValues = AsyncEnumerableProvider.GetInts(100);
            ConcurrentBag <int> usedValues = new ConcurrentBag <int>();
            Func <int, Task>    action     = async(int i) =>
            {
                await Task.Delay(10);

                usedValues.Add(i);
            };
            await inputValues.SafeParallelAsync(action);

            usedValues.Count().ShouldBe(100);
            usedValues.ShouldBe(Enumerable.Range(1, 100), true);
        }
        public async Task RespectCancellationToken()
        {
            var inputValues                = AsyncEnumerableProvider.GetInts(100);
            var cancellationTokenSource    = new CancellationTokenSource();
            ConcurrentBag <int> usedValues = new ConcurrentBag <int>();
            Func <int, Task>    action     = async(int i) =>
            {
                await Task.Delay(200);

                usedValues.Add(i);
            };
            var task = inputValues.SafeParallelAsync(action, 10, cancellationTokenSource.Token);

            cancellationTokenSource.CancelAfter(500);
            await task;

            usedValues.Count().ShouldBeLessThan(100);
            usedValues.Count().ShouldBeGreaterThan(0);
        }
        public async Task HandlesReferenceTypes()
        {
            var inputValues = AsyncEnumerableProvider.GetObjects(100);
            var expected    = Enumerable.Range(1, 100).Sum(i => i * 2);
            int actual      = 0;

            Func <DummyClass, Task <DummyClass> > action = async(DummyClass input) =>
            {
                await Task.Delay(10);

                return(new DummyClass(input.MyInt * 2));
            };

            await foreach (var result in inputValues.SafeParallelAsyncWithResult(action))
            {
                actual += result.Output.MyInt;
            }

            actual.ShouldBe(expected);
        }
        public async Task HandlesValueTypes()
        {
            var inputValues = AsyncEnumerableProvider.GetInts(100);
            var expected    = Enumerable.Range(1, 100).Sum(i => i) * 2;
            int actual      = 0;

            Func <int, Task <int> > action = async(int i) =>
            {
                await Task.Delay(10);

                return(i * 2);
            };

            await foreach (var result in inputValues.SafeParallelAsyncWithResult(action))
            {
                actual += result.Output;
            }

            actual.ShouldBe(expected);
        }
        public async Task EachItemIsProcessedOnce()
        {
            var inputValues = AsyncEnumerableProvider.GetInts(100);

            Func <int, Task> action = async(int i) =>
            {
                await Task.Delay(10);
            };

            ConcurrentBag <Result <int> > results = new ConcurrentBag <Result <int> >();

            await foreach (var result in inputValues.SafeParallelAsyncWithResult(action))
            {
                results.Add(result);
            }

            results.Count().ShouldBe(100);
            results.Select(r => r.Input).ShouldBe(Enumerable.Range(1, 100), true);
            results.ShouldAllBe(r => r.Success);
        }
        public async Task RespectsCancellationToken()
        {
            var inputValues             = AsyncEnumerableProvider.GetInts(100);
            var cancellationTokenSource = new CancellationTokenSource();
            Func <int, Task> action     = async(int i) =>
            {
                await Task.Delay(100);
            };

            ConcurrentBag <Result <int> > results = new ConcurrentBag <Result <int> >();

            cancellationTokenSource.CancelAfter(500);
            var asyncEnumerable = inputValues.SafeParallelAsyncWithResult(action, 10);

            await foreach (var result in asyncEnumerable.WithCancellation(cancellationTokenSource.Token))
            {
                results.Add(result);
            }

            results.Count().ShouldBeLessThan(100);
            results.Count().ShouldBeGreaterThan(0);
        }