1
0
mirror of https://github.com/golang/go synced 2024-11-19 02:54:42 -07:00
go/cmd/digraph/digraph_test.go
Jean de Klerk a4e10e6d0e digraph: refactor somepath to print an adjacency list
Refactors somepath to print an adjacency list from "from" to "to", instead of
just the nodes. This allows other tools like modgraphviz to print the subgraph.

This is a breaking change. The previous behavior returned vertices; now it
returns edges. If you relied on the previous behavior, use:

    my-application | digraph somepath <from> <to> | digraph nodes

Change-Id: Id5e77e19eea79849ec6f25018f627c8dc4e2a831
Reviewed-on: https://go-review.googlesource.com/c/tools/+/184338
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
2019-08-09 14:56:25 +00:00

285 lines
6.7 KiB
Go

// Copyright 2019 The Go 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 main
import (
"bytes"
"fmt"
"reflect"
"sort"
"strings"
"testing"
)
func TestDigraph(t *testing.T) {
const g1 = `
socks shoes
shorts pants
pants belt shoes
shirt tie sweater
sweater jacket
hat
`
const g2 = `
a b c
b d
c d
d c
`
for _, test := range []struct {
name string
input string
cmd string
args []string
want string
}{
{"nodes", g1, "nodes", nil, "belt\nhat\njacket\npants\nshirt\nshoes\nshorts\nsocks\nsweater\ntie\n"},
{"reverse", g1, "reverse", []string{"jacket"}, "jacket\nshirt\nsweater\n"},
{"forward", g1, "forward", []string{"socks"}, "shoes\nsocks\n"},
{"forward multiple args", g1, "forward", []string{"socks", "sweater"}, "jacket\nshoes\nsocks\nsweater\n"},
{"scss", g2, "sccs", nil, "a\nb\nc d\n"},
{"scc", g2, "scc", []string{"d"}, "c\nd\n"},
{"succs", g2, "succs", []string{"a"}, "b\nc\n"},
{"preds", g2, "preds", []string{"c"}, "a\nd\n"},
{"preds multiple args", g2, "preds", []string{"c", "d"}, "a\nb\nc\nd\n"},
} {
t.Run(test.name, func(t *testing.T) {
stdin = strings.NewReader(test.input)
stdout = new(bytes.Buffer)
if err := digraph(test.cmd, test.args); err != nil {
t.Fatal(err)
}
got := stdout.(fmt.Stringer).String()
if got != test.want {
t.Errorf("digraph(%s, %s) = got %q, want %q", test.cmd, test.args, got, test.want)
}
})
}
// TODO(adonovan):
// - test somepath (it's nondeterministic).
// - test errors
}
func TestAllpaths(t *testing.T) {
for _, test := range []struct {
name string
in string
to string // from is always "A"
want string
}{
{
name: "Basic",
in: "A B\nB C",
to: "B",
want: "A B\n",
},
{
name: "Long",
in: "A B\nB C\n",
to: "C",
want: "A B\nB C\n",
},
{
name: "Cycle Basic",
in: "A B\nB A",
to: "B",
want: "A B\nB A\n",
},
{
name: "Cycle Path Out",
// A <-> B -> C -> D
in: "A B\nB A\nB C\nC D",
to: "C",
want: "A B\nB A\nB C\n",
},
{
name: "Cycle Path Out Further Out",
// A -> B <-> C -> D -> E
in: "A B\nB C\nC D\nC B\nD E",
to: "D",
want: "A B\nB C\nC B\nC D\n",
},
{
name: "Two Paths Basic",
// /-> C --\
// A -> B -- -> E -> F
// \-> D --/
in: "A B\nB C\nC E\nB D\nD E\nE F",
to: "E",
want: "A B\nB C\nB D\nC E\nD E\n",
},
{
name: "Two Paths With One Immediately From Start",
// /-> B -+ -> D
// A -- |
// \-> C <+
in: "A B\nA C\nB C\nB D",
to: "C",
want: "A B\nA C\nB C\n",
},
{
name: "Two Paths Further Up",
// /-> B --\
// A -- -> D -> E -> F
// \-> C --/
in: "A B\nA C\nB D\nC D\nD E\nE F",
to: "E",
want: "A B\nA C\nB D\nC D\nD E\n",
},
{
// We should include A - C - D even though it's further up the
// second path than D (which would already be in the graph by
// the time we get around to integrating the second path).
name: "Two Splits",
// /-> B --\ /-> E --\
// A -- -> D -- -> G -> H
// \-> C --/ \-> F --/
in: "A B\nA C\nB D\nC D\nD E\nD F\nE G\nF G\nG H",
to: "G",
want: "A B\nA C\nB D\nC D\nD E\nD F\nE G\nF G\n",
},
{
// D - E should not be duplicated.
name: "Two Paths - Two Splits With Gap",
// /-> B --\ /-> F --\
// A -- -> D -> E -- -> H -> I
// \-> C --/ \-> G --/
in: "A B\nA C\nB D\nC D\nD E\nE F\nE G\nF H\nG H\nH I",
to: "H",
want: "A B\nA C\nB D\nC D\nD E\nE F\nE G\nF H\nG H\n",
},
} {
t.Run(test.name, func(t *testing.T) {
stdin = strings.NewReader(test.in)
stdout = new(bytes.Buffer)
if err := digraph("allpaths", []string{"A", test.to}); err != nil {
t.Fatal(err)
}
got := stdout.(fmt.Stringer).String()
if got != test.want {
t.Errorf("digraph(allpaths, A, %s) = got %q, want %q", test.to, got, test.want)
}
})
}
}
func TestSomepath(t *testing.T) {
for _, test := range []struct {
name string
in string
to string
// somepath is non-deterministic, so we have to provide all the
// possible options. Each option is separated with |.
wantAnyOf string
}{
{
name: "Basic",
in: "A B\n",
to: "B",
wantAnyOf: "A B",
},
{
name: "Basic With Cycle",
in: "A B\nB A",
to: "B",
wantAnyOf: "A B",
},
{
name: "Two Paths",
// /-> B --\
// A -- -> D
// \-> C --/
in: "A B\nA C\nB D\nC D",
to: "D",
wantAnyOf: "A B\nB D|A C\nC D",
},
} {
t.Run(test.name, func(t *testing.T) {
stdin = strings.NewReader(test.in)
stdout = new(bytes.Buffer)
if err := digraph("somepath", []string{"A", test.to}); err != nil {
t.Fatal(err)
}
got := stdout.(fmt.Stringer).String()
lines := strings.Split(got, "\n")
sort.Strings(lines)
got = strings.Join(lines[1:], "\n")
var oneMatch bool
for _, want := range strings.Split(test.wantAnyOf, "|") {
if got == want {
oneMatch = true
}
}
if !oneMatch {
t.Errorf("digraph(somepath, A, %s) = got %q, want any of\n%s", test.to, got, test.wantAnyOf)
}
})
}
}
func TestSplit(t *testing.T) {
for _, test := range []struct {
line string
want []string
}{
{`one "2a 2b" three`, []string{"one", "2a 2b", "three"}},
{`one tw"\n\x0a\u000a\012"o three`, []string{"one", "tw\n\n\n\no", "three"}},
} {
got, err := split(test.line)
if err != nil {
t.Errorf("split(%s) failed: %v", test.line, err)
}
if !reflect.DeepEqual(got, test.want) {
t.Errorf("split(%s) = %v, want %v", test.line, got, test.want)
}
}
}
func TestQuotedLength(t *testing.T) {
for _, test := range []struct {
input string
want int
}{
{`"abc"`, 5},
{`"abc"def`, 5},
{`"abc\"d"ef`, 8}, // "abc\"d" is consumed, ef is residue
{`"\012\n\x0a\u000a\U0000000a"`, 28},
{"\"\xff\"", 3}, // bad UTF-8 is ok
{`"\xff"`, 6}, // hex escape for bad UTF-8 is ok
} {
got, ok := quotedLength(test.input)
if !ok {
got = 0
}
if got != test.want {
t.Errorf("quotedLength(%s) = %d, want %d", test.input, got, test.want)
}
}
// errors
for _, input := range []string{
``, // not a quotation
`a`, // not a quotation
`'a'`, // not a quotation
`"a`, // not terminated
`"\0"`, // short octal escape
`"\x1"`, // short hex escape
`"\u000"`, // short \u escape
`"\U0000000"`, // short \U escape
`"\k"`, // invalid escape
"\"ab\nc\"", // newline
} {
if n, ok := quotedLength(input); ok {
t.Errorf("quotedLength(%s) = %d, want !ok", input, n)
}
}
}