/Source/TestingServices/Engines/ReplayEngine.cs

https://github.com/p-org/PSharp · C# · 251 lines · 154 code · 33 blank · 64 comment · 20 complexity · ce1206444f59332d7fba522ac9d8345e MD5 · raw file

  1. // ------------------------------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. // Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
  4. // ------------------------------------------------------------------------------------------------
  5. using System;
  6. using System.Reflection;
  7. using System.Runtime.ExceptionServices;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using Microsoft.PSharp.IO;
  11. using Microsoft.PSharp.TestingServices.Runtime;
  12. using Microsoft.PSharp.TestingServices.Scheduling.Strategies;
  13. using Microsoft.PSharp.Utilities;
  14. namespace Microsoft.PSharp.TestingServices
  15. {
  16. /// <summary>
  17. /// The P# replay engine.
  18. /// </summary>
  19. internal sealed class ReplayEngine : AbstractTestingEngine
  20. {
  21. /// <summary>
  22. /// Text describing an internal replay error.
  23. /// </summary>
  24. internal string InternalError { get; private set; }
  25. /// <summary>
  26. /// Creates a new P# replaying engine.
  27. /// </summary>
  28. public static ReplayEngine Create(Configuration configuration)
  29. {
  30. configuration.SchedulingStrategy = SchedulingStrategy.Replay;
  31. return new ReplayEngine(configuration);
  32. }
  33. /// <summary>
  34. /// Creates a new P# replaying engine.
  35. /// </summary>
  36. public static ReplayEngine Create(Configuration configuration, Assembly assembly)
  37. {
  38. configuration.SchedulingStrategy = SchedulingStrategy.Replay;
  39. return new ReplayEngine(configuration, assembly);
  40. }
  41. /// <summary>
  42. /// Creates a new P# replaying engine.
  43. /// </summary>
  44. public static ReplayEngine Create(Configuration configuration, Action<IMachineRuntime> action)
  45. {
  46. configuration.SchedulingStrategy = SchedulingStrategy.Replay;
  47. return new ReplayEngine(configuration, action);
  48. }
  49. /// <summary>
  50. /// Creates a new P# replaying engine.
  51. /// </summary>
  52. public static ReplayEngine Create(Configuration configuration, Action<IMachineRuntime> action, string trace)
  53. {
  54. configuration.SchedulingStrategy = SchedulingStrategy.Replay;
  55. configuration.ScheduleTrace = trace;
  56. return new ReplayEngine(configuration, action);
  57. }
  58. /// <summary>
  59. /// Creates a new P# replaying engine.
  60. /// </summary>
  61. public static ReplayEngine Create(Configuration configuration, Func<IMachineRuntime, Task> function)
  62. {
  63. configuration.SchedulingStrategy = SchedulingStrategy.Replay;
  64. return new ReplayEngine(configuration, function);
  65. }
  66. /// <summary>
  67. /// Creates a new P# replaying engine.
  68. /// </summary>
  69. public static ReplayEngine Create(Configuration configuration, Func<IMachineRuntime, Task> function, string trace)
  70. {
  71. configuration.SchedulingStrategy = SchedulingStrategy.Replay;
  72. configuration.ScheduleTrace = trace;
  73. return new ReplayEngine(configuration, function);
  74. }
  75. /// <summary>
  76. /// Initializes a new instance of the <see cref="ReplayEngine"/> class.
  77. /// </summary>
  78. private ReplayEngine(Configuration configuration)
  79. : base(configuration)
  80. {
  81. }
  82. /// <summary>
  83. /// Initializes a new instance of the <see cref="ReplayEngine"/> class.
  84. /// </summary>
  85. private ReplayEngine(Configuration configuration, Assembly assembly)
  86. : base(configuration, assembly)
  87. {
  88. }
  89. /// <summary>
  90. /// Initializes a new instance of the <see cref="ReplayEngine"/> class.
  91. /// </summary>
  92. private ReplayEngine(Configuration configuration, Action<IMachineRuntime> action)
  93. : base(configuration, action)
  94. {
  95. }
  96. /// <summary>
  97. /// Initializes a new instance of the <see cref="ReplayEngine"/> class.
  98. /// </summary>
  99. private ReplayEngine(Configuration configuration, Func<IMachineRuntime, Task> function)
  100. : base(configuration, function)
  101. {
  102. }
  103. /// <summary>
  104. /// Creates a new testing task.
  105. /// </summary>
  106. protected override Task CreateTestingTask()
  107. {
  108. return new Task(() =>
  109. {
  110. // Runtime used to serialize and test the program.
  111. SystematicTestingRuntime runtime = null;
  112. // Logger used to intercept the program output if no custom logger
  113. // is installed and if verbosity is turned off.
  114. InMemoryLogger runtimeLogger = null;
  115. // Gets a handle to the standard output and error streams.
  116. var stdOut = Console.Out;
  117. var stdErr = Console.Error;
  118. try
  119. {
  120. if (this.TestInitMethod != null)
  121. {
  122. // Initializes the test state.
  123. this.TestInitMethod.Invoke(null, Array.Empty<object>());
  124. }
  125. // Creates a new instance of the testing runtime.
  126. if (this.TestRuntimeFactoryMethod != null)
  127. {
  128. runtime = (SystematicTestingRuntime)this.TestRuntimeFactoryMethod.Invoke(
  129. null,
  130. new object[] { this.Configuration, this.Strategy, this.Reporter });
  131. }
  132. else
  133. {
  134. runtime = new SystematicTestingRuntime(this.Configuration, this.Strategy, this.Reporter);
  135. }
  136. // If verbosity is turned off, then intercept the program log, and also redirect
  137. // the standard output and error streams into the runtime logger.
  138. if (!this.Configuration.IsVerbose)
  139. {
  140. runtimeLogger = new InMemoryLogger();
  141. runtime.SetLogger(runtimeLogger);
  142. var writer = new LogWriter(new NulLogger());
  143. Console.SetOut(writer);
  144. Console.SetError(writer);
  145. }
  146. // Runs the test inside the test-harness machine.
  147. if (this.TestAction != null)
  148. {
  149. runtime.RunTestHarness(this.TestAction, this.TestName);
  150. }
  151. else
  152. {
  153. runtime.RunTestHarness(this.TestFunction, this.TestName);
  154. }
  155. // Wait for the test to terminate.
  156. runtime.WaitAsync().Wait();
  157. // Invokes user-provided cleanup for this iteration.
  158. if (this.TestIterationDisposeMethod != null)
  159. {
  160. // Disposes the test state.
  161. this.TestIterationDisposeMethod.Invoke(null, Array.Empty<object>());
  162. }
  163. // Invokes user-provided cleanup for all iterations.
  164. if (this.TestDisposeMethod != null)
  165. {
  166. // Disposes the test state.
  167. this.TestDisposeMethod.Invoke(null, Array.Empty<object>());
  168. }
  169. this.InternalError = (this.Strategy as ReplayStrategy).ErrorText;
  170. // Checks that no monitor is in a hot state at termination. Only
  171. // checked if no safety property violations have been found.
  172. if (!runtime.Scheduler.BugFound && this.InternalError.Length == 0)
  173. {
  174. runtime.CheckNoMonitorInHotStateAtTermination();
  175. }
  176. if (runtime.Scheduler.BugFound && this.InternalError.Length == 0)
  177. {
  178. this.ErrorReporter.WriteErrorLine(runtime.Scheduler.BugReport);
  179. }
  180. TestReport report = runtime.Scheduler.GetReport();
  181. report.CoverageInfo.Merge(runtime.CoverageInfo);
  182. this.TestReport.Merge(report);
  183. }
  184. catch (TargetInvocationException ex)
  185. {
  186. if (!(ex.InnerException is TaskCanceledException))
  187. {
  188. ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
  189. }
  190. }
  191. finally
  192. {
  193. if (!this.Configuration.IsVerbose)
  194. {
  195. // Restores the standard output and error streams.
  196. Console.SetOut(stdOut);
  197. Console.SetError(stdErr);
  198. }
  199. // Cleans up the runtime.
  200. runtimeLogger?.Dispose();
  201. runtime?.Dispose();
  202. }
  203. }, this.CancellationTokenSource.Token);
  204. }
  205. /// <summary>
  206. /// Returns a report with the testing results.
  207. /// </summary>
  208. public override string GetReport()
  209. {
  210. StringBuilder report = new StringBuilder();
  211. report.AppendFormat("... Reproduced {0} bug{1}.", this.TestReport.NumOfFoundBugs,
  212. this.TestReport.NumOfFoundBugs == 1 ? string.Empty : "s");
  213. report.AppendLine();
  214. report.Append($"... Elapsed {this.Profiler.Results()} sec.");
  215. return report.ToString();
  216. }
  217. }
  218. }