Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/hyakuhei/fluentm
Browse files Browse the repository at this point in the history
  • Loading branch information
hyakuhei committed Jul 13, 2021
2 parents 7e23c81 + 870fd5c commit 68b65b3
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 136 deletions.
77 changes: 33 additions & 44 deletions fluentm.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,63 +518,52 @@ def dfd(scenes: dict, title: str, dfdLabels=True, render=False):
}

boundaryClusters = {}
nodes = {}

flowCounter = 1
# Track which nodes should be placed in which clusters but place neither until we've built the subgraph structure.
placements = {}

# Gather the boundaries and understand how they're nested (but don't nest the graphviz objects ,yet)
# Graphviz subgraphs can't have nodes added, so you need to populate a graph with nodes first, then subgraph it under another graph
for flow in scenes[title]:
print(flow)
for e in (flow.pitcher, flow.catcher):
if e.name in nodes:
# We've already placed this node in a previous flow
continue
if e.name in placements:
continue # skip to next loop

ptr = e
while hasattr(ptr, "boundary"):
if ptr.boundary not in boundaryClusters:
boundaryClusters[ptr.boundary] = Digraph(
name=f"cluster_{ptr.boundary.name}",
graph_attr=clusterAttr | {"label": ptr.boundary.name},
)
ptr = ptr.boundary

if hasattr(e, "boundary"):
ptr = e
while hasattr(ptr, "boundary"):
print(f"Walking: {ptr.boundary.name}")
if ptr.boundary.name not in boundaryClusters:
boundaryClusters[ptr.boundary.name] = Digraph(
name=f"cluster_{ptr.boundary.name}",
graph_attr=clusterAttr | {"label": ptr.boundary.name},
)

if type(ptr) is not Boundary:
nodes[ptr.name] = boundaryClusters[ptr.boundary.name].node(
ptr.name
) # This is where nodes get added inside boundaries

if hasattr(
ptr.boundary, "boundary"
): # See if this boundary, is also in a boundary
if ptr.boundary.boundary.name not in boundaryClusters:
boundaryClusters[ptr.boundary.boundary.name] = Digraph(
f"cluster_{ptr.boundary.boundary.name}",
graph_attr=clusterAttr
| {"label": ptr.boundary.boundary.name},
)

boundaryClusters[ptr.boundary.boundary.name].subgraph(
boundaryClusters[ptr.boundary.name]
)
else:
graph.subgraph(boundaryClusters[ptr.boundary.name])

ptr = ptr.boundary

if e.name not in nodes:
print(f"placing {e.name} in {e.boundary.name}")
nodes[e.name] = boundaryClusters[e.boundary.name].node(e.name)
placements[e.name] = boundaryClusters[e.boundary]
else:
if e.name not in nodes:
print(f"placing {e.name} in graph root")
nodes[e.name] = graph.node(e.name)
placements[e.name] = graph

# Place nodes in Graphs, ready for subgraphing
for n in placements:
placements[n].node(n)

# Subgraph the nodes
for c in boundaryClusters:
if hasattr(c, "boundary"):
boundaryClusters[c.boundary].subgraph(boundaryClusters[c])
else:
graph.subgraph(boundaryClusters[c])

# Add the edges
flowCounter = 1
for flow in scenes[title]:
if dfdLabels is True:
graph.edge(
flow.pitcher.name, flow.catcher.name, f"({flowCounter}) {flow.name}"
)
else:
graph.edge(flow.pitcher.name, flow.catcher.name, f"({flowCounter})")

flowCounter += 1

return graph
Expand Down
47 changes: 24 additions & 23 deletions tests/test_dfds.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,47 @@
from fluentm import Actor, Boundary, Process, Data, DataFlow
from fluentm import dfd
from fluentm import dfd, renderDfd

scenes={
"Test ABC":[
DataFlow(Process("A"), Process("B"), "Edge 1"),
DataFlow(Process.get("B"), Process("C"), "Edge 2")
],
"Test ABCInOneBoundary":[ #These aren't using .get, they will replace A, B above... that can't be right...
DataFlow(Process("A").inBoundary("BOUNDARY"), Process("B").inBoundary("BOUNDARY"), "Edge 3")
"Test DEInOneBoundary":[
DataFlow(Process("D").inBoundary("BOUNDARY"), Process("E").inBoundary("BOUNDARY"), "Edge 3")

]
}

# Be careful with these, they have to be tabs, not spaces
expectedResults={
"Test ABC":"""digraph "Test ABC" {
\tcolor=blue rankdir=LR
\tnode [fontname=Arial fontsize=14]
\tA
\tB
\tC
\tA -> B [label="(1) Edge 1"]
\tB -> C [label="(2) Edge 2"]
"Test ABC":"""digraph "Test ABC" {
color=blue rankdir=LR
node [fontname=Arial fontsize=14]
A
B
C
A -> B [label="(1) Edge 1"]
B -> C [label="(2) Edge 2"]
}""",
"Test ABCInOneBoundary":"""digraph "Test ABCInOneBoundary" {
\tcolor=blue rankdir=LR
\tnode [fontname=Arial fontsize=14]
\tsubgraph cluster_BOUNDARY {
\t\tcolor=red label=BOUNDARY
\t\tA
\t\tB
\t}
\tA -> B [label="(1) Edge 3"]
"Test DEInOneBoundary":"""digraph "Test DEInOneBoundary" {
color=blue rankdir=LR
node [fontname=Arial fontsize=14]
subgraph cluster_BOUNDARY {
graph [color=red fontname=Arial fontsize=12 label=BOUNDARY line=dotted]
D
E
}
D -> E [label="(1) Edge 3"]
}"""
}

def testABC():
graph = dfd(scenes, "Test ABC")
renderDfd(graph,"Test ABC", outputDir="testOutput")
assert graph.__str__() == expectedResults['Test ABC']

def testABInOneBoundary():
graph = dfd(scenes, "Test ABCInOneBoundary")
print(graph)
assert graph.__str__() == expectedResults['Test ABCInOneBoundary']
graph = dfd(scenes, "Test DEInOneBoundary")
renderDfd(graph,"Test DEInOneBoundary", outputDir="testOutput")
assert graph.__str__() == expectedResults['Test DEInOneBoundary']

32 changes: 16 additions & 16 deletions tests/test_inline_declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,26 @@

expectedResults = {
"Warehouse packages order":"""digraph "Warehouse packages order" {
\tcolor=blue rankdir=LR
\tnode [fontname=Arial fontsize=14]
\tsubgraph cluster_Logistics {
\t\tcolor=red label=Logistics
\t\t"Order Resolver"
\t}
\tsubgraph cluster_Warehouse {
\t\tcolor=red label=Warehouse
\t\t"Label Printer"
\t\t"Warehouse Notifier"
\t}
\t"Order Resolver" -> "Label Printer" [label="(1) Print label"]
\t"Label Printer" -> "Order Resolver" [label="(2) Label ID"]
\t"Order Resolver" -> "Warehouse Notifier" [label="(3) Pick stock item and use label"]
\t"Warehouse Notifier" -> "Order Resolver" [label="(4) Confirmation ID"]
color=blue rankdir=LR
node [fontname=Arial fontsize=14]
subgraph cluster_Logistics {
graph [color=red fontname=Arial fontsize=12 label=Logistics line=dotted]
"Order Resolver"
}
subgraph cluster_Warehouse {
graph [color=red fontname=Arial fontsize=12 label=Warehouse line=dotted]
"Label Printer"
"Warehouse Notifier"
}
"Order Resolver" -> "Label Printer" [label="(1) Print label"]
"Label Printer" -> "Order Resolver" [label="(2) Label ID"]
"Order Resolver" -> "Warehouse Notifier" [label="(3) Pick stock item and use label"]
"Warehouse Notifier" -> "Order Resolver" [label="(4) Confirmation ID"]
}"""
}

def test_dfd():
graph = dfd(scenes, "Warehouse packages order")
#print(graph)
renderDfd(graph, "Warehouse packages order", outputDir="testOutput")
assert graph.__str__() == expectedResults["Warehouse packages order"]
renderDfd(graph, "Jordan", outputDir="testOutput")
94 changes: 41 additions & 53 deletions tests/test_variables_dfd.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from fluentm import Actor, Boundary, Process, Data, DataFlow
from fluentm import dfd
from fluentm import dfd, renderDfd

# fluentTM Classes are scoped by name, so, I can only have one Boundary called "Internet"
# Attempting to instantiate another with the same name will give you the first one you instantiated
Expand Down Expand Up @@ -48,65 +48,53 @@

expectedResults = {
"Customer views book online":"""digraph "Customer views book online" {
\tcolor=blue rankdir=LR
\tnode [fontname=Arial fontsize=14]
\tsubgraph cluster_Internet {
\t\tcolor=red label=Internet
\t\tCustomer
\t}
\tsubgraph "cluster_BookStore Co" {
\t\tcolor=red label="BookStore Co"
\t\tsubgraph "cluster_Front End Systems" {
\t\t\tcolor=red label="Front End Systems"
\t\t\t"Web Server"
\t\t}
\t\tsubgraph cluster_Databases {
\t\t\tcolor=red label=Databases
\t\t\t"Content DB"
\t\t\t"Stock DB"
\t\t}
\t}
\tCustomer -> "Web Server" [label="(1) View item ID"]
\t"Web Server" -> "Content DB" [label="(2) Fetch content for ID"]
\t"Content DB" -> "Web Server" [label="(3) Image Content, Description"]
\t"Web Server" -> "Stock DB" [label="(4) Get Stock Level"]
\t"Stock DB" -> "Web Server" [label="(5) Stock Level"]
\t"Web Server" -> Customer [label="(6) Rendered Item Information"]
color=blue rankdir=LR
node [fontname=Arial fontsize=14]
subgraph cluster_Internet {
graph [color=red fontname=Arial fontsize=12 label=Internet line=dotted]
Customer
}
subgraph "cluster_BookStore Co" {
graph [color=red fontname=Arial fontsize=12 label="BookStore Co" line=dotted]
subgraph "cluster_Front End Systems" {
graph [color=red fontname=Arial fontsize=12 label="Front End Systems" line=dotted]
"Web Server"
}
}
Customer -> "Web Server" [label="(1) View item ID"]
"Web Server" -> "Content DB" [label="(2) Fetch content for ID"]
"Content DB" -> "Web Server" [label="(3) Image Content, Description"]
"Web Server" -> "Stock DB" [label="(4) Get Stock Level"]
"Stock DB" -> "Web Server" [label="(5) Stock Level"]
"Web Server" -> Customer [label="(6) Rendered Item Information"]
}""",
"Customer buys book":"""digraph "Customer buys book" {
\tcolor=blue rankdir=LR
\tnode [fontname=Arial fontsize=14]
\tsubgraph cluster_Internet {
\t\tcolor=red label=Internet
\t\tCustomer
\t}
\tsubgraph "cluster_BookStore Co" {
\t\tcolor=red label="BookStore Co"
\t\tsubgraph "cluster_Front End Systems" {
\t\t\tcolor=red label="Front End Systems"
\t\t\t"Web Server"
\t\t}
\t\tsubgraph cluster_Databases {
\t\t\tcolor=red label=Databases
\t\t\t"Content DB"
\t\t\t"Stock DB"
\t\t}
\t\tsubgraph "cluster_Front End Systems" {
\t\t\tcolor=red label="Front End Systems"
\t\t\t"Web Server"
\t\t}
\t}
\tCustomer -> "Web Server" [label="(1) Buy Item ID"]
\t"Web Server" -> "Merchant API" [label="(2) Process payment"]
\t"Merchant API" -> "Web Server" [label="(3) Confirmation"]
\t"Web Server" -> Customer [label="(4) SOLD!"]
color=blue rankdir=LR
node [fontname=Arial fontsize=14]
subgraph cluster_Internet {
graph [color=red fontname=Arial fontsize=12 label=Internet line=dotted]
Customer
}
subgraph "cluster_BookStore Co" {
graph [color=red fontname=Arial fontsize=12 label="BookStore Co" line=dotted]
subgraph "cluster_Front End Systems" {
graph [color=red fontname=Arial fontsize=12 label="Front End Systems" line=dotted]
"Web Server"
}
}
Customer -> "Web Server" [label="(1) Buy Item ID"]
"Web Server" -> "Merchant API" [label="(2) Process payment"]
"Merchant API" -> "Web Server" [label="(3) Confirmation"]
"Web Server" -> Customer [label="(4) SOLD!"]
}"""
}

def test_dfd_from_variables():
graph = dfd(scenes, "Customer views book online")
assert graph.__str__() == expectedResults["Customer views book online"]
renderDfd(graph,"Customer views book online", outputDir="testOutput")
assert graph.__str__() == expectedResults['Customer views book online']

def test_dfd_from_gets():
graph = dfd(scenes, "Customer buys book")
assert graph.__str__() == expectedResults["Customer buys book"]
renderDfd(graph,"Customer buys book", outputDir="testOutput")
assert graph.__str__() == expectedResults['Customer buys book']

0 comments on commit 68b65b3

Please sign in to comment.