Merge pull request #6480 from bobhauser/6479-fix-flowjoin-waitany-with-backward-connection
Fixes issue with FlowJoin(WaitAny) where activities can be executed multiple times
This commit is contained in:
commit
7caaad5a8f
|
|
@ -89,7 +89,10 @@ public class FlowScope
|
|||
public bool HasFollowedInboundConnection(FlowGraph flowGraph, IActivity activity)
|
||||
{
|
||||
var forwardInboundConnections = flowGraph.GetForwardInboundConnections(activity);
|
||||
return forwardInboundConnections.Any(c => GetConnectionLastVisitFollowed(c));
|
||||
var outboundActivityVisitCount = GetActivityVisitCount(activity);
|
||||
var maxConnectionVisitCount = forwardInboundConnections.Max(c => GetConnectionVisitCount(c));
|
||||
return maxConnectionVisitCount > outboundActivityVisitCount
|
||||
&& forwardInboundConnections.Any(c => GetConnectionVisitCount(c) == maxConnectionVisitCount && GetConnectionLastVisitFollowed(c));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,41 +1,41 @@
|
|||
using Elsa.Expressions.Models;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Activities.Flowchart.Activities;
|
||||
using Elsa.Workflows.Activities.Flowchart.Models;
|
||||
using Elsa.Workflows.IntegrationTests.Scenarios.FlowchartNextActivity.Workflows;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Scenarios.FlowchartNextActivity;
|
||||
|
||||
public class FlowchartNextActivityTests
|
||||
{
|
||||
private readonly CapturingTextWriter _capturingTextWriter = new();
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly IWorkflowRunner _workflowRunner;
|
||||
|
||||
public FlowchartNextActivityTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_services = new TestApplicationBuilder(testOutputHelper)
|
||||
.WithCapturingTextWriter(_capturingTextWriter)
|
||||
.AddActivitiesFrom<FlowchartNextActivityTests>()
|
||||
.Build();
|
||||
|
||||
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Flowchart only schedules next activity connected to outcome of previous activity.")]
|
||||
public async Task Test1()
|
||||
{
|
||||
await _services.PopulateRegistriesAsync();
|
||||
await _workflowRunner.RunAsync<FlowchartWorkflow>();
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(new[] { "Line 1" }, lines);
|
||||
using Elsa.Expressions.Models;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Activities.Flowchart.Activities;
|
||||
using Elsa.Workflows.Activities.Flowchart.Models;
|
||||
using Elsa.Workflows.IntegrationTests.Scenarios.FlowchartNextActivity.Workflows;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Scenarios.FlowchartNextActivity;
|
||||
|
||||
public class FlowchartNextActivityTests
|
||||
{
|
||||
private readonly CapturingTextWriter _capturingTextWriter = new();
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly IWorkflowRunner _workflowRunner;
|
||||
|
||||
public FlowchartNextActivityTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_services = new TestApplicationBuilder(testOutputHelper)
|
||||
.WithCapturingTextWriter(_capturingTextWriter)
|
||||
.AddActivitiesFrom<FlowchartNextActivityTests>()
|
||||
.Build();
|
||||
|
||||
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Flowchart with backward connections and a dangling activity")]
|
||||
[Fact(DisplayName = "Flowchart only schedules next activity connected to outcome of previous activity.")]
|
||||
public async Task Test1()
|
||||
{
|
||||
await _services.PopulateRegistriesAsync();
|
||||
await _workflowRunner.RunAsync<FlowchartWorkflow>();
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(new[] { "Line 1" }, lines);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Flowchart with backward connections and a dangling activity")]
|
||||
public async Task BackwardConnectionTest()
|
||||
{
|
||||
var workflow = new TestWorkflow(workflowBuilder =>
|
||||
|
|
@ -76,12 +76,12 @@ public class FlowchartNextActivityTests
|
|||
|
||||
workflowBuilder.Root = new Flowchart
|
||||
{
|
||||
Variables =
|
||||
{
|
||||
loopVariable
|
||||
Variables =
|
||||
{
|
||||
loopVariable
|
||||
},
|
||||
Activities =
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
start,
|
||||
dangling,
|
||||
writeLineDecision,
|
||||
|
|
@ -93,7 +93,7 @@ public class FlowchartNextActivityTests
|
|||
d,
|
||||
e,
|
||||
f,
|
||||
end
|
||||
end
|
||||
},
|
||||
Connections =
|
||||
{
|
||||
|
|
@ -117,14 +117,14 @@ public class FlowchartNextActivityTests
|
|||
};
|
||||
});
|
||||
|
||||
await _services.PopulateRegistriesAsync();
|
||||
var result = await _workflowRunner.RunAsync(workflow);
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus);
|
||||
Assert.Equal(new[] { "A", "B", "C", "D", "E", "A", "B", "E", "F" }, lines);
|
||||
await _services.PopulateRegistriesAsync();
|
||||
var result = await _workflowRunner.RunAsync(workflow);
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus);
|
||||
Assert.Equal(new[] { "A", "B", "C", "D", "E", "A", "B", "E", "F" }, lines);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Flowchart with an invalid backward connection")]
|
||||
[Fact(DisplayName = "Flowchart with an invalid backward connection")]
|
||||
public async Task InvalidBackwardConnectionTest()
|
||||
{
|
||||
var workflow = new TestWorkflow(workflowBuilder =>
|
||||
|
|
@ -138,8 +138,8 @@ public class FlowchartNextActivityTests
|
|||
|
||||
workflowBuilder.Root = new Flowchart
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
start,
|
||||
a,
|
||||
b,
|
||||
|
|
@ -160,18 +160,18 @@ public class FlowchartNextActivityTests
|
|||
};
|
||||
});
|
||||
|
||||
await _services.PopulateRegistriesAsync();
|
||||
var result = await _workflowRunner.RunAsync(workflow);
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(WorkflowSubStatus.Faulted, result.WorkflowState.SubStatus);
|
||||
Assert.Equal(1, result.WorkflowState.Incidents.Count());
|
||||
Assert.Equal("Invalid backward connection: Every path from the source ('WriteLineE') must go through the target ('WriteLineC') when tracing back to the start.", result.WorkflowState.Incidents.First().Message);
|
||||
Assert.Equal(new[] { "A", "B", "C", "D", "E" }, lines);
|
||||
await _services.PopulateRegistriesAsync();
|
||||
var result = await _workflowRunner.RunAsync(workflow);
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(WorkflowSubStatus.Faulted, result.WorkflowState.SubStatus);
|
||||
Assert.Equal(1, result.WorkflowState.Incidents.Count());
|
||||
Assert.Equal("Invalid backward connection: Every path from the source ('WriteLineE') must go through the target ('WriteLineC') when tracing back to the start.", result.WorkflowState.Incidents.First().Message);
|
||||
Assert.Equal(new[] { "A", "B", "C", "D", "E" }, lines);
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "Flowchart with a Join activity executed multiple times")]
|
||||
[InlineData(FlowJoinMode.WaitAll)]
|
||||
[InlineData(FlowJoinMode.WaitAny)]
|
||||
[Theory(DisplayName = "Flowchart with a Join activity executed multiple times")]
|
||||
[InlineData(FlowJoinMode.WaitAll)]
|
||||
[InlineData(FlowJoinMode.WaitAny)]
|
||||
public async Task WaitAnyLoopTest(FlowJoinMode joinMode)
|
||||
{
|
||||
var workflow = new TestWorkflow(workflowBuilder =>
|
||||
|
|
@ -204,12 +204,12 @@ public class FlowchartNextActivityTests
|
|||
|
||||
workflowBuilder.Root = new Flowchart
|
||||
{
|
||||
Variables =
|
||||
{
|
||||
loopVariable
|
||||
Variables =
|
||||
{
|
||||
loopVariable
|
||||
},
|
||||
Activities =
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
start,
|
||||
a,
|
||||
b,
|
||||
|
|
@ -218,7 +218,7 @@ public class FlowchartNextActivityTests
|
|||
join,
|
||||
incrementLoop,
|
||||
loopbackDecision,
|
||||
end
|
||||
end
|
||||
},
|
||||
Connections =
|
||||
{
|
||||
|
|
@ -237,10 +237,79 @@ public class FlowchartNextActivityTests
|
|||
};
|
||||
});
|
||||
|
||||
await _services.PopulateRegistriesAsync();
|
||||
var result = await _workflowRunner.RunAsync(workflow);
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus);
|
||||
Assert.Equal(new[] { "A", "B", "C", "D", "A", "B", "C", "D", "A", "B", "C", "D"}, lines);
|
||||
}
|
||||
await _services.PopulateRegistriesAsync();
|
||||
var result = await _workflowRunner.RunAsync(workflow);
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus);
|
||||
Assert.Equal(new[] { "A", "B", "C", "D", "A", "B", "C", "D", "A", "B", "C", "D"}, lines);
|
||||
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "Flowchart with a Join activity executed multiple times, bug 6479")]
|
||||
[InlineData(FlowJoinMode.WaitAll)]
|
||||
[InlineData(FlowJoinMode.WaitAny)]
|
||||
public async Task WaitLoopBug6479Test(FlowJoinMode joinMode)
|
||||
{
|
||||
var workflow = new TestWorkflow(workflowBuilder =>
|
||||
{
|
||||
var loopVariable = new Variable<int>("LoopCount", 0);
|
||||
|
||||
var start = new Start();
|
||||
var loopbackSwitch = new FlowSwitch()
|
||||
{
|
||||
Cases = {
|
||||
new FlowSwitchCase("DoLoopback", new Expression("JavaScript", "getVariable('LoopCount') < 3")),
|
||||
},
|
||||
Mode = new(SwitchMode.MatchFirst)
|
||||
};
|
||||
var a = new WriteLine("A");
|
||||
var incrementLoop = new SetVariable()
|
||||
{
|
||||
Variable = loopVariable,
|
||||
Value = new Models.Input<object?>(new Expression("JavaScript", "getVariable('LoopCount') + 1"))
|
||||
};
|
||||
var join = new FlowJoin()
|
||||
{
|
||||
Mode = new(joinMode)
|
||||
};
|
||||
var b = new WriteLine("B");
|
||||
var end = new End();
|
||||
|
||||
|
||||
workflowBuilder.Root = new Flowchart
|
||||
{
|
||||
Variables =
|
||||
{
|
||||
loopVariable
|
||||
},
|
||||
Activities =
|
||||
{
|
||||
start,
|
||||
loopbackSwitch,
|
||||
a,
|
||||
incrementLoop,
|
||||
join,
|
||||
b,
|
||||
end
|
||||
},
|
||||
Connections =
|
||||
{
|
||||
new(start, loopbackSwitch),
|
||||
new(new Endpoint(loopbackSwitch, "DoLoopback"), new Endpoint(a)),
|
||||
new(new Endpoint(loopbackSwitch, "DoLoopback"), new Endpoint(incrementLoop)),
|
||||
new(new Endpoint(loopbackSwitch, "Default"), new Endpoint(b)),
|
||||
new(a, join),
|
||||
new(incrementLoop, join),
|
||||
new(join, loopbackSwitch),
|
||||
new(b, end),
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
await _services.PopulateRegistriesAsync();
|
||||
var result = await _workflowRunner.RunAsync(workflow);
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus);
|
||||
Assert.Equal(new[] { "A", "A", "A", "B" }, lines);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue