diff --git a/internal/graph/dotgraph.go b/internal/graph/dotgraph.go index d704fc4e13..72c942940e 100644 --- a/internal/graph/dotgraph.go +++ b/internal/graph/dotgraph.go @@ -110,7 +110,7 @@ func (b *builder) start() { if b.config.Title != "" { graphname = b.config.Title } - fmt.Fprintln(b, `digraph "`+graphname+`" {`) + fmt.Fprintln(b, `digraph "`+escapeForDot(graphname)+`" {`) fmt.Fprintln(b, `node [style=filled fillcolor="#f8f8f8"]`) } @@ -132,7 +132,7 @@ func (b *builder) addLegend() { fmt.Fprintf(b, ` URL="%s" target="_blank"`, b.config.LegendURL) } if b.config.Title != "" { - fmt.Fprintf(b, ` tooltip="%s"`, b.config.Title) + fmt.Fprintf(b, ` tooltip="%s"`, escapeForDot(b.config.Title)) } fmt.Fprintf(b, "] }\n") } @@ -390,7 +390,10 @@ func multilinePrintableName(info *NodeInfo) string { infoCopy.Name = strings.ReplaceAll(infoCopy.Name, "[...]", "[…]") infoCopy.Name = strings.ReplaceAll(infoCopy.Name, ".", `\n`) if infoCopy.File != "" { - infoCopy.File = filepath.Base(infoCopy.File) + infoCopy.File = escapeForDot(filepath.Base(infoCopy.File)) + } + if infoCopy.Objfile != "" { + infoCopy.Objfile = escapeForDot(filepath.Base(infoCopy.Objfile)) } return strings.Join(infoCopy.NameComponents(), `\n`) + `\n` } diff --git a/internal/graph/dotgraph_test.go b/internal/graph/dotgraph_test.go index b375bb6225..e8db65b663 100644 --- a/internal/graph/dotgraph_test.go +++ b/internal/graph/dotgraph_test.go @@ -141,13 +141,29 @@ func TestComposeWithStandardGraphAndURL(t *testing.T) { func TestComposeWithNamesThatNeedEscaping(t *testing.T) { g := baseGraph() a, c := baseAttrsAndConfig() - g.Nodes[0].Info = NodeInfo{Name: `var"src"`} - g.Nodes[1].Info = NodeInfo{Name: `var"#dest#"`} + g.Nodes[0].Info = NodeInfo{Name: `var"src"`, File: `/src/file"name.go`, Lineno: 10} + g.Nodes[1].Info = NodeInfo{Address: 0x10, Objfile: `/lib/obj"file.so`} + c.Title = `test"title` + c.Labels = []string{"legend", `label"text`} var buf bytes.Buffer ComposeDot(&buf, g, a, c) - - compareGraphs(t, buf.Bytes(), "compose7.dot") + got := buf.String() + + for _, tc := range []struct{ raw, escaped string }{ + {`var"src"`, `var\"src\"`}, + {`file"name.go`, `file\"name.go`}, + {`obj"file.so`, `obj\"file.so`}, + {`test"title`, `test\"title`}, + {`label"text`, `label\"text`}, + } { + if strings.Contains(got, tc.raw) { + t.Errorf("unescaped %q in dot output:\n%s", tc.raw, got) + } + if !strings.Contains(got, tc.escaped) { + t.Errorf("escaped %q not found in dot output:\n%s", tc.escaped, got) + } + } } func TestComposeWithCommentsWithNewlines(t *testing.T) { diff --git a/internal/graph/testdata/compose7.dot b/internal/graph/testdata/compose7.dot deleted file mode 100644 index 8f749a7797..0000000000 --- a/internal/graph/testdata/compose7.dot +++ /dev/null @@ -1,7 +0,0 @@ -digraph "testtitle" { -node [style=filled fillcolor="#f8f8f8"] -subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\llabel3: \"foo\"\l" tooltip="testtitle"] } -N1 [label="var\"src\"\n10 (10.00%)\nof 25 (25.00%)" id="node1" fontsize=22 shape=box tooltip="var\"src\" (25)" color="#b23c00" fillcolor="#edddd5"] -N2 [label="var\"#dest#\"\n15 (15.00%)\nof 25 (25.00%)" id="node2" fontsize=24 shape=box tooltip="var\"#dest#\" (25)" color="#b23c00" fillcolor="#edddd5"] -N1 -> N2 [label=" 10" weight=11 color="#b28559" tooltip="var\"src\" -> var\"#dest#\" (10)" labeltooltip="var\"src\" -> var\"#dest#\" (10)"] -}