public void CountUnitsOfWorkAreReportedAccurately()
        {
            const int expectedUnitsOfWork = 1000;
            const int countThreads        = 5;

            var iterativeExerciserUnitsOfWorkExecuted = 0;
            var iterativeExerciserResult = IterativeExerciser.Create()
                                           .UsingThreads(countThreads)
                                           .PerformUnitOfWorkNTimes(expectedUnitsOfWork)
                                           .DoThisUnitOfWork(() => { Interlocked.Increment(ref iterativeExerciserUnitsOfWorkExecuted); })
                                           .ExecAll();

            var workQueueExerciserUnitsOfWorkExecuted = 0;
            var workQueueExerciserResult = WorkQueueExerciser.Create()
                                           .UsingThreads(countThreads)
                                           .WithWorkQueueSize(expectedUnitsOfWork)
                                           .DoThisUnitOfWork(() => { Interlocked.Increment(ref workQueueExerciserUnitsOfWorkExecuted); })
                                           .ExecAll();

            var throughputExerciserUnitsOfWorkExercised = 0;
            var throughputExerciserResult = ThroughputExerciser.Create()
                                            .UsingThreads(countThreads)
                                            .ForDuration(100)
                                            .DoThisUnitOfWork(() => { Interlocked.Increment(ref throughputExerciserUnitsOfWorkExercised); })
                                            .ExecAll();

            NrAssert.Multiple(
                () => Assert.AreEqual(expectedUnitsOfWork * countThreads, iterativeExerciserResult.CountUnitsOfWorkPerformed),
                () => Assert.AreEqual(expectedUnitsOfWork * countThreads, iterativeExerciserUnitsOfWorkExecuted),
                () => Assert.AreEqual(expectedUnitsOfWork, workQueueExerciserResult.CountUnitsOfWorkPerformed),
                () => Assert.AreEqual(expectedUnitsOfWork, workQueueExerciserUnitsOfWorkExecuted),
                () => Assert.AreEqual(throughputExerciserUnitsOfWorkExercised, throughputExerciserResult.CountUnitsOfWorkPerformed)
                );
        }
        public void ExerciserMethodsAreCalledCorrectNumberOfTimes()
        {
            const int expectedUnitsOfWork = 100;
            const int countThreads        = 5;

            ConcurrentBag <DateTime> setupTimes     = null;
            ConcurrentBag <DateTime> beforeUOWTimes = null;
            ConcurrentBag <DateTime> uowTimes       = null;
            ConcurrentBag <DateTime> afterUowTimes  = null;
            ConcurrentBag <DateTime> teardownTimes  = null;

            var iterativeExerciser = IterativeExerciser.Create()
                                     .UsingThreads(countThreads)
                                     .PerformUnitOfWorkNTimes(expectedUnitsOfWork)
                                     .DoThisToSetup(() => setupTimes.Add(DateTime.Now))
                                     .DoThisBeforeEachUnitOfWork(() => beforeUOWTimes.Add(DateTime.Now))
                                     .DoThisUnitOfWork(() => uowTimes.Add(DateTime.Now))
                                     .DoThisAfterEachUnitOfWork(() => afterUowTimes.Add(DateTime.Now))
                                     .DoThisToTearDown(() => teardownTimes.Add(DateTime.Now));

            var workQueueExerciser = WorkQueueExerciser.Create()
                                     .UsingThreads(countThreads)
                                     .WithWorkQueueSize(expectedUnitsOfWork)
                                     .DoThisToSetup(() => setupTimes.Add(DateTime.Now))
                                     .DoThisBeforeEachUnitOfWork(() => beforeUOWTimes.Add(DateTime.Now))
                                     .DoThisUnitOfWork(() => uowTimes.Add(DateTime.Now))
                                     .DoThisAfterEachUnitOfWork(() => afterUowTimes.Add(DateTime.Now))
                                     .DoThisToTearDown(() => teardownTimes.Add(DateTime.Now));

            var throughputExerciser = ThroughputExerciser.Create()
                                      .UsingThreads(countThreads)
                                      .ForDuration(100)
                                      .DoThisToSetup(() => setupTimes.Add(DateTime.Now))
                                      .DoThisBeforeEachUnitOfWork(() => beforeUOWTimes.Add(DateTime.Now))
                                      .DoThisUnitOfWork(() => uowTimes.Add(DateTime.Now))
                                      .DoThisAfterEachUnitOfWork(() => afterUowTimes.Add(DateTime.Now))
                                      .DoThisToTearDown(() => teardownTimes.Add(DateTime.Now));


            foreach (var exerciser in new Exerciser[] { iterativeExerciser, workQueueExerciser, throughputExerciser })
            {
                setupTimes     = new ConcurrentBag <DateTime>();
                beforeUOWTimes = new ConcurrentBag <DateTime>();
                uowTimes       = new ConcurrentBag <DateTime>();
                afterUowTimes  = new ConcurrentBag <DateTime>();
                teardownTimes  = new ConcurrentBag <DateTime>();

                var exerciserResult = exerciser.ExecAll();

                NrAssert.Multiple(
                    () => Assert.AreEqual(1, setupTimes.Count),
                    () => Assert.AreEqual(exerciserResult.CountUnitsOfWorkPerformed, beforeUOWTimes.Count, $"{exerciser.GetType().FullName}, beforeUOW"),
                    () => Assert.AreEqual(exerciserResult.CountUnitsOfWorkPerformed, uowTimes.Count, $"{exerciser.GetType().FullName}, the Actual UOW"),
                    () => Assert.AreEqual(exerciserResult.CountUnitsOfWorkPerformed, afterUowTimes.Count, $"{exerciser.GetType().FullName}, After UOW"),
                    () => Assert.AreEqual(1, teardownTimes.Count));
            }
        }
        public void UnitOfWorkVariablesAreBeingCapturedCorrectly()
        {
            const int expectedUnitsOfWork = 100;
            const int countThreads        = 5;

            //Update counters based on the global or local counter
            //later we will check to make sure that they are all set to 1
            ConcurrentDictionary <int, ConcurrentDictionary <int, int[]> > localCounters = null;
            ConcurrentDictionary <int, int[]> globalResultCounters = null;

            var iterativeExerciser = IterativeExerciser.Create()
                                     .UsingThreads(countThreads)
                                     .PerformUnitOfWorkNTimes(expectedUnitsOfWork)
                                     .DoThisBeforeEachUnitOfWork((threadId, uowIdLocal, uowIdGlobal) =>
            {
                Interlocked.Increment(ref localCounters[threadId].GetOrAdd(uowIdLocal, new int[3])[0]);
                Interlocked.Increment(ref globalResultCounters.GetOrAdd(uowIdGlobal, new int[3])[0]);
            })
                                     .DoThisUnitOfWork((threadId, uowIdLocal, uowIdGlobal) =>
            {
                Interlocked.Increment(ref localCounters[threadId].GetOrAdd(uowIdLocal, new int[3])[1]);
                Interlocked.Increment(ref globalResultCounters.GetOrAdd(uowIdGlobal, new int[3])[1]);
            })
                                     .DoThisAfterEachUnitOfWork((threadId, uowIdLocal, uowIdGlobal) =>
            {
                Interlocked.Increment(ref localCounters[threadId].GetOrAdd(uowIdLocal, new int[3])[2]);
                Interlocked.Increment(ref globalResultCounters.GetOrAdd(uowIdGlobal, new int[3])[2]);
            });

            var workQueueExerciser = WorkQueueExerciser.Create()
                                     .UsingThreads(countThreads)
                                     .WithWorkQueueSize(expectedUnitsOfWork)
                                     .DoThisBeforeEachUnitOfWork((threadId, uowIdLocal, uowIdGlobal) =>
            {
                Interlocked.Increment(ref localCounters[threadId].GetOrAdd(uowIdLocal, new int[3])[0]);
                Interlocked.Increment(ref globalResultCounters.GetOrAdd(uowIdGlobal, new int[3])[0]);
            })
                                     .DoThisUnitOfWork((threadId, uowIdLocal, uowIdGlobal) =>
            {
                Interlocked.Increment(ref localCounters[threadId].GetOrAdd(uowIdLocal, new int[3])[1]);
                Interlocked.Increment(ref globalResultCounters.GetOrAdd(uowIdGlobal, new int[3])[1]);
            })
                                     .DoThisAfterEachUnitOfWork((threadId, uowIdLocal, uowIdGlobal) =>
            {
                Interlocked.Increment(ref localCounters[threadId].GetOrAdd(uowIdLocal, new int[3])[2]);
                Interlocked.Increment(ref globalResultCounters.GetOrAdd(uowIdGlobal, new int[3])[2]);
            });

            var throughputExerciser = ThroughputExerciser.Create()
                                      .UsingThreads(countThreads)
                                      .ForDuration(100)
                                      .DoThisBeforeEachUnitOfWork((threadId, uowIdLocal, uowIdGlobal) =>
            {
                Interlocked.Increment(ref localCounters[threadId].GetOrAdd(uowIdLocal, new int[3])[0]);
                Interlocked.Increment(ref globalResultCounters.GetOrAdd(uowIdGlobal, new int[3])[0]);
            })
                                      .DoThisUnitOfWork((threadId, uowIdLocal, uowIdGlobal) =>
            {
                Interlocked.Increment(ref localCounters[threadId].GetOrAdd(uowIdLocal, new int[3])[1]);
                Interlocked.Increment(ref globalResultCounters.GetOrAdd(uowIdGlobal, new int[3])[1]);
            })
                                      .DoThisAfterEachUnitOfWork((threadId, uowIdLocal, uowIdGlobal) =>
            {
                Interlocked.Increment(ref localCounters[threadId].GetOrAdd(uowIdLocal, new int[3])[2]);
                Interlocked.Increment(ref globalResultCounters.GetOrAdd(uowIdGlobal, new int[3])[2]);
            });


            foreach (var exerciser in new Exerciser[] { iterativeExerciser, workQueueExerciser, throughputExerciser })
            {
                localCounters        = new ConcurrentDictionary <int, ConcurrentDictionary <int, int[]> >();
                globalResultCounters = new ConcurrentDictionary <int, int[]>();

                foreach (var thread in localCounters)
                {
                    foreach (var uow in thread.Value)
                    {
                        NrAssert.Multiple(
                            () => Assert.AreEqual(1, uow.Value[0], $"{exerciser.GetType().FullName}, threadID: {thread.Key}, uowIdLocal: {uow.Key}, BeforeUOW"),
                            () => Assert.AreEqual(1, uow.Value[1], $"{exerciser.GetType().FullName}, threadID: {thread.Key}, uowIdLocal: {uow.Key}, TheUOW"),
                            () => Assert.AreEqual(1, uow.Value[2], $"{exerciser.GetType().FullName}, threadID: {thread.Key}, uowIdLocal: {uow.Key}, AfterUOW"));
                    }
                }

                foreach (var uow in globalResultCounters)
                {
                    NrAssert.Multiple(
                        () => Assert.AreEqual(1, uow.Value[0], $"{exerciser.GetType().FullName}, uowIdGlobal {uow.Key}, BeforeUOW"),
                        () => Assert.AreEqual(1, uow.Value[1], $"{exerciser.GetType().FullName}, uowIdGlobal {uow.Key}, TheUOW"),
                        () => Assert.AreEqual(1, uow.Value[2], $"{exerciser.GetType().FullName}, uowIdGlobal {uow.Key}, AfterUOW"));
                }
            }
        }
        public void ExerciserMethodsAreCalledInCorrectOrder()
        {
            const int expectedUnitsOfWork = 10;
            const int countThreads        = 5;

            ConcurrentBag <DateTime> setupTimes     = null;
            ConcurrentBag <DateTime> beforeUOWTimes = null;
            ConcurrentBag <DateTime> uowTimes       = null;
            ConcurrentBag <DateTime> afterUowTimes  = null;
            ConcurrentBag <DateTime> teardownTimes  = null;

            var iterativeExerciser = IterativeExerciser.Create()
                                     .UsingThreads(countThreads)
                                     .PerformUnitOfWorkNTimes(expectedUnitsOfWork)
                                     .DoThisToSetup(() =>
            {
                setupTimes.Add(DateTime.Now);
                Thread.Sleep(50);
            })
                                     .DoThisBeforeEachUnitOfWork(() =>
            {
                beforeUOWTimes.Add(DateTime.Now);
                Thread.Sleep(10);
            })
                                     .DoThisUnitOfWork(() => uowTimes.Add(DateTime.Now))
                                     .DoThisAfterEachUnitOfWork(() =>
            {
                Thread.Sleep(10);
                afterUowTimes.Add(DateTime.Now);
            })
                                     .DoThisToTearDown(() =>
            {
                Thread.Sleep(50);
                teardownTimes.Add(DateTime.Now);
            });

            var workQueueExerciser = WorkQueueExerciser.Create()
                                     .UsingThreads(countThreads)
                                     .WithWorkQueueSize(expectedUnitsOfWork)
                                     .DoThisToSetup(() =>
            {
                setupTimes.Add(DateTime.Now);
                Thread.Sleep(50);
            })
                                     .DoThisBeforeEachUnitOfWork(() =>
            {
                beforeUOWTimes.Add(DateTime.Now);
                Thread.Sleep(10);
            })
                                     .DoThisUnitOfWork(() => uowTimes.Add(DateTime.Now))
                                     .DoThisAfterEachUnitOfWork(() =>
            {
                Thread.Sleep(10);
                afterUowTimes.Add(DateTime.Now);
            })
                                     .DoThisToTearDown(() =>
            {
                Thread.Sleep(50);
                teardownTimes.Add(DateTime.Now);
            });

            var throughputExerciser = ThroughputExerciser.Create()
                                      .UsingThreads(countThreads)
                                      .ForDuration(100)
                                      .DoThisToSetup(() =>
            {
                setupTimes.Add(DateTime.Now);
                Thread.Sleep(50);
            })
                                      .DoThisBeforeEachUnitOfWork(() =>
            {
                beforeUOWTimes.Add(DateTime.Now);
                Thread.Sleep(10);
            })
                                      .DoThisUnitOfWork(() => uowTimes.Add(DateTime.Now))
                                      .DoThisAfterEachUnitOfWork(() =>
            {
                Thread.Sleep(10);
                afterUowTimes.Add(DateTime.Now);
            })
                                      .DoThisToTearDown(() =>
            {
                Thread.Sleep(50);
                teardownTimes.Add(DateTime.Now);
            });

            foreach (var exerciser in new Exerciser[] { iterativeExerciser, workQueueExerciser, throughputExerciser })
            {
                setupTimes     = new ConcurrentBag <DateTime>();
                beforeUOWTimes = new ConcurrentBag <DateTime>();
                uowTimes       = new ConcurrentBag <DateTime>();
                afterUowTimes  = new ConcurrentBag <DateTime>();
                teardownTimes  = new ConcurrentBag <DateTime>();
                var exerciserResult = exerciser.ExecAll();

                NrAssert.Multiple(
                    () => Assert.Greater(beforeUOWTimes.Min(), setupTimes.Max(), $"{exerciser.GetType().FullName} BeforeUOW detected before end of Setup"),
                    () => Assert.Greater(uowTimes.Min(), beforeUOWTimes.Min(), $"{exerciser.GetType().FullName} UOW detected before end of BeforeUOW"),
                    () => Assert.Greater(afterUowTimes.Max(), uowTimes.Max(), $"{exerciser.GetType().FullName} AfterUOW detected before end of UOW"),
                    () => Assert.Greater(teardownTimes.Min(), afterUowTimes.Max(), $"{exerciser.GetType().FullName} Terdown detected before end of AfterUOW"));
            }
        }