From 2b542361a4fc4b018c0770324a3b65d0393db1e0 Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Tue, 19 May 2020 00:33:09 -0400 Subject: [PATCH] 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 TryBot-Result: Gobot Gobot Reviewed-by: Michael Matloob --- internal/fastwalk/fastwalk_test.go | 61 ++++++++++++++++++------------ 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/internal/fastwalk/fastwalk_test.go b/internal/fastwalk/fastwalk_test.go index a6d9beaf53..d896aebc95 100644 --- a/internal/fastwalk/fastwalk_test.go +++ b/internal/fastwalk/fastwalk_test.go @@ -40,6 +40,8 @@ func testFastWalk(t *testing.T, files map[string]string, callback func(path stri 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 { @@ -47,7 +49,7 @@ func testFastWalk(t *testing.T, files map[string]string, callback func(path stri } var err error if strings.HasPrefix(contents, "LINK:") { - err = os.Symlink(strings.TrimPrefix(contents, "LINK:"), file) + symlinks[file] = filepath.FromSlash(strings.TrimPrefix(contents, "LINK:")) } else { 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) } } + + // 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 - 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() defer mu.Unlock() 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)) 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 return callback(path, typ) - }); err != nil { + }) + + if err != nil { t.Fatalf("callback returned: %v", err) } if !reflect.DeepEqual(got, want) { @@ -116,26 +135,25 @@ func TestFastWalk_LongFileName(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{ - "foo/foo.go": "one", - "bar/bar.go": "LINK:../foo.go", - "symdir": "LINK:foo", + "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, + "": 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, }) } @@ -195,11 +213,6 @@ func TestFastWalk_SkipFiles(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{ "foo/foo.go": "one", "bar/bar.go": "two",