/// <summary> /// Initializes a new instance of the <see cref="ScheduleExecutionInfo"/> class. /// </summary> /// <param name="parent">The information object for the parent schedule.</param> public ScheduleExecutionInfo(ScheduleExecutionInfo parent) : this() { m_Parent = parent; if (m_Parent != null) { m_CombinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource( m_Parent.Cancellation, m_LocalCancellationSource.Token); } }
public void RunWithSubScheduleVertex() { var subExecutor = new Mock <IExecuteSchedules>(); { subExecutor.Setup(s => s.IsLocal) .Returns(false); } var distributor = new Mock <IDistributeScheduleExecutions>(); { distributor.Setup( d => d.Execute( It.IsAny <ScheduleId>(), It.IsAny <IEnumerable <IScheduleVariable> >(), It.IsAny <ScheduleExecutionInfo>(), It.IsAny <bool>())) .Returns(subExecutor.Object); } var id = new ScheduleId(); var schedule = BuildThreeVertexSchedule(new SubScheduleVertex(3, id)); using (var info = new ScheduleExecutionInfo(new CurrentThreadTaskScheduler())) { using (var executor = new ScheduleExecutor( new List <IProcesExecutableScheduleVertices> { new StartVertexProcessor(), new EndVertexProcessor(), new SubScheduleVertexProcessor(distributor.Object), }, ScheduleConditionStorage.CreateInstanceWithoutTimeline(), schedule, new ScheduleId(), info)) { var state = ScheduleExecutionState.None; executor.OnFinish += (s, e) => { state = e.State; }; executor.Start(); Assert.AreEqual(ScheduleExecutionState.Completed, state); } } }
private IExecuteSchedules ExecuteInProcess( ScheduleId scheduleId, IEnumerable <IScheduleVariable> scheduleParameters, ScheduleExecutionInfo executionInfo) { // Translate the schedule to an executable schedule var editableSchedule = m_KnownSchedules.Schedule(scheduleId); // Create a new executor and provide it with the schedule var executor = m_LoadExecutor(editableSchedule, scheduleId, executionInfo); { // Attach to events. We want to remove the executor from the collection as soon as it's finished executor.OnFinish += HandleScheduleExecutionFinish; m_RunningExecutors.Add(new ExecutingScheduleKey(scheduleId, scheduleParameters), executor); executor.Start(scheduleParameters); } return(executor); }
public void RunWithActionVertex() { var action = new Mock <IScheduleAction>(); { action.Setup(a => a.Execute(It.IsAny <CancellationToken>())) .Verifiable(); } var collection = ScheduleActionStorage.CreateInstanceWithoutTimeline(); var info = collection.Add( action.Object, "a", "b"); var schedule = BuildThreeVertexSchedule(new ExecutingActionVertex(3, info.Id)); using (var executionInfo = new ScheduleExecutionInfo(new CurrentThreadTaskScheduler())) { using (var executor = new ScheduleExecutor( new List <IProcesExecutableScheduleVertices> { new StartVertexProcessor(), new EndVertexProcessor(), new ActionVertexProcessor(collection), }, ScheduleConditionStorage.CreateInstanceWithoutTimeline(), schedule, new ScheduleId(), executionInfo)) { var state = ScheduleExecutionState.None; executor.OnFinish += (s, e) => { state = e.State; }; executor.Start(); Assert.AreEqual(ScheduleExecutionState.Completed, state); action.Verify(a => a.Execute(It.IsAny <CancellationToken>()), Times.Once()); } } }
/// <summary> /// Determines the most suitable execution location for the given schedule and then starts the execution in that location. /// </summary> /// <param name="scheduleId">The ID of the schedule that should be executed.</param> /// <param name="scheduleParameters">The collection of parameters that have to be provided to the schedule before executing.</param> /// <param name="executionInfo">The object that provides information about the schedule that is currently being executed.</param> /// <param name="executeOutOfProcess">A flag indicating if the schedule should be executed in another processor or not.</param> /// <returns> /// The token that is related to the execution of the given schedule. /// </returns> /// <remarks> /// For local schedules the cancellation token is checked regularly and the schedule execution is cancelled on request. For /// remote schedule execution setting the cancellation token means that a termination signal is send to the remote /// application. /// </remarks> public IExecuteSchedules Execute( ScheduleId scheduleId, IEnumerable <IScheduleVariable> scheduleParameters = null, ScheduleExecutionInfo executionInfo = null, bool executeOutOfProcess = false) { lock (m_Lock) { if (!m_KnownSchedules.Contains(scheduleId)) { throw new UnknownScheduleException(); } var key = new ExecutingScheduleKey(scheduleId, scheduleParameters); if (m_RunningExecutors.ContainsKey(key)) { return(m_RunningExecutors[key]); } if (executeOutOfProcess) { // Determine what data needs to be serialized for schedule to execute // Serialize all data + components + schedules // Start external app // Feed data stream of data + components + schedules // Indicate which schedule should be executed + parameters // Start // Return distributed schedule executor // --> This is all based on a set of components that can handle all kinds of distribution methods / desires // For instance we eventually also want to do Domain Decomp this way. throw new NotImplementedException(); } else { return(ExecuteInProcess(scheduleId, scheduleParameters, executionInfo)); } } }
public void RunWithActionVertex() { var action = new Mock<IScheduleAction>(); { action.Setup(a => a.Execute(It.IsAny<CancellationToken>())) .Verifiable(); } var collection = ScheduleActionStorage.CreateInstanceWithoutTimeline(); var info = collection.Add( action.Object, "a", "b"); var schedule = BuildThreeVertexSchedule(new ExecutingActionVertex(3, info.Id)); using (var executionInfo = new ScheduleExecutionInfo(new CurrentThreadTaskScheduler())) { using (var executor = new ScheduleExecutor( new List<IProcesExecutableScheduleVertices> { new StartVertexProcessor(), new EndVertexProcessor(), new ActionVertexProcessor(collection), }, ScheduleConditionStorage.CreateInstanceWithoutTimeline(), schedule, new ScheduleId(), executionInfo)) { var state = ScheduleExecutionState.None; executor.OnFinish += (s, e) => { state = e.State; }; executor.Start(); Assert.AreEqual(ScheduleExecutionState.Completed, state); action.Verify(a => a.Execute(It.IsAny<CancellationToken>()), Times.Once()); } } }
/// <summary> /// Initializes a new instance of the <see cref="ScheduleExecutor"/> class. /// </summary> /// <param name="executors">The collection of vertex processors.</param> /// <param name="conditions">The collection of execution conditions.</param> /// <param name="schedule">The schedule that should be executed.</param> /// <param name="id">The ID of the schedule that is being executed.</param> /// <param name="executionInfo">The object that stores information about the current running schedule.</param> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="executors"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="conditions"/> is <see langword="null" />. /// </exception> /// <exception cref="CannotExecuteScheduleWithoutProcessorsException"> /// Thrown if <paramref name="executors"/> is an empty collection. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="schedule"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="id"/> is <see langword="null" />. /// </exception> public ScheduleExecutor( IEnumerable <IProcesExecutableScheduleVertices> executors, IStoreScheduleConditions conditions, ISchedule schedule, ScheduleId id, ScheduleExecutionInfo executionInfo = null) { { Lokad.Enforce.Argument(() => executors); Lokad.Enforce.With <CannotExecuteScheduleWithoutProcessorsException>( executors.Any(), Resources.Exceptions_Messages_CannotExecuteScheduleWithoutProcessors); Lokad.Enforce.Argument(() => conditions); Lokad.Enforce.Argument(() => schedule); Lokad.Enforce.Argument(() => id); } m_Executors = executors.ToDictionary(v => v.VertexTypeToProcess, v => v); m_Conditions = conditions; m_Schedule = schedule; m_ScheduleId = id; m_ExecutionInfo = new ScheduleExecutionInfo(executionInfo); }
public void RunWithBlockingConditionOnFirstEdge() { var condition = new Mock<IScheduleCondition>(); { condition.Setup(c => c.CanTraverse(It.IsAny<CancellationToken>())) .Returns(false); } var conditionStorage = ScheduleConditionStorage.CreateInstanceWithoutTimeline(); var conditionInfo = conditionStorage.Add(condition.Object, "a", "b"); Schedule schedule; { var graph = new BidirectionalGraph<IScheduleVertex, ScheduleEdge>(); var start = new StartVertex(1); graph.AddVertex(start); var end = new EndVertex(2); graph.AddVertex(end); var middle1 = new InsertVertex(3); graph.AddVertex(middle1); var middle2 = new InsertVertex(4); graph.AddVertex(middle2); graph.AddEdge(new ScheduleEdge(start, middle1, conditionInfo.Id)); graph.AddEdge(new ScheduleEdge(start, middle2)); graph.AddEdge(new ScheduleEdge(middle1, end)); graph.AddEdge(new ScheduleEdge(middle2, end)); schedule = new Schedule(graph, start, end); } using (var info = new ScheduleExecutionInfo(new CurrentThreadTaskScheduler())) { using (var executor = new ScheduleExecutor( new List<IProcesExecutableScheduleVertices> { new StartVertexProcessor(), new EndVertexProcessor(), new InsertVertexProcessor(), }, conditionStorage, schedule, new ScheduleId(), info)) { var executionOrder = new List<int>(); executor.OnVertexProcess += (s, e) => executionOrder.Add(e.Vertex); executor.Start(); Assert.That(executionOrder, Is.EquivalentTo(new[] { 1, 4, 2 })); } } }
public void RunWithSubScheduleVertex() { var subExecutor = new Mock<IExecuteSchedules>(); { subExecutor.Setup(s => s.IsLocal) .Returns(false); } var distributor = new Mock<IDistributeScheduleExecutions>(); { distributor.Setup( d => d.Execute( It.IsAny<ScheduleId>(), It.IsAny<IEnumerable<IScheduleVariable>>(), It.IsAny<ScheduleExecutionInfo>(), It.IsAny<bool>())) .Returns(subExecutor.Object); } var id = new ScheduleId(); var schedule = BuildThreeVertexSchedule(new SubScheduleVertex(3, id)); using (var info = new ScheduleExecutionInfo(new CurrentThreadTaskScheduler())) { using (var executor = new ScheduleExecutor( new List<IProcesExecutableScheduleVertices> { new StartVertexProcessor(), new EndVertexProcessor(), new SubScheduleVertexProcessor(distributor.Object), }, ScheduleConditionStorage.CreateInstanceWithoutTimeline(), schedule, new ScheduleId(), info)) { var state = ScheduleExecutionState.None; executor.OnFinish += (s, e) => { state = e.State; }; executor.Start(); Assert.AreEqual(ScheduleExecutionState.Completed, state); } } }
public void RunWithNoOpVertex() { var schedule = BuildThreeVertexSchedule(new InsertVertex(3)); using (var info = new ScheduleExecutionInfo(new CurrentThreadTaskScheduler())) { using (var executor = new ScheduleExecutor( new List<IProcesExecutableScheduleVertices> { new StartVertexProcessor(), new EndVertexProcessor(), new InsertVertexProcessor(), }, ScheduleConditionStorage.CreateInstanceWithoutTimeline(), schedule, new ScheduleId(), info)) { var state = ScheduleExecutionState.None; executor.OnFinish += (s, e) => { state = e.State; }; executor.Start(); Assert.AreEqual(ScheduleExecutionState.Completed, state); } } }
public void RunWithNonPassableEdgeSet() { var condition = new Mock<IScheduleCondition>(); { condition.Setup(c => c.CanTraverse(It.IsAny<CancellationToken>())) .Returns(false); } var conditionStorage = ScheduleConditionStorage.CreateInstanceWithoutTimeline(); var conditionInfo = conditionStorage.Add(condition.Object, "a", "b"); Schedule schedule; { var graph = new BidirectionalGraph<IScheduleVertex, ScheduleEdge>(); var start = new StartVertex(1); graph.AddVertex(start); var end = new EndVertex(2); graph.AddVertex(end); var middle1 = new InsertVertex(3); graph.AddVertex(middle1); var middle2 = new InsertVertex(4); graph.AddVertex(middle2); graph.AddEdge(new ScheduleEdge(start, middle1, conditionInfo.Id)); graph.AddEdge(new ScheduleEdge(start, middle2, conditionInfo.Id)); graph.AddEdge(new ScheduleEdge(middle1, end)); graph.AddEdge(new ScheduleEdge(middle2, end)); schedule = new Schedule(graph, start, end); } using (var info = new ScheduleExecutionInfo(new CurrentThreadTaskScheduler())) { using (var executor = new ScheduleExecutor( new List<IProcesExecutableScheduleVertices> { new StartVertexProcessor(), new EndVertexProcessor(), new InsertVertexProcessor(), }, conditionStorage, schedule, new ScheduleId(), info)) { var state = ScheduleExecutionState.None; executor.OnFinish += (s, e) => { state = e.State; }; executor.Start(); Assert.AreEqual(ScheduleExecutionState.NoTraversableEdgeFound, state); } } }
public void RunWithMarkHistoryVertex() { var marker = new TimeMarker(10); var timeline = new Mock<ITimeline>(); { timeline.Setup(t => t.Mark()) .Returns(marker); } TimeMarker storedMarker = null; var schedule = BuildThreeVertexSchedule(new MarkHistoryVertex(3)); using (var info = new ScheduleExecutionInfo(new CurrentThreadTaskScheduler())) { using (var executor = new ScheduleExecutor( new List<IProcesExecutableScheduleVertices> { new StartVertexProcessor(), new EndVertexProcessor(), new MarkHistoryVertexProcessor(timeline.Object, m => storedMarker = m), }, ScheduleConditionStorage.CreateInstanceWithoutTimeline(), schedule, new ScheduleId(), info)) { var state = ScheduleExecutionState.None; executor.OnFinish += (s, e) => { state = e.State; }; executor.Start(); Assert.AreEqual(ScheduleExecutionState.Completed, state); Assert.AreEqual(marker, storedMarker); } } }
public void RunWithLoop() { bool passThrough = false; var condition = new Mock<IScheduleCondition>(); { condition.Setup(c => c.CanTraverse(It.IsAny<CancellationToken>())) .Returns(() => passThrough); } var conditionStorage = ScheduleConditionStorage.CreateInstanceWithoutTimeline(); var conditionInfo = conditionStorage.Add(condition.Object, "a", "b"); var action = new Mock<IScheduleAction>(); { action.Setup(a => a.Execute(It.IsAny<CancellationToken>())) .Callback(() => passThrough = true); } var collection = ScheduleActionStorage.CreateInstanceWithoutTimeline(); var info = collection.Add( action.Object, "a", "b"); // Making a schedule that looks like: // start -> node1 --> node2 -> end // ^ | // |-- node3 <-| Schedule schedule; { var graph = new BidirectionalGraph<IScheduleVertex, ScheduleEdge>(); var start = new StartVertex(1); graph.AddVertex(start); var end = new EndVertex(2); graph.AddVertex(end); var vertex1 = new InsertVertex(3); graph.AddVertex(vertex1); var vertex2 = new InsertVertex(4); graph.AddVertex(vertex2); var vertex3 = new ExecutingActionVertex(5, info.Id); graph.AddVertex(vertex3); graph.AddEdge(new ScheduleEdge(start, vertex1)); graph.AddEdge(new ScheduleEdge(vertex1, vertex2)); graph.AddEdge(new ScheduleEdge(vertex2, end, conditionInfo.Id)); graph.AddEdge(new ScheduleEdge(vertex2, vertex3)); graph.AddEdge(new ScheduleEdge(vertex3, vertex1)); schedule = new Schedule(graph, start, end); } using (var executionInfo = new ScheduleExecutionInfo(new CurrentThreadTaskScheduler())) { using (var executor = new ScheduleExecutor( new List<IProcesExecutableScheduleVertices> { new StartVertexProcessor(), new EndVertexProcessor(), new InsertVertexProcessor(), new ActionVertexProcessor(collection), }, conditionStorage, schedule, new ScheduleId(), executionInfo)) { var executionOrder = new List<int>(); executor.OnVertexProcess += (s, e) => executionOrder.Add(e.Vertex); executor.Start(); Assert.That(executionOrder, Is.EquivalentTo(new[] { 1, 3, 4, 5, 3, 4, 2 })); } } }
public void RunWithInnerAndOuterLoop() { bool outerLoopPassThrough = false; var outerLoopCondition = new Mock<IScheduleCondition>(); { outerLoopCondition.Setup(c => c.CanTraverse(It.IsAny<CancellationToken>())) .Returns(() => outerLoopPassThrough); } bool innerLoopPassThrough = false; var innerLoopCondition = new Mock<IScheduleCondition>(); { innerLoopCondition.Setup(c => c.CanTraverse(It.IsAny<CancellationToken>())) .Returns(() => innerLoopPassThrough); } var conditionStorage = ScheduleConditionStorage.CreateInstanceWithoutTimeline(); var outerLoopConditionInfo = conditionStorage.Add( outerLoopCondition.Object, "a", "b"); var innerLoopConditionInfo = conditionStorage.Add( innerLoopCondition.Object, "a", "b"); var outerLoopAction = new Mock<IScheduleAction>(); { outerLoopAction.Setup(a => a.Execute(It.IsAny<CancellationToken>())) .Callback(() => outerLoopPassThrough = true); } var innerLoopAction = new Mock<IScheduleAction>(); { innerLoopAction.Setup(a => a.Execute(It.IsAny<CancellationToken>())) .Callback(() => innerLoopPassThrough = true); } var collection = ScheduleActionStorage.CreateInstanceWithoutTimeline(); var outerLoopInfo = collection.Add( outerLoopAction.Object, "a", "b"); var innerLoopInfo = collection.Add( innerLoopAction.Object, "a", "b"); // Making a schedule that looks like: // start -> node1 --> node2 -> end // ^ | // |-- node3 <-| // ^ | // node5--| |-> node4 // ^ | // |--------------| Schedule schedule; { schedule = CreateScheduleGraphWithOuterAndInnerLoop(outerLoopConditionInfo, innerLoopConditionInfo, outerLoopInfo, innerLoopInfo); } using (var info = new ScheduleExecutionInfo(new CurrentThreadTaskScheduler())) { using (var executor = new ScheduleExecutor( new List<IProcesExecutableScheduleVertices> { new StartVertexProcessor(), new EndVertexProcessor(), new InsertVertexProcessor(), new ActionVertexProcessor(collection), }, conditionStorage, schedule, new ScheduleId(), info)) { var executionOrder = new List<int>(); executor.OnVertexProcess += (s, e) => executionOrder.Add(e.Vertex); executor.Start(); Assert.That(executionOrder, Is.EquivalentTo(new[] { 1, 3, 4, 5, 6, 7, 5, 3, 4, 2 })); } } }
/// <summary> /// Initializes a new instance of the <see cref="ScheduleExecutor"/> class. /// </summary> /// <param name="executors">The collection of vertex processors.</param> /// <param name="conditions">The collection of execution conditions.</param> /// <param name="schedule">The schedule that should be executed.</param> /// <param name="id">The ID of the schedule that is being executed.</param> /// <param name="executionInfo">The object that stores information about the current running schedule.</param> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="executors"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="conditions"/> is <see langword="null" />. /// </exception> /// <exception cref="CannotExecuteScheduleWithoutProcessorsException"> /// Thrown if <paramref name="executors"/> is an empty collection. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="schedule"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="id"/> is <see langword="null" />. /// </exception> public ScheduleExecutor( IEnumerable<IProcesExecutableScheduleVertices> executors, IStoreScheduleConditions conditions, ISchedule schedule, ScheduleId id, ScheduleExecutionInfo executionInfo = null) { { Lokad.Enforce.Argument(() => executors); Lokad.Enforce.With<CannotExecuteScheduleWithoutProcessorsException>( executors.Any(), Resources.Exceptions_Messages_CannotExecuteScheduleWithoutProcessors); Lokad.Enforce.Argument(() => conditions); Lokad.Enforce.Argument(() => schedule); Lokad.Enforce.Argument(() => id); } m_Executors = executors.ToDictionary(v => v.VertexTypeToProcess, v => v); m_Conditions = conditions; m_Schedule = schedule; m_ScheduleId = id; m_ExecutionInfo = new ScheduleExecutionInfo(executionInfo); }
public void RunWithLoop() { bool passThrough = false; var condition = new Mock <IScheduleCondition>(); { condition.Setup(c => c.CanTraverse(It.IsAny <CancellationToken>())) .Returns(() => passThrough); } var conditionStorage = ScheduleConditionStorage.CreateInstanceWithoutTimeline(); var conditionInfo = conditionStorage.Add(condition.Object, "a", "b"); var action = new Mock <IScheduleAction>(); { action.Setup(a => a.Execute(It.IsAny <CancellationToken>())) .Callback(() => passThrough = true); } var collection = ScheduleActionStorage.CreateInstanceWithoutTimeline(); var info = collection.Add( action.Object, "a", "b"); // Making a schedule that looks like: // start -> node1 --> node2 -> end // ^ | // |-- node3 <-| Schedule schedule; { var graph = new BidirectionalGraph <IScheduleVertex, ScheduleEdge>(); var start = new StartVertex(1); graph.AddVertex(start); var end = new EndVertex(2); graph.AddVertex(end); var vertex1 = new InsertVertex(3); graph.AddVertex(vertex1); var vertex2 = new InsertVertex(4); graph.AddVertex(vertex2); var vertex3 = new ExecutingActionVertex(5, info.Id); graph.AddVertex(vertex3); graph.AddEdge(new ScheduleEdge(start, vertex1)); graph.AddEdge(new ScheduleEdge(vertex1, vertex2)); graph.AddEdge(new ScheduleEdge(vertex2, end, conditionInfo.Id)); graph.AddEdge(new ScheduleEdge(vertex2, vertex3)); graph.AddEdge(new ScheduleEdge(vertex3, vertex1)); schedule = new Schedule(graph, start, end); } using (var executionInfo = new ScheduleExecutionInfo(new CurrentThreadTaskScheduler())) { using (var executor = new ScheduleExecutor( new List <IProcesExecutableScheduleVertices> { new StartVertexProcessor(), new EndVertexProcessor(), new InsertVertexProcessor(), new ActionVertexProcessor(collection), }, conditionStorage, schedule, new ScheduleId(), executionInfo)) { var executionOrder = new List <int>(); executor.OnVertexProcess += (s, e) => executionOrder.Add(e.Vertex); executor.Start(); Assert.That(executionOrder, Is.EquivalentTo(new[] { 1, 3, 4, 5, 3, 4, 2 })); } } }
public void RunWithInnerAndOuterLoop() { bool outerLoopPassThrough = false; var outerLoopCondition = new Mock <IScheduleCondition>(); { outerLoopCondition.Setup(c => c.CanTraverse(It.IsAny <CancellationToken>())) .Returns(() => outerLoopPassThrough); } bool innerLoopPassThrough = false; var innerLoopCondition = new Mock <IScheduleCondition>(); { innerLoopCondition.Setup(c => c.CanTraverse(It.IsAny <CancellationToken>())) .Returns(() => innerLoopPassThrough); } var conditionStorage = ScheduleConditionStorage.CreateInstanceWithoutTimeline(); var outerLoopConditionInfo = conditionStorage.Add( outerLoopCondition.Object, "a", "b"); var innerLoopConditionInfo = conditionStorage.Add( innerLoopCondition.Object, "a", "b"); var outerLoopAction = new Mock <IScheduleAction>(); { outerLoopAction.Setup(a => a.Execute(It.IsAny <CancellationToken>())) .Callback(() => outerLoopPassThrough = true); } var innerLoopAction = new Mock <IScheduleAction>(); { innerLoopAction.Setup(a => a.Execute(It.IsAny <CancellationToken>())) .Callback(() => innerLoopPassThrough = true); } var collection = ScheduleActionStorage.CreateInstanceWithoutTimeline(); var outerLoopInfo = collection.Add( outerLoopAction.Object, "a", "b"); var innerLoopInfo = collection.Add( innerLoopAction.Object, "a", "b"); // Making a schedule that looks like: // start -> node1 --> node2 -> end // ^ | // |-- node3 <-| // ^ | // node5--| |-> node4 // ^ | // |--------------| Schedule schedule; { schedule = CreateScheduleGraphWithOuterAndInnerLoop(outerLoopConditionInfo, innerLoopConditionInfo, outerLoopInfo, innerLoopInfo); } using (var info = new ScheduleExecutionInfo(new CurrentThreadTaskScheduler())) { using (var executor = new ScheduleExecutor( new List <IProcesExecutableScheduleVertices> { new StartVertexProcessor(), new EndVertexProcessor(), new InsertVertexProcessor(), new ActionVertexProcessor(collection), }, conditionStorage, schedule, new ScheduleId(), info)) { var executionOrder = new List <int>(); executor.OnVertexProcess += (s, e) => executionOrder.Add(e.Vertex); executor.Start(); Assert.That(executionOrder, Is.EquivalentTo(new[] { 1, 3, 4, 5, 6, 7, 5, 3, 4, 2 })); } } }