github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/internal/third_party/dep/fs/fs_test.go (about)

     1  /*
     2  Copyright (c) for portions of fs_test.go are held by The Go Authors, 2016 and are provided under
     3  the BSD license.
     4  
     5  Redistribution and use in source and binary forms, with or without
     6  modification, are permitted provided that the following conditions are
     7  met:
     8  
     9     * Redistributions of source code must retain the above copyright
    10  notice, this list of conditions and the following disclaimer.
    11     * Redistributions in binary form must reproduce the above
    12  copyright notice, this list of conditions and the following disclaimer
    13  in the documentation and/or other materials provided with the
    14  distribution.
    15     * Neither the name of Google Inc. nor the names of its
    16  contributors may be used to endorse or promote products derived from
    17  this software without specific prior written permission.
    18  
    19  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    20  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    21  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    22  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    23  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    24  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    25  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    26  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    27  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    28  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    29  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    30  */
    31  
    32  package fs
    33  
    34  import (
    35  	"io/ioutil"
    36  	"os"
    37  	"os/exec"
    38  	"path/filepath"
    39  	"runtime"
    40  	"sync"
    41  	"testing"
    42  )
    43  
    44  var (
    45  	mu sync.Mutex
    46  )
    47  
    48  func TestRenameWithFallback(t *testing.T) {
    49  	dir := t.TempDir()
    50  
    51  	if err := RenameWithFallback(filepath.Join(dir, "does_not_exists"), filepath.Join(dir, "dst")); err == nil {
    52  		t.Fatal("expected an error for non existing file, but got nil")
    53  	}
    54  
    55  	srcpath := filepath.Join(dir, "src")
    56  
    57  	if srcf, err := os.Create(srcpath); err != nil {
    58  		t.Fatal(err)
    59  	} else {
    60  		srcf.Close()
    61  	}
    62  
    63  	if err := RenameWithFallback(srcpath, filepath.Join(dir, "dst")); err != nil {
    64  		t.Fatal(err)
    65  	}
    66  
    67  	srcpath = filepath.Join(dir, "a")
    68  	if err := os.MkdirAll(srcpath, 0777); err != nil {
    69  		t.Fatal(err)
    70  	}
    71  
    72  	dstpath := filepath.Join(dir, "b")
    73  	if err := os.MkdirAll(dstpath, 0777); err != nil {
    74  		t.Fatal(err)
    75  	}
    76  
    77  	if err := RenameWithFallback(srcpath, dstpath); err == nil {
    78  		t.Fatal("expected an error if dst is an existing directory, but got nil")
    79  	}
    80  }
    81  
    82  func TestCopyDir(t *testing.T) {
    83  	dir := t.TempDir()
    84  
    85  	srcdir := filepath.Join(dir, "src")
    86  	if err := os.MkdirAll(srcdir, 0755); err != nil {
    87  		t.Fatal(err)
    88  	}
    89  
    90  	files := []struct {
    91  		path     string
    92  		contents string
    93  		fi       os.FileInfo
    94  	}{
    95  		{path: "myfile", contents: "hello world"},
    96  		{path: filepath.Join("subdir", "file"), contents: "subdir file"},
    97  	}
    98  
    99  	// Create structure indicated in 'files'
   100  	for i, file := range files {
   101  		fn := filepath.Join(srcdir, file.path)
   102  		dn := filepath.Dir(fn)
   103  		if err := os.MkdirAll(dn, 0755); err != nil {
   104  			t.Fatal(err)
   105  		}
   106  
   107  		fh, err := os.Create(fn)
   108  		if err != nil {
   109  			t.Fatal(err)
   110  		}
   111  
   112  		if _, err = fh.Write([]byte(file.contents)); err != nil {
   113  			t.Fatal(err)
   114  		}
   115  		fh.Close()
   116  
   117  		files[i].fi, err = os.Stat(fn)
   118  		if err != nil {
   119  			t.Fatal(err)
   120  		}
   121  	}
   122  
   123  	destdir := filepath.Join(dir, "dest")
   124  	if err := CopyDir(srcdir, destdir); err != nil {
   125  		t.Fatal(err)
   126  	}
   127  
   128  	// Compare copy against structure indicated in 'files'
   129  	for _, file := range files {
   130  		fn := filepath.Join(srcdir, file.path)
   131  		dn := filepath.Dir(fn)
   132  		dirOK, err := IsDir(dn)
   133  		if err != nil {
   134  			t.Fatal(err)
   135  		}
   136  		if !dirOK {
   137  			t.Fatalf("expected %s to be a directory", dn)
   138  		}
   139  
   140  		got, err := ioutil.ReadFile(fn)
   141  		if err != nil {
   142  			t.Fatal(err)
   143  		}
   144  
   145  		if file.contents != string(got) {
   146  			t.Fatalf("expected: %s, got: %s", file.contents, string(got))
   147  		}
   148  
   149  		gotinfo, err := os.Stat(fn)
   150  		if err != nil {
   151  			t.Fatal(err)
   152  		}
   153  
   154  		if file.fi.Mode() != gotinfo.Mode() {
   155  			t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v",
   156  				file.path, file.fi.Mode(), fn, gotinfo.Mode())
   157  		}
   158  	}
   159  }
   160  
   161  func TestCopyDirFail_SrcInaccessible(t *testing.T) {
   162  	if runtime.GOOS == "windows" {
   163  		// XXX: setting permissions works differently in
   164  		// Microsoft Windows. Skipping this until a
   165  		// compatible implementation is provided.
   166  		t.Skip("skipping on windows")
   167  	}
   168  
   169  	var currentUID = os.Getuid()
   170  
   171  	if currentUID == 0 {
   172  		// Skipping if root, because all files are accessible
   173  		t.Skip("Skipping for root user")
   174  	}
   175  
   176  	var srcdir, dstdir string
   177  
   178  	cleanup := setupInaccessibleDir(t, func(dir string) error {
   179  		srcdir = filepath.Join(dir, "src")
   180  		return os.MkdirAll(srcdir, 0755)
   181  	})
   182  	defer cleanup()
   183  
   184  	dir := t.TempDir()
   185  
   186  	dstdir = filepath.Join(dir, "dst")
   187  	if err := CopyDir(srcdir, dstdir); err == nil {
   188  		t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
   189  	}
   190  }
   191  
   192  func TestCopyDirFail_DstInaccessible(t *testing.T) {
   193  	if runtime.GOOS == "windows" {
   194  		// XXX: setting permissions works differently in
   195  		// Microsoft Windows. Skipping this until a
   196  		// compatible implementation is provided.
   197  		t.Skip("skipping on windows")
   198  	}
   199  
   200  	var currentUID = os.Getuid()
   201  
   202  	if currentUID == 0 {
   203  		// Skipping if root, because all files are accessible
   204  		t.Skip("Skipping for root user")
   205  	}
   206  
   207  	var srcdir, dstdir string
   208  
   209  	dir := t.TempDir()
   210  
   211  	srcdir = filepath.Join(dir, "src")
   212  	if err := os.MkdirAll(srcdir, 0755); err != nil {
   213  		t.Fatal(err)
   214  	}
   215  
   216  	cleanup := setupInaccessibleDir(t, func(dir string) error {
   217  		dstdir = filepath.Join(dir, "dst")
   218  		return nil
   219  	})
   220  	defer cleanup()
   221  
   222  	if err := CopyDir(srcdir, dstdir); err == nil {
   223  		t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
   224  	}
   225  }
   226  
   227  func TestCopyDirFail_SrcIsNotDir(t *testing.T) {
   228  	var srcdir, dstdir string
   229  	var err error
   230  
   231  	dir := t.TempDir()
   232  
   233  	srcdir = filepath.Join(dir, "src")
   234  	if _, err = os.Create(srcdir); err != nil {
   235  		t.Fatal(err)
   236  	}
   237  
   238  	dstdir = filepath.Join(dir, "dst")
   239  
   240  	if err = CopyDir(srcdir, dstdir); err == nil {
   241  		t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
   242  	}
   243  
   244  	if err != errSrcNotDir {
   245  		t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errSrcNotDir, srcdir, dstdir, err)
   246  	}
   247  
   248  }
   249  
   250  func TestCopyDirFail_DstExists(t *testing.T) {
   251  	var srcdir, dstdir string
   252  	var err error
   253  
   254  	dir := t.TempDir()
   255  
   256  	srcdir = filepath.Join(dir, "src")
   257  	if err = os.MkdirAll(srcdir, 0755); err != nil {
   258  		t.Fatal(err)
   259  	}
   260  
   261  	dstdir = filepath.Join(dir, "dst")
   262  	if err = os.MkdirAll(dstdir, 0755); err != nil {
   263  		t.Fatal(err)
   264  	}
   265  
   266  	if err = CopyDir(srcdir, dstdir); err == nil {
   267  		t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
   268  	}
   269  
   270  	if err != errDstExist {
   271  		t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errDstExist, srcdir, dstdir, err)
   272  	}
   273  }
   274  
   275  func TestCopyDirFailOpen(t *testing.T) {
   276  	if runtime.GOOS == "windows" {
   277  		// XXX: setting permissions works differently in
   278  		// Microsoft Windows. os.Chmod(..., 0222) below is not
   279  		// enough for the file to be readonly, and os.Chmod(...,
   280  		// 0000) returns an invalid argument error. Skipping
   281  		// this until a compatible implementation is
   282  		// provided.
   283  		t.Skip("skipping on windows")
   284  	}
   285  
   286  	var currentUID = os.Getuid()
   287  
   288  	if currentUID == 0 {
   289  		// Skipping if root, because all files are accessible
   290  		t.Skip("Skipping for root user")
   291  	}
   292  
   293  	var srcdir, dstdir string
   294  
   295  	dir := t.TempDir()
   296  
   297  	srcdir = filepath.Join(dir, "src")
   298  	if err := os.MkdirAll(srcdir, 0755); err != nil {
   299  		t.Fatal(err)
   300  	}
   301  
   302  	srcfn := filepath.Join(srcdir, "file")
   303  	srcf, err := os.Create(srcfn)
   304  	if err != nil {
   305  		t.Fatal(err)
   306  	}
   307  	srcf.Close()
   308  
   309  	// setup source file so that it cannot be read
   310  	if err = os.Chmod(srcfn, 0222); err != nil {
   311  		t.Fatal(err)
   312  	}
   313  
   314  	dstdir = filepath.Join(dir, "dst")
   315  
   316  	if err = CopyDir(srcdir, dstdir); err == nil {
   317  		t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
   318  	}
   319  }
   320  
   321  func TestCopyFile(t *testing.T) {
   322  	dir := t.TempDir()
   323  
   324  	srcf, err := os.Create(filepath.Join(dir, "srcfile"))
   325  	if err != nil {
   326  		t.Fatal(err)
   327  	}
   328  
   329  	want := "hello world"
   330  	if _, err := srcf.Write([]byte(want)); err != nil {
   331  		t.Fatal(err)
   332  	}
   333  	srcf.Close()
   334  
   335  	destf := filepath.Join(dir, "destf")
   336  	if err := copyFile(srcf.Name(), destf); err != nil {
   337  		t.Fatal(err)
   338  	}
   339  
   340  	got, err := ioutil.ReadFile(destf)
   341  	if err != nil {
   342  		t.Fatal(err)
   343  	}
   344  
   345  	if want != string(got) {
   346  		t.Fatalf("expected: %s, got: %s", want, string(got))
   347  	}
   348  
   349  	wantinfo, err := os.Stat(srcf.Name())
   350  	if err != nil {
   351  		t.Fatal(err)
   352  	}
   353  
   354  	gotinfo, err := os.Stat(destf)
   355  	if err != nil {
   356  		t.Fatal(err)
   357  	}
   358  
   359  	if wantinfo.Mode() != gotinfo.Mode() {
   360  		t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v", srcf.Name(), wantinfo.Mode(), destf, gotinfo.Mode())
   361  	}
   362  }
   363  
   364  func cleanUpDir(dir string) {
   365  	// NOTE(mattn): It seems that sometimes git.exe is not dead
   366  	// when cleanUpDir() is called. But we do not know any way to wait for it.
   367  	if runtime.GOOS == "windows" {
   368  		mu.Lock()
   369  		exec.Command(`taskkill`, `/F`, `/IM`, `git.exe`).Run()
   370  		mu.Unlock()
   371  	}
   372  	if dir != "" {
   373  		os.RemoveAll(dir)
   374  	}
   375  }
   376  
   377  func TestCopyFileSymlink(t *testing.T) {
   378  	tempdir := t.TempDir()
   379  
   380  	testcases := map[string]string{
   381  		filepath.Join("./testdata/symlinks/file-symlink"):         filepath.Join(tempdir, "dst-file"),
   382  		filepath.Join("./testdata/symlinks/windows-file-symlink"): filepath.Join(tempdir, "windows-dst-file"),
   383  		filepath.Join("./testdata/symlinks/invalid-symlink"):      filepath.Join(tempdir, "invalid-symlink"),
   384  	}
   385  
   386  	for symlink, dst := range testcases {
   387  		t.Run(symlink, func(t *testing.T) {
   388  			var err error
   389  			if err = copyFile(symlink, dst); err != nil {
   390  				t.Fatalf("failed to copy symlink: %s", err)
   391  			}
   392  
   393  			var want, got string
   394  
   395  			if runtime.GOOS == "windows" {
   396  				// Creating symlinks on Windows require an additional permission
   397  				// regular users aren't granted usually. So we copy the file
   398  				// content as a fall back instead of creating a real symlink.
   399  				srcb, err := ioutil.ReadFile(symlink)
   400  				if err != nil {
   401  					t.Fatalf("%+v", err)
   402  				}
   403  				dstb, err := ioutil.ReadFile(dst)
   404  				if err != nil {
   405  					t.Fatalf("%+v", err)
   406  				}
   407  
   408  				want = string(srcb)
   409  				got = string(dstb)
   410  			} else {
   411  				want, err = os.Readlink(symlink)
   412  				if err != nil {
   413  					t.Fatalf("%+v", err)
   414  				}
   415  
   416  				got, err = os.Readlink(dst)
   417  				if err != nil {
   418  					t.Fatalf("could not resolve symlink: %s", err)
   419  				}
   420  			}
   421  
   422  			if want != got {
   423  				t.Fatalf("resolved path is incorrect. expected %s, got %s", want, got)
   424  			}
   425  		})
   426  	}
   427  }
   428  
   429  func TestCopyFileFail(t *testing.T) {
   430  	if runtime.GOOS == "windows" {
   431  		// XXX: setting permissions works differently in
   432  		// Microsoft Windows. Skipping this until a
   433  		// compatible implementation is provided.
   434  		t.Skip("skipping on windows")
   435  	}
   436  
   437  	var currentUID = os.Getuid()
   438  
   439  	if currentUID == 0 {
   440  		// Skipping if root, because all files are accessible
   441  		t.Skip("Skipping for root user")
   442  	}
   443  
   444  	dir := t.TempDir()
   445  
   446  	srcf, err := os.Create(filepath.Join(dir, "srcfile"))
   447  	if err != nil {
   448  		t.Fatal(err)
   449  	}
   450  	srcf.Close()
   451  
   452  	var dstdir string
   453  
   454  	cleanup := setupInaccessibleDir(t, func(dir string) error {
   455  		dstdir = filepath.Join(dir, "dir")
   456  		return os.Mkdir(dstdir, 0777)
   457  	})
   458  	defer cleanup()
   459  
   460  	fn := filepath.Join(dstdir, "file")
   461  	if err := copyFile(srcf.Name(), fn); err == nil {
   462  		t.Fatalf("expected error for %s, got none", fn)
   463  	}
   464  }
   465  
   466  // setupInaccessibleDir creates a temporary location with a single
   467  // directory in it, in such a way that directory is not accessible
   468  // after this function returns.
   469  //
   470  // op is called with the directory as argument, so that it can create
   471  // files or other test artifacts.
   472  //
   473  // If setupInaccessibleDir fails in its preparation, or op fails, t.Fatal
   474  // will be invoked.
   475  //
   476  // This function returns a cleanup function that removes all the temporary
   477  // files this function creates. It is the caller's responsibility to call
   478  // this function before the test is done running, whether there's an error or not.
   479  func setupInaccessibleDir(t *testing.T, op func(dir string) error) func() {
   480  	dir := t.TempDir()
   481  
   482  	subdir := filepath.Join(dir, "dir")
   483  
   484  	cleanup := func() {
   485  		if err := os.Chmod(subdir, 0777); err != nil {
   486  			t.Error(err)
   487  		}
   488  	}
   489  
   490  	if err := os.Mkdir(subdir, 0777); err != nil {
   491  		cleanup()
   492  		t.Fatal(err)
   493  		return nil
   494  	}
   495  
   496  	if err := op(subdir); err != nil {
   497  		cleanup()
   498  		t.Fatal(err)
   499  		return nil
   500  	}
   501  
   502  	if err := os.Chmod(subdir, 0666); err != nil {
   503  		cleanup()
   504  		t.Fatal(err)
   505  		return nil
   506  	}
   507  
   508  	return cleanup
   509  }
   510  
   511  func TestIsDir(t *testing.T) {
   512  
   513  	var currentUID = os.Getuid()
   514  
   515  	if currentUID == 0 {
   516  		// Skipping if root, because all files are accessible
   517  		t.Skip("Skipping for root user")
   518  	}
   519  
   520  	wd, err := os.Getwd()
   521  	if err != nil {
   522  		t.Fatal(err)
   523  	}
   524  
   525  	var dn string
   526  
   527  	cleanup := setupInaccessibleDir(t, func(dir string) error {
   528  		dn = filepath.Join(dir, "dir")
   529  		return os.Mkdir(dn, 0777)
   530  	})
   531  	defer cleanup()
   532  
   533  	tests := map[string]struct {
   534  		exists bool
   535  		err    bool
   536  	}{
   537  		wd:                            {true, false},
   538  		filepath.Join(wd, "testdata"): {true, false},
   539  		filepath.Join(wd, "main.go"):  {false, true},
   540  		filepath.Join(wd, "this_file_does_not_exist.thing"): {false, true},
   541  		dn: {false, true},
   542  	}
   543  
   544  	if runtime.GOOS == "windows" {
   545  		// This test doesn't work on Microsoft Windows because
   546  		// of the differences in how file permissions are
   547  		// implemented. For this to work, the directory where
   548  		// the directory exists should be inaccessible.
   549  		delete(tests, dn)
   550  	}
   551  
   552  	for f, want := range tests {
   553  		got, err := IsDir(f)
   554  		if err != nil && !want.err {
   555  			t.Fatalf("expected no error, got %v", err)
   556  		}
   557  
   558  		if got != want.exists {
   559  			t.Fatalf("expected %t for %s, got %t", want.exists, f, got)
   560  		}
   561  	}
   562  }
   563  
   564  func TestIsSymlink(t *testing.T) {
   565  
   566  	var currentUID = os.Getuid()
   567  
   568  	if currentUID == 0 {
   569  		// Skipping if root, because all files are accessible
   570  		t.Skip("Skipping for root user")
   571  	}
   572  
   573  	dir := t.TempDir()
   574  
   575  	dirPath := filepath.Join(dir, "directory")
   576  	if err := os.MkdirAll(dirPath, 0777); err != nil {
   577  		t.Fatal(err)
   578  	}
   579  
   580  	filePath := filepath.Join(dir, "file")
   581  	f, err := os.Create(filePath)
   582  	if err != nil {
   583  		t.Fatal(err)
   584  	}
   585  	f.Close()
   586  
   587  	dirSymlink := filepath.Join(dir, "dirSymlink")
   588  	fileSymlink := filepath.Join(dir, "fileSymlink")
   589  
   590  	if err = os.Symlink(dirPath, dirSymlink); err != nil {
   591  		t.Fatal(err)
   592  	}
   593  	if err = os.Symlink(filePath, fileSymlink); err != nil {
   594  		t.Fatal(err)
   595  	}
   596  
   597  	var (
   598  		inaccessibleFile    string
   599  		inaccessibleSymlink string
   600  	)
   601  
   602  	cleanup := setupInaccessibleDir(t, func(dir string) error {
   603  		inaccessibleFile = filepath.Join(dir, "file")
   604  		if fh, err := os.Create(inaccessibleFile); err != nil {
   605  			return err
   606  		} else if err = fh.Close(); err != nil {
   607  			return err
   608  		}
   609  
   610  		inaccessibleSymlink = filepath.Join(dir, "symlink")
   611  		return os.Symlink(inaccessibleFile, inaccessibleSymlink)
   612  	})
   613  	defer cleanup()
   614  
   615  	tests := map[string]struct{ expected, err bool }{
   616  		dirPath:             {false, false},
   617  		filePath:            {false, false},
   618  		dirSymlink:          {true, false},
   619  		fileSymlink:         {true, false},
   620  		inaccessibleFile:    {false, true},
   621  		inaccessibleSymlink: {false, true},
   622  	}
   623  
   624  	if runtime.GOOS == "windows" {
   625  		// XXX: setting permissions works differently in Windows. Skipping
   626  		// these cases until a compatible implementation is provided.
   627  		delete(tests, inaccessibleFile)
   628  		delete(tests, inaccessibleSymlink)
   629  	}
   630  
   631  	for path, want := range tests {
   632  		got, err := IsSymlink(path)
   633  		if err != nil {
   634  			if !want.err {
   635  				t.Errorf("expected no error, got %v", err)
   636  			}
   637  		}
   638  
   639  		if got != want.expected {
   640  			t.Errorf("expected %t for %s, got %t", want.expected, path, got)
   641  		}
   642  	}
   643  }