github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/terraform/graph_dot_test.go (about)

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