Example #1
0
        /// <summary>
        /// Runs the specified test method.
        /// </summary>
        internal void RunTest(Delegate testMethod, string testName)
        {
            testName = string.IsNullOrEmpty(testName) ? string.Empty : $" '{testName}'";
            this.Logger.WriteLine($"<TestLog> Running test{testName}.");
            this.Assert(testMethod != null, "Unable to execute a null test method.");
            this.Assert(Task.CurrentId != null, "The test must execute inside a controlled task.");

            ulong operationId = this.GetNextOperationId();
            var   op          = new TaskOperation(operationId, this.Scheduler);

            this.Scheduler.RegisterOperation(op);
            op.OnEnabled();

            Task task = new Task(async() =>
            {
                try
                {
                    // Update the current asynchronous control flow with the current runtime instance,
                    // allowing future retrieval in the same asynchronous call stack.
                    AssignAsyncControlFlowRuntime(this);

                    OperationScheduler.StartOperation(op);

                    if (testMethod is Action <IActorRuntime> actionWithRuntime)
                    {
                        actionWithRuntime(this);
                    }
                    else if (testMethod is Action action)
                    {
                        action();
                    }
                    else if (testMethod is Func <IActorRuntime, CoyoteTasks.Task> functionWithRuntime)
                    {
                        await functionWithRuntime(this);
                    }
                    else if (testMethod is Func <CoyoteTasks.Task> function)
                    {
                        await function();
                    }
                    else
                    {
                        throw new InvalidOperationException($"Unsupported test delegate of type '{testMethod.GetType()}'.");
                    }

                    IO.Debug.WriteLine("<ScheduleDebug> Completed operation {0} on task '{1}'.", op.Name, Task.CurrentId);
                    op.OnCompleted();

                    // Task has completed, schedule the next enabled operation.
                    this.Scheduler.ScheduleNextEnabledOperation();
                }
                catch (Exception ex)
                {
                    this.ProcessUnhandledExceptionInOperation(op, ex);
                }
            });

            this.Scheduler.ScheduleOperation(op, task.Id);
            task.Start();
            this.Scheduler.WaitOperationStart(op);
        }
Example #2
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ControlledRuntime"/> class.
        /// </summary>
        internal ControlledRuntime(Configuration configuration, ISchedulingStrategy strategy,
                                   IRandomValueGenerator valueGenerator)
            : base(configuration, valueGenerator)
        {
            IsExecutionControlled = true;

            this.RootTaskId         = Task.CurrentId;
            this.NameValueToActorId = new ConcurrentDictionary <string, ActorId>();

            this.CoverageInfo = new CoverageInfo();

            var scheduleTrace = new ScheduleTrace();

            if (configuration.IsLivenessCheckingEnabled)
            {
                strategy = new TemperatureCheckingStrategy(configuration, this.Monitors, strategy);
            }

            this.Scheduler      = new OperationScheduler(this, strategy, scheduleTrace, this.Configuration);
            this.TaskController = new TaskController(this, this.Scheduler);

            // Update the current asynchronous control flow with this runtime instance,
            // allowing future retrieval in the same asynchronous call stack.
            AssignAsyncControlFlowRuntime(this);
        }
Example #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="TestingEngine"/> class.
        /// </summary>
        private TestingEngine(Configuration configuration, TestMethodInfo testMethodInfo)
        {
            this.Configuration  = configuration;
            this.TestMethodInfo = testMethodInfo;

            this.DefaultLogger = new ConsoleLogger()
            {
                LogLevel = configuration.LogLevel
            };
            this.Logger   = this.DefaultLogger;
            this.Profiler = new Profiler();

            this.PerIterationCallbacks = new HashSet <Action <uint> >();

            this.TestReport        = new TestReport(configuration);
            this.ReadableTrace     = string.Empty;
            this.ReproducibleTrace = string.Empty;

            this.CancellationTokenSource = new CancellationTokenSource();
            this.PrintGuard = 1;

            if (configuration.IsDebugVerbosityEnabled)
            {
                IO.Debug.IsEnabled = true;
            }

            // Do some sanity checking.
            string error = string.Empty;

            if (configuration.IsConcurrencyFuzzingEnabled &&
                (configuration.SchedulingStrategy is "replay" || configuration.ScheduleFile.Length > 0))
            {
                error = "Replaying a bug trace is not supported in concurrency fuzzing.";
            }

            if (configuration.SchedulingStrategy is "portfolio")
            {
                error = "Portfolio testing strategy is only available in parallel testing.";
            }

            if (!string.IsNullOrEmpty(error))
            {
                if (configuration.DisableEnvironmentExit)
                {
                    throw new Exception(error);
                }
                else
                {
                    Error.ReportAndExit(error);
                }
            }

            this.Scheduler = OperationScheduler.Setup(configuration);

            if (TelemetryClient is null)
            {
                TelemetryClient = new CoyoteTelemetryClient(this.Configuration);
            }
        }
Example #4
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TaskOperation"/> class.
 /// </summary>
 internal TaskOperation(ulong operationId, OperationScheduler scheduler)
     : base()
 {
     this.Scheduler        = scheduler;
     this.Id               = operationId;
     this.Name             = $"Task({operationId})";
     this.JoinDependencies = new HashSet <Task>();
 }
Example #5
0
        public CoyoteTasks.Task ScheduleAction(Action action, Task predecessor, CancellationToken cancellationToken)
        {
            // TODO: support cancellations during testing.
            this.Assert(action != null, "The task cannot execute a null action.");

            ulong operationId = this.Runtime.GetNextOperationId();
            var   op          = new TaskOperation(operationId, this.Scheduler);

            this.Scheduler.RegisterOperation(op);
            op.OnEnabled();

            var task = new Task(() =>
            {
                try
                {
                    // Update the current asynchronous control flow with the current runtime instance,
                    // allowing future retrieval in the same asynchronous call stack.
                    CoyoteRuntime.AssignAsyncControlFlowRuntime(this.Runtime);

                    OperationScheduler.StartOperation(op);
                    if (predecessor != null)
                    {
                        op.OnWaitTask(predecessor);
                    }

                    action();
                }
                catch (Exception ex)
                {
                    // Report the unhandled exception unless it is our ExecutionCanceledException which is our
                    // way of terminating async task operations at the end of the test iteration.
                    if (!(ex is ExecutionCanceledException))
                    {
                        this.ReportUnhandledExceptionInOperation(op, ex);
                    }

                    // and rethrow it
                    throw;
                }
                finally
                {
                    IO.Debug.WriteLine("<ScheduleDebug> Completed operation '{0}' on task '{1}'.", op.Name, Task.CurrentId);
                    op.OnCompleted();
                }
            });

            // Schedule a task continuation that will schedule the next enabled operation upon completion.
            task.ContinueWith(t => this.Scheduler.ScheduleNextEnabledOperation(), TaskScheduler.Current);

            IO.Debug.WriteLine("<CreateLog> Operation '{0}' was created to execute task '{1}'.", op.Name, task.Id);
            this.Scheduler.ScheduleOperation(op, task.Id);
            task.Start();
            this.Scheduler.WaitOperationStart(op);
            this.Scheduler.ScheduleNextEnabledOperation();

            return(new CoyoteTasks.Task(this, task));
        }
Example #6
0
        public CoyoteTasks.Task <TResult> ScheduleFunction <TResult>(Func <CoyoteTasks.Task <TResult> > function, Task predecessor,
                                                                     CancellationToken cancellationToken)
        {
            // TODO: support cancellations during testing.
            this.Assert(function != null, "The task cannot execute a null function.");

            ulong operationId = this.Runtime.GetNextOperationId();
            var   op          = new TaskOperation(operationId, this.Scheduler);

            this.Scheduler.RegisterOperation(op);
            op.OnEnabled();

            var task = new Task <Task <TResult> >(() =>
            {
                try
                {
                    // Update the current asynchronous control flow with the current runtime instance,
                    // allowing future retrieval in the same asynchronous call stack.
                    CoyoteRuntime.AssignAsyncControlFlowRuntime(this.Runtime);

                    OperationScheduler.StartOperation(op);
                    if (predecessor != null)
                    {
                        op.OnWaitTask(predecessor);
                    }

                    CoyoteTasks.Task <TResult> resultTask = function();
                    this.OnWaitTask(operationId, resultTask.UncontrolledTask);
                    return(resultTask.UncontrolledTask);
                }
                catch (Exception ex)
                {
                    // Report the unhandled exception and rethrow it.
                    this.ReportUnhandledExceptionInOperation(op, ex);
                    throw;
                }
                finally
                {
                    IO.Debug.WriteLine("<ScheduleDebug> Completed operation '{0}' on task '{1}'.", op.Name, Task.CurrentId);
                    op.OnCompleted();
                }
            });

            Task <TResult> innerTask = task.Unwrap();

            // Schedule a task continuation that will schedule the next enabled operation upon completion.
            innerTask.ContinueWith(t => this.Scheduler.ScheduleNextEnabledOperation(), TaskScheduler.Current);

            IO.Debug.WriteLine("<CreateLog> Operation '{0}' was created to execute task '{1}'.", op.Name, task.Id);
            this.Scheduler.ScheduleOperation(op, task.Id);
            task.Start();
            this.Scheduler.WaitOperationStart(op);
            this.Scheduler.ScheduleNextEnabledOperation();

            return(new CoyoteTasks.Task <TResult>(this, innerTask));
        }
Example #7
0
        /// <summary>
        /// Returns a test report with the scheduling statistics.
        /// </summary>
        internal TestReport GetSchedulerReport(OperationScheduler scheduler)
        {
            lock (scheduler.SyncObject)
            {
                TestReport report = new TestReport(this.Configuration);

                if (scheduler.BugFound)
                {
                    report.NumOfFoundBugs++;
                    report.ThrownException = scheduler.UnhandledException;
                    report.BugReports.Add(scheduler.BugReport);
                }

                if (this.Strategy.IsFair())
                {
                    report.NumOfExploredFairSchedules++;
                    report.TotalExploredFairSteps += scheduler.ScheduledSteps;

                    if (report.MinExploredFairSteps < 0 ||
                        report.MinExploredFairSteps > scheduler.ScheduledSteps)
                    {
                        report.MinExploredFairSteps = scheduler.ScheduledSteps;
                    }

                    if (report.MaxExploredFairSteps < scheduler.ScheduledSteps)
                    {
                        report.MaxExploredFairSteps = scheduler.ScheduledSteps;
                    }

                    if (scheduler.Strategy.HasReachedMaxSchedulingSteps())
                    {
                        report.MaxFairStepsHitInFairTests++;
                    }

                    if (scheduler.ScheduledSteps >= report.Configuration.MaxUnfairSchedulingSteps)
                    {
                        report.MaxUnfairStepsHitInFairTests++;
                    }
                }
                else
                {
                    report.NumOfExploredUnfairSchedules++;

                    if (scheduler.Strategy.HasReachedMaxSchedulingSteps())
                    {
                        report.MaxUnfairStepsHitInUnfairTests++;
                    }
                }

                return(report);
            }
        }
Example #8
0
        /// <summary>
        /// Runs a new asynchronous event handler for the specified actor.
        /// This is a fire and forget invocation.
        /// </summary>
        /// <param name="actor">The actor that executes this event handler.</param>
        /// <param name="initialEvent">Optional event for initializing the actor.</param>
        /// <param name="isFresh">If true, then this is a new actor.</param>
        /// <param name="syncCaller">Caller actor that is blocked for quiscence.</param>
        private void RunActorEventHandler(Actor actor, Event initialEvent, bool isFresh, Actor syncCaller)
        {
            var op = this.Scheduler.GetOperationWithId <ActorOperation>(actor.Id.Value);

            op.OnEnabled();

            Task task = new Task(async() =>
            {
                try
                {
                    // Update the current asynchronous control flow with this runtime instance,
                    // allowing future retrieval in the same asynchronous call stack.
                    AssignAsyncControlFlowRuntime(this);

                    OperationScheduler.StartOperation(op);

                    if (isFresh)
                    {
                        await actor.InitializeAsync(initialEvent);
                    }

                    await actor.RunEventHandlerAsync();
                    if (syncCaller != null)
                    {
                        this.EnqueueEvent(syncCaller, new QuiescentEvent(actor.Id), actor, actor.OperationGroupId, null);
                    }

                    if (!actor.IsHalted)
                    {
                        ResetProgramCounter(actor);
                    }

                    IO.Debug.WriteLine("<ScheduleDebug> Completed operation {0} on task '{1}'.", actor.Id, Task.CurrentId);
                    op.OnCompleted();

                    // The actor is inactive or halted, schedule the next enabled operation.
                    this.Scheduler.ScheduleNextEnabledOperation();
                }
                catch (Exception ex)
                {
                    this.ProcessUnhandledExceptionInOperation(op, ex);
                }
            });

            this.Scheduler.ScheduleOperation(op, task.Id);
            task.Start();
            this.Scheduler.WaitOperationStart(op);
        }
Example #9
0
        public CoyoteTasks.Task <TResult> ScheduleDelegate <TResult>(Delegate work, Task predecessor, CancellationToken cancellationToken)
        {
            // TODO: support cancellations during testing.
            this.Assert(work != null, "The task cannot execute a null delegate.");

            ulong operationId = this.Runtime.GetNextOperationId();
            var   op          = new TaskOperation(operationId, this.Scheduler);

            this.Scheduler.RegisterOperation(op);
            op.OnEnabled();

            var task = new Task <TResult>(() =>
            {
                try
                {
                    // Update the current asynchronous control flow with the current runtime instance,
                    // allowing future retrieval in the same asynchronous call stack.
                    CoyoteRuntime.AssignAsyncControlFlowRuntime(this.Runtime);

                    OperationScheduler.StartOperation(op);
                    if (predecessor != null)
                    {
                        op.OnWaitTask(predecessor);
                    }

                    if (work is Func <Task> funcWithTaskResult)
                    {
                        Task resultTask = funcWithTaskResult();
                        this.OnWaitTask(operationId, resultTask);
                        if (resultTask is TResult typedResultTask)
                        {
                            return(typedResultTask);
                        }
                    }
                    else if (work is Func <Task <TResult> > funcWithGenericTaskResult)
                    {
                        Task <TResult> resultTask = funcWithGenericTaskResult();
                        this.OnWaitTask(operationId, resultTask);
                        return(resultTask.Result);
                    }
                    else if (work is Func <TResult> funcWithGenericResult)
                    {
                        return(funcWithGenericResult());
                    }

                    return(default);
Example #10
0
        /// <summary>
        /// Initializes a new instance of the <see cref="TestingEngine"/> class.
        /// </summary>
        private TestingEngine(Configuration configuration, TestMethodInfo testMethodInfo)
        {
            this.Configuration  = configuration;
            this.TestMethodInfo = testMethodInfo;

            this.DefaultLogger = new ConsoleLogger()
            {
                LogLevel = configuration.LogLevel
            };
            this.Logger   = this.DefaultLogger;
            this.Profiler = new Profiler();

            this.PerIterationCallbacks = new HashSet <Action <uint> >();

            this.TestReport        = new TestReport(configuration);
            this.ReadableTrace     = string.Empty;
            this.ReproducibleTrace = string.Empty;

            this.CancellationTokenSource = new CancellationTokenSource();
            this.PrintGuard = 1;

            if (configuration.IsDebugVerbosityEnabled)
            {
                IO.Debug.IsEnabled = true;
            }

            // Do some sanity checking.
            string error = string.Empty;

            if (configuration.IsSystematicFuzzingEnabled &&
                (configuration.SchedulingStrategy is "replay" || configuration.ScheduleFile.Length > 0))
            {
                error = "Replaying a bug trace is not currently supported in systematic fuzzing.";
            }

            if (!string.IsNullOrEmpty(error))
            {
                Error.Report(error);
                throw new InvalidOperationException(error);
            }

            this.Scheduler = OperationScheduler.Setup(configuration);

            TelemetryClient = TelemetryClient.GetOrCreate(this.Configuration);
        }
Example #11
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ControlledRuntime"/> class.
        /// </summary>
        internal ControlledRuntime(Configuration configuration, ISchedulingStrategy strategy,
                                   IRandomValueGenerator valueGenerator)
            : base(configuration, valueGenerator)
        {
            IncrementExecutionControlledUseCount();

            this.RootTaskId         = Task.CurrentId;
            this.NameValueToActorId = new ConcurrentDictionary <string, ActorId>();

            this.CoverageInfo = new CoverageInfo();

            var scheduleTrace = new ScheduleTrace();

            if (configuration.IsLivenessCheckingEnabled)
            {
                strategy = new TemperatureCheckingStrategy(configuration, this.Monitors, strategy);
            }

            this.Scheduler      = new OperationScheduler(this, strategy, scheduleTrace, this.Configuration);
            this.TaskController = new TaskController(this, this.Scheduler);
        }
Example #12
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TaskController"/> class.
 /// </summary>
 internal TaskController(ControlledRuntime runtime, OperationScheduler scheduler)
 {
     this.Runtime   = runtime;
     this.Scheduler = scheduler;
 }
Example #13
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TaskDelayOperation"/> class.
 /// </summary>
 internal TaskDelayOperation(ulong operationId, string name, uint delay, OperationScheduler scheduler)
     : base(operationId, name, scheduler)
 {
     this.Timeout = delay > int.MaxValue ? int.MaxValue : (int)delay;
 }
Example #14
0
        public CoyoteTasks.Task<TResult> ScheduleDelegate<TResult>(Delegate work, Task predecessor, CancellationToken cancellationToken)
        {
            // TODO: support cancellations during testing.
            this.Assert(work != null, "The task cannot execute a null delegate.");

            ulong operationId = this.Runtime.GetNextOperationId();
            var op = new TaskOperation(operationId, this.Scheduler);
            this.Scheduler.RegisterOperation(op);
            op.OnEnabled();

            var task = new Task<TResult>(() =>
            {
                try
                {
                    // Update the current asynchronous control flow with the current runtime instance,
                    // allowing future retrieval in the same asynchronous call stack.
                    CoyoteRuntime.AssignAsyncControlFlowRuntime(this.Runtime);

                    OperationScheduler.StartOperation(op);
                    if (predecessor != null)
                    {
                        op.OnWaitTask(predecessor);
                    }

                    if (work is Func<Task> funcWithTaskResult)
                    {
                        Task resultTask = funcWithTaskResult();
                        this.OnWaitTask(operationId, resultTask);
                        if (resultTask is TResult typedResultTask)
                        {
                            return typedResultTask;
                        }
                    }
                    else if (work is Func<Task<TResult>> funcWithGenericTaskResult)
                    {
                        Task<TResult> resultTask = funcWithGenericTaskResult();
                        this.OnWaitTask(operationId, resultTask);
                        return resultTask.Result;
                    }
                    else if (work is Func<TResult> funcWithGenericResult)
                    {
                        return funcWithGenericResult();
                    }

                    return default;
                }
                catch (Exception ex)
                {
                    // Report the unhandled exception and rethrow it.
                    ReportUnhandledExceptionInOperation(op, ex);
                    throw;
                }
                finally
                {
                    IO.Debug.WriteLine("<ScheduleDebug> Completed operation '{0}' on task '{1}'.", op.Name, Task.CurrentId);
                    op.OnCompleted();
                }
            }, cancellationToken);

            // Schedule a task continuation that will schedule the next enabled operation upon completion.
            task.ContinueWith(t => this.Scheduler.ScheduleNextEnabledOperation(), TaskScheduler.Current);

            IO.Debug.WriteLine("<CreateLog> Operation '{0}' was created to execute task '{1}'.", op.Name, task.Id);
            this.Scheduler.ScheduleOperation(op, task.Id);
            task.Start();
            this.Scheduler.WaitOperationStart(op);
            this.Scheduler.ScheduleNextEnabledOperation();

            return new CoyoteTasks.Task<TResult>(this, task);
        }
Example #15
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TaskOperation"/> class.
 /// </summary>
 internal TaskOperation(ulong operationId, string name, OperationScheduler scheduler)
     : base(operationId, name)
 {
     this.Scheduler        = scheduler;
     this.JoinDependencies = new HashSet <Task>();
 }
Example #16
0
        /// <summary>
        /// Runs the next testing iteration for the specified test method.
        /// </summary>
        private bool RunNextIteration(TestMethodInfo methodInfo, uint iteration)
        {
            if (!this.Scheduler.InitializeNextIteration(iteration))
            {
                // The next iteration cannot run, so stop exploring.
                return(false);
            }

            if (!this.Scheduler.IsReplayingSchedule && this.ShouldPrintIteration(iteration + 1))
            {
                this.Logger.WriteLine(LogSeverity.Important, $"..... Iteration #{iteration + 1}");

                // Flush when logging to console.
                if (this.Logger is ConsoleLogger)
                {
                    Console.Out.Flush();
                }
            }

            // Runtime used to serialize and test the program in this iteration.
            CoyoteRuntime runtime = null;

            // Logger used to intercept the program output if no custom logger
            // is installed and if verbosity is turned off.
            InMemoryLogger runtimeLogger = null;

            // Gets a handle to the standard output and error streams.
            var stdOut = Console.Out;
            var stdErr = Console.Error;

            try
            {
                // Creates a new instance of the controlled runtime.
                runtime = new CoyoteRuntime(this.Configuration, this.Scheduler);

                // If verbosity is turned off, then intercept the program log, and also redirect
                // the standard output and error streams to the runtime logger.
                if (!this.Configuration.IsVerbose)
                {
                    runtimeLogger = new InMemoryLogger();
                    if (this.Logger != this.DefaultLogger)
                    {
                        runtimeLogger.UserLogger = this.Logger;
                    }

                    runtime.Logger = runtimeLogger;

                    Console.SetOut(runtimeLogger.TextWriter);
                    Console.SetError(runtimeLogger.TextWriter);
                }
                else if (this.Logger != this.DefaultLogger)
                {
                    runtime.Logger = this.Logger;
                }

                this.InitializeCustomActorLogging(runtime.DefaultActorExecutionContext);

                // Runs the test and waits for it to terminate.
                Task task = runtime.RunTestAsync(methodInfo.Method, methodInfo.Name);
                task.Wait();

                // Invokes the user-specified iteration disposal method.
                methodInfo.DisposeCurrentIteration();

                // Invoke the per iteration callbacks, if any.
                foreach (var callback in this.PerIterationCallbacks)
                {
                    callback(iteration);
                }

                runtime.LogWriter.LogCompletion();

                this.GatherTestingStatistics(runtime);

                if (!this.Scheduler.IsReplayingSchedule && this.TestReport.NumOfFoundBugs > 0)
                {
                    if (runtimeLogger != null)
                    {
                        this.ReadableTrace = string.Empty;
                        if (this.Configuration.IsTelemetryEnabled)
                        {
                            this.ReadableTrace += $"<TelemetryLog> Anonymized telemetry is enabled, see {LearnAboutTelemetryUrl}.\n";
                        }

                        this.ReadableTrace += runtimeLogger.ToString();
                        this.ReadableTrace += this.TestReport.GetText(this.Configuration, "<StrategyLog>");
                    }

                    if (runtime.SchedulingPolicy is SchedulingPolicy.Interleaving)
                    {
                        this.ReproducibleTrace = this.Scheduler.Trace.Serialize(
                            this.Configuration, this.Scheduler.IsScheduleFair);
                    }
                }
            }
            finally
            {
                if (!this.Configuration.IsVerbose)
                {
                    // Restores the standard output and error streams.
                    Console.SetOut(stdOut);
                    Console.SetError(stdErr);
                }

                if (this.Configuration.IsSystematicFuzzingFallbackEnabled &&
                    runtime.SchedulingPolicy is SchedulingPolicy.Interleaving &&
                    (runtime.ExecutionStatus is ExecutionStatus.ConcurrencyUncontrolled ||
                     runtime.ExecutionStatus is ExecutionStatus.Deadlocked))
                {
                    // Detected uncontrolled concurrency or deadlock, so switch to systematic fuzzing.
                    this.Scheduler = OperationScheduler.Setup(this.Configuration, SchedulingPolicy.Fuzzing,
                                                              this.Scheduler.ValueGenerator);
                    this.Logger.WriteLine(LogSeverity.Important, $"..... Iteration #{iteration + 1} " +
                                          $"enables systematic fuzzing due to uncontrolled concurrency");
                }
                else if (runtime.ExecutionStatus is ExecutionStatus.BoundReached)
                {
                    this.Logger.WriteLine(LogSeverity.Important, $"..... Iteration #{iteration + 1} " +
                                          $"hit bound of '{this.Scheduler.StepCount}' scheduling steps");
                }
                else if (runtime.ExecutionStatus is ExecutionStatus.BugFound)
                {
                    if (!this.Scheduler.IsReplayingSchedule)
                    {
                        this.Logger.WriteLine(LogSeverity.Important, $"..... Iteration #{iteration + 1} " +
                                              $"found bug #{this.TestReport.NumOfFoundBugs}");
                    }

                    this.Logger.WriteLine(LogSeverity.Error, runtime.BugReport);
                }
                else if (this.Scheduler.IsReplayingSchedule)
                {
                    this.Logger.WriteLine(LogSeverity.Error, "Failed to reproduce the bug.");
                }

                // Cleans up the runtime before the next iteration starts.
                runtimeLogger?.Close();
                runtimeLogger?.Dispose();
                runtime?.Dispose();
            }

            return(true);
        }