Skip to content

Commit

Permalink
Added test and fixed problem with choiceBranchBuilder.NextBranch(): w…
Browse files Browse the repository at this point in the history
…hen adding empty dags to a branch, there was a possibility to add multiple end nodes!

Implemented Fail State parsing: it is converted to a regular  EndNode. (For now it doesn't set reason = Failure)
  • Loading branch information
redjack96 committed Sep 1, 2024
1 parent c72ea30 commit 4ceec12
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 46 deletions.
20 changes: 20 additions & 0 deletions internal/asl/fail.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,23 @@ func (f *FailState) String() string {
str += "\t\t}"
return str
}

func (f *FailState) GetError() string {
if f.Error != "" {
return f.Error
} else if f.ErrorPath != "" {
return string(f.ErrorPath) // will be evaluated at run time
} else {
return "GenericError"
}
}

func (f *FailState) GetCause() string {
if f.Cause != "" {
return f.Cause
} else if f.CausePath != "" {
return string(f.CausePath) // will be evaluated at run time
} else {
return "Execution failed due to a generic error"
}
}
14 changes: 8 additions & 6 deletions internal/fc/asl.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ func BuildFromChoiceState(builder *DagBuilder, c *asl.ChoiceState, name string,
// we add one more branch to the ChoiceNode to handle the default branch
nextState = c.Default
}
sm, errBranch := GetBranchForChoiceFromStates(entireSM, nextState, i)
dag, errBranch := GetBranchForChoiceFromStates(entireSM, nextState, i)
if errBranch != nil {
return nil, errBranch
}
branchBuilder = branchBuilder.NextBranch(sm, errBranch)
branchBuilder = branchBuilder.NextBranch(dag, errBranch)
i++
}

Expand Down Expand Up @@ -290,8 +290,10 @@ func BuildFromSucceedState(builder *DagBuilder, s *asl.SucceedState, name string
return builder, nil
}

// BuildFromFailState is not fully compatible with serverledge, but it adds an EndNode
func BuildFromFailState(builder *DagBuilder, s *asl.FailState, name string) (*DagBuilder, error) {
// TODO: implement me
return builder, nil
// BuildFromFailState adds an EndNode with Reason = Failure and sets the Result with the Error as key and the Cause as value.
// if error and cause are not specified, a GenericError key and a generic message will be set in the EndNode Result field.
func BuildFromFailState(builder *DagBuilder, s *asl.FailState, name string) (*Dag, error) {
// Error or ErrorPath will be the key in the EndNode Result field
// Cause oe CausePath will be the string value in the EndNode Result field.
return builder.BuildFailing(s.GetError(), s.GetCause())
}
37 changes: 30 additions & 7 deletions internal/fc/dag.go
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,35 @@ func (dag *Dag) decodeNode(nodeId string, value json.RawMessage) error {
return fmt.Errorf("failed to decode node")
}

// IsEmpty returns true if the dag has 0 nodes or exactly one StartNode and one EndNode.
func (dag *Dag) IsEmpty() bool {
if len(dag.Nodes) == 0 {
return true
}

onlyTwoNodes := len(dag.Nodes) == 2
hasOnlyStartAndEnd := false
if onlyTwoNodes {
hasStart := 0
hasEnd := 0
for _, node := range dag.Nodes {
if node.GetNodeType() == Start {
hasStart++
}
if node.GetNodeType() == End {
hasEnd++
}
}
hasOnlyStartAndEnd = (hasStart == 1) && (hasEnd == 1)
}

if hasOnlyStartAndEnd {
return true
}

return false
}

// Debug can be used to find if expected output of test TestInvokeFC_Concurrent is correct based on requestId of format "goroutine_#" and simple node ids of format "simple #"
func Debug(r *CompositionRequest, nodeId string, output map[string]interface{}) error {
if strings.Contains(r.ReqId, "goroutine") {
Expand Down Expand Up @@ -898,13 +927,7 @@ func DagBuildingLoop(sm *asl.StateMachine, nextState asl.State, nextStateName st
break
case asl.Fail:
failState := nextState.(*asl.FailState)
b, err := BuildFromFailState(builder, failState, nextStateName)
if err != nil {
return nil, fmt.Errorf("failed building EndNode with reason Failure from Fail state: %v", err)
}
builder = b
nextState, nextStateName, isTerminal = findNextOrTerminate(failState, sm)
break
return BuildFromFailState(builder, failState, nextStateName)
default:
return nil, fmt.Errorf("unknown state type %s", nextState.GetType())
}
Expand Down
71 changes: 38 additions & 33 deletions internal/fc/dag_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,42 +197,44 @@ func (c *ChoiceBranchBuilder) NextBranch(dagToChain *Dag, err1 error) *ChoiceBra
// getting start.Next from the dagToChain
startNext, _ := dagToChain.Find(dagToChain.Start.Next)
// chains the alternative to the input dag, which is already connected to a whole series of nodes
c.dagBuilder.dag.addNode(startNext)
err := c.dagBuilder.dag.chain(c.dagBuilder.prevNode.(*ChoiceNode), startNext)
//dagToChain.Start.Next.setBranchId(branchNumber)
if err != nil {
c.dagBuilder.appendError(err)
}
// TODO: RICONTROLLARE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// adds the nodes to the building dag
for _, n := range dagToChain.Nodes {
switch n.(type) {
case *StartNode:
continue
case *EndNode:
continue
case *FanOutNode:
c.dagBuilder.dag.addNode(n)
n.setBranchId(n.GetBranchId() + baseBranchNumber)
continue
default:
c.dagBuilder.dag.addNode(n)
n.setBranchId(n.GetBranchId() + baseBranchNumber)
nextNode, _ := dagToChain.Find(n.GetNext()[0])
// chain the last node(s) of the input dag to the end node of the building dag
if n.GetNext() != nil && len(n.GetNext()) > 0 && nextNode == dagToChain.End {
errEnd := c.dagBuilder.dag.ChainToEndNode(n)
if errEnd != nil {
c.dagBuilder.appendError(errEnd)
return c
if !dagToChain.IsEmpty() {
c.dagBuilder.dag.addNode(startNext)
err := c.dagBuilder.dag.chain(c.dagBuilder.prevNode.(*ChoiceNode), startNext)
if err != nil {
c.dagBuilder.appendError(err)
}
// adds the nodes to the building dag
for _, n := range dagToChain.Nodes {
switch n.(type) {
case *StartNode:
continue
case *EndNode:
continue
case *FanOutNode:
c.dagBuilder.dag.addNode(n)
n.setBranchId(n.GetBranchId() + baseBranchNumber)
continue
default:
c.dagBuilder.dag.addNode(n)
n.setBranchId(n.GetBranchId() + baseBranchNumber)
nextNode, _ := dagToChain.Find(n.GetNext()[0])
// chain the last node(s) of the input dag to the end node of the building dag
if n.GetNext() != nil && len(n.GetNext()) > 0 && nextNode == dagToChain.End {
errEnd := c.dagBuilder.dag.ChainToEndNode(n)
if errEnd != nil {
c.dagBuilder.appendError(errEnd)
return c
}
}
}
}
// so we completed a branch
c.completed++
c.dagBuilder.branches--
} else {
fmt.Printf("not adding another end node\n")
c.EndNextBranch()
}

// so we completed a branch
c.completed++
c.dagBuilder.branches--
} else {
panic("There is not a NextBranch. Use EndChoiceAndBuild to end the choiceNode.")
}
Expand Down Expand Up @@ -560,10 +562,13 @@ func (b *DagBuilder) Build() (*Dag, error) {
return &b.dag, nil
}

func (b *DagBuilder) BuildFailing() (*Dag, error) {
func (b *DagBuilder) BuildFailing(error, cause string) (*Dag, error) {
if b.dag.End != nil {
b.dag.End.Reason = Failure
}
if error != "" && cause != "" {
b.dag.End.Result[error] = cause
}
switch b.prevNode.(type) {
case nil:
return &b.dag, nil
Expand Down
1 change: 1 addition & 0 deletions internal/fc/progress.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ func (p *Progress) SkipAll(nodes []DagNode) error {
return nil
}

// FailNode marks a node progress to failed
func (p *Progress) FailNode(id DagNodeId) error {
for _, node := range p.DagNodes {
if node.Id == id {
Expand Down
68 changes: 68 additions & 0 deletions internal/test/dag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,53 @@ func TestChoiceDag_BuiltWithNextBranch(t *testing.T) {
}
}

// TestChoiceDag_BuiltWithNextBranch_OneEmptyBranch builds a choice node with 3 branch. The third branch is made of an empty Dag.
// There should be only one end node!!!
func TestChoiceDag_BuiltWithNextBranch_OneEmptyBranch(t *testing.T) {
//fc.BranchNumber = 0

m := make(map[string]interface{})
m["input"] = 1
length := 2
_, fArr, err := initializeSameFunctionSlice(length, "py")
u.AssertNil(t, err)

dag, err := fc.NewDagBuilder().
AddChoiceNode(
fc.NewConstCondition(false),
fc.NewSmallerCondition(2, 1),
fc.NewConstCondition(true),
).
NextBranch(fc.CreateSequenceDag(fArr...)).
NextBranch(fc.CreateSequenceDag(fArr...)).
NextBranch(fc.CreateEmptyDag()).
EndChoiceAndBuild()
u.AssertNil(t, err)
choiceDag, _ := dag.Find(dag.Start.Next)
choice := choiceDag.(*fc.ChoiceNode)
width := len(choice.Alternatives)

u.AssertNil(t, err)
fmt.Println("==== Choice Dag ====")
dag.Print()

u.AssertNonNil(t, dag.Start)
u.AssertNonNil(t, dag.End)
u.AssertEquals(t, dag.Width, width)
u.AssertNonNil(t, dag.Nodes)
// u.AssertEquals(t, width+1, len(dag.Nodes))

countEndNodes := 0
for _, n := range dag.Nodes {
_, ok := n.(*fc.EndNode)
if ok {
countEndNodes++
}
}
// IMPORTANT: there should be only one end node!
u.AssertEqualsMsg(t, 1, countEndNodes, "there is not exactly one end node!")
}

// TestBroadcastDag verifies that a broadcast dag is created correctly with fan out, simple nodes and fan in.
// All dag branches have the same sequence of simple nodes.
func TestBroadcastDag(t *testing.T) {
Expand Down Expand Up @@ -520,3 +567,24 @@ func TestVisit(t *testing.T) {
u.AssertEquals(t, 7, len(visitedNodes))

}

func TestVisitMultipleEnd(t *testing.T) {
f, err := initializeExamplePyFunction()
u.AssertNil(t, err)
complexDag, err := fc.NewDagBuilder().
AddSimpleNode(f).
AddChoiceNode(fc.NewEqCondition(1, 4), fc.NewEqCondition(1, 3), fc.NewDiffCondition(1, 4)).
NextBranch(fc.CreateSequenceDag(f)).
NextBranch(fc.CreateSequenceDag(f)).
NextBranch(fc.NewDagBuilder().BuildFailing("Fail", "Default case should never happen")).
EndChoiceAndBuild()
u.AssertNil(t, err)

startNext, _ := complexDag.Find(complexDag.Start.Next)

_ = startNext.GetNext()[0]

nodeList := make([]fc.DagNode, 0)
visitedNodes := fc.VisitDag(complexDag, complexDag.Start.Id, nodeList, false)
u.AssertEquals(t, len(complexDag.Nodes), len(visitedNodes))
}

0 comments on commit 4ceec12

Please sign in to comment.