mirror of
https://github.com/golang/go
synced 2024-11-18 14:44:41 -07:00
2b542361a4
Windows does actually support symlinks, but older versions of Windows only support symlinks when running as an administrator. Newer versions of Windows support symlinks for all users. Instead of skipping based on GOOS, first try the Symlink operation. If it succeeds, we can proceed with the test; otherwise, we can try to write a regular file to determine whether the problem was the symlink operation itself or the destination path. For golang/go#38772 Change-Id: Idaa9592011473de7f514b889859e420a84db6d01 Reviewed-on: https://go-review.googlesource.com/c/tools/+/234537 Run-TryBot: Bryan C. Mills <bcmills@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Michael Matloob <matloob@golang.org>
253 lines
6.5 KiB
Go
253 lines
6.5 KiB
Go
// Copyright 2016 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 fastwalk_test
|
|
|
|
import (
|
|
"bytes"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"runtime"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"golang.org/x/tools/internal/fastwalk"
|
|
)
|
|
|
|
func formatFileModes(m map[string]os.FileMode) string {
|
|
var keys []string
|
|
for k := range m {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
var buf bytes.Buffer
|
|
for _, k := range keys {
|
|
fmt.Fprintf(&buf, "%-20s: %v\n", k, m[k])
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func testFastWalk(t *testing.T, files map[string]string, callback func(path string, typ os.FileMode) error, want map[string]os.FileMode) {
|
|
tempdir, err := ioutil.TempDir("", "test-fast-walk")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tempdir)
|
|
|
|
symlinks := map[string]string{}
|
|
for path, contents := range files {
|
|
file := filepath.Join(tempdir, "/src", path)
|
|
if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var err error
|
|
if strings.HasPrefix(contents, "LINK:") {
|
|
symlinks[file] = filepath.FromSlash(strings.TrimPrefix(contents, "LINK:"))
|
|
} else {
|
|
err = ioutil.WriteFile(file, []byte(contents), 0644)
|
|
}
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// Create symlinks after all other files. Otherwise, directory symlinks on
|
|
// Windows are unusable (see https://golang.org/issue/39183).
|
|
for file, dst := range symlinks {
|
|
err = os.Symlink(dst, file)
|
|
if err != nil {
|
|
if writeErr := ioutil.WriteFile(file, []byte(dst), 0644); writeErr == nil {
|
|
// Couldn't create symlink, but could write the file.
|
|
// Probably this filesystem doesn't support symlinks.
|
|
// (Perhaps we are on an older Windows and not running as administrator.)
|
|
t.Skipf("skipping because symlinks appear to be unsupported: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
got := map[string]os.FileMode{}
|
|
var mu sync.Mutex
|
|
err = fastwalk.Walk(tempdir, func(path string, typ os.FileMode) error {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if !strings.HasPrefix(path, tempdir) {
|
|
t.Errorf("bogus prefix on %q, expect %q", path, tempdir)
|
|
}
|
|
key := filepath.ToSlash(strings.TrimPrefix(path, tempdir))
|
|
if old, dup := got[key]; dup {
|
|
t.Errorf("callback called twice for key %q: %v -> %v", key, old, typ)
|
|
}
|
|
got[key] = typ
|
|
return callback(path, typ)
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("callback returned: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("walk mismatch.\n got:\n%v\nwant:\n%v", formatFileModes(got), formatFileModes(want))
|
|
}
|
|
}
|
|
|
|
func TestFastWalk_Basic(t *testing.T) {
|
|
testFastWalk(t, map[string]string{
|
|
"foo/foo.go": "one",
|
|
"bar/bar.go": "two",
|
|
"skip/skip.go": "skip",
|
|
},
|
|
func(path string, typ os.FileMode) error {
|
|
return nil
|
|
},
|
|
map[string]os.FileMode{
|
|
"": os.ModeDir,
|
|
"/src": os.ModeDir,
|
|
"/src/bar": os.ModeDir,
|
|
"/src/bar/bar.go": 0,
|
|
"/src/foo": os.ModeDir,
|
|
"/src/foo/foo.go": 0,
|
|
"/src/skip": os.ModeDir,
|
|
"/src/skip/skip.go": 0,
|
|
})
|
|
}
|
|
|
|
func TestFastWalk_LongFileName(t *testing.T) {
|
|
longFileName := strings.Repeat("x", 255)
|
|
|
|
testFastWalk(t, map[string]string{
|
|
longFileName: "one",
|
|
},
|
|
func(path string, typ os.FileMode) error {
|
|
return nil
|
|
},
|
|
map[string]os.FileMode{
|
|
"": os.ModeDir,
|
|
"/src": os.ModeDir,
|
|
"/src/" + longFileName: 0,
|
|
},
|
|
)
|
|
}
|
|
|
|
func TestFastWalk_Symlink(t *testing.T) {
|
|
testFastWalk(t, map[string]string{
|
|
"foo/foo.go": "one",
|
|
"bar/bar.go": "LINK:../foo/foo.go",
|
|
"symdir": "LINK:foo",
|
|
"broken/broken.go": "LINK:../nonexistent",
|
|
},
|
|
func(path string, typ os.FileMode) error {
|
|
return nil
|
|
},
|
|
map[string]os.FileMode{
|
|
"": os.ModeDir,
|
|
"/src": os.ModeDir,
|
|
"/src/bar": os.ModeDir,
|
|
"/src/bar/bar.go": os.ModeSymlink,
|
|
"/src/foo": os.ModeDir,
|
|
"/src/foo/foo.go": 0,
|
|
"/src/symdir": os.ModeSymlink,
|
|
"/src/broken": os.ModeDir,
|
|
"/src/broken/broken.go": os.ModeSymlink,
|
|
})
|
|
}
|
|
|
|
func TestFastWalk_SkipDir(t *testing.T) {
|
|
testFastWalk(t, map[string]string{
|
|
"foo/foo.go": "one",
|
|
"bar/bar.go": "two",
|
|
"skip/skip.go": "skip",
|
|
},
|
|
func(path string, typ os.FileMode) error {
|
|
if typ == os.ModeDir && strings.HasSuffix(path, "skip") {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
},
|
|
map[string]os.FileMode{
|
|
"": os.ModeDir,
|
|
"/src": os.ModeDir,
|
|
"/src/bar": os.ModeDir,
|
|
"/src/bar/bar.go": 0,
|
|
"/src/foo": os.ModeDir,
|
|
"/src/foo/foo.go": 0,
|
|
"/src/skip": os.ModeDir,
|
|
})
|
|
}
|
|
|
|
func TestFastWalk_SkipFiles(t *testing.T) {
|
|
// Directory iteration order is undefined, so there's no way to know
|
|
// which file to expect until the walk happens. Rather than mess
|
|
// with the test infrastructure, just mutate want.
|
|
var mu sync.Mutex
|
|
want := map[string]os.FileMode{
|
|
"": os.ModeDir,
|
|
"/src": os.ModeDir,
|
|
"/src/zzz": os.ModeDir,
|
|
"/src/zzz/c.go": 0,
|
|
}
|
|
|
|
testFastWalk(t, map[string]string{
|
|
"a_skipfiles.go": "a",
|
|
"b_skipfiles.go": "b",
|
|
"zzz/c.go": "c",
|
|
},
|
|
func(path string, typ os.FileMode) error {
|
|
if strings.HasSuffix(path, "_skipfiles.go") {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
want["/src/"+filepath.Base(path)] = 0
|
|
return fastwalk.ErrSkipFiles
|
|
}
|
|
return nil
|
|
},
|
|
want)
|
|
if len(want) != 5 {
|
|
t.Errorf("saw too many files: wanted 5, got %v (%v)", len(want), want)
|
|
}
|
|
}
|
|
|
|
func TestFastWalk_TraverseSymlink(t *testing.T) {
|
|
testFastWalk(t, map[string]string{
|
|
"foo/foo.go": "one",
|
|
"bar/bar.go": "two",
|
|
"skip/skip.go": "skip",
|
|
"symdir": "LINK:foo",
|
|
},
|
|
func(path string, typ os.FileMode) error {
|
|
if typ == os.ModeSymlink {
|
|
return fastwalk.ErrTraverseLink
|
|
}
|
|
return nil
|
|
},
|
|
map[string]os.FileMode{
|
|
"": os.ModeDir,
|
|
"/src": os.ModeDir,
|
|
"/src/bar": os.ModeDir,
|
|
"/src/bar/bar.go": 0,
|
|
"/src/foo": os.ModeDir,
|
|
"/src/foo/foo.go": 0,
|
|
"/src/skip": os.ModeDir,
|
|
"/src/skip/skip.go": 0,
|
|
"/src/symdir": os.ModeSymlink,
|
|
"/src/symdir/foo.go": 0,
|
|
})
|
|
}
|
|
|
|
var benchDir = flag.String("benchdir", runtime.GOROOT(), "The directory to scan for BenchmarkFastWalk")
|
|
|
|
func BenchmarkFastWalk(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
err := fastwalk.Walk(*benchDir, func(path string, typ os.FileMode) error { return nil })
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|