gps: Fully refactor pkgtree to use IgnoredRuleset

This commit is contained in:
sam boyer 2017-10-13 04:16:12 -04:00
Родитель cb51cd0949
Коммит 5be0c0000c
2 изменённых файлов: 149 добавлений и 113 удалений

Просмотреть файл

@ -550,14 +550,7 @@ type PackageTree struct {
// "A": []string{},
// "A/bar": []string{"B/baz"},
// }
func (t PackageTree) ToReachMap(main, tests, backprop bool, ignore map[string]bool) (ReachMap, map[string]*ProblemImportError) {
if ignore == nil {
ignore = make(map[string]bool)
}
// Radix tree for ignore prefixes.
xt := CreateIgnorePrefixTree(ignore)
func (t PackageTree) ToReachMap(main, tests, backprop bool, ignore *IgnoredRuleset) (ReachMap, map[string]*ProblemImportError) {
// world's simplest adjacency list
workmap := make(map[string]wm)
@ -576,18 +569,10 @@ func (t PackageTree) ToReachMap(main, tests, backprop bool, ignore map[string]bo
continue
}
// Skip ignored packages
if ignore[ip] {
if ignore.IsIgnored(ip) {
continue
}
if xt != nil {
// Skip ignored packages prefix matches
_, _, ok := xt.LongestPrefix(ip)
if ok {
continue
}
}
// TODO (kris-nova) Disable to get staticcheck passing
//imps = imps[:0]
@ -605,7 +590,7 @@ func (t PackageTree) ToReachMap(main, tests, backprop bool, ignore map[string]bo
// For each import, decide whether it should be ignored, or if it
// belongs in the external or internal imports list.
for _, imp := range imps {
if ignore[imp] || imp == "." {
if ignore.IsIgnored(imp) || imp == "." {
continue
}
@ -1093,28 +1078,23 @@ type IgnoredRuleset struct {
// matching prefix will be ignored. IgnoredRulesets are immutable once created.
//
// Duplicate and redundant (i.e. a literal path that has a prefix of a wildcard
// path) declarations are discarded, resulting in the most efficient
// representation.
func NewIgnoredRuleset(ig map[string]struct{}) IgnoredRuleset {
// path) declarations are discarded. Consequently, it is possible that the
// returned IgnoredRuleset may have a smaller Len() than the input slice.
func NewIgnoredRuleset(ig []string) *IgnoredRuleset {
if len(ig) == 0 {
return IgnoredRuleset{}
return &IgnoredRuleset{}
}
ir := IgnoredRuleset{
ir := &IgnoredRuleset{
t: radix.New(),
}
// Create a sorted list of all the ignores in order to ensure that wildcard
// Sort the list of all the ignores in order to ensure that wildcard
// precedence is recorded correctly in the trie.
sortedIgnores := make([]string, len(ig))
for k := range ig {
sortedIgnores = append(sortedIgnores, k)
}
sort.Strings(sortedIgnores)
for _, i := range sortedIgnores {
// Skip global ignore.
if i == wcIgnoreSuffix {
sort.Strings(ig)
for _, i := range ig {
// Skip global ignore and empty string.
if i == wcIgnoreSuffix || i == "" {
continue
}
@ -1135,9 +1115,7 @@ func NewIgnoredRuleset(ig map[string]struct{}) IgnoredRuleset {
}
}
// The radix tree implementation is initialized with a single element
// representing the empty string.
if ir.t.Len() == 1 {
if ir.t.Len() == 0 {
ir.t = nil
}
@ -1146,11 +1124,44 @@ func NewIgnoredRuleset(ig map[string]struct{}) IgnoredRuleset {
// IsIgnored indicates whether the provided path should be ignored, according to
// the ruleset.
func (ir IgnoredRuleset) IsIgnored(path string) bool {
if ir.t == nil {
func (ir *IgnoredRuleset) IsIgnored(path string) bool {
if path == "" || ir == nil || ir.t == nil {
return false
}
prefix, wildi, has := ir.t.LongestPrefix(path)
return has && (wildi.(bool) || path == prefix)
}
// Len indicates the number of rules in the ruleset.
func (ir *IgnoredRuleset) Len() int {
if ir == nil || ir.t == nil {
return 0
}
return ir.t.Len()
}
// ToSlice converts the contents of the IgnoredRuleset to a string slice.
//
// This operation is symmetrically dual to NewIgnoredRuleset.
func (ir *IgnoredRuleset) ToSlice() []string {
irlen := ir.Len()
if irlen == 0 {
return nil
}
items := make([]string, 0, irlen)
ir.t.Walk(func(s string, v interface{}) bool {
if s != "" {
if v.(bool) {
items = append(items, s+wcIgnoreSuffix)
} else {
items = append(items, s)
}
}
return false
})
return items
}

Просмотреть файл

@ -1524,10 +1524,10 @@ func TestToReachMap(t *testing.T) {
var want ReachMap
var name string
var main, tests bool
var ignore map[string]bool
var ignore []string
validate := func() {
got, em := vptree.ToReachMap(main, tests, true, ignore)
got, em := vptree.ToReachMap(main, tests, true, NewIgnoredRuleset(ignore))
if len(em) != 0 {
t.Errorf("Should not have any error packages from ToReachMap, got %s", em)
}
@ -1687,9 +1687,7 @@ func TestToReachMap(t *testing.T) {
// ignoring the "varied" pkg has same effect as disabling main pkgs
name = "ignore root"
ignore = map[string]bool{
b(""): true,
}
ignore = []string{b("")}
main = true
validate()
@ -1723,9 +1721,7 @@ func TestToReachMap(t *testing.T) {
// now, the fun stuff. punch a hole in the middle by cutting out
// varied/simple
name = "ignore varied/simple"
ignore = map[string]bool{
b("simple"): true,
}
ignore = []string{b("simple")}
except(
// root pkg loses on everything in varied/simple/another
// FIXME this is a bit odd, but should probably exclude m1p as well,
@ -1738,9 +1734,9 @@ func TestToReachMap(t *testing.T) {
// widen the hole by excluding otherpath
name = "ignore varied/{otherpath,simple}"
ignore = map[string]bool{
b("otherpath"): true,
b("simple"): true,
ignore = []string{
b("otherpath"),
b("simple"),
}
except(
// root pkg loses on everything in varied/simple/another and varied/m1p
@ -1752,7 +1748,7 @@ func TestToReachMap(t *testing.T) {
// remove namemismatch, though we're mostly beating a dead horse now
name = "ignore varied/{otherpath,simple,namemismatch}"
ignore[b("namemismatch")] = true
ignore = append(ignore, b("namemismatch"))
except(
// root pkg loses on everything in varied/simple/another and varied/m1p
bl("", "simple", "simple/another", "m1p", "otherpath", "namemismatch")+" hash encoding/binary go/parser github.com/golang/dep/internal/gps sort os github.com/Masterminds/semver",
@ -1851,9 +1847,9 @@ func TestFlattenReachMap(t *testing.T) {
isStdLibFn: nil,
main: true,
tests: true,
ignore: map[string]bool{
"nomatch": true,
},
ignore: NewIgnoredRuleset([]string{
"nomatch",
}),
},
// should have the same effect as ignoring main
{
@ -1862,9 +1858,9 @@ func TestFlattenReachMap(t *testing.T) {
isStdLibFn: nil,
main: true,
tests: true,
ignore: map[string]bool{
"github.com/example/varied": true,
},
ignore: NewIgnoredRuleset([]string{
"github.com/example/varied",
}),
},
// now drop a more interesting one
// we get github.com/golang/dep/internal/gps from m1p, too, so it should still be there
@ -1874,9 +1870,9 @@ func TestFlattenReachMap(t *testing.T) {
isStdLibFn: nil,
main: true,
tests: true,
ignore: map[string]bool{
"github.com/example/varied/simple": true,
},
ignore: NewIgnoredRuleset([]string{
"github.com/example/varied/simple",
}),
},
// now drop two
{
@ -1885,10 +1881,10 @@ func TestFlattenReachMap(t *testing.T) {
isStdLibFn: nil,
main: true,
tests: true,
ignore: map[string]bool{
"github.com/example/varied/simple": true,
"github.com/example/varied/namemismatch": true,
},
ignore: NewIgnoredRuleset([]string{
"github.com/example/varied/simple",
"github.com/example/varied/namemismatch",
}),
},
// make sure tests and main play nice with ignore
{
@ -1897,10 +1893,10 @@ func TestFlattenReachMap(t *testing.T) {
isStdLibFn: nil,
main: true,
tests: false,
ignore: map[string]bool{
"github.com/example/varied/simple": true,
"github.com/example/varied/namemismatch": true,
},
ignore: NewIgnoredRuleset([]string{
"github.com/example/varied/simple",
"github.com/example/varied/namemismatch",
}),
},
{
name: "ignore simple and namemismatch, and no main",
@ -1908,10 +1904,10 @@ func TestFlattenReachMap(t *testing.T) {
isStdLibFn: nil,
main: false,
tests: true,
ignore: map[string]bool{
"github.com/example/varied/simple": true,
"github.com/example/varied/namemismatch": true,
},
ignore: NewIgnoredRuleset([]string{
"github.com/example/varied/simple",
"github.com/example/varied/namemismatch",
}),
},
{
name: "ignore simple and namemismatch, and no main or tests",
@ -1919,10 +1915,10 @@ func TestFlattenReachMap(t *testing.T) {
isStdLibFn: nil,
main: false,
tests: false,
ignore: map[string]bool{
"github.com/example/varied/simple": true,
"github.com/example/varied/namemismatch": true,
},
ignore: NewIgnoredRuleset([]string{
"github.com/example/varied/simple",
"github.com/example/varied/namemismatch",
}),
},
// ignore two that should knock out gps
{
@ -1931,10 +1927,10 @@ func TestFlattenReachMap(t *testing.T) {
isStdLibFn: nil,
main: true,
tests: true,
ignore: map[string]bool{
"github.com/example/varied/simple": true,
"github.com/example/varied/m1p": true,
},
ignore: NewIgnoredRuleset([]string{
"github.com/example/varied/simple",
"github.com/example/varied/m1p",
}),
},
// finally, directly ignore some external packages
{
@ -1943,11 +1939,11 @@ func TestFlattenReachMap(t *testing.T) {
isStdLibFn: nil,
main: true,
tests: true,
ignore: map[string]bool{
"github.com/golang/dep/internal/gps": true,
"go/parser": true,
"sort": true,
},
ignore: NewIgnoredRuleset([]string{
"github.com/golang/dep/internal/gps",
"go/parser",
"sort",
}),
},
} {
t.Run(testCase.name, testFlattenReachMap(&vptree, testCase))
@ -1971,7 +1967,7 @@ func TestFlattenReachMap(t *testing.T) {
type flattenReachMapCase struct {
expect []string
name string
ignore map[string]bool
ignore *IgnoredRuleset
main, tests bool
isStdLibFn func(string) bool
}
@ -2135,7 +2131,7 @@ func TestIgnoredRuleset(t *testing.T) {
}
cases := []struct {
name string
inputs map[string]struct{}
inputs []string
wantInTree tfixm
wantEmptyTree bool
shouldIgnore []string
@ -2143,15 +2139,15 @@ func TestIgnoredRuleset(t *testing.T) {
}{
{
name: "only skip global ignore",
inputs: map[string]struct{}{wcIgnoreSuffix: struct{}{}},
inputs: []string{wcIgnoreSuffix},
wantEmptyTree: true,
},
{
name: "ignores without ignore suffix",
inputs: map[string]struct{}{
"x/y/z": struct{}{},
"*a/b/c": struct{}{},
"gophers": struct{}{},
inputs: []string{
"x/y/z",
"*a/b/c",
"gophers",
},
wantInTree: tfixm{
{path: "x/y/z", wild: false},
@ -2171,10 +2167,10 @@ func TestIgnoredRuleset(t *testing.T) {
},
{
name: "ignores with ignore suffix",
inputs: map[string]struct{}{
"x/y/z*": struct{}{},
"a/b/c": struct{}{},
"gophers": struct{}{},
inputs: []string{
"x/y/z*",
"a/b/c",
"gophers",
},
wantInTree: tfixm{
{path: "x/y/z", wild: true},
@ -2194,11 +2190,11 @@ func TestIgnoredRuleset(t *testing.T) {
},
{
name: "global ignore with other strings",
inputs: map[string]struct{}{
"*": struct{}{},
"gophers*": struct{}{},
"x/y/z*": struct{}{},
"a/b/c": struct{}{},
inputs: []string{
"*",
"gophers*",
"x/y/z*",
"a/b/c",
},
wantInTree: tfixm{
{path: "x/y/z", wild: true},
@ -2220,11 +2216,11 @@ func TestIgnoredRuleset(t *testing.T) {
},
{
name: "ineffectual ignore with wildcard",
inputs: map[string]struct{}{
"a/b*": struct{}{},
"a/b/c*": struct{}{},
"a/b/x/y": struct{}{},
"a/c*": struct{}{},
inputs: []string{
"a/b*",
"a/b/c*",
"a/b/x/y",
"a/c*",
},
wantInTree: tfixm{
{path: "a/c", wild: true},
@ -2240,9 +2236,9 @@ func TestIgnoredRuleset(t *testing.T) {
},
{
name: "same path with and without wildcard",
inputs: map[string]struct{}{
"a/b*": struct{}{},
"a/b": struct{}{},
inputs: []string{
"a/b*",
"a/b",
},
wantInTree: tfixm{
{path: "a/b", wild: true},
@ -2256,15 +2252,40 @@ func TestIgnoredRuleset(t *testing.T) {
"a/d",
},
},
{
name: "empty paths",
inputs: []string{
"",
"a/b*",
},
wantInTree: tfixm{
{path: "a/b", wild: true},
},
shouldNotIgnore: []string{
"",
},
},
{
name: "single wildcard",
inputs: []string{
"a/b*",
},
wantInTree: tfixm{
{path: "a/b", wild: true},
},
shouldIgnore: []string{
"a/b/c",
},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
igm := NewIgnoredRuleset(c.inputs)
igm := NewIgnoredRuleset(c.inputs)
f := func(t *testing.T) {
if c.wantEmptyTree {
if igm != nil && igm.t != nil {
t.Fatalf("wanted empty tree, got non-nil tree")
if igm.Len() != 0 {
t.Fatalf("wanted empty tree, but had %v elements", igm.Len())
}
}
@ -2292,6 +2313,10 @@ func TestIgnoredRuleset(t *testing.T) {
t.Fatalf("%q should not be ignored, but it was", p)
}
}
})
}
t.Run(c.name, f)
igm = NewIgnoredRuleset(igm.ToSlice())
t.Run(c.name+"/inandout", f)
}
}