Skip to content

Commit

Permalink
-
Browse files Browse the repository at this point in the history
  • Loading branch information
mihainradu committed Mar 20, 2024
1 parent 949dd0c commit 52cf89e
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 139 deletions.
38 changes: 27 additions & 11 deletions src/Test/TestCases.Workflows/Bpm/FlowchartTestExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
using System.Activities;
using System.Activities.Statements;
using System;
using System.Linq;

namespace TestCases.Activitiess.Bpm;

public static class FlowchartTestExtensions
{
public static FlowSplit AddBranches(this FlowSplit split, params Activity[] nodes)
{
foreach(var node in nodes)
{
var branch = FlowSplitBranch.New(split);
branch.StartNode = new FlowStep() { Action = node, Next = branch.StartNode };
split.Branches.Add(branch);
}
return split;
return split.AddBranches(nodes.Select(a => new FlowStep { Action = a }).ToArray());
}
public static FlowSplit AddBranches(this FlowSplit split, params FlowNode[] nodes)
{
foreach (var node in nodes)
{
var branch = FlowSplitBranch.New(split);
node.FlowTo(split.MergeNode);
branch.StartNode = node;
var branch = new FlowSplitBranch()
{
StartNode = node,
};
split.Branches.Add(branch);
}
return split;
Expand All @@ -31,6 +27,10 @@ public static FlowStep Step(this Activity activity)
{
return new FlowStep { Action = activity };
}
public static FlowMerge Merge(this Activity activity)
{
return new () { Next = new FlowStep() { Action = activity } };
}

public static FlowStep FlowTo(this Activity predeccessor, FlowNode successor)
{
Expand All @@ -40,6 +40,19 @@ public static FlowStep FlowTo(this Activity predeccessor, Activity successor)
{
return new FlowStep { Action = predeccessor }.FlowTo(successor);
}
public static FlowMerge MergeTo(this FlowSplit split, FlowNode successor)
{
var merge = new FlowMerge() { Next = successor };
foreach (var branch in split.Branches)
{
branch.StartNode.FlowTo(merge);
}
return merge;
}
public static FlowMerge MergeTo(this FlowSplit split, Activity successor)
{
return split.MergeTo(new FlowStep { Action = successor });
}
public static T FlowTo<T>(this T predeccessor, Activity successor)
where T : FlowNode
{
Expand All @@ -59,7 +72,10 @@ public static T FlowTo<T>(this T predeccessor, FlowNode successor)
(join.Next ??= successor).FlowTo(successor);
break;
case FlowSplit split:
(split.MergeNode.Next ??= successor).FlowTo(successor);
foreach (var branch in split.Branches)
{
branch.StartNode.FlowTo(successor);
}
break;
case FlowDecision decision:
(decision.True ??= successor).FlowTo(successor);
Expand Down
39 changes: 23 additions & 16 deletions src/Test/TestCases.Workflows/Bpm/SplitAndMergeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using TestObjects.XamlTestDriver;
using System.IO;
using System;

using WorkflowApplicationTestExtensions;
namespace TestCases.Activitiess.Bpm;

public class AddStringActivity : NativeActivity
Expand All @@ -25,7 +25,7 @@ protected override void Execute(NativeActivityContext context)
{
using var _ = context.InheritVariables();
var stringsLocation = context.GetInheritedLocation<List<string>>("strings");
stringsLocation.Value ??= new();
stringsLocation.Value ??= [];
stringsLocation.Value.Add(Item);
}
}
Expand All @@ -37,7 +37,7 @@ public class SplitAndMergeTests

private AddStringActivity AddString(string stringToAdd)
{
var act = new AddStringActivity() { Item = stringToAdd };
var act = new AddStringActivity() { Item = stringToAdd, DisplayName = stringToAdd };
return act;
}

Expand All @@ -50,7 +50,8 @@ private List<string> ExecuteFlowchart(FlowNode startNode)
private List<string> ExecuteFlowchart(Flowchart flowchart)
{
var root = new ActivityWithResult<List<string>> { Body = flowchart, In = _stringsVariable };
return Results = WorkflowInvoker.Invoke(root);
var app = new WorkflowApplication(root);
return Results = (List<string>) app.RunUntilCompletion().Outputs["Result"];
}

[Fact]
Expand Down Expand Up @@ -101,7 +102,7 @@ public void Should_join_branches()
AddString(branch1Str),
AddString(branch2Str)
);
split.MergeNode.FlowTo(AddString(stopString));
split.MergeTo(AddString(stopString));

ExecuteFlowchart(split);
Results.ShouldBe(new() { branch1Str, branch2Str, stopString });
Expand All @@ -110,21 +111,27 @@ public void Should_join_branches()
[Fact]
public void Should_join_branches_with_inner_split()
{
/// |--A--|
/// |---A---Split< Merge--|
/// Split< |--A--| Merge---Stop
/// |___A______________________|

var outerMerge = AddString("stop").Merge();
var innerMerge = AddString("innerMerged").Merge();
var innerSplit = new FlowSplit()
.AddBranches(
AddString("branch1Inner"),
AddString("branch2Inner")
AddString("branch1Inner").FlowTo(innerMerge),
AddString("branch2Inner").FlowTo(innerMerge)
);
innerSplit.MergeNode.FlowTo(AddString("innerMerged"));
innerMerge.FlowTo(outerMerge);
var outerSplit = new FlowSplit()
.AddBranches(
innerSplit,
AddString("branch2Outer").Step()
AddString("branch1Outer").FlowTo(innerSplit),
AddString("branch2Outer").Step().FlowTo(outerMerge)
);
outerSplit.MergeNode.FlowTo(AddString("stop"));

ExecuteFlowchart(outerSplit);
Results.ShouldBe(new() { "branch1Inner", "branch2Inner", "innerMerged", "branch2Outer", "stop"});
Results.ShouldBe(["branch1Outer", "branch1Inner", "branch2Inner", "innerMerged", "branch2Outer", "stop"]);
}


Expand All @@ -141,7 +148,7 @@ public void Should_join_with_skiped_branches()
AddString(branch2Str)
);
split.Branches.First().Condition = new LambdaValue<bool>(c => false);
split.MergeNode.FlowTo(AddString(stopString));
split.MergeTo(AddString(stopString));

ExecuteFlowchart(split);
Results.ShouldBe(new() { branch2Str, stopString });
Expand All @@ -159,8 +166,8 @@ public void Should_join_branches_when_condition_is_met()
AddString(branch1Str).FlowTo(AddString(branch1Str)),
new BlockingActivity("whatever").FlowTo(AddString(branch2Str))
);
split.MergeNode.FlowTo(AddString(stopString));
split.MergeNode.Completion = new LambdaValue<bool>(c => true);
split.MergeTo(AddString(stopString))
.Completion = new LambdaValue<bool>(c => true);

ExecuteFlowchart(split);
Results.ShouldBe(new() { branch1Str, branch1Str, stopString });
Expand Down Expand Up @@ -209,7 +216,7 @@ ActivityWithResult<List<string>> ParallelActivities()
AddString(branch1Str).FlowTo(new FlowStep()),
AddString(branch2Str).FlowTo(new FlowStep()),
blockingActivity.FlowTo(blockingContinuation).FlowTo(new FlowStep()));
split.MergeNode.FlowTo(AddString(stopString));
split.MergeTo(AddString(stopString));
var flowchart = new Flowchart { StartNode = split };
return new() { In = _stringsVariable, Body = flowchart };
}
Expand Down
1 change: 1 addition & 0 deletions src/Test/TestCases.Workflows/TestCases.Workflows.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<ProjectReference Include="..\CustomTestObjects\CustomTestObjects.csproj" />
<ProjectReference Include="..\JsonFileInstanceStore\JsonFileInstanceStore.csproj" />
<ProjectReference Include="..\TestObjects\TestObjects.csproj" />
<ProjectReference Include="..\WorkflowApplicationTestExtensions\WorkflowApplicationTestExtensions.csproj" />
</ItemGroup>
<ItemGroup>
<Page Remove="**\*.xaml" />
Expand Down
65 changes: 22 additions & 43 deletions src/UiPath.Workflow.Runtime/Statements/FlowMerge.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
using System.Linq;
using System.Activities.Validation;
using System.Linq;
namespace System.Activities.Statements;

public class FlowMerge : FlowNode
{
private FlowSplit _split;
[DefaultValue(null)]
public Activity<bool> Completion { get; set; }
[DefaultValue(null)]
public FlowNode Next { get; set; }
public FlowSplit SplitNode
{
get => _split;
init
{
if (value?.MergeNode is { } merge && merge != this)
throw new InvalidOperationException("Split and merge must be linked both ways.");
_split = value;
}
}


internal override Activity ChildActivity => Completion;

private List<Flowchart.BranchLinks> ConnectedBranches { get; set; }
private List<FlowSplitBranch> ConnectedBranches { get; set; }

private record MergeState
{
Expand All @@ -37,37 +25,24 @@ public FlowMerge()
protected override void OnEndCacheMetadata()
{
var predecessors = Owner.GetPredecessors(this);
ConnectedBranches = predecessors.Select(p => Owner.GetBranch(p)).ToList();
ConnectedBranches = predecessors.SelectMany(p => Owner.GetStaticBranches(p)).Distinct().ToList();
var outgoingBranches = ConnectedBranches.SelectMany(b => Owner.GetStaticBranches(b.SplitNode)).Distinct().ToList();
Owner.AddStaticBranches(this, outgoingBranches);

ValidateAllBranches();
//ValidateAllBranches();

void ValidateAllBranches()
{
HashSet<FlowNode> visited = new()
{
this,
_split,
};
List<FlowNode> toVisit = new(1) { this };
do
var splits = ConnectedBranches.Select(bl => bl.SplitNode).Distinct().ToList();
if (splits.Count() > 1)
{
var predecessors = toVisit.SelectMany(v => Owner.GetPredecessors(v)).ToList();
toVisit = new List<FlowNode>(predecessors.Count);
foreach (var predecessor in predecessors)
{
if (!visited.Add(predecessor))
continue;
if (predecessor == null)
{
Metadata.AddValidationError("All join branches should start in the parent parallel node.");
continue;
}
toVisit.Add(predecessor);
}
Metadata.AddValidationError("All join branches should start in the same parallel node.");
}
while (toVisit.Any());
var allBranchesJoined = _split.Branches.All(b => visited.Contains(b.StartNode));
if (!allBranchesJoined)
var split = splits.FirstOrDefault();
if (split is null)
return;
var branches = ConnectedBranches.Select(b => b.RuntimeNode.Index).Distinct().ToList();
if (branches.Count != split.Branches.Count)
Metadata.AddValidationError("All parallel branches should end in same join node.");
}
}
Expand All @@ -79,7 +54,7 @@ internal override void GetConnectedNodes(IList<FlowNode> connections)
}
}

MergeState GetJoinState(Func<MergeState> add = null)
MergeState GetJoinState()
{
var key = $"{Index}";
var joinStates = Owner.GetPersistableState<Dictionary<string, MergeState>>("FlowMerge");
Expand All @@ -100,7 +75,11 @@ internal override void Execute(FlowNode predecessorNode)
var branch = Owner.GetBranch(predecessorNode);
if (!ConnectedBranches.Contains(branch))
return;
joinState.CompletedNodeIndeces.Add(branch.NodeIndex);
joinState.CompletedNodeIndeces.Add(branch.RuntimeNode.Index);
joinState.CompletedNodeIndeces
.AddRange(Owner
.GetCompletedBranches()
.Select(b => b.RuntimeNode.Index));
if (Completion is not null)
{
Owner.ScheduleWithCallback(Completion);
Expand All @@ -114,7 +93,7 @@ protected override void OnCompletionCallback(bool result)
{
var joinState = GetJoinState();
var incompleteBranches = ConnectedBranches
.Select(b => b.NodeIndex)
.Select(b => b.RuntimeNode.Index)
.Except(joinState.CompletedNodeIndeces).ToList();
if (result)
{
Expand Down
Loading

0 comments on commit 52cf89e

Please sign in to comment.