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