Skip to content

Commit

Permalink
added in subprocess testing
Browse files Browse the repository at this point in the history
added in additional testing for handling sub processes
  • Loading branch information
roger-castaldo committed Jan 7, 2024
1 parent a48dd9c commit 4506423
Show file tree
Hide file tree
Showing 17 changed files with 433 additions and 42 deletions.
Binary file modified BPMNEngine/.vs/BPMNEngine/DesignTimeBuild/.dtbcache.v2
Binary file not shown.
Binary file modified BPMNEngine/.vs/BPMNEngine/v17/.futdcache.v2
Binary file not shown.
Binary file modified BPMNEngine/.vs/BPMNEngine/v17/.suo
Binary file not shown.
Binary file modified BPMNEngine/.vs/BPMNEngine/v17/TestStore/0/testlog.manifest
Binary file not shown.
Binary file modified BPMNEngine/.vs/ProjectEvaluation/bpmnengine.metadata.v7.bin
Binary file not shown.
Binary file modified BPMNEngine/.vs/ProjectEvaluation/bpmnengine.projects.v7.bin
Binary file not shown.
60 changes: 37 additions & 23 deletions BPMNEngine/BusinessProcess.Actions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ internal void ProcessStepComplete(ProcessInstance instance, string sourceID, str
}
});
}
if (elem is SubProcess subProcess)
{
ReadOnlyProcessVariablesContainer vars = new(sourceID, instance);
subProcess.Children
.Where(child=>instance.State.Path.AbortableSteps.Contains(child.ID))
.ForEach(child=>AbortStep(instance, sourceID, child, vars));
}
}
WriteLogLine(sourceID, LogLevel.Debug, new StackFrame(1, true), DateTime.Now, string.Format("Process Step[{0}] has been completed", sourceID));
if (outgoingID != null)
Expand Down Expand Up @@ -322,41 +329,48 @@ internal void ProcessEvent(ProcessInstance instance, string sourceID, AEvent evn
evnt,
new ReadOnlyProcessVariablesContainer(evnt.ID, instance)
);
if (evnt is EndEvent event1 && event1.IsProcessEnd)
if (evnt is EndEvent endEvent)
{
if (!event1.IsTermination)
var sp = endEvent.SubProcess as SubProcess;
if (sp!=null &&
(
!endEvent.IsProcessEnd
||(endEvent.IsProcessEnd && !endEvent.IsTermination)
)
){
instance.State.Path.SucceedFlowNode(sp);
BusinessProcess.TriggerDelegateAsync(
instance.Delegates.Events.SubProcesses.Completed,
sp,
new ReadOnlyProcessVariablesContainer(sp.ID, instance)
);
}
else if (endEvent.IsProcessEnd)
{
SubProcess sp = (SubProcess)event1.SubProcess;
if (sp != null)
if (!endEvent.IsTermination)
{
instance.State.Path.SucceedFlowNode(sp);
BusinessProcess.TriggerDelegateAsync(
instance.Delegates.Events.SubProcesses.Completed,
sp,
new ReadOnlyProcessVariablesContainer(sp.ID, instance)
);
if (sp==null)
{
BusinessProcess.TriggerDelegateAsync(
instance.Delegates.Events.Processes.Completed,
endEvent.Process,
new ReadOnlyProcessVariablesContainer(evnt.ID, instance)
);
instance.CompleteProcess();
}
}
else
{
ReadOnlyProcessVariablesContainer vars = new(evnt.ID, instance);
instance.State.AbortableSteps.ForEach(str => { AbortStep(instance, evnt.ID, GetElement(str), vars); });
BusinessProcess.TriggerDelegateAsync(
instance.Delegates.Events.Processes.Completed,
event1.Process,
instance.Delegates.Events.Processes.Completed,
endEvent.Process,
new ReadOnlyProcessVariablesContainer(evnt.ID, instance)
);
instance.CompleteProcess();
}
}
else
{
ReadOnlyProcessVariablesContainer vars = new(evnt.ID, instance);
instance.State.AbortableSteps.ForEach(str => { AbortStep(instance, evnt.ID, GetElement(str), vars); });
BusinessProcess.TriggerDelegateAsync(
instance.Delegates.Events.Processes.Completed,
event1.Process,
new ReadOnlyProcessVariablesContainer(evnt.ID, instance)
);
instance.CompleteProcess();
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion BPMNEngine/BusinessProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ public IProcessInstance LoadState(Utf8JsonReader reader,
/// <param name="stateLogLevel">Used to set the logging level for the process state document</param>
/// <returns>a process instance if the process was successfully started</returns>
public IProcessInstance BeginProcess(
Dictionary<string,object> pars,
Dictionary<string,object> pars=null,
ProcessEvents events = null,
StepValidations validations = null,
ProcessTasks tasks = null,
Expand Down
4 changes: 2 additions & 2 deletions BPMNEngine/Elements/Diagrams/Edge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public override RectF Rectangle
});
Label l = Label;
_rectangle = new RectF(_rectangle.Value.X-3.5f, _rectangle.Value.Y-3.5f, _rectangle.Value.Width+6.5f, _rectangle.Value.Height+6.5f);
if (l != null)
if (l != null && l.Bounds!=null)
_rectangle = MergeRectangle(_rectangle.Value,l.Bounds.Rectangle);
}
return _rectangle.Value;
Expand Down Expand Up @@ -122,7 +122,7 @@ public void Render(ICanvas surface, ProcessPath path,Definition definition)
}
}

if (Label!=null)
if (Label!=null && Label.Bounds!=null)
{
surface.FontColor = color;
surface.DrawString(elem.ToString(), Label.Bounds.Rectangle, HorizontalAlignment.Center, VerticalAlignment.Center);
Expand Down
15 changes: 2 additions & 13 deletions BPMNEngine/Elements/Diagrams/Label.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,10 @@ namespace BPMNEngine.Elements.Diagrams
[ValidParent(typeof(Shape))]
internal class Label : AParentElement
{
public Bounds Bounds
=> Children.OfType<Bounds>().FirstOrDefault();
public Bounds? Bounds

Check warning on line 10 in BPMNEngine/Elements/Diagrams/Label.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 10 in BPMNEngine/Elements/Diagrams/Label.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 10 in BPMNEngine/Elements/Diagrams/Label.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 10 in BPMNEngine/Elements/Diagrams/Label.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
=> Children.OfType<Bounds?>().FirstOrDefault();

Check warning on line 11 in BPMNEngine/Elements/Diagrams/Label.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 11 in BPMNEngine/Elements/Diagrams/Label.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 11 in BPMNEngine/Elements/Diagrams/Label.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 11 in BPMNEngine/Elements/Diagrams/Label.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

public Label(XmlElement elem, XmlPrefixMap map, AElement parent)
: base(elem, map, parent) { }

public override bool IsValid(out IEnumerable<string> err)
{
var res = base.IsValid(out err);
if (Bounds == null)
{
err = (err??Array.Empty<string>()).Concat(new string[] { "No bounds for the label are specified." });
return false;
}
return res;
}
}
}
2 changes: 1 addition & 1 deletion BPMNEngine/Elements/SubProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public override bool IsValid(out IEnumerable<string> err)
var res = base.IsValid(out err);
bool hasStart = Children.Any(elem => elem is StartEvent || (elem is IntermediateCatchEvent ice && ice.SubType.HasValue));
bool hasEnd = Children.Any(elem=>elem is EndEvent);
bool hasIncoming = this.Incoming!=null || Children.Any(elem=>elem is IntermediateCatchEvent ice && ice.SubType.HasValue);
bool hasIncoming = Incoming.Any() || Children.Any(elem=>elem is IntermediateCatchEvent ice && ice.SubType.HasValue);
var terr = new List<string>();
if (!(hasStart && hasEnd && hasIncoming))
{
Expand Down
2 changes: 1 addition & 1 deletion UnitTest/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;

[assembly: Parallelize(Workers = 50, Scope = ExecutionScope.MethodLevel)]
[assembly: Parallelize(Workers = 25, Scope = ExecutionScope.MethodLevel)]
4 changes: 3 additions & 1 deletion UnitTest/DiagramLoading.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,15 @@ public void LoadInvalidDiagram()
Assert.IsTrue(res.Contains("No bounds specified for the shape."));
Assert.IsTrue(res.Contains("End Events cannot have an outgoing path."));
Assert.IsTrue(res.Contains("End Events must have an incoming path."));
Assert.IsTrue(res.Contains("No bounds for the label are specified."));
Assert.IsTrue(res.Contains("No child elements found in the definition."));
Assert.IsTrue(res.Contains("Collaboration requires at least 1 child element."));
Assert.IsTrue(res.Contains("Right value specified more than once."));
Assert.IsTrue(res.Contains("Left value specified more than once."));
Assert.IsTrue(res.Contains("Right and Left value missing."));
Assert.IsTrue(res.Contains("Left value missing."));
Assert.IsTrue(res.Contains("A Sub Process Must have a StartEvent or valid IntermediateCatchEvent"));
Assert.IsTrue(res.Contains("A Sub Process Must have a valid Incoming path, achieved through an incoming flow or IntermediateCatchEvent"));
Assert.IsTrue(res.Contains("A Sub Process Must have an EndEvent"));
}

[TestMethod]
Expand Down
122 changes: 122 additions & 0 deletions UnitTest/SubProcesses.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using BPMNEngine;
using BPMNEngine.Interfaces.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTest
{
[TestClass]
public class SubProcesses
{
private const string SIGNAL = "interupted";

private const string SUB_PROCESS_ID = "Activity_01eulv2";
private const string SUB_SUB_PROCESS_ID = "Activity_099a0io";

private const string MAIN_SIGNAL_ID = "Event_167oagf";
private const string SUB_SIGNAL_ID = "Event_1etf3o8";
private const string SUB_SUB_SIGNAL_ID = "Event_0s8bx7i";

private const string MAIN_TASK_ID = "Activity_1hle9w5";
private const string SUB_TASK_ID = "Activity_1ifnuk7";
private const string SUB_SUB_TASK_ID = "Activity_1e0arlx";

private static BusinessProcess _process;

[ClassInitialize()]
public static void Initialize(TestContext testContext)
{
_process = new BusinessProcess(Utility.LoadResourceDocument("SubProcesses/subprocesses.bpmn"));
}

[ClassCleanup]
public static void Cleanup()
{
_process.Dispose();
}

[TestMethod]
public void TestSubProcessCompletion()
{
var instance = _process.BeginProcess();
Assert.IsNotNull(instance);
Assert.IsTrue(instance.WaitForManualTask(SUB_TASK_ID, out IManualTask task));
Assert.IsNotNull(task);
task.MarkComplete();
Assert.IsTrue(Utility.WaitForCompletion(instance));
var state = instance.CurrentState;
Assert.IsTrue(Utility.StepCompleted(state, SUB_PROCESS_ID));
Assert.IsTrue(Utility.StepAborted(state, SUB_SUB_PROCESS_ID));
Assert.IsTrue(Utility.StepAborted(state, SUB_SUB_TASK_ID));
}

[TestMethod]
public void TestSubTaskSignal()
{
var instance = _process.BeginProcess();
Assert.IsNotNull(instance);
Assert.IsTrue(instance.WaitForManualTask(SUB_TASK_ID, out IManualTask task));
Assert.IsNotNull(task);
task.Signal(SIGNAL, out bool isAborted);
Assert.IsTrue(isAborted);
Assert.IsTrue(Utility.WaitForCompletion(instance));
var state = instance.CurrentState;
Assert.IsTrue(Utility.StepAborted(state, SUB_PROCESS_ID));
Assert.IsTrue(Utility.StepCompleted(state, SUB_SIGNAL_ID));
Assert.IsFalse(Utility.StepCompleted(state, SUB_SUB_SIGNAL_ID));
Assert.IsFalse(Utility.StepCompleted(state, MAIN_SIGNAL_ID));
Assert.IsTrue(Utility.StepAborted(state, SUB_SUB_PROCESS_ID));
Assert.IsTrue(Utility.StepAborted(state, SUB_PROCESS_ID));
}

[TestMethod]
public void TestSubSubProcessCompletion()
{
var instance = _process.BeginProcess();
Assert.IsNotNull(instance);
Assert.IsTrue(instance.WaitForManualTask(SUB_SUB_TASK_ID, out IManualTask task));
Assert.IsNotNull(task);
task.MarkComplete();
Assert.IsTrue(Utility.WaitForCompletion(instance));
var state = instance.CurrentState;
Assert.IsTrue(Utility.StepCompleted(state, SUB_PROCESS_ID));
Assert.IsTrue(Utility.StepCompleted(state, SUB_SUB_PROCESS_ID));
Assert.IsTrue(Utility.StepAborted(state, SUB_TASK_ID));
}

[TestMethod]
public void TestSubSubTaskSignal()
{
var instance = _process.BeginProcess();
Assert.IsNotNull(instance);
Assert.IsTrue(instance.WaitForManualTask(SUB_SUB_TASK_ID, out IManualTask task));
Assert.IsNotNull(task);
task.Signal(SIGNAL,out bool isAborted);
Assert.IsTrue(isAborted);
Assert.IsTrue(Utility.WaitForCompletion(instance));
var state = instance.CurrentState;
Assert.IsTrue(Utility.StepCompleted(state, SUB_PROCESS_ID));
Assert.IsTrue(Utility.StepCompleted(state, SUB_SUB_SIGNAL_ID));
Assert.IsFalse(Utility.StepCompleted(state, SUB_SIGNAL_ID));
Assert.IsFalse(Utility.StepCompleted(state, MAIN_SIGNAL_ID));
Assert.IsTrue(Utility.StepAborted(state, SUB_SUB_PROCESS_ID));
Assert.IsTrue(Utility.StepAborted(state, SUB_SUB_TASK_ID));
}

[TestMethod]
public void TestMainTaskSignal()
{
var instance = _process.BeginProcess();
Assert.IsNotNull(instance);
Assert.IsTrue(instance.WaitForManualTask(MAIN_TASK_ID, out IManualTask task));
Assert.IsNotNull(task);
task.Signal(SIGNAL, out bool isAborted);
Assert.IsFalse(isAborted);
Assert.IsTrue(Utility.WaitForCompletion(instance));
var state = instance.CurrentState;
Assert.IsFalse(Utility.StepCompleted(state, SUB_PROCESS_ID));
Assert.IsFalse(Utility.StepCompleted(state, SUB_SUB_SIGNAL_ID));
Assert.IsFalse(Utility.StepCompleted(state, SUB_SIGNAL_ID));
Assert.IsTrue(Utility.StepCompleted(state, MAIN_SIGNAL_ID));
}
}
}
2 changes: 2 additions & 0 deletions UnitTest/UnitTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<None Remove="diagrams\ProcessEnd\sub_process_terminate_abort.bpmn" />
<None Remove="diagrams\ProcessSuspension\timer.bpmn" />
<None Remove="diagrams\ProcessSuspension\user_task.bpmn" />
<None Remove="diagrams\SubProcesses\subprocesses.bpmn" />
<None Remove="diagrams\Tasks\all_tasks.bpmn" />
<None Remove="diagrams\Tasks\catch_event_checking.bpmn" />
<None Remove="diagrams\Timers\timing.bpmn" />
Expand Down Expand Up @@ -89,6 +90,7 @@
<EmbeddedResource Include="diagrams\ProcessEnd\sub_process_terminate.bpmn" />
<EmbeddedResource Include="diagrams\ProcessSuspension\timer.bpmn" />
<EmbeddedResource Include="diagrams\ProcessSuspension\user_task.bpmn" />
<EmbeddedResource Include="diagrams\SubProcesses\subprocesses.bpmn" />
<EmbeddedResource Include="diagrams\Tasks\all_tasks.bpmn" />
<EmbeddedResource Include="diagrams\ErrorHandling\catch_event_checking.bpmn" />
<EmbeddedResource Include="diagrams\Timers\timing.bpmn" />
Expand Down
2 changes: 2 additions & 0 deletions UnitTest/diagrams/DiagramLoading/invalid.bpmn
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@
</bpmn:startEvent>

<bpmn:collaboration/>

<bpmn:subProcess id="Activity_01eulv2"/>
</bpmn:process>
<bpmn:process id="Process_2" isExecutable="false">
</bpmn:process>
Expand Down
Loading

0 comments on commit 4506423

Please sign in to comment.