public async Task KeepingItCleanWithParallelTasks()
        {
            const int    expectedResult      = 42;
            const string expectedCleanResult = "Forty Two";

            var mock = Substitute.For <IControlCandidateTask <int, string> >();

            mock.Control().Returns(expectedResult);
            mock.Candidate().Returns(0);
            mock.Clean(expectedResult).Returns(expectedCleanResult);

            var result = await Scientist.ScienceAsync <int, string>(nameof(KeepingItCleanWithParallelTasks), experiment =>
            {
                experiment.Use(mock.Control);
                experiment.Try(mock.Candidate);
                experiment.Clean(mock.Clean);
            });

            Assert.Equal(expectedResult, result);

            // Make sure that the observations aren't cleaned unless called explicitly.
            mock.DidNotReceive().Clean(expectedResult);

            Assert.Equal(
                expectedCleanResult,
                TestHelper.Results <int, string>(nameof(KeepingItCleanWithParallelTasks)).First().Control.CleanedValue);

            mock.Received().Clean(expectedResult);
        }
Example #2
0
        public async Task <string[]> GetValues(Guid correlationId)
        {
            var result = await Scientist.ScienceAsync <string[]>("upstream-api", experiment =>
            {
                experiment.Use(async() =>
                {
                    using (_metrics.Measure.Timer.Time(_apiToUseTimerOptions))
                    {
                        return(await _apiToUseClient.GetValues(correlationId));
                    }
                });

                experiment.Try(async() =>
                {
                    using (_metrics.Measure.Timer.Time(_apiToTryTimerOptions))
                    {
                        return(await _apiToTestClient.GetValues(correlationId));
                    }
                });

                experiment.Compare((a, b) =>
                {
                    _logger.LogInformation("{@CorrelationId} Sets: {@a} - {@b}", correlationId, a, b);

                    return(a.SequenceEqual(b));
                });
            });

            return(result);
        }
Example #3
0
        public async Task OnPost()
        {
            HasResult = true;
            Position  = FibonacciInput.Position;

            Result = await Scientist.ScienceAsync <int>("fibonacci-implementation", experiment =>
            {
                experiment.Use(async() => await _recursiveFibonacciCalculator.CalculateAsync(Position));
                experiment.Try(async() => await _linearFibonacciCalculator.CalculateAsync(Position));

                experiment.AddContext("Position", Position);
            });

            LastResults    = _experimentResultsGetter.LastResults;
            OverallResults = _experimentResultsGetter.OverallResults;
        }
        public async Task RunsBothBranchesOfTheExperimentAndThrowsCorrectInnerException()
        {
            var mock               = Substitute.For <IControlCandidate <Task <int> > >();
            var controlException   = new InvalidOperationException(null, new Exception());
            var candidateException = new InvalidOperationException(null, new Exception());

            mock.Control().Returns <Task <int> >(x => { throw controlException; });
            mock.Candidate().Returns <Task <int> >(x => { throw controlException; });
            const string experimentName = nameof(RunsBothBranchesOfTheExperimentAndThrowsCorrectInnerException);

            await Assert.ThrowsAsync <InvalidOperationException>(async() =>
            {
                await Scientist.ScienceAsync <int>(experimentName, experiment =>
                {
                    experiment.Use(mock.Control);
                    experiment.Try("candidate", mock.Candidate);
                });
            });
        }
Example #5
0
        public async Task RunsBothBranchesOfTheExperimentAsyncAndReportsFailure()
        {
            bool candidateRan = false;
            bool controlRan   = false;

            // We introduce side effects for testing. Don't do this in real life please.
            Func <Task <int> > control   = () => { controlRan = true; return(Task.FromResult(42)); };
            Func <Task <int> > candidate = () => { candidateRan = true; return(Task.FromResult(43)); };

            var result = await Scientist.ScienceAsync <int>("failure", experiment =>
            {
                experiment.Use(control);
                experiment.Try(candidate);
            });

            Assert.Equal(42, result);
            Assert.True(candidateRan);
            Assert.True(controlRan);
            Assert.False(TestHelper.Observation.First(m => m.Name == "failure").Success);
        }
        public async Task RunsBothBranchesOfTheExperimentAsyncAndReportsFailure()
        {
            var mock = Substitute.For <IControlCandidateTask <int> >();

            mock.Control().Returns(Task.FromResult(42));
            mock.Candidate().Returns(Task.FromResult(43));
            const string experimentName = nameof(RunsBothBranchesOfTheExperimentAsyncAndReportsFailure);

            var result = await Scientist.ScienceAsync <int>(experimentName, experiment =>
            {
                experiment.Use(mock.Control);
                experiment.Try("candidate", mock.Candidate);
            });

            Assert.Equal(42, result);
            await mock.Received().Control();

            await mock.Received().Candidate();

            Assert.False(TestHelper.Results <int>(experimentName).First().Matched);
        }
Example #7
0
        public async Task RunsBothBranchesOfTheExperimentAsyncAndReportsFailure()
        {
            var mock = Substitute.For <IControlCandidateTask <int> >();

            mock.Control().Returns(Task.FromResult(42));
            mock.Candidate().Returns(Task.FromResult(43));



            var result = await Scientist.ScienceAsync <int>("failure", experiment =>
            {
                experiment.Use(mock.Control);
                experiment.Try(mock.Candidate);
            });

            Assert.Equal(42, result);
            await mock.Received().Control();

            await mock.Received().Candidate();

            Assert.False(TestHelper.Observation.First(m => m.Name == "failure").Success);
        }
        public void ThrowsArgumentExceptionWhenConcurrentTasksInvalid()
        {
            var mock = Substitute.For <IControlCandidateTask <int> >();

            mock.Control().Returns(x => 1);
            mock.Candidate().Returns(x => 2);
            const string experimentName = nameof(ThrowsArgumentExceptionWhenConcurrentTasksInvalid);

            var ex = Assert.Throws <ArgumentException>(() =>
            {
                Scientist.ScienceAsync <int>(experimentName, 0, experiment =>
                {
                    experiment.Use(mock.Control);
                    experiment.Try(mock.Candidate);
                });
            });

            Exception baseException = ex.GetBaseException();

            Assert.IsType <ArgumentException>(baseException);
            mock.DidNotReceive().Control();
            mock.DidNotReceive().Candidate();
        }
        public async Task RunsTasksConcurrently(int concurrentTasks)
        {
            // Control + 3 experiments
            var totalTasks = 1 + 3;

            // Expected number of batches
            var expectedBatches = Math.Ceiling(1D * totalTasks / concurrentTasks);

            // Use CountdownEvents to ensure tasks don't finish before all tasks in that batch have started
            var startedSignal  = new CountdownEvent(concurrentTasks);
            var finishedSignal = new CountdownEvent(concurrentTasks);

            // Batch counter
            int batch = 1;

            // Our test task
            var task = new Func <Task <KeyValuePair <int, int> > >(() =>
            {
                return(Task.Run(() =>
                {
                    // Signal that we have started
                    var last = startedSignal.Signal();

                    var myBatch = batch;

                    // Wait till all tasks for this batch have started
                    startedSignal.Wait();

                    // Signal we have finished
                    finishedSignal.Signal();

                    // Last task to start needs to reset the events
                    if (last)
                    {
                        // Wait for all tasks in the batch to have finished
                        finishedSignal.Wait();

                        // Reset the countdown events
                        startedSignal.Reset();
                        finishedSignal.Reset();
                        batch++;
                    }

                    // Return threadId
                    return new KeyValuePair <int, int>(myBatch, Thread.CurrentThread.ManagedThreadId);
                }));
            });

            // Run the experiment
            string experimentName = nameof(RunsTasksConcurrently) + concurrentTasks;
            await Scientist.ScienceAsync <KeyValuePair <int, int> >(experimentName, concurrentTasks, experiment =>
            {
                // Add our control and experiments
                experiment.Use(task);
                for (int idx = 2; idx <= totalTasks; idx++)
                {
                    experiment.Try($"experiment{idx}", task);
                }
            });

            // Get the test result
            var result = TestHelper.Results <KeyValuePair <int, int> >(experimentName).First();

            // Consolidate the returned values from the tasks
            var results = result.Observations.Select(x => x.Value);

            // Assert correct number of batches
            Assert.Equal(expectedBatches, results.Select(x => x.Key).Distinct().Count());

            // Now check each batch
            for (int batchNo = 1; batchNo <= expectedBatches; batchNo++)
            {
                // Get the threadIds used by each task in the batch
                var batchThreadIds = results.Where(x => x.Key == batchNo).Select(x => x.Value);

                // Assert expected number of concurrent tasks in batch
                Assert.Equal(concurrentTasks, batchThreadIds.Count());

                // Assert unique threadIds in batch
                Assert.Equal(batchThreadIds.Count(), batchThreadIds.Distinct().Count());
            }
        }