Beispiel #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TaskAwaiter"/> struct.
 /// </summary>
 private TaskAwaiter(SystemTask awaitedTask, ref SystemCompiler.TaskAwaiter awaiter)
 {
     this.AwaitedTask = awaitedTask;
     this.Awaiter     = awaiter;
     RuntimeProvider.TryGetFromSynchronizationContext(out CoyoteRuntime runtime);
     this.Runtime = runtime;
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="ConfiguredTaskAwaiter"/> struct.
 /// </summary>
 internal ConfiguredTaskAwaiter(CoyoteRuntime runtime, SystemTasks.Task awaitedTask,
                                bool continueOnCapturedContext)
 {
     this.Runtime     = runtime;
     this.AwaitedTask = awaitedTask;
     this.Awaiter     = awaitedTask.ConfigureAwait(continueOnCapturedContext).GetAwaiter();
 }
Beispiel #3
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TaskAwaiter"/> struct.
 /// </summary>
 internal TaskAwaiter(SystemTask awaitedTask)
 {
     this.AwaitedTask = awaitedTask;
     this.Awaiter     = awaitedTask.GetAwaiter();
     RuntimeProvider.TryGetFromSynchronizationContext(out CoyoteRuntime runtime);
     this.Runtime = runtime;
 }
Beispiel #4
0
 /// <summary>
 /// Initializes a new instance of the <see cref="SpecificationEngine"/> class.
 /// </summary>
 internal SpecificationEngine(Configuration configuration, CoyoteRuntime runtime)
 {
     this.Configuration        = configuration;
     this.Runtime              = runtime;
     this.LivenessMonitors     = new List <TaskLivenessMonitor>();
     this.StateMachineMonitors = new List <Monitor>();
     this.IsMonitoringEnabled  = runtime.IsControlled || configuration.IsMonitoringEnabledInInProduction;
 }
Beispiel #5
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ValueTaskAwaiter"/> struct.
 /// </summary>
 private ValueTaskAwaiter(ref SystemValueTask awaitedTask, ref SystemCompiler.ValueTaskAwaiter awaiter)
 {
     this.AwaitedTask = ValueTaskAwaiter.TryGetTask(ref awaitedTask, out Task innerTask) ?
                        innerTask : null;
     this.Awaiter = awaiter;
     RuntimeProvider.TryGetFromSynchronizationContext(out CoyoteRuntime runtime);
     this.Runtime = runtime;
 }
Beispiel #6
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ValueTaskAwaiter"/> struct.
 /// </summary>
 internal ValueTaskAwaiter(ref SystemValueTask awaitedTask)
 {
     this.AwaitedTask = ValueTaskAwaiter.TryGetTask(ref awaitedTask, out Task innerTask) ?
                        innerTask : null;
     this.Awaiter = awaitedTask.GetAwaiter();
     RuntimeProvider.TryGetFromSynchronizationContext(out CoyoteRuntime runtime);
     this.Runtime = runtime;
 }
Beispiel #7
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));
        }
Beispiel #8
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));
        }
        public void IncrementCountTest()
        {
            ICoyoteRuntime runtime   = new CoyoteRuntime();
            var            viewmodel = new SecondCoyoteLibrary.Pages.CounterViewModel(runtime);

            viewmodel.CurrentCount    = 10;
            viewmodel.IncrementAmount = 3;
            viewmodel.IncrementCount();
            Assert.AreEqual(13, viewmodel.CurrentCount);
        }
            /// <summary>
            /// Initializes a new instance of the <see cref="ConfiguredTaskAwaiter"/> struct.
            /// </summary>
            internal ConfiguredTaskAwaiter(SystemTask awaitedTask, bool continueOnCapturedContext)
            {
                if (RuntimeProvider.TryGetFromSynchronizationContext(out CoyoteRuntime runtime))
                {
                    // Force the continuation to run on the current context so that it can be controlled.
                    continueOnCapturedContext = true;
                }

                this.AwaitedTask = awaitedTask;
                this.Awaiter     = awaitedTask.ConfigureAwait(continueOnCapturedContext).GetAwaiter();
                this.Runtime     = runtime;
            }
        /// <summary>
        /// Creates an instance of the <see cref="AsyncTaskMethodBuilder"/> struct.
        /// </summary>
        public static AsyncTaskMethodBuilder Create()
        {
            CoyoteRuntime runtime = null;

            if (SynchronizationContext.Current is ControlledSynchronizationContext controlledContext &&
                controlledContext.Runtime.SchedulingPolicy != SchedulingPolicy.None)
            {
                runtime = controlledContext.Runtime;
            }

            return(new AsyncTaskMethodBuilder(runtime));
        }
Beispiel #12
0
        public async Task IncrementCountTest()
        {
            SecondCoyoteLibrary.ICoyoteRuntime runtime = new CoyoteRuntime();
            var viewmodel = new SecondCoyoteLibrary.Pages.CounterViewModel(runtime);

            viewmodel.CurrentCount    = 10;
            viewmodel.IncrementAmount = 3;
            //var task =
            await viewmodel.IncrementCount();

            //task.Wait();
            Assert.Equal(13, viewmodel.CurrentCount);
        }
Beispiel #13
0
            /// <summary>
            /// Initializes a new instance of the <see cref="ConfiguredTaskAwaiter"/> struct.
            /// </summary>
            internal ConfiguredTaskAwaiter(CoyoteRuntime runtime, SystemTasks.Task awaitedTask,
                                           bool continueOnCapturedContext)
            {
                if (runtime?.SchedulingPolicy != SchedulingPolicy.None)
                {
                    // Force the continuation to run on the current context so that it can be controlled.
                    continueOnCapturedContext = true;
                }

                this.Runtime     = runtime;
                this.AwaitedTask = awaitedTask;
                this.Awaiter     = awaitedTask.ConfigureAwait(continueOnCapturedContext).GetAwaiter();
            }
Beispiel #14
0
        /// <summary>
        /// Gathers the exploration strategy statistics from the specified runtimne.
        /// </summary>
        private void GatherTestingStatistics(CoyoteRuntime runtime)
        {
            TestReport report = new TestReport(this.Configuration);

            runtime.PopulateTestReport(report);

            var coverageInfo = runtime.DefaultActorExecutionContext.BuildCoverageInfo();

            report.CoverageInfo.Merge(coverageInfo);
            this.TestReport.Merge(report);

            // Save the DGML graph of the execution path explored in the last iteration.
            this.LastExecutionGraph = runtime.DefaultActorExecutionContext.GetExecutionGraph();
        }
Beispiel #15
0
        /// <summary>
        /// Creates a new <see cref="HttpOperation"/> from the specified parameters.
        /// </summary>
#pragma warning disable CA1801 // Parameter not used
        internal static HttpOperation Create(HttpMethod method, string path, CoyoteRuntime runtime,
                                             ControlledOperation source)
#pragma warning restore CA1801 // Parameter not used
        {
            ulong operationId = runtime.GetNextOperationId();
            var   op          = new HttpOperation(operationId, method, path);

            runtime.RegisterOperation(op);
            if (runtime.GetExecutingOperation() is null)
            {
                op.IsSourceUncontrolled = true;
            }

            return(op);
        }
Beispiel #16
0
 /// <summary>
 /// Initializes a new instance of the <see cref="OperationScheduler"/> class.
 /// </summary>
 internal OperationScheduler(CoyoteRuntime runtime, SchedulingStrategy strategy,
                             ScheduleTrace trace, Configuration configuration)
 {
     this.Configuration            = configuration;
     this.Runtime                  = runtime;
     this.Strategy                 = strategy;
     this.OperationMap             = new Dictionary <ulong, AsyncOperation>();
     this.ScheduleTrace            = trace;
     this.SyncObject               = new object();
     this.CompletionSource         = new TaskCompletionSource <bool>();
     this.IsProgramExecuting       = true;
     this.IsAttached               = true;
     this.BugFound                 = false;
     this.HasFullyExploredSchedule = false;
 }
Beispiel #17
0
        /// <summary>
        /// Constructs a reproducable trace.
        /// </summary>
        private void ConstructReproducibleTrace(CoyoteRuntime runtime)
        {
            StringBuilder stringBuilder = new StringBuilder();

            if (this.Strategy.IsFair())
            {
                stringBuilder.Append("--fair-scheduling").Append(Environment.NewLine);
            }

            if (this.Configuration.IsLivenessCheckingEnabled)
            {
                stringBuilder.Append("--liveness-temperature-threshold:" +
                                     this.Configuration.LivenessTemperatureThreshold).
                Append(Environment.NewLine);
            }

            if (!string.IsNullOrEmpty(this.Configuration.TestMethodName))
            {
                stringBuilder.Append("--test-method:" +
                                     this.Configuration.TestMethodName).
                Append(Environment.NewLine);
            }

            for (int idx = 0; idx < runtime.Scheduler.ScheduleTrace.Count; idx++)
            {
                ScheduleStep step = runtime.Scheduler.ScheduleTrace[idx];
                if (step.Type == ScheduleStepType.SchedulingChoice)
                {
                    stringBuilder.Append($"({step.ScheduledOperationId})");
                }
                else if (step.BooleanChoice != null)
                {
                    stringBuilder.Append(step.BooleanChoice.Value);
                }
                else
                {
                    stringBuilder.Append(step.IntegerChoice.Value);
                }

                if (idx < runtime.Scheduler.ScheduleTrace.Count - 1)
                {
                    stringBuilder.Append(Environment.NewLine);
                }
            }

            this.ReproducibleTrace = stringBuilder.ToString();
        }
Beispiel #18
0
        /// <summary>
        /// Gathers the exploration strategy statistics from the specified runtimne.
        /// </summary>
        private void GatherTestingStatistics(CoyoteRuntime runtime)
        {
            TestReport report = this.GetSchedulerReport(runtime.Scheduler);

            if (this.Configuration.ReportActivityCoverage)
            {
                report.CoverageInfo.CoverageGraph = this.Graph;
            }

            var coverageInfo = runtime.DefaultActorExecutionContext.BuildCoverageInfo();

            report.CoverageInfo.Merge(coverageInfo);
            this.TestReport.Merge(report);

            // Also save the graph snapshot of the last iteration, if there is one.
            this.Graph = coverageInfo.CoverageGraph;
        }
Beispiel #19
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);
Beispiel #20
0
        /// <summary>
        /// Gathers the exploration strategy statistics from the specified runtimne.
        /// </summary>
        private void GatherTestingStatistics(CoyoteRuntime runtime)
        {
            runtime.GetSchedulingStatisticsAndResults(out bool isBugFound, out string bugReport, out int steps,
                                                      out bool isMaxStepsReached, out bool isScheduleFair, out Exception unhandledException);

            TestReport report = TestReport.CreateTestReportFromStats(this.Configuration, isBugFound, bugReport,
                                                                     steps, isMaxStepsReached, isScheduleFair, unhandledException);

            if (this.Configuration.ReportActivityCoverage)
            {
                report.CoverageInfo.CoverageGraph = this.Graph;
            }

            var coverageInfo = runtime.DefaultActorExecutionContext.BuildCoverageInfo();

            report.CoverageInfo.Merge(coverageInfo);
            this.TestReport.Merge(report);

            // Also save the graph snapshot of the last iteration, if there is one.
            this.Graph = coverageInfo.CoverageGraph;
        }
Beispiel #21
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TaskDelayOperation"/> class.
 /// </summary>
 internal TaskDelayOperation(ulong operationId, string name, uint delay, CoyoteRuntime runtime)
     : base(operationId, name, runtime)
 {
     this.Timeout = delay > int.MaxValue ? int.MaxValue : (int)delay;
 }
Beispiel #22
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ConfiguredTaskAwaitable"/> struct.
 /// </summary>
 internal ConfiguredTaskAwaitable(CoyoteRuntime runtime, SystemTasks.Task awaitedTask,
                                  bool continueOnCapturedContext)
 {
     this.Awaiter = new ConfiguredTaskAwaiter(runtime, awaitedTask, continueOnCapturedContext);
 }
Beispiel #23
0
 internal Task(CoyoteRuntime runtime, SystemTasks.Task <TResult> task)
     : base(runtime, task)
 {
 }
Beispiel #24
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TaskAwaiter"/> struct.
 /// </summary>
 internal TaskAwaiter(CoyoteRuntime runtime, SystemTasks.Task awaitedTask)
 {
     this.Runtime     = runtime;
     this.AwaitedTask = awaitedTask;
     this.Awaiter     = awaitedTask.GetAwaiter();
 }
Beispiel #25
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TaskAwaiter"/> struct.
 /// </summary>
 internal TaskAwaiter(CoyoteRuntime runtime, SystemCompiler.TaskAwaiter awaiter)
 {
     this.Runtime     = runtime;
     this.AwaitedTask = null;
     this.Awaiter     = awaiter;
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="AsyncTaskMethodBuilder"/> struct.
 /// </summary>
 private AsyncTaskMethodBuilder(CoyoteRuntime runtime)
 {
     this.Runtime       = runtime;
     this.MethodBuilder = default;
 }
Beispiel #27
0
 internal Task(CoyoteRuntime runtime, SystemTasks.Task task)
 {
     this.Runtime      = runtime;
     this.InternalTask = task ?? throw new ArgumentNullException(nameof(task));
 }
Beispiel #28
0
        /// <summary>
        /// Execute the operation with the specified context.
        /// </summary>
        private void ExecuteOperation(object state)
        {
            // Extract the expected operation context from the task state.
            var context = state as OperationContext <Action, object>;

            TaskOperation     op        = context.Operation;
            CancellationToken ct        = context.CancellationToken;
            Exception         exception = null;

            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);

                // Notify the scheduler that the operation started. This will yield execution until
                // the operation is ready to get scheduled.
                this.Scheduler.StartOperation(op);
                if (context.Predecessor != null)
                {
                    // If there is a predecessor task, then wait until the predecessor completes.
                    ct.ThrowIfCancellationRequested();
                    op.OnWaitTask(context.Predecessor);
                }

                if (context.Options.HasFlag(OperationExecutionOptions.YieldAtStart))
                {
                    // Try yield execution to the next operation.
                    this.Scheduler.ScheduleNextOperation(true);
                }

                // Check if the operation must be canceled before starting the work.
                ct.ThrowIfCancellationRequested();

                // Start executing the work.
                context.Work();
            }
            catch (Exception ex)
            {
                if (context.Options.HasFlag(OperationExecutionOptions.FailOnException))
                {
                    this.Assert(false, "Unhandled exception. {0}", ex);
                }
                else
                {
                    // Unwrap and cache the exception to propagate it.
                    exception = ControlledRuntime.UnwrapException(ex);
                    this.ReportThrownException(exception);
                }
            }
            finally
            {
                IO.Debug.WriteLine("<ScheduleDebug> Completed operation '{0}' on task '{1}'.", op.Name, Task.CurrentId);
                op.OnCompleted();

                // Set the result task completion source to notify to the awaiters that the operation
                // has been completed, and schedule the next enabled operation.
                this.SetTaskCompletionSource(context.ResultSource, null, exception, default);
                this.Scheduler.ScheduleNextOperation();
            }
        }
Beispiel #29
0
        /// <summary>
        /// Execute the (asynchronous) operation with the specified context.
        /// </summary>
        private TResult ExecuteOperation <TWork, TExecutor, TResult>(object state)
        {
            // Extract the expected operation context from the task state.
            var context = state as AsyncOperationContext <TWork, TExecutor, TResult> ?? state as OperationContext <TWork, TResult>;

            TaskOperation     op        = context.Operation;
            CancellationToken ct        = context.CancellationToken;
            TResult           result    = default;
            Exception         exception = null;

            // The operation execution logic uses two task completion sources: (1) an executor TCS and (2) a result TCS.
            // We do this to model the execution of tasks in the .NET runtime. For example, the `Task.Factory.StartNew`
            // method has different semantics from `Task.Run`, e.g. the returned task from `Task.Factory.StartNew(Func<T>)`
            // completes at the start of an asynchronous operation, so someone cannot await on it for the completion of
            // the operation. Instead, someone needs to first use `task.Unwrap()`, and then await on the unwrapped task.
            // To model this, the executor TCS completes at the start of the operation, and contains in its `task.AsyncState`
            // a reference to the result TCS. This approach allows us to implement `task.Unwrap` in a way that gives access
            // to the result TCS, which someone can then await for the asynchronous completion of the operation.

            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);

                // Notify the scheduler that the operation started. This will yield execution until
                // the operation is ready to get scheduled.
                this.Scheduler.StartOperation(op);
                if (context is AsyncOperationContext <TWork, TExecutor, TResult> asyncContext)
                {
                    // If the operation is asynchronous, then set the executor task completion source, which
                    // can be used by `UnwrapTask` to unwrap and return the task executing this operation.
                    this.SetTaskCompletionSource(asyncContext.ExecutorSource, asyncContext.Executor, null, ct);
                }

                if (context.Predecessor != null)
                {
                    // If there is a predecessor task, then wait until the predecessor completes.
                    ct.ThrowIfCancellationRequested();
                    op.OnWaitTask(context.Predecessor);
                }

                // Check if the operation must be canceled before starting the work.
                ct.ThrowIfCancellationRequested();

                // Start executing the (asynchronous) work.
                Task executor = null;
                if (context.Work is Func <Task <TResult> > funcWithTaskResult)
                {
                    executor = funcWithTaskResult();
                }
                else if (context.Work is Func <Task> funcWithTask)
                {
                    executor = funcWithTask();
                }
                else if (context.Work is Func <CoyoteTasks.Task <TResult> > funcWithCoyoteTaskResult)
                {
                    // TODO: temporary until we remove the custom task type.
                    executor = funcWithCoyoteTaskResult().UncontrolledTask;
                }
                else if (context.Work is Func <CoyoteTasks.Task> funcWithCoyoteTask)
                {
                    // TODO: temporary until we remove the custom task type.
                    executor = funcWithCoyoteTask().UncontrolledTask;
                }
                else if (context.Work is Func <TResult> func)
                {
                    result = func();
                }
                else
                {
                    throw new NotSupportedException($"Unable to execute work with unsupported type {context.Work.GetType()}.");
                }

                if (executor != null)
                {
                    // If the work is asynchronous, then wait until it completes.
                    this.OnWaitTask(op.Id, executor);
                    if (executor.IsFaulted)
                    {
                        // Propagate the failing exception by rethrowing it.
                        ExceptionDispatchInfo.Capture(executor.Exception).Throw();
                    }
                    else if (executor.IsCanceled)
                    {
                        if (op.Exception != null)
                        {
                            // An exception has been already captured, so propagate it.
                            ExceptionDispatchInfo.Capture(op.Exception).Throw();
                        }
                        else
                        {
                            // Wait the canceled executor (which is non-blocking as it has already completed)
                            // to throw the generated `OperationCanceledException`.
                            executor.Wait();
                        }
                    }

                    // Safely get the result without blocking as the work has completed.
                    result = executor is Task <TResult> resultTask ? resultTask.Result :
                             executor is TResult r ? r : default;
                }
            }
            catch (Exception ex)
            {
                // Unwrap and cache the exception to propagate it.
                exception = ControlledRuntime.UnwrapException(ex);
                this.ReportThrownException(exception);
            }
            finally
            {
                IO.Debug.WriteLine("<ScheduleDebug> Completed operation '{0}' on task '{1}'.", op.Name, Task.CurrentId);
                op.OnCompleted();

                // Set the result task completion source to notify to the awaiters that the operation
                // has been completed, and schedule the next enabled operation.
                this.SetTaskCompletionSource(context.ResultSource, result, exception, default);
                this.Scheduler.ScheduleNextOperation();
            }

            return(result);
        }
Beispiel #30
0
        /// <summary>
        /// Runs the next testing iteration.
        /// </summary>
        private bool RunNextIteration(uint iteration)
        {
            if (!this.Strategy.InitializeNextIteration(iteration))
            {
                // The next iteration cannot run, so stop exploring.
                return(false);
            }

            if (!this.IsReplayModeEnabled && 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.Strategy, this.RandomValueGenerator);

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

                    runtime.Logger = runtimeLogger;

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

                this.InitializeCustomActorLogging(runtime.DefaultActorExecutionContext);

                // Runs the test and waits for it to terminate.
                runtime.RunTest(this.TestMethodInfo.Method, this.TestMethodInfo.Name);
                runtime.WaitAsync().Wait();

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

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

                if (!runtime.Scheduler.BugFound)
                {
                    // Checks for liveness errors. Only checked if no safety errors have been found.
                    runtime.CheckLivenessErrors();
                }

                if (runtime.Scheduler.BugFound)
                {
                    this.Logger.WriteLine(LogSeverity.Error, runtime.Scheduler.BugReport);
                }

                runtime.LogWriter.LogCompletion();

                this.GatherTestingStatistics(runtime);

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

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

                    this.ConstructReproducibleTrace(runtime);
                }
            }
            finally
            {
                if (!this.Configuration.IsVerbose)
                {
                    // Restores the standard output and error streams.
                    Console.SetOut(stdOut);
                    Console.SetError(stdErr);
                }

                if (!this.IsReplayModeEnabled && this.Configuration.PerformFullExploration && runtime.Scheduler.BugFound)
                {
                    this.Logger.WriteLine(LogSeverity.Important, $"..... Iteration #{iteration + 1} " +
                                          $"triggered bug #{this.TestReport.NumOfFoundBugs} " +
                                          $"[task-{this.Configuration.TestingProcessId}]");
                }

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

            return(true);
        }