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 }