public async Task WaitBlockersAsynс_DropBlockerCancelAwaitingOfBlockerRelease_WaitingContinuesExecution()
        {
            // ARRANGE
            using var animationBlockerService = new AnimationBlockerService();

            var blockerMock = new Mock <ICommandBlocker>();
            var blocker     = blockerMock.Object;

            animationBlockerService.AddBlocker(blocker);

            using var semaphore = new SemaphoreSlim(0, 1);

#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            Task.Run(async() =>
            {
                await Task.Delay(100).ConfigureAwait(false);
                await semaphore.WaitAsync().ConfigureAwait(false);
                animationBlockerService.DropBlockers();
            });
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed

            // ACT
            var serviceTask = Task.Run(async() =>
            {
                await animationBlockerService.WaitBlockersAsync().ConfigureAwait(false);
            });

            semaphore.Release();

            await serviceTask.ConfigureAwait(false);

            // ASSERT
            Assert.Pass();
        }
        public async Task WaitBlockersAsync_RepeatWithNoBlockers_NoWaitings(int iterationCount)
        {
            // ARRANGE
            using var animationBlockerService = new AnimationBlockerService();

            // ACT
            for (var iterationIndex = 0; iterationIndex < iterationCount; iterationIndex++)
            {
                await animationBlockerService.WaitBlockersAsync().ConfigureAwait(false);
            }

            // ASSERT
            Assert.Pass();
        }
        public async Task WaitBlockersAsynс_Real1()
        {
            using var animationBlockerService = new AnimationBlockerService();

            var blockerMock = new Mock <ICommandBlocker>();
            var blocker     = blockerMock.Object;

            animationBlockerService.AddBlocker(blocker);

            await WaitAndReleaseBlockerAsync(animationBlockerService, blockerMock).ConfigureAwait(false);

            var blockerMock2 = new Mock <ICommandBlocker>();
            var blocker2     = blockerMock2.Object;

            var blockerMock3 = new Mock <ICommandBlocker>();
            var blocker3     = blockerMock3.Object;

            animationBlockerService.AddBlocker(blocker2);
            animationBlockerService.AddBlocker(blocker3);

            var isBlockerReleasedCheck = false;

            using var semaphore = new SemaphoreSlim(0, 1);
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            Task.Run(async() =>
            {
                await Task.Delay(100).ConfigureAwait(false);
                await semaphore.WaitAsync().ConfigureAwait(false);
                isBlockerReleasedCheck              = true;
                blockerMock2.Raise(x => x.Released += null, EventArgs.Empty);
                blockerMock3.Raise(x => x.Released += null, EventArgs.Empty);
            });
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed

            var serviceTask = Task.Run(async() =>
            {
                await animationBlockerService.WaitBlockersAsync().ConfigureAwait(false);
            });

            isBlockerReleasedCheck.Should().BeFalse();

            semaphore.Release();

            await serviceTask.ConfigureAwait(false);

            // ASSERT
            isBlockerReleasedCheck.Should().BeTrue();
        }
        public async Task WaitBlockersAsync_AddAndReleaseBlockerBeforeWaiting_NoWaiting()
        {
            // ARRANGE
            using var animationBlockerService = new AnimationBlockerService();

            var blockerMock = new Mock <ICommandBlocker>();
            var blocker     = blockerMock.Object;

            animationBlockerService.AddBlocker(blocker);

            blockerMock.Raise(x => x.Released += null, EventArgs.Empty);

            // ACT
            await animationBlockerService.WaitBlockersAsync().ConfigureAwait(false);

            // ASSERT
            Assert.Pass();
        }
        public async Task WaitBlockersAsynс_SecondBlockerWasAddedUntilFirstWasReleased_WaitingContinuesExecution()
        {
            // ARRANGE
            using var animationBlockerService = new AnimationBlockerService();

            var blockerMock1 = new Mock <ICommandBlocker>();
            var blocker1     = blockerMock1.Object;

            var blockerMock2 = new Mock <ICommandBlocker>();
            var blocker2     = blockerMock2.Object;

            animationBlockerService.AddBlocker(blocker1);

            using var semaphore = new SemaphoreSlim(0, 1);

#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            Task.Run(async() =>
            {
                await Task.Delay(100).ConfigureAwait(false);
                await semaphore.WaitAsync().ConfigureAwait(false);

                animationBlockerService.AddBlocker(blocker2);

                blockerMock1.Raise(x => x.Released += null, EventArgs.Empty);

                await Task.Delay(100).ConfigureAwait(false);
                blockerMock2.Raise(x => x.Released += null, EventArgs.Empty);
            });
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed

            // ACT
            var serviceTask = Task.Run(async() =>
            {
                await animationBlockerService.WaitBlockersAsync().ConfigureAwait(false);
            });

            semaphore.Release();

            await serviceTask.ConfigureAwait(false);

            // ASSERT
            Assert.Pass();
        }
        public async Task WaitBlockersAsynс_BlockerReleasedAfterServiceStartsWaiting_WaitingContinuesExecution()
        {
            // ARRANGE
            using var animationBlockerService = new AnimationBlockerService();

            var blockerMock = new Mock <ICommandBlocker>();
            var blocker     = blockerMock.Object;

            var isBlockerReallyReleased = false;

            blocker.Released += (s, e) =>
            {
                isBlockerReallyReleased = true;
            };

            animationBlockerService.AddBlocker(blocker);

            using var semaphore = new SemaphoreSlim(0, 1);

#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            Task.Run(async() =>
            {
                await Task.Delay(100).ConfigureAwait(false);
                await semaphore.WaitAsync().ConfigureAwait(false);
                blockerMock.Raise(x => x.Released += null, EventArgs.Empty);
            });
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed

            // ACT
            var serviceTask = Task.Run(async() =>
            {
                await animationBlockerService.WaitBlockersAsync().ConfigureAwait(false);
            });

            isBlockerReallyReleased.Should().BeFalse();

            semaphore.Release();

            await serviceTask.ConfigureAwait(false);

            // ASSERT
            isBlockerReallyReleased.Should().BeTrue();
        }
        public async Task WaitBlockersAsynс_AddAndReleaseBlocker2Times_WaitingContinuesExecution()
        {
            // ARRANGE
            using var animationBlockerService = new AnimationBlockerService();

            var blockerMock = new Mock <ICommandBlocker>();
            var blocker     = blockerMock.Object;

            var blockerMock2 = new Mock <ICommandBlocker>();
            var blocker2     = blockerMock2.Object;

            animationBlockerService.AddBlocker(blocker);

            await WaitAndReleaseBlockerAsync(animationBlockerService, blockerMock).ConfigureAwait(false);

            animationBlockerService.AddBlocker(blocker2);
            await WaitAndReleaseBlockerAsync(animationBlockerService, blockerMock2).ConfigureAwait(false);

            // ASSERT
            Assert.Pass();
        }
        private static async Task WaitAndReleaseBlockerAsync(AnimationBlockerService animationBlockerService,
                                                             Mock <ICommandBlocker> blockerMock)
        {
            using var semaphore = new SemaphoreSlim(0, 1);
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            Task.Run(async() =>
            {
                await Task.Delay(100).ConfigureAwait(false);
                await semaphore.WaitAsync().ConfigureAwait(false);
                blockerMock.Raise(x => x.Released += null, EventArgs.Empty);
            });
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed

            var serviceTask = Task.Run(async() =>
            {
                await animationBlockerService.WaitBlockersAsync().ConfigureAwait(false);
            });

            semaphore.Release();

            await serviceTask.ConfigureAwait(false);
        }
        private static async Task AddAndDropBlockerAsync(AnimationBlockerService animationBlockerService,
                                                         SemaphoreSlim semaphore)
        {
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            Task.Run(async() =>
            {
                await Task.Delay(100).ConfigureAwait(false);
                await semaphore.WaitAsync().ConfigureAwait(false);
                animationBlockerService.DropBlockers();
            });
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed

            // ACT
            var serviceTask = Task.Run(async() =>
            {
                await animationBlockerService.WaitBlockersAsync().ConfigureAwait(false);
            });

            semaphore.Release();

            await serviceTask.ConfigureAwait(false);
        }