public static WorkplanDummy CreatePausableSub() { var workplan = new WorkplanDummy(); var inital = Workflow.CreateConnector("Start", NodeClassification.Start); var complete = Workflow.CreateConnector("End", NodeClassification.End); workplan.Add(inital, complete); var step = new DummyStep(1, "A"); step.Inputs[0] = inital; workplan.Add(step); var aComplete = Workflow.CreateConnector("A1"); workplan.Add(aComplete); step.Outputs[0] = aComplete; var finalStep = new PausableStep(); finalStep.Inputs[0] = aComplete; workplan.Add(finalStep); finalStep.Outputs[0] = complete; return(workplan); }
public void PredictorShouldSupportLoops() { // Arrange var workplan = WorkplanDummy.WithLoop(); var predictor = Workflow.PathPrediction(workplan); var executor = new SingleLoopExecution(); NodeClassification prediction = NodeClassification.Intermediate; predictor.PathPrediction += (sender, args) => prediction = args.PredictedOutcome; var engine = Workflow.CreateEngine(workplan, new NullContext()); engine.ExecutedWorkflow.Transitions.OfType <DummyTransition>().ForEach(dt => dt.ResultOutput = -1); // Disable automatic execution // Act var finalResult = NodeClassification.Intermediate; engine.Completed += (sender, place) => { finalResult = place.Classification; }; engine.TransitionTriggered += (sender, transition) => ThreadPool.QueueUserWorkItem(executor.ResumeAsync, transition); predictor.Monitor(engine); engine.Start(); // Assert while (finalResult == NodeClassification.Intermediate) { Thread.Sleep(1); // Await completion } Assert.AreEqual(finalResult, prediction, "Predication was incorrect"); }
public void SubWorkflow() { // Arrange var workplan = WorkplanDummy.CreateSub(); var step = new SubworkflowStep(workplan); var exits = workplan.Connectors.Where(c => c.Classification.HasFlag(NodeClassification.Exit)).ToArray(); // Act var trans = step.CreateInstance(null); // Assert Assert.IsInstanceOf <SubworkflowTransition>(trans); Assert.AreEqual(2, step.Outputs.Length); Assert.AreEqual(2, step.OutputDescriptions.Length); var success = step.OutputDescriptions[0]; Assert.AreEqual("End", success.Name); Assert.AreEqual(OutputType.Success, success.OutputType); Assert.AreEqual(exits[0].Id, success.MappingValue); var failed = step.OutputDescriptions[1]; Assert.AreEqual("Failed", failed.Name); Assert.AreEqual(OutputType.Failure, failed.OutputType); Assert.AreEqual(exits[1].Id, failed.MappingValue); }
public void ExecuteWorkflow(ExecutionPath route, int expectedTransitions, string expectedPath) { // Arrange var workplan = WorkplanDummy.CreateFull(); var engine = Workflow.CreateEngine(workplan, new NullContext()); // Act var triggerCount = 0; var path = string.Empty; engine.TransitionTriggered += delegate(object sender, ITransition transition) { var dummy = (DummyTransition)transition; if (route == ExecutionPath.Alternative) { dummy.ResultOutput = dummy.Outputs.Length - 1; } triggerCount++; path += "->" + dummy.Name; }; engine.Completed += EngineCompleted; engine.Start(); // Synchronus execution means we are done here Workflow.Destroy(engine); // Assert Assert.IsTrue(_completed); Assert.AreEqual(expectedTransitions, triggerCount, "Less transitions triggered than expected!"); Assert.AreEqual(expectedPath, path, "Workflow engine did not take the correct path!"); }
public void SubWorkflowTransition() { // Arrange var workplan = WorkplanDummy.CreateSub(); var exits = workplan.Connectors.Where(c => c.Classification.HasFlag(NodeClassification.Exit)).ToArray(); var outputs = new[] { new OutputDescription { MappingValue = (int)exits[0].Id }, new OutputDescription { MappingValue = (int)exits[1].Id }, }; var trans = new SubworkflowTransition(Workflow.CreateEngine(workplan, new NullContext()), TransitionBase.CreateIndexResolver(outputs)) { Id = 1, Inputs = new[] { _inputs[0] }, Outputs = _outputs }; var triggered = new List <ITransition>(); // Act trans.Initialize(); trans.Triggered += (sender, args) => triggered.Add((ITransition)sender); _inputs[0].Add(_token); // Assert Assert.AreEqual(0, _inputs[0].Tokens.Count()); Assert.AreEqual(_token, _outputs[0].Tokens.First()); Assert.AreEqual(2, triggered.Count); Assert.IsTrue(triggered.All(t => t is DummyTransition)); }
public static WorkplanDummy CreateMixed() { var workplan = new WorkplanDummy(); var inital = Workflow.CreateConnector("Start", NodeClassification.Start); var complete = Workflow.CreateConnector("End", NodeClassification.End); workplan.Add(inital, complete); var step = new DummyStep(1, "A"); step.Inputs[0] = inital; // workplan.Add(step); // Add first step at the end var aComplete = Workflow.CreateConnector("A1"); workplan.Add(aComplete); step.Outputs[0] = aComplete; var finalStep = new DummyStep(1, "B"); finalStep.Inputs[0] = aComplete; workplan.Add(finalStep); finalStep.Outputs[0] = complete; workplan.Add(step); // Mix order of steps return(workplan); }
public void PauseAndResume() { // Arrange var workplan = WorkplanDummy.CreatePausable(); var engine = Workflow.CreateEngine(workplan, new NullContext()); engine.TransitionTriggered += (sender, transition) => { }; engine.Completed += EngineCompleted; // Act engine.Start(); // <-- This runs till the first pausable transition var snapShot = engine.Pause(); // Once we resume it will continue engine.Start(); // Assert var stepId = workplan.Steps.Single(s => s is PausableStep).Id; Assert.IsTrue(_completed); Assert.AreEqual(1, snapShot.Holders.Length); Assert.AreEqual(stepId, snapShot.Holders[0].HolderId); Assert.IsInstanceOf <MainToken>(snapShot.Holders[0].HolderState); Assert.AreEqual(1, snapShot.Holders[0].Tokens.Length); }
public void RestoreAndResume() { // Arrange var workplan = WorkplanDummy.CreatePausable(); var engine = Workflow.CreateEngine(workplan, new NullContext()); var stepId = workplan.Steps.Single(s => s is PausableStep).Id; var snapShot = new WorkflowSnapshot { Holders = new[] { new HolderSnapshot { HolderId = stepId, Tokens = new IToken[] { new MainToken() } } } }; // Act engine.Restore(snapShot); engine.Completed += EngineCompleted; engine.TransitionTriggered += (sender, transition) => { }; engine.Start(); // <-- This run to the end // Assert Assert.IsTrue(_completed); }
public static WorkplanDummy WithLoop() { var workplan = new WorkplanDummy(); var inital = Workflow.CreateConnector("Start", NodeClassification.Start); var complete = Workflow.CreateConnector("End", NodeClassification.End); var failed = Workflow.CreateConnector("Failed", NodeClassification.Failed); workplan.Add(inital, complete, failed); var step = new DummyStep(2, "Feed case"); step.Inputs[0] = inital; workplan.Add(step); var left = Workflow.CreateConnector("Left"); workplan.Add(left); step.Outputs[0] = left; var right = Workflow.CreateConnector("Right"); workplan.Add(right); step.Outputs[1] = right; step = new DummyStep(3, "Mount"); step.Inputs[0] = left; workplan.Add(step); step.Outputs[2] = right; left = Workflow.CreateConnector("Merge"); workplan.Add(left); step.Outputs[0] = step.Outputs[1] = left; step = new DummyStep(3, "Set pole"); step.Inputs[0] = left; workplan.Add(step); var oldLeft = left; left = Workflow.CreateConnector("Pole set"); workplan.Add(left); step.Outputs[0] = left; step.Outputs[1] = oldLeft; step.Outputs[2] = failed; step = new DummyStep(3, "Set screw"); step.Inputs[0] = left; workplan.Add(step); step.Outputs[0] = right; step.Outputs[1] = complete; step.Outputs[2] = failed; var rightOnly = new DummyStep(1, "Remove case"); rightOnly.Inputs[0] = right; rightOnly.Outputs[0] = failed; workplan.Add(rightOnly); return(workplan); }
public void ValidateFullSuccess() { // Arrange var workplan = WorkplanDummy.CreateFull(); // Act var validation = Workflow.Validate(workplan, ValidationAspect.DeadEnd | ValidationAspect.LoneWolf); // Assert Assert.IsTrue(validation.Success, "Validation did not return success for a valid workplan!"); Assert.AreEqual(0, validation.Errors.Length, "Valid workplan must not report errors!"); }
public void ValidateDeadEnd() { // Arrange var workplan = WorkplanDummy.CreateDeadEnd(); // Act var validation = Workflow.Validate(workplan, ValidationAspect.DeadEnd); // Assert Assert.IsFalse(validation.Success, "Validation did not detect error!"); Assert.AreEqual(1, validation.Errors.Length, "Validation should have found one error!"); Assert.IsInstanceOf <DeadEndValidationError>(validation.Errors[0], "Error should be of type \"DeadEndValidationError\""); var expected = workplan.Connectors.First(c => c.Name == "DeadEnd"); Assert.AreEqual(expected.Id, validation.Errors[0].PositionId); var error = validation.Errors[0].Print(workplan); Assert.NotNull(error); }
public void NoPredictionIfNotPossible() { // Arrange var workplan = WorkplanDummy.CreateBig(); bool triggered = false; var predictor = Workflow.PathPrediction(workplan); predictor.PathPrediction += (sender, args) => triggered = triggered = true; // Act var engine = Workflow.CreateEngine(workplan, new NullContext()); engine.Completed += (sender, place) => { }; engine.TransitionTriggered += (sender, transition) => { }; predictor.Monitor(engine); engine.Start(); // Assert Assert.IsFalse(triggered, "Path predictor should not have been activiated"); }
public static WorkplanDummy CreateWithSub() { var workplan = new WorkplanDummy(); var inital = Workflow.CreateConnector("Start", NodeClassification.Start); var complete = Workflow.CreateConnector("End", NodeClassification.End); workplan.Add(inital, complete); var subPlan = CreateSub(); subPlan.Id = 42; var sub = new SubworkflowStep(subPlan); sub.Inputs[0] = inital; sub.Outputs[0] = sub.Outputs[0] = complete; workplan.Add(sub); return(workplan); }
public void SubworkflowPause() { // Arrange var workplan = WorkplanDummy.CreatePausableSub(); var exits = workplan.Connectors.Where(c => c.Classification.HasFlag(NodeClassification.Exit)).ToArray(); var outputs = new[] { new OutputDescription { MappingValue = (int)exits[0].Id }, }; var trans = new SubworkflowTransition(Workflow.CreateEngine(workplan, new NullContext()), TransitionBase.CreateIndexResolver(outputs)) { Id = 1, Inputs = new[] { _inputs[0] }, Outputs = _outputs }; var triggered = new List <ITransition>(); // Act trans.Initialize(); trans.Triggered += (sender, args) => triggered.Add((ITransition)sender); _inputs[0].Add(_token); trans.Pause(); var state = trans.InternalState; trans.Resume(); // Assert Assert.AreEqual(0, _inputs[0].Tokens.Count()); Assert.AreEqual(_token, _outputs[0].Tokens.First()); Assert.AreEqual(1, triggered.Count); Assert.IsInstanceOf <WorkflowSnapshot>(state); var snapshot = (WorkflowSnapshot)state; Assert.AreEqual(1, snapshot.Holders.Length); var stepId = workplan.Steps.First(s => s is PausableStep).Id; Assert.AreEqual(stepId, snapshot.Holders[0].HolderId); }
public void PredictFailureBeforeCompletion() { // Arrange var stopWatch = new Stopwatch(); var workplan = WorkplanDummy.CreateBig(); var predictor = Workflow.PathPrediction(workplan); long predictionTime = long.MaxValue; NodeClassification prediction = NodeClassification.Intermediate; predictor.PathPrediction += (sender, args) => { prediction = args.PredictedOutcome; predictionTime = stopWatch.ElapsedMilliseconds; }; var engine = Workflow.CreateEngine(workplan, new NullContext()); engine.ExecutedWorkflow.Transitions.OfType <DummyTransition>().ForEach(dt => dt.ResultOutput = -1); // Disable automatic execution // Act long completionTime = 0; var finalResult = NodeClassification.Intermediate; engine.Completed += (sender, place) => { completionTime = stopWatch.ElapsedMilliseconds; finalResult = place.Classification; }; engine.TransitionTriggered += (sender, transition) => ThreadPool.QueueUserWorkItem(ResumeAsync, transition); predictor.Monitor(engine); stopWatch.Start(); engine.Start(); // Assert while (finalResult == NodeClassification.Intermediate) { Thread.Sleep(1); // Await completion } stopWatch.Stop(); Assert.Less(predictionTime, completionTime, "Engine was completed before a prediction was published."); Assert.AreEqual(finalResult, prediction, "Predication was incorrect"); }
public static WorkplanDummy CreatePausable() { var workplan = new WorkplanDummy(); var inital = Workflow.CreateConnector("Start", NodeClassification.Start); var complete = Workflow.CreateConnector("End", NodeClassification.End); workplan.Add(inital); workplan.Add(complete); var step = new DummyStep(1, "A"); step.Inputs[0] = inital; workplan.Add(step); var a1 = Workflow.CreateConnector("Before pause"); workplan.Add(a1); step.Outputs[0] = a1; var rightOnly = new PausableStep(); rightOnly.Inputs[0] = a1; workplan.Add(rightOnly); step.Outputs[0] = a1; var b1 = Workflow.CreateConnector("Right complete"); workplan.Add(b1); rightOnly.Outputs[0] = b1; var merge = new DummyStep(1, "C"); merge.Inputs[0] = b1; workplan.Add(merge); merge.Outputs[0] = complete; return(workplan); }
public void InstantiateWorkflow() { // Arrange var workplan = WorkplanDummy.CreateFull(); // Act var context = new FakeContext(); var workflow = WorkflowFactory.Instantiate(workplan, context); // Simple assert Assert.AreEqual(workplan.Connectors.Count(), workflow.Places.Count(), "Not all connectors transformed to places!"); Assert.AreEqual(workplan.Steps.Count(), workflow.Transitions.Count(), "Not all steps transformed to transitions!"); Assert.IsTrue(workflow.Transitions.Cast <DummyTransition>().All(t => t.Context == context), "Context not passed to all transitions!"); // Structure assert var transitions = workflow.Transitions; Assert.AreEqual(2, transitions[0].Outputs.Length); Assert.AreEqual(transitions[0].Outputs[1], transitions[1].Inputs[0]); Assert.AreEqual(transitions[0].Outputs[0], transitions[2].Inputs[0]); Assert.AreEqual(transitions[1].Outputs[0], transitions[2].Inputs[0]); Assert.AreEqual(transitions[2].Outputs[0], transitions[2].Outputs[1]); Assert.AreEqual(transitions[2].Outputs[0], workflow.EndPlaces().First()); }
public void PublishPredictionAfterInterruption() { // Arrange var workplan = WorkplanDummy.CreateBig(); var predictor = Workflow.PathPrediction(workplan); NodeClassification prediction = NodeClassification.Intermediate; predictor.PathPrediction += (sender, args) => prediction = args.PredictedOutcome; // Start and pause engine in var engine = Workflow.CreateEngine(workplan, new NullContext()); var transitions = engine.ExecutedWorkflow.Transitions.OfType <DummyTransition>(); transitions.ForEach(dt => dt.ResultOutput = -1); // Disable automatic execution transitions.First().ResultOutput = 1; // Except for the first one engine.TransitionTriggered += (sender, transition) => { }; engine.Start(); // Act var snapshot = engine.Pause(); // Snapshot of the engine in a sure failure path engine.Dispose(); engine = Workflow.CreateEngine(workplan, new NullContext()); engine.Restore(snapshot); // Restore new engine from the snapshot var finalResult = NodeClassification.Intermediate; engine.Completed += (sender, place) => finalResult = place.Classification; engine.TransitionTriggered += (sender, transition) => ThreadPool.QueueUserWorkItem(ResumeAsync, transition); predictor.Monitor(engine); engine.Start(); // This should resume the engine in a failure path and directly raise the event // Assert while (finalResult == NodeClassification.Intermediate) { Thread.Sleep(1); // Await completion } Assert.AreEqual(finalResult, prediction, "Predication was incorrect"); }
public static WorkplanDummy CreateLoneWolf() { var workplan = new WorkplanDummy(); var inital = Workflow.CreateConnector("Start", NodeClassification.Start); var complete = Workflow.CreateConnector("End", NodeClassification.End); workplan.Add(inital, complete); var step = new DummyStep(2, "A"); step.Inputs[0] = inital; workplan.Add(step); var left = Workflow.CreateConnector("Left"); workplan.Add(left); step.Outputs[0] = left; var right = Workflow.CreateConnector("Right"); workplan.Add(right); // step.Outputs[1] = right; <-- This causes a Lone Wolf validation error var rightOnly = new DummyStep(1, "LoneWolf"); rightOnly.Inputs[0] = right; workplan.Add(rightOnly); rightOnly.Outputs[0] = left; var merge = new DummyStep(2); merge.Inputs[0] = left; workplan.Add(merge); merge.Outputs[0] = merge.Outputs[1] = complete; return(workplan); }
public static WorkplanDummy CreateDeadEnd() { var workplan = new WorkplanDummy(); var inital = Workflow.CreateConnector("Start", NodeClassification.Start); var complete = Workflow.CreateConnector("End", NodeClassification.End); workplan.Add(inital, complete); var step = new DummyStep(2, "A"); step.Inputs[0] = inital; workplan.Add(step); var left = Workflow.CreateConnector("Left"); workplan.Add(left); step.Outputs[0] = left; var right = Workflow.CreateConnector("DeadEnd"); workplan.Add(right); step.Outputs[1] = right; var rightOnly = new DummyStep(1); workplan.Add(rightOnly); rightOnly.Outputs[0] = left; var merge = new DummyStep(2); merge.Inputs[0] = left; workplan.Add(merge); merge.Outputs[0] = merge.Outputs[1] = complete; return(workplan); }