mirror of
https://github.com/gonum/gonum.git
synced 2025-10-06 23:52:47 +08:00
248 lines
6.0 KiB
Go
248 lines
6.0 KiB
Go
// Copyright ©2017 The gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package dot
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"gonum.org/v1/gonum/graph"
|
|
"gonum.org/v1/gonum/graph/encoding"
|
|
"gonum.org/v1/gonum/graph/simple"
|
|
)
|
|
|
|
func TestRoundTrip(t *testing.T) {
|
|
golden := []struct {
|
|
want string
|
|
directed bool
|
|
}{
|
|
{
|
|
want: directed,
|
|
directed: true,
|
|
},
|
|
{
|
|
want: undirected,
|
|
directed: false,
|
|
},
|
|
}
|
|
for i, g := range golden {
|
|
var dst encoding.Builder
|
|
if g.directed {
|
|
dst = newDotDirectedGraph()
|
|
} else {
|
|
dst = newDotUndirectedGraph()
|
|
}
|
|
data := []byte(g.want)
|
|
if err := Unmarshal(data, dst); err != nil {
|
|
t.Errorf("i=%d: unable to unmarshal DOT graph; %v", i, err)
|
|
continue
|
|
}
|
|
buf, err := Marshal(dst, "", "", "\t", false)
|
|
if err != nil {
|
|
t.Errorf("i=%d: unable to marshal graph; %v", i, dst)
|
|
continue
|
|
}
|
|
got := string(buf)
|
|
if got != g.want {
|
|
t.Errorf("i=%d: graph content mismatch; want:\n%s\n\ngot:\n%s", i, g.want, got)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
const directed = `digraph {
|
|
graph [
|
|
outputorder=edgesfirst
|
|
];
|
|
node [
|
|
shape=circle
|
|
style=filled
|
|
];
|
|
edge [
|
|
penwidth=5
|
|
color=gray
|
|
];
|
|
|
|
// Node definitions.
|
|
A [label="foo 2"];
|
|
B [label="bar 2"];
|
|
|
|
// Edge definitions.
|
|
A -> B [label="baz 2"];
|
|
}`
|
|
|
|
const undirected = `graph {
|
|
graph [
|
|
outputorder=edgesfirst
|
|
];
|
|
node [
|
|
shape=circle
|
|
style=filled
|
|
];
|
|
edge [
|
|
penwidth=5
|
|
color=gray
|
|
];
|
|
|
|
// Node definitions.
|
|
A [label="foo 2"];
|
|
B [label="bar 2"];
|
|
|
|
// Edge definitions.
|
|
A -- B [label="baz 2"];
|
|
}`
|
|
|
|
// Below follows a minimal implementation of a graph capable of validating the
|
|
// round-trip encoding and decoding of DOT graphs with nodes and edges
|
|
// containing DOT attributes.
|
|
|
|
// dotDirectedGraph extends simple.DirectedGraph to add NewNode and NewEdge
|
|
// methods for creating user-defined nodes and edges.
|
|
//
|
|
// dotDirectedGraph implements the dot.Builder interface.
|
|
type dotDirectedGraph struct {
|
|
*simple.DirectedGraph
|
|
graph, node, edge attributes
|
|
}
|
|
|
|
// newDotDirectedGraph returns a new directed capable of creating user-defined
|
|
// nodes and edges.
|
|
func newDotDirectedGraph() *dotDirectedGraph {
|
|
return &dotDirectedGraph{DirectedGraph: simple.NewDirectedGraph(0, 0)}
|
|
}
|
|
|
|
// NewNode returns a new node with a unique node ID for the graph.
|
|
func (g *dotDirectedGraph) NewNode() graph.Node {
|
|
return &dotNode{Node: g.DirectedGraph.NewNode()}
|
|
}
|
|
|
|
// NewEdge returns a new Edge from the source to the destination node.
|
|
func (g *dotDirectedGraph) NewEdge(from, to graph.Node) graph.Edge {
|
|
return &dotEdge{Edge: g.DirectedGraph.NewEdge(from, to)}
|
|
}
|
|
|
|
// DOTAttributers implements the dot.Attributers interface.
|
|
func (g *dotDirectedGraph) DOTAttributers() (graph, node, edge encoding.Attributer) {
|
|
return g.graph, g.node, g.edge
|
|
}
|
|
|
|
// DOTAttributeSetters implements the dot.AttributeSetters interface.
|
|
func (g *dotDirectedGraph) DOTAttributeSetters() (graph, node, edge encoding.AttributeSetter) {
|
|
return &g.graph, &g.node, &g.edge
|
|
}
|
|
|
|
// dotUndirectedGraph extends simple.UndirectedGraph to add NewNode and NewEdge
|
|
// methods for creating user-defined nodes and edges.
|
|
//
|
|
// dotUndirectedGraph implements the dot.Builder interface.
|
|
type dotUndirectedGraph struct {
|
|
*simple.UndirectedGraph
|
|
graph, node, edge attributes
|
|
}
|
|
|
|
// newDotUndirectedGraph returns a new undirected capable of creating user-
|
|
// defined nodes and edges.
|
|
func newDotUndirectedGraph() *dotUndirectedGraph {
|
|
return &dotUndirectedGraph{UndirectedGraph: simple.NewUndirectedGraph(0, 0)}
|
|
}
|
|
|
|
// NewNode adds a new node with a unique node ID to the graph.
|
|
func (g *dotUndirectedGraph) NewNode() graph.Node {
|
|
return &dotNode{Node: g.UndirectedGraph.NewNode()}
|
|
}
|
|
|
|
// NewEdge returns a new Edge from the source to the destination node.
|
|
func (g *dotUndirectedGraph) NewEdge(from, to graph.Node) graph.Edge {
|
|
return &dotEdge{Edge: g.UndirectedGraph.NewEdge(from, to)}
|
|
}
|
|
|
|
// DOTAttributers implements the dot.Attributers interface.
|
|
func (g *dotUndirectedGraph) DOTAttributers() (graph, node, edge encoding.Attributer) {
|
|
return g.graph, g.node, g.edge
|
|
}
|
|
|
|
// DOTUnmarshalerAttrs implements the dot.UnmarshalerAttrs interface.
|
|
func (g *dotUndirectedGraph) DOTAttributeSetters() (graph, node, edge encoding.AttributeSetter) {
|
|
return &g.graph, &g.node, &g.edge
|
|
}
|
|
|
|
// dotNode extends simple.Node with a label field to test round-trip encoding
|
|
// and decoding of node DOT label attributes.
|
|
type dotNode struct {
|
|
graph.Node
|
|
dotID string
|
|
// Node label.
|
|
Label string
|
|
}
|
|
|
|
// DOTID returns the node's DOT ID.
|
|
func (n *dotNode) DOTID() string {
|
|
return n.dotID
|
|
}
|
|
|
|
// SetDOTID sets a DOT ID.
|
|
func (n *dotNode) SetDOTID(id string) {
|
|
n.dotID = id
|
|
}
|
|
|
|
// SetAttribute sets a DOT attribute.
|
|
func (n *dotNode) SetAttribute(attr encoding.Attribute) error {
|
|
if attr.Key != "label" {
|
|
return fmt.Errorf("unable to unmarshal node DOT attribute with key %q", attr.Key)
|
|
}
|
|
n.Label = attr.Value
|
|
return nil
|
|
}
|
|
|
|
// Attributes returns the DOT attributes of the node.
|
|
func (n *dotNode) Attributes() []encoding.Attribute {
|
|
if len(n.Label) == 0 {
|
|
return nil
|
|
}
|
|
return []encoding.Attribute{{
|
|
Key: "label",
|
|
Value: n.Label,
|
|
}}
|
|
}
|
|
|
|
// dotEdge extends simple.Edge with a label field to test round-trip encoding and
|
|
// decoding of edge DOT label attributes.
|
|
type dotEdge struct {
|
|
graph.Edge
|
|
// Edge label.
|
|
Label string
|
|
}
|
|
|
|
// SetAttribute sets a DOT attribute.
|
|
func (e *dotEdge) SetAttribute(attr encoding.Attribute) error {
|
|
if attr.Key != "label" {
|
|
return fmt.Errorf("unable to unmarshal node DOT attribute with key %q", attr.Key)
|
|
}
|
|
e.Label = attr.Value
|
|
return nil
|
|
}
|
|
|
|
// Attributes returns the DOT attributes of the edge.
|
|
func (e *dotEdge) Attributes() []encoding.Attribute {
|
|
if len(e.Label) == 0 {
|
|
return nil
|
|
}
|
|
return []encoding.Attribute{{
|
|
Key: "label",
|
|
Value: e.Label,
|
|
}}
|
|
}
|
|
|
|
// attributes is a helper for global attributes.
|
|
type attributes []encoding.Attribute
|
|
|
|
func (a attributes) Attributes() []encoding.Attribute {
|
|
return []encoding.Attribute(a)
|
|
}
|
|
func (a *attributes) SetAttribute(attr encoding.Attribute) error {
|
|
*a = append(*a, attr)
|
|
return nil
|
|
}
|