github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/dag/marshal_test.go (about) 1 package dag 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "strings" 7 "testing" 8 ) 9 10 func TestGraphDot_empty(t *testing.T) { 11 var g Graph 12 g.Add(1) 13 g.Add(2) 14 g.Add(3) 15 16 actual := strings.TrimSpace(string(g.Dot(nil))) 17 expected := strings.TrimSpace(testGraphDotEmptyStr) 18 if actual != expected { 19 t.Fatalf("bad: %s", actual) 20 } 21 } 22 23 func TestGraphDot_basic(t *testing.T) { 24 var g Graph 25 g.Add(1) 26 g.Add(2) 27 g.Add(3) 28 g.Connect(BasicEdge(1, 3)) 29 30 actual := strings.TrimSpace(string(g.Dot(nil))) 31 expected := strings.TrimSpace(testGraphDotBasicStr) 32 if actual != expected { 33 t.Fatalf("bad: %s", actual) 34 } 35 } 36 37 func TestGraphDot_attrs(t *testing.T) { 38 var g Graph 39 g.Add(&testGraphNodeDotter{ 40 Result: &DotNode{ 41 Name: "foo", 42 Attrs: map[string]string{"foo": "bar"}, 43 }, 44 }) 45 46 actual := strings.TrimSpace(string(g.Dot(nil))) 47 expected := strings.TrimSpace(testGraphDotAttrsStr) 48 if actual != expected { 49 t.Fatalf("bad: %s", actual) 50 } 51 } 52 53 type testGraphNodeDotter struct{ Result *DotNode } 54 55 func (n *testGraphNodeDotter) Name() string { return n.Result.Name } 56 func (n *testGraphNodeDotter) DotNode(string, *DotOpts) *DotNode { return n.Result } 57 58 const testGraphDotBasicStr = `digraph { 59 compound = "true" 60 newrank = "true" 61 subgraph "root" { 62 "[root] 1" -> "[root] 3" 63 } 64 } 65 ` 66 67 const testGraphDotEmptyStr = `digraph { 68 compound = "true" 69 newrank = "true" 70 subgraph "root" { 71 } 72 }` 73 74 const testGraphDotAttrsStr = `digraph { 75 compound = "true" 76 newrank = "true" 77 subgraph "root" { 78 "[root] foo" [foo = "bar"] 79 } 80 }` 81 82 func TestGraphJSON_empty(t *testing.T) { 83 var g Graph 84 g.Add(1) 85 g.Add(2) 86 g.Add(3) 87 88 js, err := g.MarshalJSON() 89 if err != nil { 90 t.Fatal(err) 91 } 92 93 actual := strings.TrimSpace(string(js)) 94 expected := strings.TrimSpace(testGraphJSONEmptyStr) 95 if actual != expected { 96 t.Fatalf("bad: %s", actual) 97 } 98 } 99 100 func TestGraphJSON_basic(t *testing.T) { 101 var g Graph 102 g.Add(1) 103 g.Add(2) 104 g.Add(3) 105 g.Connect(BasicEdge(1, 3)) 106 107 js, err := g.MarshalJSON() 108 if err != nil { 109 t.Fatal(err) 110 } 111 actual := strings.TrimSpace(string(js)) 112 expected := strings.TrimSpace(testGraphJSONBasicStr) 113 if actual != expected { 114 t.Fatalf("bad: %s", actual) 115 } 116 } 117 118 // record some graph transformations, and make sure we get the same graph when 119 // they're replayed 120 func TestGraphJSON_basicRecord(t *testing.T) { 121 var g Graph 122 var buf bytes.Buffer 123 g.SetDebugWriter(&buf) 124 125 g.Add(1) 126 g.Add(2) 127 g.Add(3) 128 g.Connect(BasicEdge(1, 2)) 129 g.Connect(BasicEdge(1, 3)) 130 g.Connect(BasicEdge(2, 3)) 131 (&AcyclicGraph{g}).TransitiveReduction() 132 133 recorded := buf.Bytes() 134 // the Walk doesn't happen in a determined order, so just count operations 135 // for now to make sure we wrote stuff out. 136 if len(bytes.Split(recorded, []byte{'\n'})) != 17 { 137 t.Fatalf("bad: %s", recorded) 138 } 139 140 original, err := g.MarshalJSON() 141 if err != nil { 142 t.Fatal(err) 143 } 144 145 // replay the logs, and marshal the graph back out again 146 m, err := decodeGraph(bytes.NewReader(buf.Bytes())) 147 if err != nil { 148 t.Fatal(err) 149 } 150 151 replayed, err := json.MarshalIndent(m, "", " ") 152 if err != nil { 153 t.Fatal(err) 154 } 155 156 if !bytes.Equal(original, replayed) { 157 t.Fatalf("\noriginal: %s\nreplayed: %s", original, replayed) 158 } 159 } 160 161 // Verify that Vertex and Edge annotations appear in the debug output 162 func TestGraphJSON_debugInfo(t *testing.T) { 163 var g Graph 164 var buf bytes.Buffer 165 g.SetDebugWriter(&buf) 166 167 g.Add(1) 168 g.Add(2) 169 g.Add(3) 170 g.Connect(BasicEdge(1, 2)) 171 172 g.DebugVertexInfo(2, "2") 173 g.DebugVertexInfo(3, "3") 174 g.DebugEdgeInfo(BasicEdge(1, 2), "1|2") 175 176 dec := json.NewDecoder(bytes.NewReader(buf.Bytes())) 177 178 var found2, found3, foundEdge bool 179 for dec.More() { 180 var d streamDecode 181 182 err := dec.Decode(&d) 183 if err != nil { 184 t.Fatal(err) 185 } 186 187 switch d.Type { 188 case typeVertexInfo: 189 va := &marshalVertexInfo{} 190 err := json.Unmarshal(d.JSON, va) 191 if err != nil { 192 t.Fatal(err) 193 } 194 195 switch va.Info { 196 case "2": 197 if va.Vertex.Name != "2" { 198 t.Fatalf("wrong vertex annotated 2: %#v", va) 199 } 200 found2 = true 201 case "3": 202 if va.Vertex.Name != "3" { 203 t.Fatalf("wrong vertex annotated 3: %#v", va) 204 } 205 found3 = true 206 default: 207 t.Fatalf("unexpected annotation: %#v", va) 208 } 209 case typeEdgeInfo: 210 ea := &marshalEdgeInfo{} 211 err := json.Unmarshal(d.JSON, ea) 212 if err != nil { 213 t.Fatal(err) 214 } 215 216 switch ea.Info { 217 case "1|2": 218 if ea.Edge.Name != "1|2" { 219 t.Fatalf("incorrect edge annotation: %#v\n", ea) 220 } 221 foundEdge = true 222 default: 223 t.Fatalf("unexpected edge Info: %#v", ea) 224 } 225 } 226 } 227 228 if !found2 { 229 t.Fatal("annotation 2 not found") 230 } 231 if !found3 { 232 t.Fatal("annotation 3 not found") 233 } 234 if !foundEdge { 235 t.Fatal("edge annotation not found") 236 } 237 } 238 239 // Verify that debug operations appear in the debug output 240 func TestGraphJSON_debugOperations(t *testing.T) { 241 var g Graph 242 var buf bytes.Buffer 243 g.SetDebugWriter(&buf) 244 245 debugOp := g.DebugOperation("AddOne", "adding node 1") 246 g.Add(1) 247 debugOp.End("done adding node 1") 248 249 // use an immediate closure to test defers 250 func() { 251 defer g.DebugOperation("AddTwo", "adding nodes 2 and 3").End("done adding 2 and 3") 252 g.Add(2) 253 defer g.DebugOperation("NestedAddThree", "second defer").End("done adding node 3") 254 g.Add(3) 255 }() 256 257 g.Connect(BasicEdge(1, 2)) 258 259 dec := json.NewDecoder(bytes.NewReader(buf.Bytes())) 260 261 var ops []string 262 for dec.More() { 263 var d streamDecode 264 265 err := dec.Decode(&d) 266 if err != nil { 267 t.Fatal(err) 268 } 269 270 if d.Type != typeOperation { 271 continue 272 } 273 274 o := &marshalOperation{} 275 err = json.Unmarshal(d.JSON, o) 276 if err != nil { 277 t.Fatal(err) 278 } 279 280 switch { 281 case o.Begin == "AddOne": 282 ops = append(ops, "BeginAddOne") 283 case o.End == "AddOne": 284 ops = append(ops, "EndAddOne") 285 case o.Begin == "AddTwo": 286 ops = append(ops, "BeginAddTwo") 287 case o.End == "AddTwo": 288 ops = append(ops, "EndAddTwo") 289 case o.Begin == "NestedAddThree": 290 ops = append(ops, "BeginAddThree") 291 case o.End == "NestedAddThree": 292 ops = append(ops, "EndAddThree") 293 } 294 } 295 296 expectedOps := []string{ 297 "BeginAddOne", 298 "EndAddOne", 299 "BeginAddTwo", 300 "BeginAddThree", 301 "EndAddThree", 302 "EndAddTwo", 303 } 304 305 if strings.Join(ops, ",") != strings.Join(expectedOps, ",") { 306 t.Fatalf("incorrect order of operations: %v", ops) 307 } 308 } 309 310 // Verify that we can replay visiting each vertex in order 311 func TestGraphJSON_debugVisits(t *testing.T) { 312 var g Graph 313 var buf bytes.Buffer 314 g.SetDebugWriter(&buf) 315 316 g.Add(1) 317 g.Add(2) 318 g.Add(3) 319 g.Add(4) 320 321 g.Connect(BasicEdge(2, 1)) 322 g.Connect(BasicEdge(4, 2)) 323 g.Connect(BasicEdge(3, 4)) 324 325 err := (&AcyclicGraph{g}).Walk(func(v Vertex) error { 326 g.DebugVisitInfo(v, "basic walk") 327 return nil 328 }) 329 330 if err != nil { 331 t.Fatal(err) 332 } 333 334 var visited []string 335 336 dec := json.NewDecoder(bytes.NewReader(buf.Bytes())) 337 for dec.More() { 338 var d streamDecode 339 340 err := dec.Decode(&d) 341 if err != nil { 342 t.Fatal(err) 343 } 344 345 if d.Type != typeVisitInfo { 346 continue 347 } 348 349 o := &marshalVertexInfo{} 350 err = json.Unmarshal(d.JSON, o) 351 if err != nil { 352 t.Fatal(err) 353 } 354 355 visited = append(visited, o.Vertex.ID) 356 } 357 358 expected := []string{"1", "2", "4", "3"} 359 360 if strings.Join(visited, "-") != strings.Join(expected, "-") { 361 t.Fatalf("incorrect order of operations: %v", visited) 362 } 363 } 364 365 const testGraphJSONEmptyStr = `{ 366 "Type": "Graph", 367 "Name": "root", 368 "Vertices": [ 369 { 370 "ID": "1", 371 "Name": "1" 372 }, 373 { 374 "ID": "2", 375 "Name": "2" 376 }, 377 { 378 "ID": "3", 379 "Name": "3" 380 } 381 ] 382 }` 383 384 const testGraphJSONBasicStr = `{ 385 "Type": "Graph", 386 "Name": "root", 387 "Vertices": [ 388 { 389 "ID": "1", 390 "Name": "1" 391 }, 392 { 393 "ID": "2", 394 "Name": "2" 395 }, 396 { 397 "ID": "3", 398 "Name": "3" 399 } 400 ], 401 "Edges": [ 402 { 403 "Name": "1|3", 404 "Source": "1", 405 "Target": "3" 406 } 407 ] 408 }`