gonum.org/v1/gonum@v0.14.0/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  	"gonum.org/v1/gonum/graph"
    13  	"gonum.org/v1/gonum/graph/encoding"
    14  	"gonum.org/v1/gonum/graph/multi"
    15  	"gonum.org/v1/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  }