/// <summary> /// Executes the benchmark scenario specified by the parameter /// containing the process start information.<br/> /// The process component will wait, for the benchmark scenario to exit, /// the time specified on the configuration argument.<br/> /// If the benchmark scenario has not exited, then it will immediately /// stop the associated process, and a TimeoutException will be thrown. /// </summary> /// <param name="processStartInfo">The ProcessStartInfo that contains the information that is used to start the benchmark scenario process.</param> /// <param name="preIterationDelegate">The action that will be executed before every benchmark scenario execution.</param> /// <param name="postIterationDelegate">The action that will be executed after every benchmark scenario execution.</param> /// <param name="teardownDelegate">The action that will be executed after running all benchmark scenario iterations.</param> /// <param name="scenarioConfiguration">ScenarioConfiguration object that defined the scenario execution.</param> public void RunScenario( ProcessStartInfo processStartInfo, Action preIterationDelegate, Action postIterationDelegate, Func <ScenarioBenchmark> teardownDelegate, ScenarioConfiguration scenarioConfiguration) { if (processStartInfo == null) { throw new ArgumentNullException($"{nameof(processStartInfo)} cannot be null."); } if (teardownDelegate == null) { throw new ArgumentNullException($"{nameof(teardownDelegate)} cannot be null."); } if (scenarioConfiguration == null) { throw new ArgumentNullException($"{nameof(scenarioConfiguration)} cannot be null."); } // Make a copy of the user input to avoid potential bugs due to changes behind the scenes. var configuration = new ScenarioConfiguration(scenarioConfiguration); for (int i = 0; i < configuration.Iterations; ++i) { preIterationDelegate?.Invoke(); // TODO: Start scenario profiling. using (var process = new Process()) { process.StartInfo = processStartInfo; process.Start(); if (process.WaitForExit((int)(configuration.TimeoutPerIteration.TotalMilliseconds)) == false) { process.Kill(); throw new TimeoutException("Running benchmark scenario has timed out."); } // Check for the exit code. if (!configuration.SuccessExitCodes.Contains(process.ExitCode)) { throw new Exception($"'{processStartInfo.FileName}' exited with an invalid exit code: {process.ExitCode}"); } } // TODO: Stop scenario profiling. postIterationDelegate?.Invoke(); } ScenarioBenchmark scenarioBenchmark = teardownDelegate(); if (scenarioBenchmark == null) { throw new InvalidOperationException("The Teardown Delegate should return a valid instance of ScenarioBenchmark."); } scenarioBenchmark.Serialize(Configuration.RunId + "-" + scenarioBenchmark.Namespace + ".xml"); var dt = scenarioBenchmark.GetStatistics(); var mdTable = MarkdownHelper.GenerateMarkdownTable(dt); var mdFileName = $"{Configuration.RunId}-{scenarioBenchmark.Namespace}-Statistics.md"; MarkdownHelper.Write(mdFileName, mdTable); WriteInfoLine($"Markdown file saved to \"{mdFileName}\""); Console.WriteLine(MarkdownHelper.ToTrimmedTable(mdTable)); var csvFileName = $"{Configuration.RunId}-{scenarioBenchmark.Namespace}-Statistics.csv"; dt.WriteToCSV(csvFileName); WriteInfoLine($"Statistics written to \"{csvFileName}\""); }
/// <summary> /// Initializes a new instance of the ScenarioConfiguration class (deep copy). /// </summary> /// <param name="scenarioConfiguration">An instance of the ScenarioConfiguration class</param> public ScenarioConfiguration(ScenarioConfiguration scenarioConfiguration) { Iterations = scenarioConfiguration.Iterations; TimeoutPerIteration = scenarioConfiguration.TimeoutPerIteration; SuccessExitCodes = scenarioConfiguration.SuccessExitCodes; }