public void AllItemsShouldBeProcessedBeforePipeLineCompletion(int testActionDurationMs, int numberOfProducers, int maximumPipelineCapacity)
        {
            var pipeLine = new ProducerConsumerPipeLine <Action>(_consumerMock.Object, maximumPipelineCapacity);

            // Dummy test action.
            Action testAction = () =>
            {
                Thread.Sleep(testActionDurationMs);
            };

            List <Task> producerTasks = new List <Task>();

            for (int producerCounter = 0; producerCounter < numberOfProducers; producerCounter++)
            {
                var producer = Task.Run(async() => await pipeLine.EnqueueAsync(testAction));

                producerTasks.Add(producer);
            }

            Task.WaitAll(producerTasks.ToArray());
            pipeLine.CompleteProducing();
            pipeLine.PipeLineCompletion.Wait();

            _consumerMock.Verify(consumerMock => consumerMock.Consume(It.IsAny <Action>()), Times.Exactly(numberOfProducers));
        }
        public void ItemsAreProcessedInCorrectOrder(int testActionDurationMs, int numberOfProducers, int maximumPipelineCapacity)
        {
            var pipeLine = new ProducerConsumerPipeLine <Action>(_consumerMock.Object, maximumPipelineCapacity);

            object pushOrderLocker = new object();

            BlockingCollection <int> testActionsPushOrder       = new BlockingCollection <int>();
            BlockingCollection <int> testActionsInvocationOrder = new BlockingCollection <int>();

            List <Task> producerTasks = new List <Task>();

            for (int producerCounter = 0; producerCounter < numberOfProducers; producerCounter++)
            {
                var    testActionId = producerCounter;
                Action testAction   = () =>
                {
                    testActionsInvocationOrder.Add(testActionId);
                    Thread.Sleep(testActionDurationMs);
                };

                var producer = Task.Run(() =>
                {
                    lock (pushOrderLocker)
                    {
                        pipeLine.EnqueueAsync(testAction).Wait();
                        testActionsPushOrder.Add(testActionId);
                    }
                });

                producerTasks.Add(producer);
            }

            Task.WaitAll(producerTasks.ToArray());
            pipeLine.CompleteProducing();
            pipeLine.PipeLineCompletion.Wait();

            _consumerMock.Verify(consumerMock => consumerMock.Consume(It.IsAny <Action>()), Times.Exactly(numberOfProducers));
            CollectionAssert.AreEqual(testActionsPushOrder, testActionsInvocationOrder);
        }
        public void OneItemAtATimeIsProcessedByTheConsumer(int testActionDurationMs, int numberOfProducers, int maximumPipelineCapacity)
        {
            int sharedCounter = 0;
            int sharedCounterMismatchNumber = 0;

            var pipeLine = new ProducerConsumerPipeLine <Action>(_consumerMock.Object, maximumPipelineCapacity);

            // Increments the shared counter to 1 before performing a long-running task and decrements immediately after.
            Action testAction = () =>
            {
                // No interlocking is needed when comparing a shared value to a constant.
                if (!Equals(sharedCounter, 0))
                {
                    Interlocked.Increment(ref sharedCounterMismatchNumber);
                }

                Interlocked.Increment(ref sharedCounter);
                Thread.Sleep(testActionDurationMs);
                Interlocked.Decrement(ref sharedCounter);
            };

            List <Task> producerTasks = new List <Task>();

            for (int producerCounter = 0; producerCounter < numberOfProducers; producerCounter++)
            {
                var producer = Task.Run(async() => await pipeLine.EnqueueAsync(testAction));

                producerTasks.Add(producer);
            }

            Task.WaitAll(producerTasks.ToArray());

            pipeLine.CompleteProducing();
            pipeLine.PipeLineCompletion.Wait();

            // If the actions were invoked in a one-at-a-time fashion, there must be no mismathes.
            Assert.AreEqual(sharedCounterMismatchNumber, 0);
        }