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  }