/// <summary> /// Adds the time-till-completion estimates of a simulation to the collection. /// </summary> /// <param name="roadmapEstimate">The estimate for the whole roadmap.</param> /// <param name="projectEstimates">The estimates for each of the individual projects.</param> /// <exception cref="ArgumentException">Thrown when <paramref name="projectEstimates"/> is not consistent with expected projects.</exception> /// <exception cref="ArgumentNullException">Thrown when <paramref name="roadmapEstimate"/> or <paramref name="projectEstimates"/> is null.</exception> public void AddEstimationsForSimulation(WorkEstimate roadmapEstimate, IReadOnlyList <WorkEstimate> projectEstimates) { if (roadmapEstimate is null) { throw new ArgumentNullException(nameof(roadmapEstimate)); } if (projectEstimates is null) { throw new ArgumentNullException(nameof(projectEstimates)); } if (roadmapEstimatesOfEachSimulation.Capacity == roadmapEstimatesOfEachSimulation.Count) { throw new InvalidOperationException("Adding these estimation would exceed the expected number of simulations."); } ValidateThatNumberOfEstimatesMatchExpectedCount(projectEstimates); roadmapEstimatesOfEachSimulation.Add(roadmapEstimate); for (int i = 0; i < projectEstimates.Count; i++) { var currentProjectEstimatesForProject = projectEstimatesOfEachSimulation[i]; var newProjectEstimateForThisSimulation = projectEstimates[i]; ValidateThatProjectIdentifiersAreConsistent(currentProjectEstimatesForProject, newProjectEstimateForThisSimulation); currentProjectEstimatesForProject.Add(newProjectEstimateForThisSimulation); } }
private static object[] GetRowData( TimeTillCompletionEstimationsCollection estimations, int simulationIndex, ConfigurationMode mode, CultureInfo culture) { WorkEstimate roadmapEstimate = estimations.GetRoadmapEstimationForSimulation(simulationIndex); var rowData = new object[] { FormatEstimatedNumberOfWorkingDays(roadmapEstimate, culture), roadmapEstimate.IsIndeterminate }; if (mode == ConfigurationMode.Advanced) { var additionalRowData = new object[estimations.NumberOfProjectsInRoadmap * 2]; for (int projectIndex = 0; projectIndex < estimations.NumberOfProjectsInRoadmap; projectIndex++) { WorkEstimate projectEstimate = estimations.GetProjectEstimationForSimulation(projectIndex, simulationIndex); additionalRowData[projectIndex * 2] = FormatEstimatedNumberOfWorkingDays(projectEstimate, culture); additionalRowData[projectIndex * 2 + 1] = projectEstimate.IsIndeterminate; } rowData = rowData.Concat(additionalRowData).ToArray(); } return(rowData); }
public void GIVEN_multiple_input_metrics_AND_one_project_WHEN_estimating_time_to_completion_THEN_returned_number_of_work_days_depends_on_randomly_selected_metrics( IReadOnlyCollection <ThroughputPerDay> throughputs, Project project, Queue <int> selectedIndices, double expectedNumberOfDaysRequired) { var inputMetrics = ToInputMetrics(throughputs); var roadmap = new Roadmap(new[] { project }); randomNumberGeneratorMock .Setup(rng => rng.GetRandomIndex(throughputs.Count)) .Returns(selectedIndices.Dequeue); var estimator = new TimeTillCompletionEstimator(inputMetrics, randomNumberGeneratorMock.Object, someMaximumNumberOfIterations); var estimations = estimator.Estimate(roadmap); Assert.Equal(2, estimations.Count); WorkEstimate roadmapWorkEstimate = estimations[0]; Assert.Equal("Roadmap", roadmapWorkEstimate.Identifier); AssertExpectedNumberOfWorkingDaysIsEqual(expectedNumberOfDaysRequired, roadmapWorkEstimate); AssertEstimateIsDeterminate(roadmapWorkEstimate); WorkEstimate projectWorkEstimate = estimations[1]; Assert.Equal("Project", projectWorkEstimate.Identifier); AssertExpectedNumberOfWorkingDaysIsEqual(expectedNumberOfDaysRequired, projectWorkEstimate); AssertEstimateIsDeterminate(projectWorkEstimate); }
private static bool IsProjectIdentifierConsistentWithPriorProjectSimulations( WorkEstimate newProjectEstimate, IReadOnlyList <WorkEstimate> currentProjectEstimatesForProject) { return(currentProjectEstimatesForProject.Count == 0 || newProjectEstimate.Identifier == currentProjectEstimatesForProject[0].Identifier); }
public void GIVEN_one_input_metric_AND_one_project_WHEN_estimating_time_to_completion_THEN_return_number_of_work_days( ThroughputPerDay throughput, Project project, double expectedNumberOfDaysRequired) { var inputMetrics = ToInputMetrics(new[] { throughput }); var roadmap = new Roadmap(new[] { project }); var estimator = new TimeTillCompletionEstimator(inputMetrics, randomNumberGeneratorMock.Object, someMaximumNumberOfIterations); var estimations = estimator.Estimate(roadmap); Assert.Equal(2, estimations.Count); WorkEstimate roadmapWorkEstimate = estimations[0]; Assert.Equal("Roadmap", roadmapWorkEstimate.Identifier); AssertExpectedNumberOfWorkingDaysIsEqual(expectedNumberOfDaysRequired, roadmapWorkEstimate); AssertEstimateIsDeterminate(roadmapWorkEstimate); WorkEstimate projectWorkEstimate = estimations[1]; Assert.Equal("Project", projectWorkEstimate.Identifier); AssertExpectedNumberOfWorkingDaysIsEqual(expectedNumberOfDaysRequired, roadmapWorkEstimate); AssertEstimateIsDeterminate(roadmapWorkEstimate); }
private static void ValidateThatProjectIdentifiersAreConsistent( IReadOnlyList <WorkEstimate> currentProjectEstimatesForProject, WorkEstimate newProjectEstimateForThisSimulation) { if (!IsProjectIdentifierConsistentWithPriorProjectSimulations(newProjectEstimateForThisSimulation, currentProjectEstimatesForProject)) { throw new ArgumentException("A project estimate's identifier mismatches a the expected project's identifier.", "projectEstimates"); } }
public void GIVEN_estimations_collection_for_1_simulation_WHEN_adding_2nd_simulation_results_THEN_throw_InvalidOperationException() { var estimates = new TimeTillCompletionEstimationsCollection(1, 1); var roadmap = CreateCompletedRoadmap(); var roadmapEstimate = new WorkEstimate(roadmap, 1.0); var projectEstimates = roadmap.Projects.Select(p => new WorkEstimate(p, 1.0)).ToArray(); estimates.AddEstimationsForSimulation(roadmapEstimate, projectEstimates); void SecondCall() => estimates.AddEstimationsForSimulation(roadmapEstimate, projectEstimates); var actualException = Assert.Throws <InvalidOperationException>(SecondCall); Assert.Equal("Adding these estimation would exceed the expected number of simulations.", actualException.Message); }
private static IReadOnlyList <WorkEstimate> CreateWorkEstimatesResult( Roadmap roadmap, double numberOfDaysToFinishRoadmap, IReadOnlyDictionary <Project, double> estimatedNumberOfWorkingDaysRequiredToFinishWorkPerProject) { var workEstimates = new WorkEstimate[estimatedNumberOfWorkingDaysRequiredToFinishWorkPerProject.Count + 1]; workEstimates[0] = new WorkEstimate(roadmap, numberOfDaysToFinishRoadmap); var index = 1; foreach (KeyValuePair <Project, double> keyValuePair in estimatedNumberOfWorkingDaysRequiredToFinishWorkPerProject) { workEstimates[index++] = new WorkEstimate(keyValuePair.Key, keyValuePair.Value); } return(workEstimates); }
public void GIVEN_some_simulation_results_added_WHEN_getting_estimates_for_invalid_project_number_THEN_throw_ArgumentOutOfRangeException( int invalidProjectIndex) { var estimates = new TimeTillCompletionEstimationsCollection(2, 1); var roadmap = CreateCompletedRoadmap(); var roadmapEstimate = new WorkEstimate(roadmap, 1.0); var projectEstimates = roadmap.Projects.Select(p => new WorkEstimate(p, 1.0)).ToArray(); estimates.AddEstimationsForSimulation(roadmapEstimate, projectEstimates); estimates.AddEstimationsForSimulation(roadmapEstimate, projectEstimates); object Call() => estimates[invalidProjectIndex]; var actualException = Assert.Throws <ArgumentOutOfRangeException>("projectIndex", Call); Assert.StartsWith("Project index must be in range [0, 0].", actualException.Message); }
public void GIVEN_estimations_collection_with_1_simulation_WHEN_adding_different_project_estimations_THEN_throw_ArgumentException() { var estimates = new TimeTillCompletionEstimationsCollection(2, 1); var roadmap = CreateCompletedRoadmap(); var roadmapEstimate = new WorkEstimate(roadmap, 1.0); var projectEstimates = roadmap.Projects.Select(p => new WorkEstimate(p, 1.0)).ToArray(); estimates.AddEstimationsForSimulation(roadmapEstimate, projectEstimates); var differentProjectEstimates = new[] { new WorkEstimate(CreateCompletedProject("someUniqueName"), 1.0) }; void SecondCall() => estimates.AddEstimationsForSimulation(roadmapEstimate, differentProjectEstimates); var actualException = Assert.Throws <ArgumentException>("projectEstimates", SecondCall); Assert.StartsWith("A project estimate's identifier mismatches a the expected project's identifier.", actualException.Message); }
public void GIVEN_all_input_metrics_with_zero_throughput_AND_some_project_WHEN_estimating_time_to_completion_THEN_return_indeterminate_with_lower_bound_estimate() { var inputMetrics = ToInputMetrics(new[] { ToThroughput(0) }); var estimator = new TimeTillCompletionEstimator(inputMetrics, randomNumberGeneratorMock.Object, someMaximumNumberOfIterations); var estimations = estimator.Estimate(someRoadmap); Assert.Equal(2, estimations.Count); WorkEstimate roadmapWorkEstimate = estimations[0]; Assert.Equal("Roadmap", roadmapWorkEstimate.Identifier); AssertExpectedNumberOfWorkingDaysIsEqual(someMaximumNumberOfIterations, roadmapWorkEstimate); AssertExpectedNumberOfWorkingDaysIsIndeterminate(roadmapWorkEstimate); WorkEstimate projectWorkEstimate = estimations[1]; Assert.Equal("Project", projectWorkEstimate.Identifier); AssertExpectedNumberOfWorkingDaysIsEqual(someMaximumNumberOfIterations, projectWorkEstimate); AssertExpectedNumberOfWorkingDaysIsIndeterminate(projectWorkEstimate); }
public void GIVEN_multiple_input_metrics_AND_one_project_AND_truely_random_number_generator_WHEN_estimating_time_to_completion_THEN_return_number_of_work_days_in_range( IReadOnlyCollection <ThroughputPerDay> throughputs, Project project, double lowerBoundExpectedNumberOfDaysRequired, double upperBoundExpectedNumberOfDaysRequired) { var inputMetrics = ToInputMetrics(throughputs); var roadmap = new Roadmap(new[] { project }); var estimator = new TimeTillCompletionEstimator(inputMetrics, realRandomNumberGenerator, someMaximumNumberOfIterations); var estimations = estimator.Estimate(roadmap); Assert.Equal(2, estimations.Count); WorkEstimate roadmapWorkEstimate = estimations[0]; Assert.Equal("Roadmap", roadmapWorkEstimate.Identifier); Assert.InRange(roadmapWorkEstimate.EstimatedNumberOfWorkingDaysRequired, lowerBoundExpectedNumberOfDaysRequired, upperBoundExpectedNumberOfDaysRequired); AssertEstimateIsDeterminate(roadmapWorkEstimate); WorkEstimate projectWorkEstimate = estimations[1]; Assert.Equal("Project", projectWorkEstimate.Identifier); Assert.InRange(projectWorkEstimate.EstimatedNumberOfWorkingDaysRequired, lowerBoundExpectedNumberOfDaysRequired, upperBoundExpectedNumberOfDaysRequired); AssertEstimateIsDeterminate(projectWorkEstimate); }
private static string FormatEstimatedNumberOfWorkingDays( WorkEstimate estimate, CultureInfo culture) => estimate.EstimatedNumberOfWorkingDaysRequired.ToString(culture);