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