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 TestDoSomethingBad()
        {
            var badIdea = new Dictionary <int, Guid>();

            Guid DoSomethingWrong(int key, Guid val)
            {
                if (!badIdea.ContainsKey(key))
                {
                    Thread.Sleep(100);      //sleep for 100ms
                    badIdea.Add(key, val);
                    return(val);
                }

                return(badIdea[key]);
            }

            //This causes a contention problem
            var exerciser = IterativeExerciser.Create()
                            .UsingThreads(10)
                            .PerformUnitOfWorkNTimes(50)
                            .DoThisUnitOfWork((threadId, unitOfWorkIdLocal, unitOfWorkIdGlobal) =>
            {
                DoSomethingWrong(unitOfWorkIdLocal, Guid.NewGuid());
            });

            Assert.Throws <ExerciserException>(() => exerciser.ExecAll());
        }
        public void ContainsDetailedInnerExceptionForUnitOfWorkFailures()
        {
            var exerciser = IterativeExerciser.Create()
                            .UsingThreads(5)
                            .PerformUnitOfWorkNTimes(1000)
                            .DoThisUnitOfWork((threadId, uowIdLocal, uowIdGlobal) =>
            {
                if (threadId == 3 && uowIdGlobal > 50)
                {
                    throw new Exception();
                }
            });

            ExerciserException caughtException = null;

            try
            {
                exerciser.ExecAll();
            }
            catch (ExerciserException ex)
            {
                caughtException = ex;
            }

            NrAssert.Multiple(
                () => Assert.IsNotNull(caughtException, "Expected an exception to be thrown and it was not"),
                () => Assert.AreEqual(1, caughtException.EncounteredExceptions.Count()),
                () => Assert.IsInstanceOf <ExerciserUnitOfWorkException>(caughtException.EncounteredExceptions.First(), "Exception is not of correct type"),
                () => Assert.Less(50, (caughtException.EncounteredExceptions.First() as ExerciserUnitOfWorkException).UnitOfWorkIdGlobal),
                () => Assert.AreEqual(3, (caughtException.EncounteredExceptions.First() as ExerciserUnitOfWorkException).ThreadId)
                );
        }
        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 Example_ExerciserAlone()
        {
            //Exerciser by itself
            var iterativeExerciser = IterativeExerciser.Create()
                                     .DoThisUnitOfWork(() => { var newGuid = GuidGenerator.GenerateNewRelicGuid(); })
                                     .PerformUnitOfWorkNTimes(1000)
                                     .UsingThreads(10);

            var iterativeExerciserResults = iterativeExerciser.ExecAll();

            //10 threads each running 1K units of work can produce 10K results
            Assert.AreEqual(10000, iterativeExerciserResults.CountUnitsOfWorkPerformed);
        }
        public void ThrowsExceptionIfAnyUnitsOfWorkFail()
        {
            var exerciser = IterativeExerciser.Create()
                            .UsingThreads(5)
                            .PerformUnitOfWorkNTimes(1000)
                            .DoThisUnitOfWork((threadId, uowIdLocal, uowIdGlobal) =>
            {
                if (threadId == 3 && uowIdGlobal > 50)
                {
                    throw new Exception();
                }
            });

            Assert.Throws <ExerciserException>(() => { exerciser.ExecAll(); });
        }
        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"));
            }
        }