diff --git a/graph/encoding/dot/decode.go b/graph/encoding/dot/decode.go index adf4cd98..17dd9d68 100644 --- a/graph/encoding/dot/decode.go +++ b/graph/encoding/dot/decode.go @@ -275,6 +275,16 @@ func applyPortsToEdge(from ast.Vertex, to *ast.Edge, edge basicEdge) { func (gen *simpleGraph) addEdgeStmt(dst encoding.Builder, stmt *ast.EdgeStmt) { fs := gen.addVertex(dst, stmt.From) ts := gen.addEdge(dst, stmt.To, stmt.Attrs) + defer func() { + switch e := recover().(type) { + case nil: + // Do nothing. + case error: + panic(e) + default: + panic(fmt.Errorf("panic setting edge: %v", e)) + } + }() for _, f := range fs { for _, t := range ts { edge := dst.NewEdge(f, t) diff --git a/graph/encoding/dot/decode_test.go b/graph/encoding/dot/decode_test.go index 6b400db3..4d652c71 100644 --- a/graph/encoding/dot/decode_test.go +++ b/graph/encoding/dot/decode_test.go @@ -644,3 +644,60 @@ func (a *attributes) SetAttribute(attr encoding.Attribute) error { *a = append(*a, attr) return nil } + +const undirectedSelfLoopGraph = `graph { + // Node definitions. + 0; + 1; + + // Edge definitions. + 0 -- 0; + 1 -- 1; +}` + +const directedSelfLoopGraph = `digraph { + // Node definitions. + 0; + 1; + + // Edge definitions. + 0 -> 0; + 1 -> 1; +}` + +func TestSelfLoopSimple(t *testing.T) { + for _, test := range []struct { + dst func() encoding.Builder + src string + }{ + { + dst: func() encoding.Builder { return simple.NewUndirectedGraph() }, + src: undirectedSelfLoopGraph, + }, + { + dst: func() encoding.Builder { return simple.NewDirectedGraph() }, + src: directedSelfLoopGraph, + }, + } { + dst := test.dst() + message, panicked := panics(func() { + err := Unmarshal([]byte(test.src), dst) + if err == nil { + t.Errorf("expected error for self loop addition to %T", dst) + } + }) + if panicked { + t.Errorf("unexpected panic for self loop addition to %T: %s", dst, message) + } + } +} + +func panics(fn func()) (message string, ok bool) { + defer func() { + r := recover() + message = fmt.Sprint(r) + ok = r != nil + }() + fn() + return +}