github.com/gopherd/gonum@v0.0.4/graph/encoding/dot/encode_test.go (about) 1 // Copyright ©2015 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package dot 6 7 import ( 8 "bytes" 9 "os/exec" 10 "testing" 11 12 "github.com/gopherd/gonum/graph" 13 "github.com/gopherd/gonum/graph/encoding" 14 "github.com/gopherd/gonum/graph/multi" 15 "github.com/gopherd/gonum/graph/simple" 16 ) 17 18 // intset is an integer set. 19 type intset map[int64]struct{} 20 21 func linksTo(i ...int64) intset { 22 if len(i) == 0 { 23 return nil 24 } 25 s := make(intset) 26 for _, v := range i { 27 s[v] = struct{}{} 28 } 29 return s 30 } 31 32 var ( 33 // Example graph from http://en.wikipedia.org/wiki/File:PageRanks-Example.svg 16:17, 8 July 2009 34 // Node identities are rewritten here to use integers from 0 to match with the DOT output. 35 pageRankGraph = []intset{ 36 0: nil, 37 1: linksTo(2), 38 2: linksTo(1), 39 3: linksTo(0, 1), 40 4: linksTo(3, 1, 5), 41 5: linksTo(1, 4), 42 6: linksTo(1, 4), 43 7: linksTo(1, 4), 44 8: linksTo(1, 4), 45 9: linksTo(4), 46 10: linksTo(4), 47 } 48 49 // Example graph from http://en.wikipedia.org/w/index.php?title=PageRank&oldid=659286279#Power_Method 50 powerMethodGraph = []intset{ 51 0: linksTo(1, 2), 52 1: linksTo(3), 53 2: linksTo(3, 4), 54 3: linksTo(4), 55 4: linksTo(0), 56 } 57 ) 58 59 func directedGraphFrom(g []intset) graph.Directed { 60 dg := simple.NewDirectedGraph() 61 for u, e := range g { 62 for v := range e { 63 dg.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) 64 } 65 } 66 return dg 67 } 68 69 func undirectedGraphFrom(g []intset) graph.Graph { 70 dg := simple.NewUndirectedGraph() 71 for u, e := range g { 72 for v := range e { 73 dg.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) 74 } 75 } 76 return dg 77 } 78 79 const alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 80 81 type namedNode struct { 82 id int64 83 name string 84 } 85 86 func (n namedNode) ID() int64 { return n.id } 87 func (n namedNode) DOTID() string { return n.name } 88 89 func directedNamedIDGraphFrom(g []intset) graph.Directed { 90 dg := simple.NewDirectedGraph() 91 for u, e := range g { 92 u := int64(u) 93 nu := namedNode{id: u, name: alpha[u : u+1]} 94 for v := range e { 95 nv := namedNode{id: v, name: alpha[v : v+1]} 96 dg.SetEdge(simple.Edge{F: nu, T: nv}) 97 } 98 } 99 return dg 100 } 101 102 func undirectedNamedIDGraphFrom(g []intset) graph.Graph { 103 dg := simple.NewUndirectedGraph() 104 for u, e := range g { 105 u := int64(u) 106 nu := namedNode{id: u, name: alpha[u : u+1]} 107 for v := range e { 108 nv := namedNode{id: v, name: alpha[v : v+1]} 109 dg.SetEdge(simple.Edge{F: nu, T: nv}) 110 } 111 } 112 return dg 113 } 114 115 type attrNode struct { 116 id int64 117 attr []encoding.Attribute 118 } 119 120 func (n attrNode) ID() int64 { return n.id } 121 func (n attrNode) Attributes() []encoding.Attribute { return n.attr } 122 123 func directedNodeAttrGraphFrom(g []intset, attr [][]encoding.Attribute) graph.Directed { 124 dg := simple.NewDirectedGraph() 125 for u, e := range g { 126 u := int64(u) 127 var at []encoding.Attribute 128 if u < int64(len(attr)) { 129 at = attr[u] 130 } 131 nu := attrNode{id: u, attr: at} 132 for v := range e { 133 if v < int64(len(attr)) { 134 at = attr[v] 135 } 136 nv := attrNode{id: v, attr: at} 137 dg.SetEdge(simple.Edge{F: nu, T: nv}) 138 } 139 } 140 return dg 141 } 142 143 func undirectedNodeAttrGraphFrom(g []intset, attr [][]encoding.Attribute) graph.Graph { 144 dg := simple.NewUndirectedGraph() 145 for u, e := range g { 146 u := int64(u) 147 var at []encoding.Attribute 148 if u < int64(len(attr)) { 149 at = attr[u] 150 } 151 nu := attrNode{id: u, attr: at} 152 for v := range e { 153 if v < int64(len(attr)) { 154 at = attr[v] 155 } 156 nv := attrNode{id: v, attr: at} 157 dg.SetEdge(simple.Edge{F: nu, T: nv}) 158 } 159 } 160 return dg 161 } 162 163 type namedAttrNode struct { 164 id int64 165 name string 166 attr []encoding.Attribute 167 } 168 169 func (n namedAttrNode) ID() int64 { return n.id } 170 func (n namedAttrNode) DOTID() string { return n.name } 171 func (n namedAttrNode) Attributes() []encoding.Attribute { return n.attr } 172 173 func directedNamedIDNodeAttrGraphFrom(g []intset, attr [][]encoding.Attribute) graph.Directed { 174 dg := simple.NewDirectedGraph() 175 for u, e := range g { 176 u := int64(u) 177 var at []encoding.Attribute 178 if u < int64(len(attr)) { 179 at = attr[u] 180 } 181 nu := namedAttrNode{id: u, name: alpha[u : u+1], attr: at} 182 for v := range e { 183 if v < int64(len(attr)) { 184 at = attr[v] 185 } 186 nv := namedAttrNode{id: v, name: alpha[v : v+1], attr: at} 187 dg.SetEdge(simple.Edge{F: nu, T: nv}) 188 } 189 } 190 return dg 191 } 192 193 func undirectedNamedIDNodeAttrGraphFrom(g []intset, attr [][]encoding.Attribute) graph.Graph { 194 dg := simple.NewUndirectedGraph() 195 for u, e := range g { 196 u := int64(u) 197 var at []encoding.Attribute 198 if u < int64(len(attr)) { 199 at = attr[u] 200 } 201 nu := namedAttrNode{id: u, name: alpha[u : u+1], attr: at} 202 for v := range e { 203 if v < int64(len(attr)) { 204 at = attr[v] 205 } 206 nv := namedAttrNode{id: v, name: alpha[v : v+1], attr: at} 207 dg.SetEdge(simple.Edge{F: nu, T: nv}) 208 } 209 } 210 return dg 211 } 212 213 type attrEdge struct { 214 from, to graph.Node 215 216 attr []encoding.Attribute 217 } 218 219 func (e attrEdge) From() graph.Node { return e.from } 220 func (e attrEdge) To() graph.Node { return e.to } 221 func (e attrEdge) ReversedEdge() graph.Edge { e.from, e.to = e.to, e.from; return e } 222 func (e attrEdge) Weight() float64 { return 0 } 223 func (e attrEdge) Attributes() []encoding.Attribute { return e.attr } 224 225 func directedEdgeAttrGraphFrom(g []intset, attr map[edge][]encoding.Attribute) graph.Directed { 226 dg := simple.NewDirectedGraph() 227 for u, e := range g { 228 u := int64(u) 229 for v := range e { 230 dg.SetEdge(attrEdge{from: simple.Node(u), to: simple.Node(v), attr: attr[edge{from: u, to: v}]}) 231 } 232 } 233 return dg 234 } 235 236 func undirectedEdgeAttrGraphFrom(g []intset, attr map[edge][]encoding.Attribute) graph.Graph { 237 dg := simple.NewUndirectedGraph() 238 for u, e := range g { 239 u := int64(u) 240 for v := range e { 241 dg.SetEdge(attrEdge{from: simple.Node(u), to: simple.Node(v), attr: attr[edge{from: u, to: v}]}) 242 } 243 } 244 return dg 245 } 246 247 type portedEdge struct { 248 from, to graph.Node 249 250 fromPort string 251 fromCompass string 252 toPort string 253 toCompass string 254 } 255 256 func (e portedEdge) From() graph.Node { return e.from } 257 func (e portedEdge) To() graph.Node { return e.to } 258 func (e portedEdge) ReversedEdge() graph.Edge { 259 e.from, e.to = e.to, e.from 260 e.fromPort, e.toPort = e.toPort, e.fromPort 261 e.fromCompass, e.toCompass = e.toCompass, e.fromCompass 262 return e 263 } 264 func (e portedEdge) Weight() float64 { return 0 } 265 266 func (e portedEdge) FromPort() (port, compass string) { 267 return e.fromPort, e.fromCompass 268 } 269 func (e portedEdge) ToPort() (port, compass string) { 270 return e.toPort, e.toCompass 271 } 272 273 func directedPortedAttrGraphFrom(g []intset, attr [][]encoding.Attribute, ports map[edge]portedEdge) graph.Directed { 274 dg := simple.NewDirectedGraph() 275 for u, e := range g { 276 u := int64(u) 277 var at []encoding.Attribute 278 if u < int64(len(attr)) { 279 at = attr[u] 280 } 281 nu := attrNode{id: u, attr: at} 282 for v := range e { 283 if v < int64(len(attr)) { 284 at = attr[v] 285 } 286 pe := ports[edge{from: u, to: v}] 287 pe.from = nu 288 pe.to = attrNode{id: v, attr: at} 289 dg.SetEdge(pe) 290 } 291 } 292 return dg 293 } 294 295 func undirectedPortedAttrGraphFrom(g []intset, attr [][]encoding.Attribute, ports map[edge]portedEdge) graph.Graph { 296 dg := simple.NewUndirectedGraph() 297 for u, e := range g { 298 u := int64(u) 299 var at []encoding.Attribute 300 if u < int64(len(attr)) { 301 at = attr[u] 302 } 303 nu := attrNode{id: u, attr: at} 304 for v := range e { 305 if v < int64(len(attr)) { 306 at = attr[v] 307 } 308 pe := ports[edge{from: u, to: v}] 309 pe.from = nu 310 pe.to = attrNode{id: v, attr: at} 311 dg.SetEdge(pe) 312 } 313 } 314 return dg 315 } 316 317 type graphAttributer struct { 318 graph.Graph 319 graph encoding.Attributer 320 node encoding.Attributer 321 edge encoding.Attributer 322 } 323 324 func (g graphAttributer) DOTAttributers() (graph, node, edge encoding.Attributer) { 325 return g.graph, g.node, g.edge 326 } 327 328 type structuredGraph struct { 329 *simple.UndirectedGraph 330 sub []Graph 331 } 332 333 func undirectedStructuredGraphFrom(c []edge, g ...[]intset) graph.Graph { 334 s := &structuredGraph{UndirectedGraph: simple.NewUndirectedGraph()} 335 var base int64 336 for i, sg := range g { 337 sub := simple.NewUndirectedGraph() 338 for u, e := range sg { 339 u := int64(u) 340 for v := range e { 341 ce := simple.Edge{F: simple.Node(u + base), T: simple.Node(v + base)} 342 sub.SetEdge(ce) 343 } 344 } 345 s.sub = append(s.sub, namedGraph{id: int64(i), Graph: sub}) 346 base += int64(len(sg)) 347 } 348 for _, e := range c { 349 s.SetEdge(simple.Edge{F: simple.Node(e.from), T: simple.Node(e.to)}) 350 } 351 return s 352 } 353 354 func (g structuredGraph) Structure() []Graph { 355 return g.sub 356 } 357 358 type namedGraph struct { 359 id int64 360 graph.Graph 361 } 362 363 func (g namedGraph) DOTID() string { return alpha[g.id : g.id+1] } 364 365 type subGraph struct { 366 id int64 367 graph.Graph 368 } 369 370 func (g subGraph) ID() int64 { return g.id } 371 func (g subGraph) Subgraph() graph.Graph { 372 return namedGraph(g) 373 } 374 375 func undirectedSubGraphFrom(g []intset, s map[int64][]intset) graph.Graph { 376 var base int64 377 subs := make(map[int64]subGraph) 378 for i, sg := range s { 379 sub := simple.NewUndirectedGraph() 380 for u, e := range sg { 381 u := int64(u) 382 for v := range e { 383 ce := simple.Edge{F: simple.Node(u + base), T: simple.Node(v + base)} 384 sub.SetEdge(ce) 385 } 386 } 387 subs[i] = subGraph{id: i, Graph: sub} 388 base += int64(len(sg)) 389 } 390 391 dg := simple.NewUndirectedGraph() 392 for u, e := range g { 393 u := int64(u) 394 var nu graph.Node 395 if sg, ok := subs[u]; ok { 396 sg.id += base 397 nu = sg 398 } else { 399 nu = simple.Node(u + base) 400 } 401 for v := range e { 402 var nv graph.Node 403 if sg, ok := subs[v]; ok { 404 sg.id += base 405 nv = sg 406 } else { 407 nv = simple.Node(v + base) 408 } 409 dg.SetEdge(simple.Edge{F: nu, T: nv}) 410 } 411 } 412 return dg 413 } 414 415 var encodeTests = []struct { 416 name string 417 g graph.Graph 418 419 prefix string 420 421 want string 422 }{ 423 // Empty graph. 424 { 425 name: "Empty Undirected", 426 g: simple.NewUndirectedGraph(), 427 want: `strict graph "Empty Undirected" { 428 }`, 429 }, 430 { 431 name: "Empty Directed", 432 g: simple.NewDirectedGraph(), 433 want: `strict digraph "Empty Directed" { 434 }`, 435 }, 436 437 // Basic graph.Graph handling. 438 { 439 name: "PageRank", 440 g: directedGraphFrom(pageRankGraph), 441 442 want: `strict digraph PageRank { 443 // Node definitions. 444 0; 445 1; 446 2; 447 3; 448 4; 449 5; 450 6; 451 7; 452 8; 453 9; 454 10; 455 456 // Edge definitions. 457 1 -> 2; 458 2 -> 1; 459 3 -> 0; 460 3 -> 1; 461 4 -> 1; 462 4 -> 3; 463 4 -> 5; 464 5 -> 1; 465 5 -> 4; 466 6 -> 1; 467 6 -> 4; 468 7 -> 1; 469 7 -> 4; 470 8 -> 1; 471 8 -> 4; 472 9 -> 4; 473 10 -> 4; 474 }`, 475 }, 476 { 477 g: undirectedGraphFrom(pageRankGraph), 478 479 want: `strict graph { 480 // Node definitions. 481 0; 482 1; 483 2; 484 3; 485 4; 486 5; 487 6; 488 7; 489 8; 490 9; 491 10; 492 493 // Edge definitions. 494 0 -- 3; 495 1 -- 2; 496 1 -- 3; 497 1 -- 4; 498 1 -- 5; 499 1 -- 6; 500 1 -- 7; 501 1 -- 8; 502 3 -- 4; 503 4 -- 5; 504 4 -- 6; 505 4 -- 7; 506 4 -- 8; 507 4 -- 9; 508 4 -- 10; 509 }`, 510 }, 511 { 512 g: directedGraphFrom(powerMethodGraph), 513 514 want: `strict digraph { 515 // Node definitions. 516 0; 517 1; 518 2; 519 3; 520 4; 521 522 // Edge definitions. 523 0 -> 1; 524 0 -> 2; 525 1 -> 3; 526 2 -> 3; 527 2 -> 4; 528 3 -> 4; 529 4 -> 0; 530 }`, 531 }, 532 { 533 g: undirectedGraphFrom(powerMethodGraph), 534 535 want: `strict graph { 536 // Node definitions. 537 0; 538 1; 539 2; 540 3; 541 4; 542 543 // Edge definitions. 544 0 -- 1; 545 0 -- 2; 546 0 -- 4; 547 1 -- 3; 548 2 -- 3; 549 2 -- 4; 550 3 -- 4; 551 }`, 552 }, 553 { 554 g: undirectedGraphFrom(powerMethodGraph), 555 prefix: "# ", 556 557 want: `# strict graph { 558 # // Node definitions. 559 # 0; 560 # 1; 561 # 2; 562 # 3; 563 # 4; 564 # 565 # // Edge definitions. 566 # 0 -- 1; 567 # 0 -- 2; 568 # 0 -- 4; 569 # 1 -- 3; 570 # 2 -- 3; 571 # 2 -- 4; 572 # 3 -- 4; 573 # }`, 574 }, 575 576 // Names named nodes. 577 { 578 name: "PageRank", 579 g: directedNamedIDGraphFrom(pageRankGraph), 580 581 want: `strict digraph PageRank { 582 // Node definitions. 583 A; 584 B; 585 C; 586 D; 587 E; 588 F; 589 G; 590 H; 591 I; 592 J; 593 K; 594 595 // Edge definitions. 596 B -> C; 597 C -> B; 598 D -> A; 599 D -> B; 600 E -> B; 601 E -> D; 602 E -> F; 603 F -> B; 604 F -> E; 605 G -> B; 606 G -> E; 607 H -> B; 608 H -> E; 609 I -> B; 610 I -> E; 611 J -> E; 612 K -> E; 613 }`, 614 }, 615 { 616 g: undirectedNamedIDGraphFrom(pageRankGraph), 617 618 want: `strict graph { 619 // Node definitions. 620 A; 621 B; 622 C; 623 D; 624 E; 625 F; 626 G; 627 H; 628 I; 629 J; 630 K; 631 632 // Edge definitions. 633 A -- D; 634 B -- C; 635 B -- D; 636 B -- E; 637 B -- F; 638 B -- G; 639 B -- H; 640 B -- I; 641 D -- E; 642 E -- F; 643 E -- G; 644 E -- H; 645 E -- I; 646 E -- J; 647 E -- K; 648 }`, 649 }, 650 { 651 g: directedNamedIDGraphFrom(powerMethodGraph), 652 653 want: `strict digraph { 654 // Node definitions. 655 A; 656 B; 657 C; 658 D; 659 E; 660 661 // Edge definitions. 662 A -> B; 663 A -> C; 664 B -> D; 665 C -> D; 666 C -> E; 667 D -> E; 668 E -> A; 669 }`, 670 }, 671 { 672 g: undirectedNamedIDGraphFrom(powerMethodGraph), 673 674 want: `strict graph { 675 // Node definitions. 676 A; 677 B; 678 C; 679 D; 680 E; 681 682 // Edge definitions. 683 A -- B; 684 A -- C; 685 A -- E; 686 B -- D; 687 C -- D; 688 C -- E; 689 D -- E; 690 }`, 691 }, 692 { 693 g: undirectedNamedIDGraphFrom(powerMethodGraph), 694 prefix: "# ", 695 696 want: `# strict graph { 697 # // Node definitions. 698 # A; 699 # B; 700 # C; 701 # D; 702 # E; 703 # 704 # // Edge definitions. 705 # A -- B; 706 # A -- C; 707 # A -- E; 708 # B -- D; 709 # C -- D; 710 # C -- E; 711 # D -- E; 712 # }`, 713 }, 714 715 // Handling nodes with attributes. 716 { 717 g: directedNodeAttrGraphFrom(powerMethodGraph, nil), 718 719 want: `strict digraph { 720 // Node definitions. 721 0; 722 1; 723 2; 724 3; 725 4; 726 727 // Edge definitions. 728 0 -> 1; 729 0 -> 2; 730 1 -> 3; 731 2 -> 3; 732 2 -> 4; 733 3 -> 4; 734 4 -> 0; 735 }`, 736 }, 737 { 738 g: undirectedNodeAttrGraphFrom(powerMethodGraph, nil), 739 740 want: `strict graph { 741 // Node definitions. 742 0; 743 1; 744 2; 745 3; 746 4; 747 748 // Edge definitions. 749 0 -- 1; 750 0 -- 2; 751 0 -- 4; 752 1 -- 3; 753 2 -- 3; 754 2 -- 4; 755 3 -- 4; 756 }`, 757 }, 758 { 759 g: directedNodeAttrGraphFrom(powerMethodGraph, [][]encoding.Attribute{ 760 2: {{Key: "fontsize", Value: "16"}, {Key: "shape", Value: "ellipse"}}, 761 4: {}, 762 }), 763 764 want: `strict digraph { 765 // Node definitions. 766 0; 767 1; 768 2 [ 769 fontsize=16 770 shape=ellipse 771 ]; 772 3; 773 4; 774 775 // Edge definitions. 776 0 -> 1; 777 0 -> 2; 778 1 -> 3; 779 2 -> 3; 780 2 -> 4; 781 3 -> 4; 782 4 -> 0; 783 }`, 784 }, 785 { 786 g: undirectedNodeAttrGraphFrom(powerMethodGraph, [][]encoding.Attribute{ 787 2: {{Key: "fontsize", Value: "16"}, {Key: "shape", Value: "ellipse"}}, 788 4: {}, 789 }), 790 791 want: `strict graph { 792 // Node definitions. 793 0; 794 1; 795 2 [ 796 fontsize=16 797 shape=ellipse 798 ]; 799 3; 800 4; 801 802 // Edge definitions. 803 0 -- 1; 804 0 -- 2; 805 0 -- 4; 806 1 -- 3; 807 2 -- 3; 808 2 -- 4; 809 3 -- 4; 810 }`, 811 }, 812 { 813 g: directedNamedIDNodeAttrGraphFrom(powerMethodGraph, [][]encoding.Attribute{ 814 2: {{Key: "fontsize", Value: "16"}, {Key: "shape", Value: "ellipse"}}, 815 4: {}, 816 }), 817 818 want: `strict digraph { 819 // Node definitions. 820 A; 821 B; 822 C [ 823 fontsize=16 824 shape=ellipse 825 ]; 826 D; 827 E; 828 829 // Edge definitions. 830 A -> B; 831 A -> C; 832 B -> D; 833 C -> D; 834 C -> E; 835 D -> E; 836 E -> A; 837 }`, 838 }, 839 { 840 g: undirectedNamedIDNodeAttrGraphFrom(powerMethodGraph, [][]encoding.Attribute{ 841 0: nil, 842 1: nil, 843 2: {{Key: "fontsize", Value: "16"}, {Key: "shape", Value: "ellipse"}}, 844 3: nil, 845 4: {}, 846 }), 847 848 want: `strict graph { 849 // Node definitions. 850 A; 851 B; 852 C [ 853 fontsize=16 854 shape=ellipse 855 ]; 856 D; 857 E; 858 859 // Edge definitions. 860 A -- B; 861 A -- C; 862 A -- E; 863 B -- D; 864 C -- D; 865 C -- E; 866 D -- E; 867 }`, 868 }, 869 870 // Handling edge with attributes. 871 { 872 g: directedEdgeAttrGraphFrom(powerMethodGraph, nil), 873 874 want: `strict digraph { 875 // Node definitions. 876 0; 877 1; 878 2; 879 3; 880 4; 881 882 // Edge definitions. 883 0 -> 1; 884 0 -> 2; 885 1 -> 3; 886 2 -> 3; 887 2 -> 4; 888 3 -> 4; 889 4 -> 0; 890 }`, 891 }, 892 { 893 g: undirectedEdgeAttrGraphFrom(powerMethodGraph, nil), 894 895 want: `strict graph { 896 // Node definitions. 897 0; 898 1; 899 2; 900 3; 901 4; 902 903 // Edge definitions. 904 0 -- 1; 905 0 -- 2; 906 0 -- 4; 907 1 -- 3; 908 2 -- 3; 909 2 -- 4; 910 3 -- 4; 911 }`, 912 }, 913 { 914 g: directedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{ 915 {from: 0, to: 2}: {{Key: "label", Value: `"???"`}, {Key: "style", Value: "dashed"}}, 916 {from: 2, to: 4}: {}, 917 {from: 3, to: 4}: {{Key: "color", Value: "red"}}, 918 }), 919 920 want: `strict digraph { 921 // Node definitions. 922 0; 923 1; 924 2; 925 3; 926 4; 927 928 // Edge definitions. 929 0 -> 1; 930 0 -> 2 [ 931 label="???" 932 style=dashed 933 ]; 934 1 -> 3; 935 2 -> 3; 936 2 -> 4; 937 3 -> 4 [color=red]; 938 4 -> 0; 939 }`, 940 }, 941 { 942 g: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{ 943 {from: 0, to: 2}: {{Key: "label", Value: `"???"`}, {Key: "style", Value: "dashed"}}, 944 {from: 2, to: 4}: {}, 945 {from: 3, to: 4}: {{Key: "color", Value: "red"}}, 946 }), 947 948 want: `strict graph { 949 // Node definitions. 950 0; 951 1; 952 2; 953 3; 954 4; 955 956 // Edge definitions. 957 0 -- 1; 958 0 -- 2 [ 959 label="???" 960 style=dashed 961 ]; 962 0 -- 4; 963 1 -- 3; 964 2 -- 3; 965 2 -- 4; 966 3 -- 4 [color=red]; 967 }`, 968 }, 969 { 970 g: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{ 971 // label attribute not quoted and containing spaces. 972 {from: 0, to: 2}: {{Key: "label", Value: `hello world`}, {Key: "style", Value: "dashed"}}, 973 {from: 2, to: 4}: {}, 974 {from: 3, to: 4}: {{Key: "label", Value: `foo bar`}}, 975 }), 976 977 want: `strict graph { 978 // Node definitions. 979 0; 980 1; 981 2; 982 3; 983 4; 984 985 // Edge definitions. 986 0 -- 1; 987 0 -- 2 [ 988 label="hello world" 989 style=dashed 990 ]; 991 0 -- 4; 992 1 -- 3; 993 2 -- 3; 994 2 -- 4; 995 3 -- 4 [label="foo bar"]; 996 }`, 997 }, 998 { 999 g: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{ 1000 // keywords must be quoted if used as attributes. 1001 {from: 0, to: 2}: {{Key: "label", Value: `NODE`}, {Key: "style", Value: "dashed"}}, 1002 {from: 2, to: 4}: {}, 1003 {from: 3, to: 4}: {{Key: "label", Value: `subgraph`}}, 1004 }), 1005 1006 want: `strict graph { 1007 // Node definitions. 1008 0; 1009 1; 1010 2; 1011 3; 1012 4; 1013 1014 // Edge definitions. 1015 0 -- 1; 1016 0 -- 2 [ 1017 label="NODE" 1018 style=dashed 1019 ]; 1020 0 -- 4; 1021 1 -- 3; 1022 2 -- 3; 1023 2 -- 4; 1024 3 -- 4 [label="subgraph"]; 1025 }`, 1026 }, 1027 1028 // Handling nodes with ports. 1029 { 1030 g: directedPortedAttrGraphFrom(powerMethodGraph, nil, nil), 1031 1032 want: `strict digraph { 1033 // Node definitions. 1034 0; 1035 1; 1036 2; 1037 3; 1038 4; 1039 1040 // Edge definitions. 1041 0 -> 1; 1042 0 -> 2; 1043 1 -> 3; 1044 2 -> 3; 1045 2 -> 4; 1046 3 -> 4; 1047 4 -> 0; 1048 }`, 1049 }, 1050 { 1051 g: undirectedPortedAttrGraphFrom(powerMethodGraph, nil, nil), 1052 1053 want: `strict graph { 1054 // Node definitions. 1055 0; 1056 1; 1057 2; 1058 3; 1059 4; 1060 1061 // Edge definitions. 1062 0 -- 1; 1063 0 -- 2; 1064 0 -- 4; 1065 1 -- 3; 1066 2 -- 3; 1067 2 -- 4; 1068 3 -- 4; 1069 }`, 1070 }, 1071 { 1072 g: directedPortedAttrGraphFrom(powerMethodGraph, 1073 [][]encoding.Attribute{ 1074 2: {{Key: "shape", Value: "record"}, {Key: "label", Value: `"<Two>English|<Zwei>German"`}}, 1075 4: {{Key: "shape", Value: "record"}, {Key: "label", Value: `"<Four>English|<Vier>German"`}}, 1076 }, 1077 map[edge]portedEdge{ 1078 {from: 0, to: 1}: {fromCompass: "s"}, 1079 {from: 0, to: 2}: {fromCompass: "s", toPort: "Zwei", toCompass: "e"}, 1080 {from: 2, to: 3}: {fromPort: "Zwei", fromCompass: "e"}, 1081 {from: 2, to: 4}: {fromPort: "Two", fromCompass: "w", toPort: "Four", toCompass: "w"}, 1082 {from: 3, to: 4}: {toPort: "Four", toCompass: "w"}, 1083 {from: 4, to: 0}: {fromPort: "Four", fromCompass: "_", toCompass: "s"}, 1084 }, 1085 ), 1086 1087 want: `strict digraph { 1088 // Node definitions. 1089 0; 1090 1; 1091 2 [ 1092 shape=record 1093 label="<Two>English|<Zwei>German" 1094 ]; 1095 3; 1096 4 [ 1097 shape=record 1098 label="<Four>English|<Vier>German" 1099 ]; 1100 1101 // Edge definitions. 1102 0:s -> 1; 1103 0:s -> 2:Zwei:e; 1104 1 -> 3; 1105 2:Zwei:e -> 3; 1106 2:Two:w -> 4:Four:w; 1107 3 -> 4:Four:w; 1108 4:Four:_ -> 0:s; 1109 }`, 1110 }, 1111 { 1112 g: undirectedPortedAttrGraphFrom(powerMethodGraph, 1113 [][]encoding.Attribute{ 1114 2: {{Key: "shape", Value: "record"}, {Key: "label", Value: `"<Two>English|<Zwei>German"`}}, 1115 4: {{Key: "shape", Value: "record"}, {Key: "label", Value: `"<Four>English|<Vier>German"`}}, 1116 }, 1117 map[edge]portedEdge{ 1118 {from: 0, to: 1}: {fromCompass: "s"}, 1119 {from: 0, to: 2}: {fromCompass: "s", toPort: "Zwei", toCompass: "e"}, 1120 {from: 2, to: 3}: {fromPort: "Zwei", fromCompass: "e"}, 1121 {from: 2, to: 4}: {fromPort: "Two", fromCompass: "w", toPort: "Four", toCompass: "w"}, 1122 {from: 3, to: 4}: {toPort: "Four", toCompass: "w"}, 1123 {from: 4, to: 0}: {fromPort: "Four", fromCompass: "_", toCompass: "s"}, 1124 }, 1125 ), 1126 1127 want: `strict graph { 1128 // Node definitions. 1129 0; 1130 1; 1131 2 [ 1132 shape=record 1133 label="<Two>English|<Zwei>German" 1134 ]; 1135 3; 1136 4 [ 1137 shape=record 1138 label="<Four>English|<Vier>German" 1139 ]; 1140 1141 // Edge definitions. 1142 0:s -- 1; 1143 0:s -- 2:Zwei:e; 1144 0:s -- 4:Four:_; 1145 1 -- 3; 1146 2:Zwei:e -- 3; 1147 2:Two:w -- 4:Four:w; 1148 3 -- 4:Four:w; 1149 }`, 1150 }, 1151 1152 // Handling graph attributes. 1153 { 1154 g: graphAttributer{ 1155 Graph: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{ 1156 {from: 0, to: 2}: {{Key: "label", Value: `"???"`}, {Key: "style", Value: "dashed"}}, 1157 {from: 2, to: 4}: {}, 1158 {from: 3, to: 4}: {{Key: "color", Value: "red"}}, 1159 }), 1160 graph: nil, node: nil, edge: nil, 1161 }, 1162 1163 want: `strict graph { 1164 // Node definitions. 1165 0; 1166 1; 1167 2; 1168 3; 1169 4; 1170 1171 // Edge definitions. 1172 0 -- 1; 1173 0 -- 2 [ 1174 label="???" 1175 style=dashed 1176 ]; 1177 0 -- 4; 1178 1 -- 3; 1179 2 -- 3; 1180 2 -- 4; 1181 3 -- 4 [color=red]; 1182 }`, 1183 }, 1184 { 1185 g: graphAttributer{ 1186 Graph: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{ 1187 {from: 0, to: 2}: {{Key: "label", Value: `"???"`}, {Key: "style", Value: "dashed"}}, 1188 {from: 2, to: 4}: {}, 1189 {from: 3, to: 4}: {{Key: "color", Value: "red"}}, 1190 }), 1191 graph: &encoding.Attributes{{Key: "rankdir", Value: `"LR"`}}, 1192 node: &encoding.Attributes{{Key: "fontsize", Value: "16"}, {Key: "shape", Value: "ellipse"}}, 1193 edge: nil, 1194 }, 1195 1196 want: `strict graph { 1197 graph [ 1198 rankdir="LR" 1199 ]; 1200 node [ 1201 fontsize=16 1202 shape=ellipse 1203 ]; 1204 1205 // Node definitions. 1206 0; 1207 1; 1208 2; 1209 3; 1210 4; 1211 1212 // Edge definitions. 1213 0 -- 1; 1214 0 -- 2 [ 1215 label="???" 1216 style=dashed 1217 ]; 1218 0 -- 4; 1219 1 -- 3; 1220 2 -- 3; 1221 2 -- 4; 1222 3 -- 4 [color=red]; 1223 }`, 1224 }, 1225 1226 // Handling structured graphs. 1227 { 1228 g: undirectedStructuredGraphFrom(nil, powerMethodGraph, pageRankGraph), 1229 1230 want: `strict graph { 1231 subgraph A { 1232 // Node definitions. 1233 0; 1234 1; 1235 2; 1236 3; 1237 4; 1238 1239 // Edge definitions. 1240 0 -- 1; 1241 0 -- 2; 1242 0 -- 4; 1243 1 -- 3; 1244 2 -- 3; 1245 2 -- 4; 1246 3 -- 4; 1247 } 1248 subgraph B { 1249 // Node definitions. 1250 5; 1251 6; 1252 7; 1253 8; 1254 9; 1255 10; 1256 11; 1257 12; 1258 13; 1259 14; 1260 15; 1261 1262 // Edge definitions. 1263 5 -- 8; 1264 6 -- 7; 1265 6 -- 8; 1266 6 -- 9; 1267 6 -- 10; 1268 6 -- 11; 1269 6 -- 12; 1270 6 -- 13; 1271 8 -- 9; 1272 9 -- 10; 1273 9 -- 11; 1274 9 -- 12; 1275 9 -- 13; 1276 9 -- 14; 1277 9 -- 15; 1278 } 1279 }`, 1280 }, 1281 { 1282 g: undirectedStructuredGraphFrom([]edge{{from: 0, to: 9}}, powerMethodGraph, pageRankGraph), 1283 1284 want: `strict graph { 1285 subgraph A { 1286 // Node definitions. 1287 0; 1288 1; 1289 2; 1290 3; 1291 4; 1292 1293 // Edge definitions. 1294 0 -- 1; 1295 0 -- 2; 1296 0 -- 4; 1297 1 -- 3; 1298 2 -- 3; 1299 2 -- 4; 1300 3 -- 4; 1301 } 1302 subgraph B { 1303 // Node definitions. 1304 5; 1305 6; 1306 7; 1307 8; 1308 9; 1309 10; 1310 11; 1311 12; 1312 13; 1313 14; 1314 15; 1315 1316 // Edge definitions. 1317 5 -- 8; 1318 6 -- 7; 1319 6 -- 8; 1320 6 -- 9; 1321 6 -- 10; 1322 6 -- 11; 1323 6 -- 12; 1324 6 -- 13; 1325 8 -- 9; 1326 9 -- 10; 1327 9 -- 11; 1328 9 -- 12; 1329 9 -- 13; 1330 9 -- 14; 1331 9 -- 15; 1332 } 1333 // Node definitions. 1334 0; 1335 9; 1336 1337 // Edge definitions. 1338 0 -- 9; 1339 }`, 1340 }, 1341 1342 // Handling subgraphs. 1343 { 1344 g: undirectedSubGraphFrom(pageRankGraph, map[int64][]intset{2: powerMethodGraph}), 1345 1346 want: `strict graph { 1347 // Node definitions. 1348 5; 1349 6; 1350 8; 1351 9; 1352 10; 1353 11; 1354 12; 1355 13; 1356 14; 1357 15; 1358 1359 // Edge definitions. 1360 5 -- 8; 1361 6 -- subgraph H { 1362 // Node definitions. 1363 0; 1364 1; 1365 2; 1366 3; 1367 4; 1368 1369 // Edge definitions. 1370 0 -- 1; 1371 0 -- 2; 1372 0 -- 4; 1373 1 -- 3; 1374 2 -- 3; 1375 2 -- 4; 1376 3 -- 4; 1377 }; 1378 6 -- 8; 1379 6 -- 9; 1380 6 -- 10; 1381 6 -- 11; 1382 6 -- 12; 1383 6 -- 13; 1384 8 -- 9; 1385 9 -- 10; 1386 9 -- 11; 1387 9 -- 12; 1388 9 -- 13; 1389 9 -- 14; 1390 9 -- 15; 1391 }`, 1392 }, 1393 { 1394 name: "H", 1395 g: undirectedSubGraphFrom(pageRankGraph, map[int64][]intset{1: powerMethodGraph}), 1396 1397 want: `strict graph H { 1398 // Node definitions. 1399 5; 1400 7; 1401 8; 1402 9; 1403 10; 1404 11; 1405 12; 1406 13; 1407 14; 1408 15; 1409 1410 // Edge definitions. 1411 5 -- 8; 1412 subgraph G { 1413 // Node definitions. 1414 0; 1415 1; 1416 2; 1417 3; 1418 4; 1419 1420 // Edge definitions. 1421 0 -- 1; 1422 0 -- 2; 1423 0 -- 4; 1424 1 -- 3; 1425 2 -- 3; 1426 2 -- 4; 1427 3 -- 4; 1428 } -- 7; 1429 subgraph G { 1430 // Node definitions. 1431 0; 1432 1; 1433 2; 1434 3; 1435 4; 1436 } -- 8; 1437 subgraph G { 1438 // Node definitions. 1439 0; 1440 1; 1441 2; 1442 3; 1443 4; 1444 } -- 9; 1445 subgraph G { 1446 // Node definitions. 1447 0; 1448 1; 1449 2; 1450 3; 1451 4; 1452 } -- 10; 1453 subgraph G { 1454 // Node definitions. 1455 0; 1456 1; 1457 2; 1458 3; 1459 4; 1460 } -- 11; 1461 subgraph G { 1462 // Node definitions. 1463 0; 1464 1; 1465 2; 1466 3; 1467 4; 1468 } -- 12; 1469 subgraph G { 1470 // Node definitions. 1471 0; 1472 1; 1473 2; 1474 3; 1475 4; 1476 } -- 13; 1477 8 -- 9; 1478 9 -- 10; 1479 9 -- 11; 1480 9 -- 12; 1481 9 -- 13; 1482 9 -- 14; 1483 9 -- 15; 1484 }`, 1485 }, 1486 } 1487 1488 func TestEncode(t *testing.T) { 1489 for i, test := range encodeTests { 1490 got, err := Marshal(test.g, test.name, test.prefix, "\t") 1491 if err != nil { 1492 t.Errorf("unexpected error: %v", err) 1493 continue 1494 } 1495 if string(got) != test.want { 1496 t.Errorf("unexpected DOT result for test %d:\ngot: %s\nwant:%s", i, got, test.want) 1497 } 1498 checkDOT(t, got) 1499 } 1500 } 1501 1502 type intlist []int64 1503 1504 func createMultigraph(g []intlist) graph.Multigraph { 1505 dg := multi.NewUndirectedGraph() 1506 for u, e := range g { 1507 u := int64(u) 1508 nu := multi.Node(u) 1509 for _, v := range e { 1510 nv := multi.Node(v) 1511 dg.SetLine(dg.NewLine(nu, nv)) 1512 } 1513 } 1514 return dg 1515 } 1516 1517 func createNamedMultigraph(g []intlist) graph.Multigraph { 1518 dg := multi.NewUndirectedGraph() 1519 for u, e := range g { 1520 u := int64(u) 1521 nu := namedNode{id: u, name: alpha[u : u+1]} 1522 for _, v := range e { 1523 nv := namedNode{id: v, name: alpha[v : v+1]} 1524 dg.SetLine(dg.NewLine(nu, nv)) 1525 } 1526 } 1527 return dg 1528 } 1529 1530 func createDirectedMultigraph(g []intlist) graph.Multigraph { 1531 dg := multi.NewDirectedGraph() 1532 for u, e := range g { 1533 u := int64(u) 1534 nu := multi.Node(u) 1535 for _, v := range e { 1536 nv := multi.Node(v) 1537 dg.SetLine(dg.NewLine(nu, nv)) 1538 } 1539 } 1540 return dg 1541 } 1542 1543 func createNamedDirectedMultigraph(g []intlist) graph.Multigraph { 1544 dg := multi.NewDirectedGraph() 1545 for u, e := range g { 1546 u := int64(u) 1547 nu := namedNode{id: u, name: alpha[u : u+1]} 1548 for _, v := range e { 1549 nv := namedNode{id: v, name: alpha[v : v+1]} 1550 dg.SetLine(dg.NewLine(nu, nv)) 1551 } 1552 } 1553 return dg 1554 } 1555 1556 var encodeMultiTests = []struct { 1557 name string 1558 g graph.Multigraph 1559 1560 prefix string 1561 1562 want string 1563 }{ 1564 { 1565 g: createMultigraph([]intlist{}), 1566 want: `graph { 1567 }`, 1568 }, 1569 { 1570 g: createMultigraph([]intlist{ 1571 0: {1}, 1572 1: {0, 2}, 1573 2: {}, 1574 }), 1575 want: `graph { 1576 // Node definitions. 1577 0; 1578 1; 1579 2; 1580 1581 // Edge definitions. 1582 0 -- 1; 1583 0 -- 1; 1584 1 -- 2; 1585 }`, 1586 }, 1587 { 1588 g: createMultigraph([]intlist{ 1589 0: {1}, 1590 1: {2, 2}, 1591 2: {0, 0, 0}, 1592 }), 1593 want: `graph { 1594 // Node definitions. 1595 0; 1596 1; 1597 2; 1598 1599 // Edge definitions. 1600 0 -- 1; 1601 0 -- 2; 1602 0 -- 2; 1603 0 -- 2; 1604 1 -- 2; 1605 1 -- 2; 1606 }`, 1607 }, 1608 { 1609 g: createNamedMultigraph([]intlist{ 1610 0: {1}, 1611 1: {2, 2}, 1612 2: {0, 0, 0}, 1613 }), 1614 want: `graph { 1615 // Node definitions. 1616 A; 1617 B; 1618 C; 1619 1620 // Edge definitions. 1621 A -- B; 1622 A -- C; 1623 A -- C; 1624 A -- C; 1625 B -- C; 1626 B -- C; 1627 }`, 1628 }, 1629 { 1630 g: createMultigraph([]intlist{ 1631 0: {2, 1, 0}, 1632 1: {2, 1, 0}, 1633 2: {2, 1, 0}, 1634 }), 1635 want: `graph { 1636 // Node definitions. 1637 0; 1638 1; 1639 2; 1640 1641 // Edge definitions. 1642 0 -- 0; 1643 0 -- 1; 1644 0 -- 1; 1645 0 -- 2; 1646 0 -- 2; 1647 1 -- 1; 1648 1 -- 2; 1649 1 -- 2; 1650 2 -- 2; 1651 }`, 1652 }, 1653 { 1654 g: createDirectedMultigraph([]intlist{}), 1655 want: `digraph { 1656 }`, 1657 }, 1658 { 1659 g: createDirectedMultigraph([]intlist{ 1660 0: {1}, 1661 1: {0, 2}, 1662 2: {}, 1663 }), 1664 want: `digraph { 1665 // Node definitions. 1666 0; 1667 1; 1668 2; 1669 1670 // Edge definitions. 1671 0 -> 1; 1672 1 -> 0; 1673 1 -> 2; 1674 }`, 1675 }, 1676 { 1677 g: createDirectedMultigraph([]intlist{ 1678 0: {1}, 1679 1: {2, 2}, 1680 2: {0, 0, 0}, 1681 }), 1682 want: `digraph { 1683 // Node definitions. 1684 0; 1685 1; 1686 2; 1687 1688 // Edge definitions. 1689 0 -> 1; 1690 1 -> 2; 1691 1 -> 2; 1692 2 -> 0; 1693 2 -> 0; 1694 2 -> 0; 1695 }`, 1696 }, 1697 { 1698 g: createNamedDirectedMultigraph([]intlist{ 1699 0: {1}, 1700 1: {2, 2}, 1701 2: {0, 0, 0}, 1702 }), 1703 want: `digraph { 1704 // Node definitions. 1705 A; 1706 B; 1707 C; 1708 1709 // Edge definitions. 1710 A -> B; 1711 B -> C; 1712 B -> C; 1713 C -> A; 1714 C -> A; 1715 C -> A; 1716 }`, 1717 }, 1718 { 1719 g: createDirectedMultigraph([]intlist{ 1720 0: {2, 1, 0}, 1721 1: {2, 1, 0}, 1722 2: {2, 1, 0}, 1723 }), 1724 want: `digraph { 1725 // Node definitions. 1726 0; 1727 1; 1728 2; 1729 1730 // Edge definitions. 1731 0 -> 0; 1732 0 -> 1; 1733 0 -> 2; 1734 1 -> 0; 1735 1 -> 1; 1736 1 -> 2; 1737 2 -> 0; 1738 2 -> 1; 1739 2 -> 2; 1740 }`, 1741 }, 1742 } 1743 1744 func TestEncodeMulti(t *testing.T) { 1745 for i, test := range encodeMultiTests { 1746 got, err := MarshalMulti(test.g, test.name, test.prefix, "\t") 1747 if err != nil { 1748 t.Errorf("unexpected error: %v", err) 1749 continue 1750 } 1751 if string(got) != test.want { 1752 t.Errorf("unexpected DOT result for test %d:\ngot: %s\nwant:%s", i, got, test.want) 1753 } 1754 checkDOT(t, got) 1755 } 1756 } 1757 1758 // checkDOT hands b to the dot executable if it exists and fails t if dot 1759 // returns an error. 1760 func checkDOT(t *testing.T, b []byte) { 1761 dot, err := exec.LookPath("dot") 1762 if err != nil { 1763 t.Logf("skipping DOT syntax check: %v", err) 1764 return 1765 } 1766 cmd := exec.Command(dot) 1767 cmd.Stdin = bytes.NewReader(b) 1768 stderr := &bytes.Buffer{} 1769 cmd.Stderr = stderr 1770 err = cmd.Run() 1771 if err != nil { 1772 t.Errorf("invalid DOT syntax: %v\n%s\ninput:\n%s", err, stderr.String(), b) 1773 } 1774 }