github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/encoding/dot/decode_test.go (about)

     1  // Copyright ©2017 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  	"fmt"
     9  	"testing"
    10  
    11  	"github.com/jingcheng-WU/gonum/graph"
    12  	"github.com/jingcheng-WU/gonum/graph/encoding"
    13  	"github.com/jingcheng-WU/gonum/graph/multi"
    14  	"github.com/jingcheng-WU/gonum/graph/simple"
    15  )
    16  
    17  func TestRoundTrip(t *testing.T) {
    18  	golden := []struct {
    19  		want     string
    20  		directed bool
    21  	}{
    22  		{
    23  			want:     directed,
    24  			directed: true,
    25  		},
    26  		{
    27  			want:     undirected,
    28  			directed: false,
    29  		},
    30  		{
    31  			want:     directedID,
    32  			directed: true,
    33  		},
    34  		{
    35  			want:     undirectedID,
    36  			directed: false,
    37  		},
    38  		{
    39  			want:     directedWithPorts,
    40  			directed: true,
    41  		},
    42  		{
    43  			want:     undirectedWithPorts,
    44  			directed: false,
    45  		},
    46  		{
    47  			want:     directedAttrs,
    48  			directed: true,
    49  		},
    50  		{
    51  			want:     undirectedAttrs,
    52  			directed: false,
    53  		},
    54  	}
    55  	for i, g := range golden {
    56  		var dst encoding.Builder
    57  		if g.directed {
    58  			dst = newDotDirectedGraph()
    59  		} else {
    60  			dst = newDotUndirectedGraph()
    61  		}
    62  		data := []byte(g.want)
    63  		if err := Unmarshal(data, dst); err != nil {
    64  			t.Errorf("i=%d: unable to unmarshal DOT graph; %v", i, err)
    65  			continue
    66  		}
    67  		buf, err := Marshal(dst, "", "", "\t")
    68  		if err != nil {
    69  			t.Errorf("i=%d: unable to marshal graph; %v", i, dst)
    70  			continue
    71  		}
    72  		got := string(buf)
    73  		if got != g.want {
    74  			t.Errorf("i=%d: graph content mismatch; want:\n%s\n\ngot:\n%s", i, g.want, got)
    75  			continue
    76  		}
    77  	}
    78  }
    79  
    80  const directed = `strict digraph {
    81  	graph [
    82  		outputorder=edgesfirst
    83  	];
    84  	node [
    85  		shape=circle
    86  		style=filled
    87  	];
    88  	edge [
    89  		penwidth=5
    90  		color=gray
    91  	];
    92  
    93  	// Node definitions.
    94  	A [label="foo 2"];
    95  	B [label="bar 2"];
    96  
    97  	// Edge definitions.
    98  	A -> B [label="baz 2"];
    99  }`
   100  
   101  const undirected = `strict graph {
   102  	graph [
   103  		outputorder=edgesfirst
   104  	];
   105  	node [
   106  		shape=circle
   107  		style=filled
   108  	];
   109  	edge [
   110  		penwidth=5
   111  		color=gray
   112  	];
   113  
   114  	// Node definitions.
   115  	A [label="foo 2"];
   116  	B [label="bar 2"];
   117  
   118  	// Edge definitions.
   119  	A -- B [label="baz 2"];
   120  }`
   121  
   122  const directedID = `strict digraph G {
   123  	// Node definitions.
   124  	A;
   125  	B;
   126  
   127  	// Edge definitions.
   128  	A -> B;
   129  }`
   130  
   131  const undirectedID = `strict graph H {
   132  	// Node definitions.
   133  	A;
   134  	B;
   135  
   136  	// Edge definitions.
   137  	A -- B;
   138  }`
   139  
   140  const directedWithPorts = `strict digraph {
   141  	// Node definitions.
   142  	A;
   143  	B;
   144  	C;
   145  	D;
   146  	E;
   147  	F;
   148  
   149  	// Edge definitions.
   150  	A:foo -> B:bar;
   151  	A -> C:bar;
   152  	B:foo -> C;
   153  	D:foo:n -> E:bar:s;
   154  	D:e -> F:bar:w;
   155  	E:_ -> F:c;
   156  }`
   157  
   158  const undirectedWithPorts = `strict graph {
   159  	// Node definitions.
   160  	A;
   161  	B;
   162  	C;
   163  	D;
   164  	E;
   165  	F;
   166  
   167  	// Edge definitions.
   168  	A:foo -- B:bar;
   169  	A -- C:bar;
   170  	B:foo -- C;
   171  	D:foo:n -- E:bar:s;
   172  	D:e -- F:bar:w;
   173  	E:_ -- F:c;
   174  }`
   175  
   176  const directedAttrs = `strict digraph {
   177  	node [
   178  		shape=circle
   179  		style=filled
   180  		label="NODE"
   181  	];
   182  	edge [
   183  		penwidth=5
   184  		color=gray
   185  		label=3.14
   186  	];
   187  
   188  	// Node definitions.
   189  	A [label=<br>];
   190  	B [label=-14];
   191  
   192  	// Edge definitions.
   193  	A -> B [label="hello world"];
   194  }`
   195  
   196  const undirectedAttrs = `strict graph {
   197  	node [
   198  		shape=circle
   199  		style=filled
   200  		label="NODE"
   201  	];
   202  	edge [
   203  		penwidth=5
   204  		color=gray
   205  		label=3.14
   206  	];
   207  
   208  	// Node definitions.
   209  	A [label=<br>];
   210  	B [label=-14];
   211  
   212  	// Edge definitions.
   213  	A -- B [label="hello world"];
   214  }`
   215  
   216  func TestChainedEdgeAttributes(t *testing.T) {
   217  	golden := []struct {
   218  		in, want string
   219  		directed bool
   220  	}{
   221  		{
   222  			in:       directedChained,
   223  			want:     directedNonchained,
   224  			directed: true,
   225  		},
   226  		{
   227  			in:       undirectedChained,
   228  			want:     undirectedNonchained,
   229  			directed: false,
   230  		},
   231  	}
   232  	for i, g := range golden {
   233  		var dst encoding.Builder
   234  		if g.directed {
   235  			dst = newDotDirectedGraph()
   236  		} else {
   237  			dst = newDotUndirectedGraph()
   238  		}
   239  		data := []byte(g.in)
   240  		if err := Unmarshal(data, dst); err != nil {
   241  			t.Errorf("i=%d: unable to unmarshal DOT graph; %v", i, err)
   242  			continue
   243  		}
   244  		buf, err := Marshal(dst, "", "", "\t")
   245  		if err != nil {
   246  			t.Errorf("i=%d: unable to marshal graph; %v", i, dst)
   247  			continue
   248  		}
   249  		got := string(buf)
   250  		if got != g.want {
   251  			t.Errorf("i=%d: graph content mismatch; want:\n%s\n\ngot:\n%s", i, g.want, got)
   252  			continue
   253  		}
   254  	}
   255  }
   256  
   257  const directedChained = `strict digraph {
   258  	graph [
   259  		outputorder=edgesfirst
   260  	];
   261  	node [
   262  		shape=circle
   263  		style=filled
   264  	];
   265  	edge [
   266  		penwidth=5
   267  		color=gray
   268  	];
   269  
   270  	// Node definitions.
   271  	A [label="foo 2"];
   272  	B [label="bar 2"];
   273  
   274  	// Edge definitions.
   275  	A -> B -> A [label="baz 2"];
   276  }`
   277  
   278  const directedNonchained = `strict digraph {
   279  	graph [
   280  		outputorder=edgesfirst
   281  	];
   282  	node [
   283  		shape=circle
   284  		style=filled
   285  	];
   286  	edge [
   287  		penwidth=5
   288  		color=gray
   289  	];
   290  
   291  	// Node definitions.
   292  	A [label="foo 2"];
   293  	B [label="bar 2"];
   294  
   295  	// Edge definitions.
   296  	A -> B [label="baz 2"];
   297  	B -> A [label="baz 2"];
   298  }`
   299  
   300  const undirectedChained = `graph {
   301  	graph [
   302  		outputorder=edgesfirst
   303  	];
   304  	node [
   305  		shape=circle
   306  		style=filled
   307  	];
   308  	edge [
   309  		penwidth=5
   310  		color=gray
   311  	];
   312  
   313  	// Node definitions.
   314  	A [label="foo 2"];
   315  	B [label="bar 2"];
   316  	C [label="bif 2"];
   317  
   318  	// Edge definitions.
   319  	A -- B -- C [label="baz 2"];
   320  }`
   321  
   322  const undirectedNonchained = `strict graph {
   323  	graph [
   324  		outputorder=edgesfirst
   325  	];
   326  	node [
   327  		shape=circle
   328  		style=filled
   329  	];
   330  	edge [
   331  		penwidth=5
   332  		color=gray
   333  	];
   334  
   335  	// Node definitions.
   336  	A [label="foo 2"];
   337  	B [label="bar 2"];
   338  	C [label="bif 2"];
   339  
   340  	// Edge definitions.
   341  	A -- B [label="baz 2"];
   342  	B -- C [label="baz 2"];
   343  }`
   344  
   345  func TestMultigraphDecoding(t *testing.T) {
   346  	for i, test := range []struct {
   347  		directed bool
   348  		input    string
   349  		expected string
   350  	}{
   351  		{
   352  			directed: true,
   353  			input:    directedMultigraph,
   354  			expected: directedMultigraph,
   355  		},
   356  		{
   357  			directed: false,
   358  			input:    undirectedMultigraph,
   359  			expected: undirectedMultigraph,
   360  		},
   361  		{
   362  			directed: true,
   363  			input:    directedSelfLoopMultigraph,
   364  			expected: directedSelfLoopMultigraph,
   365  		},
   366  		{
   367  			directed: false,
   368  			input:    undirectedSelfLoopMultigraph,
   369  			expected: undirectedSelfLoopMultigraph,
   370  		},
   371  	} {
   372  		var dst encoding.MultiBuilder
   373  		if test.directed {
   374  			dst = multi.NewDirectedGraph()
   375  		} else {
   376  			dst = multi.NewUndirectedGraph()
   377  		}
   378  
   379  		if err := UnmarshalMulti([]byte(test.input), dst); err != nil {
   380  			t.Errorf("i=%d: unable to unmarshal DOT graph; %v", i, err)
   381  			continue
   382  		}
   383  		buf, err := MarshalMulti(dst, "", "", "\t")
   384  		if err != nil {
   385  			t.Errorf("i=%d: unable to marshal graph; %v", i, dst)
   386  			continue
   387  		}
   388  		actual := string(buf)
   389  		if actual != test.expected {
   390  			t.Errorf("i=%d: graph content mismatch; want:\n%s\n\nactual:\n%s", i, test.expected, actual)
   391  			continue
   392  		}
   393  	}
   394  }
   395  
   396  func TestMultigraphLineIDsharing(t *testing.T) {
   397  	for i, test := range []struct {
   398  		directed bool
   399  		lines    []multi.Line
   400  		expected string
   401  	}{
   402  		{
   403  			directed: true,
   404  			lines: []multi.Line{
   405  				{F: multi.Node(0), T: multi.Node(1), UID: 0},
   406  				{F: multi.Node(0), T: multi.Node(1), UID: 1},
   407  				{F: multi.Node(0), T: multi.Node(2), UID: 0},
   408  				{F: multi.Node(2), T: multi.Node(0), UID: 0},
   409  			},
   410  			expected: directedMultigraph,
   411  		},
   412  		{
   413  			directed: false,
   414  			lines: []multi.Line{
   415  				{F: multi.Node(0), T: multi.Node(1), UID: 0},
   416  				{F: multi.Node(0), T: multi.Node(1), UID: 1},
   417  				{F: multi.Node(0), T: multi.Node(2), UID: 0},
   418  				{F: multi.Node(0), T: multi.Node(2), UID: 1},
   419  			},
   420  			expected: undirectedMultigraph,
   421  		},
   422  		{
   423  			directed: true,
   424  			lines: []multi.Line{
   425  				{F: multi.Node(0), T: multi.Node(0), UID: 0},
   426  				{F: multi.Node(0), T: multi.Node(0), UID: 1},
   427  				{F: multi.Node(1), T: multi.Node(1), UID: 0},
   428  				{F: multi.Node(1), T: multi.Node(1), UID: 1},
   429  			},
   430  			expected: directedSelfLoopMultigraph,
   431  		},
   432  		{
   433  			directed: false,
   434  			lines: []multi.Line{
   435  				{F: multi.Node(0), T: multi.Node(0), UID: 0},
   436  				{F: multi.Node(0), T: multi.Node(0), UID: 1},
   437  				{F: multi.Node(1), T: multi.Node(1), UID: 0},
   438  				{F: multi.Node(1), T: multi.Node(1), UID: 1},
   439  			},
   440  			expected: undirectedSelfLoopMultigraph,
   441  		},
   442  	} {
   443  		var dst encoding.MultiBuilder
   444  		if test.directed {
   445  			dst = multi.NewDirectedGraph()
   446  		} else {
   447  			dst = multi.NewUndirectedGraph()
   448  		}
   449  
   450  		for _, l := range test.lines {
   451  			dst.SetLine(l)
   452  		}
   453  
   454  		buf, err := MarshalMulti(dst, "", "", "\t")
   455  		if err != nil {
   456  			t.Errorf("i=%d: unable to marshal graph; %v", i, dst)
   457  			continue
   458  		}
   459  		actual := string(buf)
   460  		if actual != test.expected {
   461  			t.Errorf("i=%d: graph content mismatch; want:\n%s\n\nactual:\n%s", i, test.expected, actual)
   462  			continue
   463  		}
   464  	}
   465  }
   466  
   467  const directedMultigraph = `digraph {
   468  	// Node definitions.
   469  	0;
   470  	1;
   471  	2;
   472  
   473  	// Edge definitions.
   474  	0 -> 1;
   475  	0 -> 1;
   476  	0 -> 2;
   477  	2 -> 0;
   478  }`
   479  
   480  const undirectedMultigraph = `graph {
   481  	// Node definitions.
   482  	0;
   483  	1;
   484  	2;
   485  
   486  	// Edge definitions.
   487  	0 -- 1;
   488  	0 -- 1;
   489  	0 -- 2;
   490  	0 -- 2;
   491  }`
   492  
   493  const directedSelfLoopMultigraph = `digraph {
   494  	// Node definitions.
   495  	0;
   496  	1;
   497  
   498  	// Edge definitions.
   499  	0 -> 0;
   500  	0 -> 0;
   501  	1 -> 1;
   502  	1 -> 1;
   503  }`
   504  
   505  const undirectedSelfLoopMultigraph = `graph {
   506  	// Node definitions.
   507  	0;
   508  	1;
   509  
   510  	// Edge definitions.
   511  	0 -- 0;
   512  	0 -- 0;
   513  	1 -- 1;
   514  	1 -- 1;
   515  }`
   516  
   517  // Below follows a minimal implementation of a graph capable of validating the
   518  // round-trip encoding and decoding of DOT graphs with nodes and edges
   519  // containing DOT attributes.
   520  
   521  // dotDirectedGraph extends simple.DirectedGraph to add NewNode and NewEdge
   522  // methods for creating user-defined nodes and edges.
   523  //
   524  // dotDirectedGraph implements the encoding.Builder and the dot.Graph
   525  // interfaces.
   526  type dotDirectedGraph struct {
   527  	*simple.DirectedGraph
   528  	id                string
   529  	graph, node, edge attributes
   530  }
   531  
   532  // newDotDirectedGraph returns a new directed capable of creating user-defined
   533  // nodes and edges.
   534  func newDotDirectedGraph() *dotDirectedGraph {
   535  	return &dotDirectedGraph{DirectedGraph: simple.NewDirectedGraph()}
   536  }
   537  
   538  // NewNode returns a new node with a unique node ID for the graph.
   539  func (g *dotDirectedGraph) NewNode() graph.Node {
   540  	return &dotNode{Node: g.DirectedGraph.NewNode()}
   541  }
   542  
   543  // NewEdge returns a new Edge from the source to the destination node.
   544  func (g *dotDirectedGraph) NewEdge(from, to graph.Node) graph.Edge {
   545  	return &dotEdge{Edge: g.DirectedGraph.NewEdge(from, to)}
   546  }
   547  
   548  // DOTAttributers implements the dot.Attributers interface.
   549  func (g *dotDirectedGraph) DOTAttributers() (graph, node, edge encoding.Attributer) {
   550  	return g.graph, g.node, g.edge
   551  }
   552  
   553  // DOTAttributeSetters implements the dot.AttributeSetters interface.
   554  func (g *dotDirectedGraph) DOTAttributeSetters() (graph, node, edge encoding.AttributeSetter) {
   555  	return &g.graph, &g.node, &g.edge
   556  }
   557  
   558  // SetDOTID sets the DOT ID of the graph.
   559  func (g *dotDirectedGraph) SetDOTID(id string) {
   560  	g.id = id
   561  }
   562  
   563  // DOTID returns the DOT ID of the graph.
   564  func (g *dotDirectedGraph) DOTID() string {
   565  	return g.id
   566  }
   567  
   568  // dotUndirectedGraph extends simple.UndirectedGraph to add NewNode and NewEdge
   569  // methods for creating user-defined nodes and edges.
   570  //
   571  // dotUndirectedGraph implements the encoding.Builder and the dot.Graph
   572  // interfaces.
   573  type dotUndirectedGraph struct {
   574  	*simple.UndirectedGraph
   575  	id                string
   576  	graph, node, edge attributes
   577  }
   578  
   579  // newDotUndirectedGraph returns a new undirected capable of creating user-
   580  // defined nodes and edges.
   581  func newDotUndirectedGraph() *dotUndirectedGraph {
   582  	return &dotUndirectedGraph{UndirectedGraph: simple.NewUndirectedGraph()}
   583  }
   584  
   585  // NewNode adds a new node with a unique node ID to the graph.
   586  func (g *dotUndirectedGraph) NewNode() graph.Node {
   587  	return &dotNode{Node: g.UndirectedGraph.NewNode()}
   588  }
   589  
   590  // NewEdge returns a new Edge from the source to the destination node.
   591  func (g *dotUndirectedGraph) NewEdge(from, to graph.Node) graph.Edge {
   592  	return &dotEdge{Edge: g.UndirectedGraph.NewEdge(from, to)}
   593  }
   594  
   595  // DOTAttributers implements the dot.Attributers interface.
   596  func (g *dotUndirectedGraph) DOTAttributers() (graph, node, edge encoding.Attributer) {
   597  	return g.graph, g.node, g.edge
   598  }
   599  
   600  // DOTUnmarshalerAttrs implements the dot.UnmarshalerAttrs interface.
   601  func (g *dotUndirectedGraph) DOTAttributeSetters() (graph, node, edge encoding.AttributeSetter) {
   602  	return &g.graph, &g.node, &g.edge
   603  }
   604  
   605  // SetDOTID sets the DOT ID of the graph.
   606  func (g *dotUndirectedGraph) SetDOTID(id string) {
   607  	g.id = id
   608  }
   609  
   610  // DOTID returns the DOT ID of the graph.
   611  func (g *dotUndirectedGraph) DOTID() string {
   612  	return g.id
   613  }
   614  
   615  // dotNode extends simple.Node with a label field to test round-trip encoding
   616  // and decoding of node DOT label attributes.
   617  type dotNode struct {
   618  	graph.Node
   619  	dotID string
   620  	// Node label.
   621  	Label string
   622  }
   623  
   624  // DOTID returns the node's DOT ID.
   625  func (n *dotNode) DOTID() string {
   626  	return n.dotID
   627  }
   628  
   629  // SetDOTID sets a DOT ID.
   630  func (n *dotNode) SetDOTID(id string) {
   631  	n.dotID = id
   632  }
   633  
   634  // SetAttribute sets a DOT attribute.
   635  func (n *dotNode) SetAttribute(attr encoding.Attribute) error {
   636  	if attr.Key != "label" {
   637  		return fmt.Errorf("unable to unmarshal node DOT attribute with key %q", attr.Key)
   638  	}
   639  	n.Label = attr.Value
   640  	return nil
   641  }
   642  
   643  // Attributes returns the DOT attributes of the node.
   644  func (n *dotNode) Attributes() []encoding.Attribute {
   645  	if len(n.Label) == 0 {
   646  		return nil
   647  	}
   648  	return []encoding.Attribute{{
   649  		Key:   "label",
   650  		Value: n.Label,
   651  	}}
   652  }
   653  
   654  type dotPortLabels struct {
   655  	Port, Compass string
   656  }
   657  
   658  // dotEdge extends simple.Edge with a label field to test round-trip encoding and
   659  // decoding of edge DOT label attributes.
   660  type dotEdge struct {
   661  	graph.Edge
   662  	// Edge label.
   663  	Label          string
   664  	FromPortLabels dotPortLabels
   665  	ToPortLabels   dotPortLabels
   666  }
   667  
   668  // SetAttribute sets a DOT attribute.
   669  func (e *dotEdge) SetAttribute(attr encoding.Attribute) error {
   670  	if attr.Key != "label" {
   671  		return fmt.Errorf("unable to unmarshal node DOT attribute with key %q", attr.Key)
   672  	}
   673  	e.Label = attr.Value
   674  	return nil
   675  }
   676  
   677  // Attributes returns the DOT attributes of the edge.
   678  func (e *dotEdge) Attributes() []encoding.Attribute {
   679  	if len(e.Label) == 0 {
   680  		return nil
   681  	}
   682  	return []encoding.Attribute{{
   683  		Key:   "label",
   684  		Value: e.Label,
   685  	}}
   686  }
   687  
   688  func (e *dotEdge) SetFromPort(port, compass string) error {
   689  	e.FromPortLabels.Port = port
   690  	e.FromPortLabels.Compass = compass
   691  	return nil
   692  }
   693  
   694  func (e *dotEdge) SetToPort(port, compass string) error {
   695  	e.ToPortLabels.Port = port
   696  	e.ToPortLabels.Compass = compass
   697  	return nil
   698  }
   699  
   700  func (e *dotEdge) FromPort() (port, compass string) {
   701  	return e.FromPortLabels.Port, e.FromPortLabels.Compass
   702  }
   703  
   704  func (e *dotEdge) ToPort() (port, compass string) {
   705  	return e.ToPortLabels.Port, e.ToPortLabels.Compass
   706  }
   707  
   708  // attributes is a helper for global attributes.
   709  type attributes []encoding.Attribute
   710  
   711  func (a attributes) Attributes() []encoding.Attribute {
   712  	return []encoding.Attribute(a)
   713  }
   714  func (a *attributes) SetAttribute(attr encoding.Attribute) error {
   715  	*a = append(*a, attr)
   716  	return nil
   717  }
   718  
   719  const undirectedSelfLoopGraph = `graph {
   720  	// Node definitions.
   721  	0;
   722  	1;
   723  
   724  	// Edge definitions.
   725  	0 -- 0;
   726  	1 -- 1;
   727  }`
   728  
   729  const directedSelfLoopGraph = `digraph {
   730  	// Node definitions.
   731  	0;
   732  	1;
   733  
   734  	// Edge definitions.
   735  	0 -> 0;
   736  	1 -> 1;
   737  }`
   738  
   739  func TestSelfLoopSimple(t *testing.T) {
   740  	for _, test := range []struct {
   741  		dst func() encoding.Builder
   742  		src string
   743  	}{
   744  		{
   745  			dst: func() encoding.Builder { return simple.NewUndirectedGraph() },
   746  			src: undirectedSelfLoopGraph,
   747  		},
   748  		{
   749  			dst: func() encoding.Builder { return simple.NewDirectedGraph() },
   750  			src: directedSelfLoopGraph,
   751  		},
   752  	} {
   753  		dst := test.dst()
   754  		message, panicked := panics(func() {
   755  			err := Unmarshal([]byte(test.src), dst)
   756  			if err == nil {
   757  				t.Errorf("expected error for self loop addition to %T", dst)
   758  			}
   759  		})
   760  		if panicked {
   761  			t.Errorf("unexpected panic for self loop addition to %T: %s", dst, message)
   762  		}
   763  	}
   764  }
   765  
   766  func panics(fn func()) (message string, ok bool) {
   767  	defer func() {
   768  		r := recover()
   769  		message = fmt.Sprint(r)
   770  		ok = r != nil
   771  	}()
   772  	fn()
   773  	return
   774  }