| // 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 os_test | 
 |  | 
 | import ( | 
 | 	"internal/testenv" | 
 | 	"io/ioutil" | 
 | 	"os" | 
 | 	"path/filepath" | 
 | 	"runtime" | 
 | 	"testing" | 
 | ) | 
 |  | 
 | // testStatAndLstat verifies that all os.Stat, os.Lstat os.File.Stat and os.Readdir work. | 
 | func testStatAndLstat(t *testing.T, path string, isLink bool, statCheck, lstatCheck func(*testing.T, string, os.FileInfo)) { | 
 | 	// test os.Stat | 
 | 	sfi, err := os.Stat(path) | 
 | 	if err != nil { | 
 | 		t.Error(err) | 
 | 		return | 
 | 	} | 
 | 	statCheck(t, path, sfi) | 
 |  | 
 | 	// test os.Lstat | 
 | 	lsfi, err := os.Lstat(path) | 
 | 	if err != nil { | 
 | 		t.Error(err) | 
 | 		return | 
 | 	} | 
 | 	lstatCheck(t, path, lsfi) | 
 |  | 
 | 	if isLink { | 
 | 		if os.SameFile(sfi, lsfi) { | 
 | 			t.Errorf("stat and lstat of %q should not be the same", path) | 
 | 		} | 
 | 	} else { | 
 | 		if !os.SameFile(sfi, lsfi) { | 
 | 			t.Errorf("stat and lstat of %q should be the same", path) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// test os.File.Stat | 
 | 	f, err := os.Open(path) | 
 | 	if err != nil { | 
 | 		t.Error(err) | 
 | 		return | 
 | 	} | 
 | 	defer f.Close() | 
 |  | 
 | 	sfi2, err := f.Stat() | 
 | 	if err != nil { | 
 | 		t.Error(err) | 
 | 		return | 
 | 	} | 
 | 	statCheck(t, path, sfi2) | 
 |  | 
 | 	if !os.SameFile(sfi, sfi2) { | 
 | 		t.Errorf("stat of open %q file and stat of %q should be the same", path, path) | 
 | 	} | 
 |  | 
 | 	if isLink { | 
 | 		if os.SameFile(sfi2, lsfi) { | 
 | 			t.Errorf("stat of opened %q file and lstat of %q should not be the same", path, path) | 
 | 		} | 
 | 	} else { | 
 | 		if !os.SameFile(sfi2, lsfi) { | 
 | 			t.Errorf("stat of opened %q file and lstat of %q should be the same", path, path) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// test os.FileInfo returned by os.Readdir | 
 | 	if len(path) > 0 && os.IsPathSeparator(path[len(path)-1]) { | 
 | 		// skip os.Readdir test of directories with slash at the end | 
 | 		return | 
 | 	} | 
 | 	parentdir := filepath.Dir(path) | 
 | 	parent, err := os.Open(parentdir) | 
 | 	if err != nil { | 
 | 		t.Error(err) | 
 | 		return | 
 | 	} | 
 | 	defer parent.Close() | 
 |  | 
 | 	fis, err := parent.Readdir(-1) | 
 | 	if err != nil { | 
 | 		t.Error(err) | 
 | 		return | 
 | 	} | 
 | 	var lsfi2 os.FileInfo | 
 | 	base := filepath.Base(path) | 
 | 	for _, fi2 := range fis { | 
 | 		if fi2.Name() == base { | 
 | 			lsfi2 = fi2 | 
 | 			break | 
 | 		} | 
 | 	} | 
 | 	if lsfi2 == nil { | 
 | 		t.Errorf("failed to find %q in its parent", path) | 
 | 		return | 
 | 	} | 
 | 	lstatCheck(t, path, lsfi2) | 
 |  | 
 | 	if !os.SameFile(lsfi, lsfi2) { | 
 | 		t.Errorf("lstat of %q file in %q directory and %q should be the same", lsfi2.Name(), parentdir, path) | 
 | 	} | 
 | } | 
 |  | 
 | // testIsDir verifies that fi refers to directory. | 
 | func testIsDir(t *testing.T, path string, fi os.FileInfo) { | 
 | 	t.Helper() | 
 | 	if !fi.IsDir() { | 
 | 		t.Errorf("%q should be a directory", path) | 
 | 	} | 
 | 	if fi.Mode()&os.ModeSymlink != 0 { | 
 | 		t.Errorf("%q should not be a symlink", path) | 
 | 	} | 
 | } | 
 |  | 
 | // testIsSymlink verifies that fi refers to symlink. | 
 | func testIsSymlink(t *testing.T, path string, fi os.FileInfo) { | 
 | 	t.Helper() | 
 | 	if fi.IsDir() { | 
 | 		t.Errorf("%q should not be a directory", path) | 
 | 	} | 
 | 	if fi.Mode()&os.ModeSymlink == 0 { | 
 | 		t.Errorf("%q should be a symlink", path) | 
 | 	} | 
 | } | 
 |  | 
 | // testIsFile verifies that fi refers to file. | 
 | func testIsFile(t *testing.T, path string, fi os.FileInfo) { | 
 | 	t.Helper() | 
 | 	if fi.IsDir() { | 
 | 		t.Errorf("%q should not be a directory", path) | 
 | 	} | 
 | 	if fi.Mode()&os.ModeSymlink != 0 { | 
 | 		t.Errorf("%q should not be a symlink", path) | 
 | 	} | 
 | } | 
 |  | 
 | func testDirStats(t *testing.T, path string) { | 
 | 	testStatAndLstat(t, path, false, testIsDir, testIsDir) | 
 | } | 
 |  | 
 | func testFileStats(t *testing.T, path string) { | 
 | 	testStatAndLstat(t, path, false, testIsFile, testIsFile) | 
 | } | 
 |  | 
 | func testSymlinkStats(t *testing.T, path string, isdir bool) { | 
 | 	if isdir { | 
 | 		testStatAndLstat(t, path, true, testIsDir, testIsSymlink) | 
 | 	} else { | 
 | 		testStatAndLstat(t, path, true, testIsFile, testIsSymlink) | 
 | 	} | 
 | } | 
 |  | 
 | func testSymlinkSameFile(t *testing.T, path, link string) { | 
 | 	pathfi, err := os.Stat(path) | 
 | 	if err != nil { | 
 | 		t.Error(err) | 
 | 		return | 
 | 	} | 
 |  | 
 | 	linkfi, err := os.Stat(link) | 
 | 	if err != nil { | 
 | 		t.Error(err) | 
 | 		return | 
 | 	} | 
 | 	if !os.SameFile(pathfi, linkfi) { | 
 | 		t.Errorf("os.Stat(%q) and os.Stat(%q) are not the same file", path, link) | 
 | 	} | 
 |  | 
 | 	linkfi, err = os.Lstat(link) | 
 | 	if err != nil { | 
 | 		t.Error(err) | 
 | 		return | 
 | 	} | 
 | 	if os.SameFile(pathfi, linkfi) { | 
 | 		t.Errorf("os.Stat(%q) and os.Lstat(%q) are the same file", path, link) | 
 | 	} | 
 | } | 
 |  | 
 | func TestDirAndSymlinkStats(t *testing.T) { | 
 | 	testenv.MustHaveSymlink(t) | 
 |  | 
 | 	tmpdir, err := ioutil.TempDir("", "TestDirAndSymlinkStats") | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	defer os.RemoveAll(tmpdir) | 
 |  | 
 | 	dir := filepath.Join(tmpdir, "dir") | 
 | 	err = os.Mkdir(dir, 0777) | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	testDirStats(t, dir) | 
 |  | 
 | 	dirlink := filepath.Join(tmpdir, "link") | 
 | 	err = os.Symlink(dir, dirlink) | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	testSymlinkStats(t, dirlink, true) | 
 | 	testSymlinkSameFile(t, dir, dirlink) | 
 |  | 
 | 	linklink := filepath.Join(tmpdir, "linklink") | 
 | 	err = os.Symlink(dirlink, linklink) | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	testSymlinkStats(t, linklink, true) | 
 | 	testSymlinkSameFile(t, dir, linklink) | 
 | } | 
 |  | 
 | func TestFileAndSymlinkStats(t *testing.T) { | 
 | 	testenv.MustHaveSymlink(t) | 
 |  | 
 | 	tmpdir, err := ioutil.TempDir("", "TestFileAndSymlinkStats") | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	defer os.RemoveAll(tmpdir) | 
 |  | 
 | 	file := filepath.Join(tmpdir, "file") | 
 | 	err = ioutil.WriteFile(file, []byte(""), 0644) | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	testFileStats(t, file) | 
 |  | 
 | 	filelink := filepath.Join(tmpdir, "link") | 
 | 	err = os.Symlink(file, filelink) | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	testSymlinkStats(t, filelink, false) | 
 | 	testSymlinkSameFile(t, file, filelink) | 
 |  | 
 | 	linklink := filepath.Join(tmpdir, "linklink") | 
 | 	err = os.Symlink(filelink, linklink) | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	testSymlinkStats(t, linklink, false) | 
 | 	testSymlinkSameFile(t, file, linklink) | 
 | } | 
 |  | 
 | // see issue 27225 for details | 
 | func TestSymlinkWithTrailingSlash(t *testing.T) { | 
 | 	testenv.MustHaveSymlink(t) | 
 |  | 
 | 	tmpdir, err := ioutil.TempDir("", "TestSymlinkWithTrailingSlash") | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	defer os.RemoveAll(tmpdir) | 
 |  | 
 | 	dir := filepath.Join(tmpdir, "dir") | 
 | 	err = os.Mkdir(dir, 0777) | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	dirlink := filepath.Join(tmpdir, "link") | 
 | 	err = os.Symlink(dir, dirlink) | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	dirlinkWithSlash := dirlink + string(os.PathSeparator) | 
 |  | 
 | 	if runtime.GOOS == "windows" { | 
 | 		testSymlinkStats(t, dirlinkWithSlash, true) | 
 | 	} else { | 
 | 		testDirStats(t, dirlinkWithSlash) | 
 | 	} | 
 |  | 
 | 	fi1, err := os.Stat(dir) | 
 | 	if err != nil { | 
 | 		t.Error(err) | 
 | 		return | 
 | 	} | 
 | 	fi2, err := os.Stat(dirlinkWithSlash) | 
 | 	if err != nil { | 
 | 		t.Error(err) | 
 | 		return | 
 | 	} | 
 | 	if !os.SameFile(fi1, fi2) { | 
 | 		t.Errorf("os.Stat(%q) and os.Stat(%q) are not the same file", dir, dirlinkWithSlash) | 
 | 	} | 
 | } |