--- /dev/null
+// Copyright 2018 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 gopathwalk
+
+import (
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "strings"
+ "sync"
+ "testing"
+)
+
+func TestShouldTraverse(t *testing.T) {
+ switch runtime.GOOS {
+ case "windows", "plan9":
+ t.Skipf("skipping symlink-requiring test on %s", runtime.GOOS)
+ }
+
+ dir, err := ioutil.TempDir("", "goimports-")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ // Note: mapToDir prepends "src" to each element, since
+ // mapToDir was made for creating GOPATHs.
+ if err := mapToDir(dir, map[string]string{
+ "foo/foo2/file.txt": "",
+ "foo/foo2/link-to-src": "LINK:../../",
+ "foo/foo2/link-to-src-foo": "LINK:../../foo",
+ "foo/foo2/link-to-dot": "LINK:.",
+ "bar/bar2/file.txt": "",
+ "bar/bar2/link-to-src-foo": "LINK:../../foo",
+
+ "a/b/c": "LINK:../../a/d",
+ "a/d/e": "LINK:../../a/b",
+ }); err != nil {
+ t.Fatal(err)
+ }
+ tests := []struct {
+ dir string
+ file string
+ want bool
+ }{
+ {
+ dir: "src/foo/foo2",
+ file: "link-to-src-foo",
+ want: false, // loop
+ },
+ {
+ dir: "src/foo/foo2",
+ file: "link-to-src",
+ want: false, // loop
+ },
+ {
+ dir: "src/foo/foo2",
+ file: "link-to-dot",
+ want: false, // loop
+ },
+ {
+ dir: "src/bar/bar2",
+ file: "link-to-src-foo",
+ want: true, // not a loop
+ },
+ {
+ dir: "src/a/b/c",
+ file: "e",
+ want: false, // loop: "e" is the same as "b".
+ },
+ }
+ for i, tt := range tests {
+ fi, err := os.Stat(filepath.Join(dir, tt.dir, tt.file))
+ if err != nil {
+ t.Errorf("%d. Stat = %v", i, err)
+ continue
+ }
+ var w walker
+ got := w.shouldTraverse(filepath.Join(dir, tt.dir), fi)
+ if got != tt.want {
+ t.Errorf("%d. shouldTraverse(%q, %q) = %v; want %v", i, tt.dir, tt.file, got, tt.want)
+ }
+ }
+}
+
+// TestSkip tests that various goimports rules are followed in non-modules mode.
+func TestSkip(t *testing.T) {
+ dir, err := ioutil.TempDir("", "goimports-")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ if err := mapToDir(dir, map[string]string{
+ "ignoreme/f.go": "package ignoreme", // ignored by .goimportsignore
+ "node_modules/f.go": "package nodemodules;", // ignored by hardcoded node_modules filter
+ "v/f.go": "package v;", // ignored by hardcoded vgo cache rule
+ "mod/f.go": "package mod;", // ignored by hardcoded vgo cache rule
+ "shouldfind/f.go": "package shouldfind;", // not ignored
+
+ ".goimportsignore": "ignoreme\n",
+ }); err != nil {
+ t.Fatal(err)
+ }
+
+ var found []string
+ var mu sync.Mutex
+ walkDir(Root{filepath.Join(dir, "src"), RootGOPATH},
+ func(root Root, dir string) {
+ mu.Lock()
+ defer mu.Unlock()
+ found = append(found, dir[len(root.Path)+1:])
+ }, func(root Root, dir string) bool {
+ return false
+ }, Options{ModulesEnabled: false, Logf: log.Printf})
+ if want := []string{"shouldfind"}; !reflect.DeepEqual(found, want) {
+ t.Errorf("expected to find only %v, got %v", want, found)
+ }
+}
+
+// TestSkipFunction tests that scan successfully skips directories from user callback.
+func TestSkipFunction(t *testing.T) {
+ dir, err := ioutil.TempDir("", "goimports-")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ if err := mapToDir(dir, map[string]string{
+ "ignoreme/f.go": "package ignoreme", // ignored by skip
+ "ignoreme/subignore/f.go": "package subignore", // also ignored by skip
+ "shouldfind/f.go": "package shouldfind;", // not ignored
+ }); err != nil {
+ t.Fatal(err)
+ }
+
+ var found []string
+ var mu sync.Mutex
+ walkDir(Root{filepath.Join(dir, "src"), RootGOPATH},
+ func(root Root, dir string) {
+ mu.Lock()
+ defer mu.Unlock()
+ found = append(found, dir[len(root.Path)+1:])
+ }, func(root Root, dir string) bool {
+ return strings.HasSuffix(dir, "ignoreme")
+ },
+ Options{ModulesEnabled: false})
+ if want := []string{"shouldfind"}; !reflect.DeepEqual(found, want) {
+ t.Errorf("expected to find only %v, got %v", want, found)
+ }
+}
+
+func mapToDir(destDir string, files map[string]string) error {
+ for path, contents := range files {
+ file := filepath.Join(destDir, "src", path)
+ if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
+ return err
+ }
+ var err error
+ if strings.HasPrefix(contents, "LINK:") {
+ err = os.Symlink(strings.TrimPrefix(contents, "LINK:"), file)
+ } else {
+ err = ioutil.WriteFile(file, []byte(contents), 0644)
+ }
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}