github.com/magodo/terraform@v0.11.12-beta1/terraform/graph_dot_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  
     7  	"github.com/hashicorp/terraform/dag"
     8  )
     9  
    10  func TestGraphDot(t *testing.T) {
    11  	cases := []struct {
    12  		Name   string
    13  		Graph  testGraphFunc
    14  		Opts   dag.DotOpts
    15  		Expect string
    16  		Error  string
    17  	}{
    18  		{
    19  			Name:  "empty",
    20  			Graph: func() *Graph { return &Graph{} },
    21  			Expect: `
    22  digraph {
    23  	compound = "true"
    24  	newrank = "true"
    25  	subgraph "root" {
    26  	}
    27  }`,
    28  		},
    29  		{
    30  			Name: "three-level",
    31  			Graph: func() *Graph {
    32  				var g Graph
    33  				root := &testDrawableOrigin{"root"}
    34  				g.Add(root)
    35  
    36  				levelOne := []interface{}{"foo", "bar"}
    37  				for i, s := range levelOne {
    38  					levelOne[i] = &testDrawable{
    39  						VertexName: s.(string),
    40  					}
    41  					v := levelOne[i]
    42  
    43  					g.Add(v)
    44  					g.Connect(dag.BasicEdge(v, root))
    45  				}
    46  
    47  				levelTwo := []string{"baz", "qux"}
    48  				for i, s := range levelTwo {
    49  					v := &testDrawable{
    50  						VertexName: s,
    51  					}
    52  
    53  					g.Add(v)
    54  					g.Connect(dag.BasicEdge(v, levelOne[i]))
    55  				}
    56  
    57  				return &g
    58  			},
    59  			Expect: `
    60  digraph {
    61  	compound = "true"
    62  	newrank = "true"
    63  	subgraph "root" {
    64  		"[root] bar"
    65  		"[root] baz"
    66  		"[root] foo"
    67  		"[root] qux"
    68  		"[root] root"
    69  		"[root] bar" -> "[root] root"
    70  		"[root] baz" -> "[root] foo"
    71  		"[root] foo" -> "[root] root"
    72  		"[root] qux" -> "[root] bar"
    73  	}
    74  }
    75  			`,
    76  		},
    77  
    78  		{
    79  			Name: "cycle",
    80  			Opts: dag.DotOpts{
    81  				DrawCycles: true,
    82  			},
    83  			Graph: func() *Graph {
    84  				var g Graph
    85  				root := &testDrawableOrigin{"root"}
    86  				g.Add(root)
    87  
    88  				vA := g.Add(&testDrawable{
    89  					VertexName: "A",
    90  				})
    91  
    92  				vB := g.Add(&testDrawable{
    93  					VertexName: "B",
    94  				})
    95  
    96  				vC := g.Add(&testDrawable{
    97  					VertexName: "C",
    98  				})
    99  
   100  				g.Connect(dag.BasicEdge(vA, root))
   101  				g.Connect(dag.BasicEdge(vA, vC))
   102  				g.Connect(dag.BasicEdge(vB, vA))
   103  				g.Connect(dag.BasicEdge(vC, vB))
   104  
   105  				return &g
   106  			},
   107  			Expect: `
   108  digraph {
   109  	compound = "true"
   110  	newrank = "true"
   111  	subgraph "root" {
   112  		"[root] A"
   113  		"[root] B"
   114  		"[root] C"
   115  		"[root] root"
   116  		"[root] A" -> "[root] B" [color = "red", penwidth = "2.0"]
   117  		"[root] A" -> "[root] C"
   118  		"[root] A" -> "[root] root"
   119  		"[root] B" -> "[root] A"
   120  		"[root] B" -> "[root] C" [color = "red", penwidth = "2.0"]
   121  		"[root] C" -> "[root] A" [color = "red", penwidth = "2.0"]
   122  		"[root] C" -> "[root] B"
   123  	}
   124  }
   125  					`,
   126  		},
   127  
   128  		{
   129  			Name: "subgraphs, no depth restriction",
   130  			Opts: dag.DotOpts{
   131  				MaxDepth: -1,
   132  			},
   133  			Graph: func() *Graph {
   134  				var g Graph
   135  				root := &testDrawableOrigin{"root"}
   136  				g.Add(root)
   137  
   138  				var sub Graph
   139  				vSubRoot := sub.Add(&testDrawableOrigin{"sub_root"})
   140  
   141  				var subsub Graph
   142  				subsub.Add(&testDrawableOrigin{"subsub_root"})
   143  				vSubV := sub.Add(&testDrawableSubgraph{
   144  					VertexName:   "subsub",
   145  					SubgraphMock: &subsub,
   146  				})
   147  
   148  				vSub := g.Add(&testDrawableSubgraph{
   149  					VertexName:   "sub",
   150  					SubgraphMock: &sub,
   151  				})
   152  
   153  				g.Connect(dag.BasicEdge(vSub, root))
   154  				sub.Connect(dag.BasicEdge(vSubV, vSubRoot))
   155  
   156  				return &g
   157  			},
   158  			Expect: `
   159  digraph {
   160  	compound = "true"
   161  	newrank = "true"
   162  	subgraph "root" {
   163  		"[root] root"
   164  		"[root] sub"
   165  		"[root] sub" -> "[root] root"
   166  	}
   167  	subgraph "cluster_sub" {
   168  		label = "sub"
   169  		"[sub] sub_root"
   170  		"[sub] subsub"
   171  		"[sub] subsub" -> "[sub] sub_root"
   172  	}
   173  	subgraph "cluster_subsub" {
   174  		label = "subsub"
   175  		"[subsub] subsub_root"
   176  	}
   177  }
   178  						`,
   179  		},
   180  
   181  		{
   182  			Name: "subgraphs, with depth restriction",
   183  			Opts: dag.DotOpts{
   184  				MaxDepth: 1,
   185  			},
   186  			Graph: func() *Graph {
   187  				var g Graph
   188  				root := &testDrawableOrigin{"root"}
   189  				g.Add(root)
   190  
   191  				var sub Graph
   192  				rootSub := sub.Add(&testDrawableOrigin{"sub_root"})
   193  
   194  				var subsub Graph
   195  				subsub.Add(&testDrawableOrigin{"subsub_root"})
   196  
   197  				subV := sub.Add(&testDrawableSubgraph{
   198  					VertexName:   "subsub",
   199  					SubgraphMock: &subsub,
   200  				})
   201  				vSub := g.Add(&testDrawableSubgraph{
   202  					VertexName:   "sub",
   203  					SubgraphMock: &sub,
   204  				})
   205  
   206  				g.Connect(dag.BasicEdge(vSub, root))
   207  				sub.Connect(dag.BasicEdge(subV, rootSub))
   208  				return &g
   209  			},
   210  			Expect: `
   211  digraph {
   212  	compound = "true"
   213  	newrank = "true"
   214  	subgraph "root" {
   215  		"[root] root"
   216  		"[root] sub"
   217  		"[root] sub" -> "[root] root"
   218  	}
   219  	subgraph "cluster_sub" {
   220  		label = "sub"
   221  		"[sub] sub_root"
   222  		"[sub] subsub"
   223  		"[sub] subsub" -> "[sub] sub_root"
   224  	}
   225  }
   226  						`,
   227  		},
   228  	}
   229  
   230  	for _, tc := range cases {
   231  		tn := tc.Name
   232  		t.Run(tn, func(t *testing.T) {
   233  			g := tc.Graph()
   234  			var err error
   235  			//actual, err := GraphDot(g, &tc.Opts)
   236  			actual := string(g.Dot(&tc.Opts))
   237  
   238  			if err == nil && tc.Error != "" {
   239  				t.Fatalf("%s: expected err: %s, got none", tn, tc.Error)
   240  			}
   241  			if err != nil && tc.Error == "" {
   242  				t.Fatalf("%s: unexpected err: %s", tn, err)
   243  			}
   244  			if err != nil && tc.Error != "" {
   245  				if !strings.Contains(err.Error(), tc.Error) {
   246  					t.Fatalf("%s: expected err: %s\nto contain: %s", tn, err, tc.Error)
   247  				}
   248  				return
   249  			}
   250  
   251  			expected := strings.TrimSpace(tc.Expect) + "\n"
   252  			if actual != expected {
   253  				t.Fatalf("%s:\n\nexpected:\n%s\n\ngot:\n%s", tn, expected, actual)
   254  			}
   255  		})
   256  	}
   257  }
   258  
   259  type testGraphFunc func() *Graph
   260  
   261  type testDrawable struct {
   262  	VertexName      string
   263  	DependentOnMock []string
   264  }
   265  
   266  func (node *testDrawable) Name() string {
   267  	return node.VertexName
   268  }
   269  func (node *testDrawable) DotNode(n string, opts *dag.DotOpts) *dag.DotNode {
   270  	return &dag.DotNode{Name: n, Attrs: map[string]string{}}
   271  }
   272  func (node *testDrawable) DependableName() []string {
   273  	return []string{node.VertexName}
   274  }
   275  func (node *testDrawable) DependentOn() []string {
   276  	return node.DependentOnMock
   277  }
   278  
   279  type testDrawableOrigin struct {
   280  	VertexName string
   281  }
   282  
   283  func (node *testDrawableOrigin) Name() string {
   284  	return node.VertexName
   285  }
   286  func (node *testDrawableOrigin) DotNode(n string, opts *dag.DotOpts) *dag.DotNode {
   287  	return &dag.DotNode{Name: n, Attrs: map[string]string{}}
   288  }
   289  func (node *testDrawableOrigin) DotOrigin() bool {
   290  	return true
   291  }
   292  func (node *testDrawableOrigin) DependableName() []string {
   293  	return []string{node.VertexName}
   294  }
   295  
   296  type testDrawableSubgraph struct {
   297  	VertexName      string
   298  	SubgraphMock    *Graph
   299  	DependentOnMock []string
   300  }
   301  
   302  func (node *testDrawableSubgraph) Name() string {
   303  	return node.VertexName
   304  }
   305  func (node *testDrawableSubgraph) Subgraph() dag.Grapher {
   306  	return node.SubgraphMock
   307  }
   308  func (node *testDrawableSubgraph) DotNode(n string, opts *dag.DotOpts) *dag.DotNode {
   309  	return &dag.DotNode{Name: n, Attrs: map[string]string{}}
   310  }
   311  func (node *testDrawableSubgraph) DependentOn() []string {
   312  	return node.DependentOnMock
   313  }