1
0
mirror of https://github.com/golang/go synced 2024-09-30 12:08:32 -06:00

internal/fastwalk: attempt Symlink tests on Windows

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>
This commit is contained in:
Bryan C. Mills 2020-05-19 00:33:09 -04:00
parent 91d71f6c2f
commit 2b542361a4

View File

@ -40,6 +40,8 @@ func testFastWalk(t *testing.T, files map[string]string, callback func(path stri
t.Fatal(err) t.Fatal(err)
} }
defer os.RemoveAll(tempdir) defer os.RemoveAll(tempdir)
symlinks := map[string]string{}
for path, contents := range files { for path, contents := range files {
file := filepath.Join(tempdir, "/src", path) file := filepath.Join(tempdir, "/src", path)
if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil { if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
@ -47,7 +49,7 @@ func testFastWalk(t *testing.T, files map[string]string, callback func(path stri
} }
var err error var err error
if strings.HasPrefix(contents, "LINK:") { if strings.HasPrefix(contents, "LINK:") {
err = os.Symlink(strings.TrimPrefix(contents, "LINK:"), file) symlinks[file] = filepath.FromSlash(strings.TrimPrefix(contents, "LINK:"))
} else { } else {
err = ioutil.WriteFile(file, []byte(contents), 0644) err = ioutil.WriteFile(file, []byte(contents), 0644)
} }
@ -55,21 +57,38 @@ func testFastWalk(t *testing.T, files map[string]string, callback func(path stri
t.Fatal(err) 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{} got := map[string]os.FileMode{}
var mu sync.Mutex var mu sync.Mutex
if err := fastwalk.Walk(tempdir, func(path string, typ os.FileMode) error { err = fastwalk.Walk(tempdir, func(path string, typ os.FileMode) error {
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
if !strings.HasPrefix(path, tempdir) { if !strings.HasPrefix(path, tempdir) {
t.Fatalf("bogus prefix on %q, expect %q", path, tempdir) t.Errorf("bogus prefix on %q, expect %q", path, tempdir)
} }
key := filepath.ToSlash(strings.TrimPrefix(path, tempdir)) key := filepath.ToSlash(strings.TrimPrefix(path, tempdir))
if old, dup := got[key]; dup { if old, dup := got[key]; dup {
t.Fatalf("callback called twice for key %q: %v -> %v", key, old, typ) t.Errorf("callback called twice for key %q: %v -> %v", key, old, typ)
} }
got[key] = typ got[key] = typ
return callback(path, typ) return callback(path, typ)
}); err != nil { })
if err != nil {
t.Fatalf("callback returned: %v", err) t.Fatalf("callback returned: %v", err)
} }
if !reflect.DeepEqual(got, want) { if !reflect.DeepEqual(got, want) {
@ -116,14 +135,11 @@ func TestFastWalk_LongFileName(t *testing.T) {
} }
func TestFastWalk_Symlink(t *testing.T) { func TestFastWalk_Symlink(t *testing.T) {
switch runtime.GOOS {
case "windows", "plan9":
t.Skipf("skipping on %s", runtime.GOOS)
}
testFastWalk(t, map[string]string{ testFastWalk(t, map[string]string{
"foo/foo.go": "one", "foo/foo.go": "one",
"bar/bar.go": "LINK:../foo.go", "bar/bar.go": "LINK:../foo/foo.go",
"symdir": "LINK:foo", "symdir": "LINK:foo",
"broken/broken.go": "LINK:../nonexistent",
}, },
func(path string, typ os.FileMode) error { func(path string, typ os.FileMode) error {
return nil return nil
@ -136,6 +152,8 @@ func TestFastWalk_Symlink(t *testing.T) {
"/src/foo": os.ModeDir, "/src/foo": os.ModeDir,
"/src/foo/foo.go": 0, "/src/foo/foo.go": 0,
"/src/symdir": os.ModeSymlink, "/src/symdir": os.ModeSymlink,
"/src/broken": os.ModeDir,
"/src/broken/broken.go": os.ModeSymlink,
}) })
} }
@ -195,11 +213,6 @@ func TestFastWalk_SkipFiles(t *testing.T) {
} }
func TestFastWalk_TraverseSymlink(t *testing.T) { func TestFastWalk_TraverseSymlink(t *testing.T) {
switch runtime.GOOS {
case "windows", "plan9":
t.Skipf("skipping on %s", runtime.GOOS)
}
testFastWalk(t, map[string]string{ testFastWalk(t, map[string]string{
"foo/foo.go": "one", "foo/foo.go": "one",
"bar/bar.go": "two", "bar/bar.go": "two",