github.com/ves/terraform@v0.8.0-beta2/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 }