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