pax_global_header00006660000000000000000000000064150126536150014516gustar00rootroot0000000000000052 comment=75be065eb5764f0c6d4b78db833abfb2ee0f3c62 dots-1.0.0/000077500000000000000000000000001501265361500124655ustar00rootroot00000000000000dots-1.0.0/.github/000077500000000000000000000000001501265361500140255ustar00rootroot00000000000000dots-1.0.0/.github/workflows/000077500000000000000000000000001501265361500160625ustar00rootroot00000000000000dots-1.0.0/.github/workflows/pr.yaml000066400000000000000000000017561501265361500174000ustar00rootroot00000000000000name: Checks on: push: branches: - master pull_request: jobs: go-checks: name: Go checks runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: stable - name: Check go.mod run: | go mod tidy git diff --exit-code -- go.mod git diff --exit-code -- go.sum - name: Run gofmt run: | find . -type f -name '*.go' -not -path './fixtures/*' -exec gofmt -w {} + git diff --exit-code tests: name: Tests strategy: fail-fast: false matrix: go-version: - oldstable - stable os: - ubuntu-latest - windows-latest runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - name: Run tests run: go test -race -shuffle=on ./... dots-1.0.0/LICENSE000066400000000000000000000020551501265361500134740ustar00rootroot00000000000000MIT License Copyright (c) 2018 Minko Gechev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dots-1.0.0/README.md000066400000000000000000000057501501265361500137530ustar00rootroot00000000000000# Dots [![Build Status](https://github.com/mgechev/dots/actions/workflows/pr.yaml/badge.svg)](https://github.com/mgechev/dots/actions/workflows/pr.yaml) [![Go Reference](https://pkg.go.dev/badge/github.com/mgechev/dots.svg)](https://pkg.go.dev/github.com/mgechev/dots) `dots` is a Go package that provides advanced wildcard file and package matching, similar to the behavior used by tools like `go test` and `golint`. It allows you to easily resolve file paths and packages using patterns with `...` wildcards, and supports flexible exclusion rules. ## Usage ```go import ( "fmt" "github.com/mgechev/dots" ) func main() { result, err := dots.Resolve([]string{"./fixtures/..."}, []string{"./fixtures/foo"}) for _, f := range result { fmt.Println(f); } } ``` If we suppose that we have the following directory structure: ```text ├── README.md ├── fixtures │   ├── bar │   │   ├── bar1.go │   │   └── bar2.go │   ├── baz │   │   ├── baz1.go │   │   ├── baz2.go │   │   └── baz3.go │   └── foo │   ├── foo1.go │   ├── foo2.go │   └── foo3.go └── main.go ``` The result will be: ```text fixtures/bar/bar1.go fixtures/bar/bar2.go fixtures/baz/baz1.go fixtures/baz/baz2.go fixtures/baz/baz3.go ``` `dots` supports wildcard in both - the first and the last argument of `Resolve`, which means that you can ignore files based on a wildcard: ```go dots.Resolve([]string{"github.com/mgechev/dots"}, []string{"./..."}) // empty list dots.Resolve([]string{"./fixtures/bar/..."}, []string{"./fixture/foo/...", "./fixtures/baz/..."}) // bar1.go, bar2.go ``` ## Preserve package structure `dots` allow you to receive a slice of slices where each nested slice represents an individual package: ```go dots.ResolvePackages([]string{"github.com/mgechev/dots/..."}, []string{}) ``` So we will get the result: ```text [ [ "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/bar/bar1.go", "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/bar/bar2.go" ], [ "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/baz/baz1.go", "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/baz/baz2.go", "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/baz/baz3.go" ], [ "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/foo/foo1.go", "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/foo/foo2.go", "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/foo/foo3.go" ], [ "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/baz/baz1.go", "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/baz/baz2.go" ], [ "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/foo/foo1.go", "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/foo/foo2.go" ], [ "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/foo/bar/bar1.go" ] ] ``` This method is especially useful, when you want to perform type checking over given package from the result. dots-1.0.0/fixtures/000077500000000000000000000000001501265361500143365ustar00rootroot00000000000000dots-1.0.0/fixtures/dummy/000077500000000000000000000000001501265361500154715ustar00rootroot00000000000000dots-1.0.0/fixtures/dummy/bar/000077500000000000000000000000001501265361500162355ustar00rootroot00000000000000dots-1.0.0/fixtures/dummy/bar/bar1.go000066400000000000000000000000141501265361500174040ustar00rootroot00000000000000package bar dots-1.0.0/fixtures/dummy/bar/bar2.go000066400000000000000000000000141501265361500174050ustar00rootroot00000000000000package bar dots-1.0.0/fixtures/dummy/baz/000077500000000000000000000000001501265361500162455ustar00rootroot00000000000000dots-1.0.0/fixtures/dummy/baz/baz1.go000066400000000000000000000000141501265361500174240ustar00rootroot00000000000000package baz dots-1.0.0/fixtures/dummy/baz/baz2.go000066400000000000000000000000141501265361500174250ustar00rootroot00000000000000package baz dots-1.0.0/fixtures/dummy/baz/baz3.go000066400000000000000000000000141501265361500174260ustar00rootroot00000000000000package baz dots-1.0.0/fixtures/dummy/foo/000077500000000000000000000000001501265361500162545ustar00rootroot00000000000000dots-1.0.0/fixtures/dummy/foo/foo1.go000066400000000000000000000000141501265361500174420ustar00rootroot00000000000000package foo dots-1.0.0/fixtures/dummy/foo/foo2.go000066400000000000000000000000141501265361500174430ustar00rootroot00000000000000package foo dots-1.0.0/fixtures/dummy/foo/foo3.go000066400000000000000000000000141501265361500174440ustar00rootroot00000000000000package foo dots-1.0.0/fixtures/pkg/000077500000000000000000000000001501265361500151175ustar00rootroot00000000000000dots-1.0.0/fixtures/pkg/baz/000077500000000000000000000000001501265361500156735ustar00rootroot00000000000000dots-1.0.0/fixtures/pkg/baz/baz1.go000066400000000000000000000000141501265361500170520ustar00rootroot00000000000000package baz dots-1.0.0/fixtures/pkg/baz/baz2.go000066400000000000000000000000141501265361500170530ustar00rootroot00000000000000package baz dots-1.0.0/fixtures/pkg/foo/000077500000000000000000000000001501265361500157025ustar00rootroot00000000000000dots-1.0.0/fixtures/pkg/foo/bar/000077500000000000000000000000001501265361500164465ustar00rootroot00000000000000dots-1.0.0/fixtures/pkg/foo/bar/bar1.go000066400000000000000000000000371501265361500176220ustar00rootroot00000000000000package bar type Bar struct{} dots-1.0.0/fixtures/pkg/foo/foo1.go000066400000000000000000000001641501265361500170760ustar00rootroot00000000000000package foo import "github.com/mgechev/dots/fixtures/pkg/foo/bar" func foo() bar.Bar { var b bar.Bar return b } dots-1.0.0/fixtures/pkg/foo/foo2.go000066400000000000000000000000141501265361500170710ustar00rootroot00000000000000package foo dots-1.0.0/fixtures/withtests/000077500000000000000000000000001501265361500163745ustar00rootroot00000000000000dots-1.0.0/fixtures/withtests/gofile.go000066400000000000000000000000141501265361500201630ustar00rootroot00000000000000package bar dots-1.0.0/fixtures/withtests/testgo_test.go000066400000000000000000000000211501265361500212600ustar00rootroot00000000000000package bar_test dots-1.0.0/fixtures/withtests/xtestgo_test.go000066400000000000000000000000141501265361500214520ustar00rootroot00000000000000package bar dots-1.0.0/go.mod000066400000000000000000000000521501265361500135700ustar00rootroot00000000000000module github.com/mgechev/dots go 1.23.0 dots-1.0.0/resolve.go000066400000000000000000000301341501265361500144740ustar00rootroot00000000000000package dots import ( "go/build" "log" "os" "path" "path/filepath" "regexp" "runtime" "strings" ) var ( buildContext = build.Default goroot = filepath.Clean(runtime.GOROOT()) gorootSrc = filepath.Join(goroot, "src") ) func flatten(arr [][]string) []string { var res []string for _, e := range arr { res = append(res, e...) } return res } // Resolve accepts a slice of paths with optional "..." placeholder and a slice with paths to be skipped. // The final result is the set of all files from the selected directories subtracted with // the files in the skip slice. func Resolve(includePatterns, skipPatterns []string) ([]string, error) { skip, err := resolvePatternsIgnoringErrors(skipPatterns) filter := newPathFilter(flatten(skip)) if err != nil { return nil, err } pathSet := map[string]bool{} includePackages, err := resolvePatterns(includePatterns) include := flatten(includePackages) if err != nil { return nil, err } var result []string for _, i := range include { if _, ok := pathSet[i]; !ok && !filter(i) { pathSet[i] = true result = append(result, i) } } return result, err } // ResolvePackages accepts a slice of paths with optional "..." placeholder and a slice with paths to be skipped. // The final result is the set of all files from the selected directories subtracted with // the files in the skip slice. The difference between `Resolve` and `ResolvePackages` // is that `ResolvePackages` preserves the package structure in the nested slices. func ResolvePackages(includePatterns, skipPatterns []string) ([][]string, error) { skip, err := resolvePatternsIgnoringErrors(skipPatterns) filter := newPathFilter(flatten(skip)) if err != nil { return nil, err } pathSet := map[string]bool{} include, err := resolvePatterns(includePatterns) if err != nil { return nil, err } var result [][]string for _, p := range include { var packageFiles []string for _, f := range p { if _, ok := pathSet[f]; !ok && !filter(f) { pathSet[f] = true packageFiles = append(packageFiles, f) } } result = append(result, packageFiles) } return result, err } func isDir(filename string) bool { fi, err := os.Stat(filename) return err == nil && fi.IsDir() } func exists(filename string) bool { _, err := os.Stat(filename) return err == nil } func resolveDir(dirname string) ([]string, error) { pkg, err := build.ImportDir(dirname, 0) return resolveImportedPackage(pkg, err) } func resolvePackage(pkgname string) ([]string, error) { pkg, err := build.Import(pkgname, ".", 0) return resolveImportedPackage(pkg, err) } func resolveImportedPackage(pkg *build.Package, err error) ([]string, error) { if err != nil { if _, nogo := err.(*build.NoGoError); nogo { // Don't complain if the failure is due to no Go source files. return nil, nil } return nil, err } var files []string files = append(files, pkg.GoFiles...) files = append(files, pkg.CgoFiles...) files = append(files, pkg.TestGoFiles...) files = append(files, pkg.XTestGoFiles...) if pkg.Dir != "." { for i, f := range files { files[i] = filepath.Join(pkg.Dir, f) } } return files, nil } func resolvePatterns(patterns []string) ([][]string, error) { var files [][]string for _, pattern := range patterns { f, err := resolvePattern(pattern) if err != nil { return nil, err } files = append(files, f...) } return files, nil } func resolvePatternsIgnoringErrors(patterns []string) ([][]string, error) { var files [][]string for _, pattern := range patterns { f, err := resolvePattern(pattern) if err != nil { continue } files = append(files, f...) } return files, nil } func resolvePattern(pattern string) ([][]string, error) { // dirsRun, filesRun, and pkgsRun indicate whether golint is applied to // directory, file or package targets. The distinction affects which // checks are run. It is no valid to mix target types. var dirsRun, filesRun, pkgsRun int var matches []string if strings.HasSuffix(pattern, "/...") && isDir(pattern[:len(pattern)-len("/...")]) { dirsRun = 1 for _, dirname := range matchPackagesInFS(pattern) { matches = append(matches, dirname) } } else if isDir(pattern) { dirsRun = 1 matches = append(matches, pattern) } else if exists(pattern) { filesRun = 1 matches = append(matches, pattern) } else { pkgsRun = 1 matches = append(matches, pattern) } result := [][]string{} switch { case dirsRun == 1: for _, dir := range matches { res, err := resolveDir(dir) if err != nil { return nil, err } result = append(result, res) } case filesRun == 1: return [][]string{matches}, nil case pkgsRun == 1: for _, pkg := range importPaths(matches) { res, err := resolvePackage(pkg) if err != nil { return nil, err } result = append(result, res) } } return result, nil } func newPathFilter(skip []string) func(string) bool { filter := map[string]bool{} for _, name := range skip { filter[name] = true } return func(path string) bool { base := filepath.Base(path) if filter[base] || filter[path] { return true } return base != "." && base != ".." && strings.ContainsAny(base[0:1], "_.") } } // importPathsNoDotExpansion returns the import paths to use for the given // command line, but it does no ... expansion. func importPathsNoDotExpansion(args []string) []string { if len(args) == 0 { return []string{"."} } var out []string for _, a := range args { // Arguments are supposed to be import paths, but // as a courtesy to Windows developers, rewrite \ to / // in command-line arguments. Handles .\... and so on. if filepath.Separator == '\\' { a = strings.Replace(a, `\`, `/`, -1) } // Put argument in canonical form, but preserve leading ./. if strings.HasPrefix(a, "./") { a = "./" + path.Clean(a) if a == "./." { a = "." } } else { a = path.Clean(a) } if a == "all" || a == "std" { out = append(out, matchPackages(a)...) continue } out = append(out, a) } return out } // importPaths returns the import paths to use for the given command line. func importPaths(args []string) []string { args = importPathsNoDotExpansion(args) var out []string for _, a := range args { if strings.Contains(a, "...") { if build.IsLocalImport(a) { out = append(out, matchPackagesInFS(a)...) } else { out = append(out, matchPackages(a)...) } continue } out = append(out, a) } return out } // matchPattern(pattern)(name) reports whether // name matches pattern. Pattern is a limited glob // pattern in which '...' means 'any string' and there // is no other special syntax. func matchPattern(pattern string) func(name string) bool { re := regexp.QuoteMeta(pattern) re = strings.Replace(re, `\.\.\.`, `.*`, -1) // Special case: foo/... matches foo too. if strings.HasSuffix(re, `/.*`) { re = re[:len(re)-len(`/.*`)] + `(/.*)?` } reg := regexp.MustCompile(`^` + re + `$`) return func(name string) bool { return reg.MatchString(name) } } // hasPathPrefix reports whether the path s begins with the // elements in prefix. func hasPathPrefix(s, prefix string) bool { switch { default: return false case len(s) == len(prefix): return s == prefix case len(s) > len(prefix): if prefix != "" && prefix[len(prefix)-1] == '/' { return strings.HasPrefix(s, prefix) } return s[len(prefix)] == '/' && s[:len(prefix)] == prefix } } // treeCanMatchPattern(pattern)(name) reports whether // name or children of name can possibly match pattern. // Pattern is the same limited glob accepted by matchPattern. func treeCanMatchPattern(pattern string) func(name string) bool { wildCard := false if i := strings.Index(pattern, "..."); i >= 0 { wildCard = true pattern = pattern[:i] } return func(name string) bool { return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || wildCard && strings.HasPrefix(name, pattern) } } func matchPackages(pattern string) []string { match := func(string) bool { return true } treeCanMatch := func(string) bool { return true } if pattern != "all" && pattern != "std" { match = matchPattern(pattern) treeCanMatch = treeCanMatchPattern(pattern) } have := map[string]bool{ "builtin": true, // ignore pseudo-package that exists only for documentation } if !buildContext.CgoEnabled { have["runtime/cgo"] = true // ignore during walk } var pkgs []string // Commands cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator) filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error { if err != nil || !fi.IsDir() || path == cmd { return nil } name := path[len(cmd):] if !treeCanMatch(name) { return filepath.SkipDir } // Commands are all in cmd/, not in subdirectories. if strings.Contains(name, string(filepath.Separator)) { return filepath.SkipDir } // We use, e.g., cmd/gofmt as the pseudo import path for gofmt. name = "cmd/" + name if have[name] { return nil } have[name] = true if !match(name) { return nil } _, err = buildContext.ImportDir(path, 0) if err != nil { if _, noGo := err.(*build.NoGoError); !noGo { log.Print(err) } return nil } pkgs = append(pkgs, name) return nil }) for _, src := range buildContext.SrcDirs() { if (pattern == "std" || pattern == "cmd") && src != gorootSrc { continue } src = filepath.Clean(src) + string(filepath.Separator) root := src if pattern == "cmd" { root += "cmd" + string(filepath.Separator) } filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { if err != nil || !fi.IsDir() || path == src { return nil } // Avoid .foo, _foo, and testdata directory trees. _, elem := filepath.Split(path) if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { return filepath.SkipDir } name := filepath.ToSlash(path[len(src):]) if pattern == "std" && (strings.Contains(name, ".") || name == "cmd") { // The name "std" is only the standard library. // If the name is cmd, it's the root of the command tree. return filepath.SkipDir } if !treeCanMatch(name) { return filepath.SkipDir } if have[name] { return nil } have[name] = true if !match(name) { return nil } _, err = buildContext.ImportDir(path, 0) if err != nil { if _, noGo := err.(*build.NoGoError); noGo { return nil } } pkgs = append(pkgs, name) return nil }) } return pkgs } func matchPackagesInFS(pattern string) []string { // Find directory to begin the scan. // Could be smarter but this one optimization // is enough for now, since ... is usually at the // end of a path. i := strings.Index(pattern, "...") dir, _ := path.Split(pattern[:i]) // pattern begins with ./ or ../. // path.Clean will discard the ./ but not the ../. // We need to preserve the ./ for pattern matching // and in the returned import paths. prefix := "" if strings.HasPrefix(pattern, "./") { prefix = "./" } match := matchPattern(pattern) var pkgs []string filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { if err != nil || !fi.IsDir() { return nil } if path == dir { // filepath.Walk starts at dir and recurses. For the recursive case, // the path is the result of filepath.Join, which calls filepath.Clean. // The initial case is not Cleaned, though, so we do this explicitly. // // This converts a path like "./io/" to "io". Without this step, running // "cd $GOROOT/src/pkg; go list ./io/..." would incorrectly skip the io // package, because prepending the prefix "./" to the unclean path would // result in "././io", and match("././io") returns false. path = filepath.Clean(path) } // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". _, elem := filepath.Split(path) dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { return filepath.SkipDir } name := prefix + filepath.ToSlash(path) if !match(name) { return nil } if _, err = build.ImportDir(path, 0); err != nil { if _, noGo := err.(*build.NoGoError); !noGo { log.Print(err) } return nil } pkgs = append(pkgs, name) return nil }) return pkgs } dots-1.0.0/resolve_test.go000066400000000000000000000106661501265361500155430ustar00rootroot00000000000000package dots import ( "path/filepath" "strings" "testing" ) func TestResolveNoArgs(t *testing.T) { result, err := Resolve([]string{}, []string{}) if err != nil { t.Fatal(err) } if len(result) != 0 { t.Errorf("Matched different number of files: got=%v, want=0", len(result)) } } func TestResolve(t *testing.T) { result, err := Resolve([]string{"fixtures/dummy/..."}, []string{"fixtures/dummy/foo", "fixtures/dummy/UNKNOWN"}) files := []string{ filepath.FromSlash("fixtures/dummy/bar/bar1.go"), filepath.FromSlash("fixtures/dummy/bar/bar2.go"), filepath.FromSlash("fixtures/dummy/baz/baz1.go"), filepath.FromSlash("fixtures/dummy/baz/baz2.go"), filepath.FromSlash("fixtures/dummy/baz/baz3.go"), } if err != nil { t.Fatal(err) } if len(result) != len(files) { t.Fatalf("Matched different number of files: got=%v, want=%v", len(result), len(files)) } for _, r := range result { matched := false for _, e := range files { matched = matched || strings.HasSuffix(r, e) } if !matched { t.Errorf("Not supposed to match: %v", r) } } } func TestResolveTests(t *testing.T) { result, err := Resolve([]string{"fixtures/withtests/..."}, []string{}) files := []string{ filepath.FromSlash("fixtures/withtests/gofile.go"), filepath.FromSlash("fixtures/withtests/testgo_test.go"), filepath.FromSlash("fixtures/withtests/xtestgo_test.go"), } if err != nil { t.Fatal(err) } if len(result) != len(files) { t.Fatalf("Matched different number of files: got=%v, want=%v", len(result), len(files)) } for _, r := range result { matched := false for _, e := range files { matched = matched || strings.HasSuffix(r, e) } if !matched { t.Errorf("Not supposed to match: %v", r) } } } func TestPackageResolve(t *testing.T) { result, err := Resolve([]string{"github.com/mgechev/dots"}, []string{"resolve_test.go"}) files := []string{ "resolve.go", } if err != nil { t.Fatal(err) } if len(result) != len(files) { t.Fatalf("Matched different number of files: got=%v, want=%v", len(result), len(files)) } for _, r := range result { matched := false for _, e := range files { matched = matched || strings.HasSuffix(r, e) } if !matched { t.Errorf("Not supposed to match: %v", r) } } } func TestSkipWildcard(t *testing.T) { result, err := Resolve([]string{"fixtures/dummy/"}, []string{"fixtures/dummy/..."}) files := map[string]bool{} if err != nil { t.Fatal(err) } if len(result) != len(files) { t.Fatalf("Matched different number of files: got=%v, want=%v", len(result), len(files)) } } func TestPackageWildcard(t *testing.T) { result, err := Resolve([]string{"github.com/mgechev/dots/fixtures/pkg/foo/...", "github.com/mgechev/dots/fixtures/pkg/baz"}, []string{}) files := []string{ "baz1.go", "baz2.go", } if err != nil { t.Fatal(err) } if len(result) != len(files) { t.Fatalf("Matched different number of files: got=%v, want=%v", len(result), len(files)) } for _, r := range result { matched := false for _, e := range files { matched = matched || strings.HasSuffix(r, e) } if !matched { t.Errorf("Not supposed to match: %v", r) } } } func TestPackageWildcardWithSkip(t *testing.T) { result, err := Resolve([]string{"github.com/mgechev/dots/fixtures/pkg/baz"}, []string{"github.com/mgechev/dots/fixtures/pkg/foo/..."}) files := []string{ "baz1.go", "baz2.go", } if err != nil { t.Fatal(err) } if len(result) != len(files) { t.Fatalf("Matched different number of files: got=%v, want=%v", len(result), len(files)) } for _, r := range result { matched := false for _, e := range files { matched = matched || strings.HasSuffix(r, e) } if !matched { t.Errorf("Not supposed to match: %v", r) } } } func TestComplainForMissingDirectories(t *testing.T) { _, err := Resolve([]string{"./fixturess"}, []string{}) if err == nil { t.Error("Should get an error") } } func TestComplainForMissingPackages(t *testing.T) { _, err := Resolve([]string{"github.com/mgechev/bazbaz"}, []string{}) if err == nil { t.Error("Should get an error") } } func TestResolvePackages(t *testing.T) { result, err := ResolvePackages([]string{"github.com/mgechev/dots/fixtures/pkg/foo/...", "github.com/mgechev/dots/fixtures/pkg/baz"}, []string{}) if err != nil { t.Fatal(err) } if len(result) != 1 { t.Fatalf("Matched different number of files: got=%v, want=%v", len(result), 1) } for _, pkg := range result { if len(pkg) == 0 { t.Error("Empty package") } } }