public void FaultsBeforeSuccessesWaitForCancellation()
        {
            // 2 tasks fault, others basically wait for cancellation and wait a bit after receiving cancellation,
            // fault occurs before successes, waitForCancellations true
            // -> AggregateException with faults inside, tasks in faulted state, other tasks are in canceled state
            CancellableTask[] tasks = new CancellableTask[]
            {
                GetEarlyFaultingCancellableTask(),
                GetEarlyFaultingCancellableTask(),
                GetSuccessfulCancellableTask(),
                GetSuccessfulCancellableTask()
            };

            try
            {
                Task waitTask = AsyncUtils.WhenAllCancelOnFirstExceptionWaitForCancellations(tasks);

                Assert.Throws <Exception>(() => waitTask.ConfigureAwait(false).GetAwaiter().GetResult());
                Assert.Equal(2, waitTask.Exception.InnerExceptions.Count);
                Assert.All(waitTask.Exception.InnerExceptions, ex => Assert.Equal("Early Fault", ex.Message));

                Assert.Equal(TaskStatus.Faulted, tasks[0].Task.Status);
                Assert.Equal(TaskStatus.Faulted, tasks[1].Task.Status);
                Assert.Equal(TaskStatus.Canceled, tasks[2].Task.Status);
                Assert.Equal(TaskStatus.Canceled, tasks[3].Task.Status);
            }
            finally
            {
                foreach (CancellableTask task in tasks)
                {
                    task.CancellationTokenSource.Dispose();
                }
            }
        }
        public void FaultsAfterSuccessesWaitForCancellations()
        {
            // 2 tasks fault, others succeed, faults occur after successes, waitForCancellations true
            // -> AggregateException with both faults inside
            CancellableTask[] tasks = new CancellableTask[]
            {
                GetLateFaultingCancellableTask(),
                GetLateFaultingCancellableTask(),
                GetSuccessfulCancellableTask(),
                GetSuccessfulCancellableTask()
            };
            try
            {
                Task waitTask = AsyncUtils.WhenAllCancelOnFirstExceptionWaitForCancellations(tasks);

                Assert.Throws <Exception>(() => waitTask.ConfigureAwait(false).GetAwaiter().GetResult());
                Assert.Equal(2, waitTask.Exception.InnerExceptions.Count);
                Assert.All(waitTask.Exception.InnerExceptions, ex => Assert.Equal("Late Fault", ex.Message));

                Assert.Equal(TaskStatus.Faulted, tasks[0].Task.Status);
                Assert.Equal(TaskStatus.Faulted, tasks[1].Task.Status);
                Assert.Equal(TaskStatus.RanToCompletion, tasks[2].Task.Status);
                Assert.Equal(TaskStatus.RanToCompletion, tasks[3].Task.Status);
            }
            finally
            {
                foreach (CancellableTask task in tasks)
                {
                    task.CancellationTokenSource.Dispose();
                }
            }
        }
        public void TypeOfExceptionWhenAwaitingIsNotAggregateException()
        {
            CancellableTask[] tasks = new CancellableTask[]
            {
                GetLateFaultingCancellableTask()
            };

            try
            {
                Task      waitTask = AsyncUtils.WhenAllCancelOnFirstExceptionWaitForCancellations(tasks);
                Exception exceptionWhenAwaiting = GetExceptionWhenAwaiting(waitTask).ConfigureAwait(false).GetAwaiter().GetResult();
                Assert.IsType <Exception>(exceptionWhenAwaiting);
            }
            finally
            {
                foreach (CancellableTask task in tasks)
                {
                    task.CancellationTokenSource.Dispose();
                }
            }
        }
        public void CancelBeforeOthersWaitForCancellation()
        {
            // 1 task canceled, others basically wait for cancellation and wait a bit after receiving cancellation,
            // cancel occurs before successes, waitForCancellation true
            // -> TaskCanceledException with CancellationToken == canceled task's cancellation token
            CancellableTask[] tasks = new CancellableTask[]
            {
                GetEarlyCancellingCancellableTask(),
                GetSuccessfulCancellableTask(),
                GetSuccessfulCancellableTask()
            };

            try
            {
                Task waitTask           = AsyncUtils.WhenAllCancelOnFirstExceptionWaitForCancellations(tasks);
                bool taskCanceledCaught = false;
                try
                {
                    waitTask.ConfigureAwait(false).GetAwaiter().GetResult();
                }
                catch (TaskCanceledException ex)
                {
                    taskCanceledCaught = true;
                    ex.CancellationToken.Should().Be(tasks[0].CancellationTokenSource.Token);
                }

                Assert.True(taskCanceledCaught, "TaskCanceledException was not thrown.");
                Assert.Equal(TaskStatus.Canceled, tasks[0].Task.Status);
                Assert.Equal(TaskStatus.Canceled, tasks[1].Task.Status);
                Assert.Equal(TaskStatus.Canceled, tasks[2].Task.Status);
            }
            finally
            {
                foreach (CancellableTask task in tasks)
                {
                    task.CancellationTokenSource.Dispose();
                }
            }
        }
        public void AllCompleteSuccessfullyWaitForCancellations()
        {
            CancellableTask[] tasks = new CancellableTask[3]
            {
                GetSuccessfulCancellableTask(),
                GetSuccessfulCancellableTask(),
                GetSuccessfulCancellableTask()
            };

            try
            {
                Task waitTask = AsyncUtils.WhenAllCancelOnFirstExceptionWaitForCancellations(tasks);
                waitTask.ConfigureAwait(false).GetAwaiter().GetResult();
                Assert.All(tasks, task => Assert.Equal(TaskStatus.RanToCompletion, task.Task.Status));
            }
            finally
            {
                foreach (CancellableTask task in tasks)
                {
                    task.CancellationTokenSource.Dispose();
                }
            }
        }
        public void PassingNoTasksReturnsInstantlyWaitForCancellations()
        {
            Task task = AsyncUtils.WhenAllCancelOnFirstExceptionWaitForCancellations(new CancellableTask[0]);

            Assert.Equal(TaskStatus.RanToCompletion, task.Status);
        }