1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package gopathwalk
6
7import (
8	"io/ioutil"
9	"os"
10	"path/filepath"
11	"reflect"
12	"runtime"
13	"strings"
14	"sync"
15	"testing"
16)
17
18func TestShouldTraverse(t *testing.T) {
19	switch runtime.GOOS {
20	case "windows", "plan9":
21		t.Skipf("skipping symlink-requiring test on %s", runtime.GOOS)
22	}
23
24	dir, err := ioutil.TempDir("", "goimports-")
25	if err != nil {
26		t.Fatal(err)
27	}
28	defer os.RemoveAll(dir)
29
30	// Note: mapToDir prepends "src" to each element, since
31	// mapToDir was made for creating GOPATHs.
32	if err := mapToDir(dir, map[string]string{
33		"foo/foo2/file.txt":        "",
34		"foo/foo2/link-to-src":     "LINK:../../",
35		"foo/foo2/link-to-src-foo": "LINK:../../foo",
36		"foo/foo2/link-to-dot":     "LINK:.",
37		"bar/bar2/file.txt":        "",
38		"bar/bar2/link-to-src-foo": "LINK:../../foo",
39
40		"a/b/c": "LINK:../../a/d",
41		"a/d/e": "LINK:../../a/b",
42	}); err != nil {
43		t.Fatal(err)
44	}
45	tests := []struct {
46		dir  string
47		file string
48		want bool
49	}{
50		{
51			dir:  "src/foo/foo2",
52			file: "link-to-src-foo",
53			want: false, // loop
54		},
55		{
56			dir:  "src/foo/foo2",
57			file: "link-to-src",
58			want: false, // loop
59		},
60		{
61			dir:  "src/foo/foo2",
62			file: "link-to-dot",
63			want: false, // loop
64		},
65		{
66			dir:  "src/bar/bar2",
67			file: "link-to-src-foo",
68			want: true, // not a loop
69		},
70		{
71			dir:  "src/a/b/c",
72			file: "e",
73			want: false, // loop: "e" is the same as "b".
74		},
75	}
76	for i, tt := range tests {
77		fi, err := os.Stat(filepath.Join(dir, tt.dir, tt.file))
78		if err != nil {
79			t.Errorf("%d. Stat = %v", i, err)
80			continue
81		}
82		var w walker
83		got := w.shouldTraverse(filepath.Join(dir, tt.dir), fi)
84		if got != tt.want {
85			t.Errorf("%d. shouldTraverse(%q, %q) = %v; want %v", i, tt.dir, tt.file, got, tt.want)
86		}
87	}
88}
89
90// TestSkip tests that various goimports rules are followed in non-modules mode.
91func TestSkip(t *testing.T) {
92	dir, err := ioutil.TempDir("", "goimports-")
93	if err != nil {
94		t.Fatal(err)
95	}
96	defer os.RemoveAll(dir)
97
98	if err := mapToDir(dir, map[string]string{
99		"ignoreme/f.go":     "package ignoreme",     // ignored by .goimportsignore
100		"node_modules/f.go": "package nodemodules;", // ignored by hardcoded node_modules filter
101		"v/f.go":            "package v;",           // ignored by hardcoded vgo cache rule
102		"mod/f.go":          "package mod;",         // ignored by hardcoded vgo cache rule
103		"shouldfind/f.go":   "package shouldfind;",  // not ignored
104
105		".goimportsignore": "ignoreme\n",
106	}); err != nil {
107		t.Fatal(err)
108	}
109
110	var found []string
111	var mu sync.Mutex
112	walkDir(Root{filepath.Join(dir, "src"), RootGOPATH},
113		func(root Root, dir string) {
114			mu.Lock()
115			defer mu.Unlock()
116			found = append(found, dir[len(root.Path)+1:])
117		}, func(root Root, dir string) bool {
118			return false
119		}, Options{ModulesEnabled: false, Debug: true})
120	if want := []string{"shouldfind"}; !reflect.DeepEqual(found, want) {
121		t.Errorf("expected to find only %v, got %v", want, found)
122	}
123}
124
125// TestSkipFunction tests that scan successfully skips directories from user callback.
126func TestSkipFunction(t *testing.T) {
127	dir, err := ioutil.TempDir("", "goimports-")
128	if err != nil {
129		t.Fatal(err)
130	}
131	defer os.RemoveAll(dir)
132
133	if err := mapToDir(dir, map[string]string{
134		"ignoreme/f.go":           "package ignoreme",    // ignored by skip
135		"ignoreme/subignore/f.go": "package subignore",   // also ignored by skip
136		"shouldfind/f.go":         "package shouldfind;", // not ignored
137	}); err != nil {
138		t.Fatal(err)
139	}
140
141	var found []string
142	var mu sync.Mutex
143	walkDir(Root{filepath.Join(dir, "src"), RootGOPATH},
144		func(root Root, dir string) {
145			mu.Lock()
146			defer mu.Unlock()
147			found = append(found, dir[len(root.Path)+1:])
148		}, func(root Root, dir string) bool {
149			return strings.HasSuffix(dir, "ignoreme")
150		},
151		Options{ModulesEnabled: false})
152	if want := []string{"shouldfind"}; !reflect.DeepEqual(found, want) {
153		t.Errorf("expected to find only %v, got %v", want, found)
154	}
155}
156
157func mapToDir(destDir string, files map[string]string) error {
158	for path, contents := range files {
159		file := filepath.Join(destDir, "src", path)
160		if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
161			return err
162		}
163		var err error
164		if strings.HasPrefix(contents, "LINK:") {
165			err = os.Symlink(strings.TrimPrefix(contents, "LINK:"), file)
166		} else {
167			err = ioutil.WriteFile(file, []byte(contents), 0644)
168		}
169		if err != nil {
170			return err
171		}
172	}
173	return nil
174}
175