Source file src/os/stat_test.go

     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  
     5  package os_test
     6  
     7  import (
     8  	"errors"
     9  	"internal/testenv"
    10  	"io/fs"
    11  	"os"
    12  	"path/filepath"
    13  	"runtime"
    14  	"testing"
    15  )
    16  
    17  type testStatAndLstatParams struct {
    18  	isLink     bool
    19  	statCheck  func(*testing.T, string, fs.FileInfo)
    20  	lstatCheck func(*testing.T, string, fs.FileInfo)
    21  }
    22  
    23  // testStatAndLstat verifies that all os.Stat, os.Lstat os.File.Stat and os.Readdir work.
    24  func testStatAndLstat(t *testing.T, path string, params testStatAndLstatParams) {
    25  	// test os.Stat
    26  	sfi, err := os.Stat(path)
    27  	if err != nil {
    28  		t.Error(err)
    29  		return
    30  	}
    31  	params.statCheck(t, path, sfi)
    32  
    33  	// test os.Lstat
    34  	lsfi, err := os.Lstat(path)
    35  	if err != nil {
    36  		t.Error(err)
    37  		return
    38  	}
    39  	params.lstatCheck(t, path, lsfi)
    40  
    41  	if params.isLink {
    42  		if os.SameFile(sfi, lsfi) {
    43  			t.Errorf("stat and lstat of %q should not be the same", path)
    44  		}
    45  	} else {
    46  		if !os.SameFile(sfi, lsfi) {
    47  			t.Errorf("stat and lstat of %q should be the same", path)
    48  		}
    49  	}
    50  
    51  	// test os.File.Stat
    52  	f, err := os.Open(path)
    53  	if err != nil {
    54  		t.Error(err)
    55  		return
    56  	}
    57  	defer f.Close()
    58  
    59  	sfi2, err := f.Stat()
    60  	if err != nil {
    61  		t.Error(err)
    62  		return
    63  	}
    64  	params.statCheck(t, path, sfi2)
    65  
    66  	if !os.SameFile(sfi, sfi2) {
    67  		t.Errorf("stat of open %q file and stat of %q should be the same", path, path)
    68  	}
    69  
    70  	if params.isLink {
    71  		if os.SameFile(sfi2, lsfi) {
    72  			t.Errorf("stat of opened %q file and lstat of %q should not be the same", path, path)
    73  		}
    74  	} else {
    75  		if !os.SameFile(sfi2, lsfi) {
    76  			t.Errorf("stat of opened %q file and lstat of %q should be the same", path, path)
    77  		}
    78  	}
    79  
    80  	parentdir, base := filepath.Split(path)
    81  	if parentdir == "" || base == "" {
    82  		// skip os.Readdir test of files without directory or file name component,
    83  		// such as directories with slash at the end or Windows device names.
    84  		return
    85  	}
    86  
    87  	parent, err := os.Open(parentdir)
    88  	if err != nil {
    89  		t.Error(err)
    90  		return
    91  	}
    92  	defer parent.Close()
    93  
    94  	fis, err := parent.Readdir(-1)
    95  	if err != nil {
    96  		t.Error(err)
    97  		return
    98  	}
    99  	var lsfi2 fs.FileInfo
   100  	for _, fi2 := range fis {
   101  		if fi2.Name() == base {
   102  			lsfi2 = fi2
   103  			break
   104  		}
   105  	}
   106  	if lsfi2 == nil {
   107  		t.Errorf("failed to find %q in its parent", path)
   108  		return
   109  	}
   110  	params.lstatCheck(t, path, lsfi2)
   111  
   112  	if !os.SameFile(lsfi, lsfi2) {
   113  		t.Errorf("lstat of %q file in %q directory and %q should be the same", lsfi2.Name(), parentdir, path)
   114  	}
   115  }
   116  
   117  // testIsDir verifies that fi refers to directory.
   118  func testIsDir(t *testing.T, path string, fi fs.FileInfo) {
   119  	t.Helper()
   120  	if !fi.IsDir() {
   121  		t.Errorf("%q should be a directory", path)
   122  	}
   123  	if fi.Mode()&fs.ModeSymlink != 0 {
   124  		t.Errorf("%q should not be a symlink", path)
   125  	}
   126  }
   127  
   128  // testIsSymlink verifies that fi refers to symlink.
   129  func testIsSymlink(t *testing.T, path string, fi fs.FileInfo) {
   130  	t.Helper()
   131  	if fi.IsDir() {
   132  		t.Errorf("%q should not be a directory", path)
   133  	}
   134  	if fi.Mode()&fs.ModeSymlink == 0 {
   135  		t.Errorf("%q should be a symlink", path)
   136  	}
   137  }
   138  
   139  // testIsFile verifies that fi refers to file.
   140  func testIsFile(t *testing.T, path string, fi fs.FileInfo) {
   141  	t.Helper()
   142  	if fi.IsDir() {
   143  		t.Errorf("%q should not be a directory", path)
   144  	}
   145  	if fi.Mode()&fs.ModeSymlink != 0 {
   146  		t.Errorf("%q should not be a symlink", path)
   147  	}
   148  }
   149  
   150  func testDirStats(t *testing.T, path string) {
   151  	params := testStatAndLstatParams{
   152  		isLink:     false,
   153  		statCheck:  testIsDir,
   154  		lstatCheck: testIsDir,
   155  	}
   156  	testStatAndLstat(t, path, params)
   157  }
   158  
   159  func testFileStats(t *testing.T, path string) {
   160  	params := testStatAndLstatParams{
   161  		isLink:     false,
   162  		statCheck:  testIsFile,
   163  		lstatCheck: testIsFile,
   164  	}
   165  	testStatAndLstat(t, path, params)
   166  }
   167  
   168  func testSymlinkStats(t *testing.T, path string, isdir bool) {
   169  	params := testStatAndLstatParams{
   170  		isLink:     true,
   171  		lstatCheck: testIsSymlink,
   172  	}
   173  	if isdir {
   174  		params.statCheck = testIsDir
   175  	} else {
   176  		params.statCheck = testIsFile
   177  	}
   178  	testStatAndLstat(t, path, params)
   179  }
   180  
   181  func testSymlinkSameFile(t *testing.T, path, link string) {
   182  	pathfi, err := os.Stat(path)
   183  	if err != nil {
   184  		t.Error(err)
   185  		return
   186  	}
   187  
   188  	linkfi, err := os.Stat(link)
   189  	if err != nil {
   190  		t.Error(err)
   191  		return
   192  	}
   193  	if !os.SameFile(pathfi, linkfi) {
   194  		t.Errorf("os.Stat(%q) and os.Stat(%q) are not the same file", path, link)
   195  	}
   196  
   197  	linkfi, err = os.Lstat(link)
   198  	if err != nil {
   199  		t.Error(err)
   200  		return
   201  	}
   202  	if os.SameFile(pathfi, linkfi) {
   203  		t.Errorf("os.Stat(%q) and os.Lstat(%q) are the same file", path, link)
   204  	}
   205  }
   206  
   207  func testSymlinkSameFileOpen(t *testing.T, link string) {
   208  	f, err := os.Open(link)
   209  	if err != nil {
   210  		t.Error(err)
   211  		return
   212  	}
   213  	defer f.Close()
   214  
   215  	fi, err := f.Stat()
   216  	if err != nil {
   217  		t.Error(err)
   218  		return
   219  	}
   220  
   221  	fi2, err := os.Stat(link)
   222  	if err != nil {
   223  		t.Error(err)
   224  		return
   225  	}
   226  
   227  	if !os.SameFile(fi, fi2) {
   228  		t.Errorf("os.Open(%q).Stat() and os.Stat(%q) are not the same file", link, link)
   229  	}
   230  }
   231  
   232  func TestDirAndSymlinkStats(t *testing.T) {
   233  	testenv.MustHaveSymlink(t)
   234  	t.Parallel()
   235  
   236  	tmpdir := t.TempDir()
   237  	dir := filepath.Join(tmpdir, "dir")
   238  	if err := os.Mkdir(dir, 0777); err != nil {
   239  		t.Fatal(err)
   240  	}
   241  	testDirStats(t, dir)
   242  
   243  	dirlink := filepath.Join(tmpdir, "link")
   244  	if err := os.Symlink(dir, dirlink); err != nil {
   245  		t.Fatal(err)
   246  	}
   247  	testSymlinkStats(t, dirlink, true)
   248  	testSymlinkSameFile(t, dir, dirlink)
   249  	testSymlinkSameFileOpen(t, dirlink)
   250  
   251  	linklink := filepath.Join(tmpdir, "linklink")
   252  	if err := os.Symlink(dirlink, linklink); err != nil {
   253  		t.Fatal(err)
   254  	}
   255  	testSymlinkStats(t, linklink, true)
   256  	testSymlinkSameFile(t, dir, linklink)
   257  	testSymlinkSameFileOpen(t, linklink)
   258  }
   259  
   260  func TestFileAndSymlinkStats(t *testing.T) {
   261  	testenv.MustHaveSymlink(t)
   262  	t.Parallel()
   263  
   264  	tmpdir := t.TempDir()
   265  	file := filepath.Join(tmpdir, "file")
   266  	if err := os.WriteFile(file, []byte(""), 0644); err != nil {
   267  		t.Fatal(err)
   268  	}
   269  	testFileStats(t, file)
   270  
   271  	filelink := filepath.Join(tmpdir, "link")
   272  	if err := os.Symlink(file, filelink); err != nil {
   273  		t.Fatal(err)
   274  	}
   275  	testSymlinkStats(t, filelink, false)
   276  	testSymlinkSameFile(t, file, filelink)
   277  	testSymlinkSameFileOpen(t, filelink)
   278  
   279  	linklink := filepath.Join(tmpdir, "linklink")
   280  	if err := os.Symlink(filelink, linklink); err != nil {
   281  		t.Fatal(err)
   282  	}
   283  	testSymlinkStats(t, linklink, false)
   284  	testSymlinkSameFile(t, file, linklink)
   285  	testSymlinkSameFileOpen(t, linklink)
   286  }
   287  
   288  // see issue 27225 for details
   289  func TestSymlinkWithTrailingSlash(t *testing.T) {
   290  	testenv.MustHaveSymlink(t)
   291  	t.Parallel()
   292  
   293  	tmpdir := t.TempDir()
   294  	dir := filepath.Join(tmpdir, "dir")
   295  	if err := os.Mkdir(dir, 0777); err != nil {
   296  		t.Fatal(err)
   297  	}
   298  	dirlink := filepath.Join(tmpdir, "link")
   299  	if err := os.Symlink(dir, dirlink); err != nil {
   300  		t.Fatal(err)
   301  	}
   302  	dirlinkWithSlash := dirlink + string(os.PathSeparator)
   303  
   304  	testDirStats(t, dirlinkWithSlash)
   305  
   306  	fi1, err := os.Stat(dir)
   307  	if err != nil {
   308  		t.Error(err)
   309  		return
   310  	}
   311  	fi2, err := os.Stat(dirlinkWithSlash)
   312  	if err != nil {
   313  		t.Error(err)
   314  		return
   315  	}
   316  	if !os.SameFile(fi1, fi2) {
   317  		t.Errorf("os.Stat(%q) and os.Stat(%q) are not the same file", dir, dirlinkWithSlash)
   318  	}
   319  }
   320  
   321  func TestStatConsole(t *testing.T) {
   322  	if runtime.GOOS != "windows" {
   323  		t.Skip("skipping on non-Windows")
   324  	}
   325  	t.Parallel()
   326  	consoleNames := []string{
   327  		"CONIN$",
   328  		"CONOUT$",
   329  		"CON",
   330  	}
   331  	for _, name := range consoleNames {
   332  		params := testStatAndLstatParams{
   333  			isLink:     false,
   334  			statCheck:  testIsFile,
   335  			lstatCheck: testIsFile,
   336  		}
   337  		testStatAndLstat(t, name, params)
   338  		testStatAndLstat(t, `\\.\`+name, params)
   339  	}
   340  }
   341  
   342  func TestClosedStat(t *testing.T) {
   343  	// Historically we do not seem to match ErrClosed on non-Unix systems.
   344  	switch runtime.GOOS {
   345  	case "windows", "plan9":
   346  		t.Skipf("skipping on %s", runtime.GOOS)
   347  	}
   348  
   349  	t.Parallel()
   350  	f, err := os.Open("testdata/hello")
   351  	if err != nil {
   352  		t.Fatal(err)
   353  	}
   354  	if err := f.Close(); err != nil {
   355  		t.Fatal(err)
   356  	}
   357  	_, err = f.Stat()
   358  	if err == nil {
   359  		t.Error("Stat succeeded on closed File")
   360  	} else if !errors.Is(err, os.ErrClosed) {
   361  		t.Errorf("error from Stat on closed file did not match ErrClosed: %q, type %T", err, err)
   362  	}
   363  }
   364  

View as plain text