github.com/golang/dep@v0.5.4/internal/fs/fs_test.go (about)

     1  // Copyright 2016 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 fs
     6  
     7  import (
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"reflect"
    12  	"runtime"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/golang/dep/internal/test"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  // This function tests HadFilepathPrefix. It should test it on both case
    21  // sensitive and insensitive situations. However, the only reliable way to test
    22  // case-insensitive behaviour is if using case-insensitive filesystem.  This
    23  // cannot be guaranteed in an automated test. Therefore, the behaviour of the
    24  // tests is not to test case sensitivity on *nix and to assume that Windows is
    25  // case-insensitive. Please see link below for some background.
    26  //
    27  // https://superuser.com/questions/266110/how-do-you-make-windows-7-fully-case-sensitive-with-respect-to-the-filesystem
    28  //
    29  // NOTE: NTFS can be made case-sensitive. However many Windows programs,
    30  // including Windows Explorer do not handle gracefully multiple files that
    31  // differ only in capitalization. It is possible that this can cause these tests
    32  // to fail on some setups.
    33  func TestHasFilepathPrefix(t *testing.T) {
    34  	dir, err := ioutil.TempDir("", "dep")
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	defer os.RemoveAll(dir)
    39  
    40  	// dir2 is the same as dir but with different capitalization on Windows to
    41  	// test case insensitivity
    42  	var dir2 string
    43  	if runtime.GOOS == "windows" {
    44  		dir = strings.ToLower(dir)
    45  		dir2 = strings.ToUpper(dir)
    46  	} else {
    47  		dir2 = dir
    48  	}
    49  
    50  	// For testing trailing and repeated separators
    51  	sep := string(os.PathSeparator)
    52  
    53  	cases := []struct {
    54  		path   string
    55  		prefix string
    56  		want   bool
    57  	}{
    58  		{filepath.Join(dir, "a", "b"), filepath.Join(dir2), true},
    59  		{filepath.Join(dir, "a", "b"), dir2 + sep + sep + "a", true},
    60  		{filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a") + sep, true},
    61  		{filepath.Join(dir, "a", "b") + sep, filepath.Join(dir2), true},
    62  		{dir + sep + sep + filepath.Join("a", "b"), filepath.Join(dir2, "a"), true},
    63  		{filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a"), true},
    64  		{filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a", "b"), true},
    65  		{filepath.Join(dir, "a", "b"), filepath.Join(dir2, "c"), false},
    66  		{filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a", "d", "b"), false},
    67  		{filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a", "b2"), false},
    68  		{filepath.Join(dir), filepath.Join(dir2, "a", "b"), false},
    69  		{filepath.Join(dir, "ab"), filepath.Join(dir2, "a", "b"), false},
    70  		{filepath.Join(dir, "ab"), filepath.Join(dir2, "a"), false},
    71  		{filepath.Join(dir, "123"), filepath.Join(dir2, "123"), true},
    72  		{filepath.Join(dir, "123"), filepath.Join(dir2, "1"), false},
    73  		{filepath.Join(dir, "⌘"), filepath.Join(dir2, "⌘"), true},
    74  		{filepath.Join(dir, "a"), filepath.Join(dir2, "⌘"), false},
    75  		{filepath.Join(dir, "⌘"), filepath.Join(dir2, "a"), false},
    76  	}
    77  
    78  	for _, c := range cases {
    79  		if err := os.MkdirAll(c.path, 0755); err != nil {
    80  			t.Fatal(err)
    81  		}
    82  
    83  		if err = os.MkdirAll(c.prefix, 0755); err != nil {
    84  			t.Fatal(err)
    85  		}
    86  
    87  		got, err := HasFilepathPrefix(c.path, c.prefix)
    88  		if err != nil {
    89  			t.Fatalf("unexpected error: %s", err)
    90  		}
    91  		if c.want != got {
    92  			t.Fatalf("dir: %q, prefix: %q, expected: %v, got: %v", c.path, c.prefix, c.want, got)
    93  		}
    94  	}
    95  }
    96  
    97  // This function tests HadFilepathPrefix. It should test it on both case
    98  // sensitive and insensitive situations. However, the only reliable way to test
    99  // case-insensitive behaviour is if using case-insensitive filesystem.  This
   100  // cannot be guaranteed in an automated test. Therefore, the behaviour of the
   101  // tests is not to test case sensitivity on *nix and to assume that Windows is
   102  // case-insensitive. Please see link below for some background.
   103  //
   104  // https://superuser.com/questions/266110/how-do-you-make-windows-7-fully-case-sensitive-with-respect-to-the-filesystem
   105  //
   106  // NOTE: NTFS can be made case-sensitive. However many Windows programs,
   107  // including Windows Explorer do not handle gracefully multiple files that
   108  // differ only in capitalization. It is possible that this can cause these tests
   109  // to fail on some setups.
   110  func TestHasFilepathPrefix_Files(t *testing.T) {
   111  	dir, err := ioutil.TempDir("", "dep")
   112  	if err != nil {
   113  		t.Fatal(err)
   114  	}
   115  	defer os.RemoveAll(dir)
   116  
   117  	// dir2 is the same as dir but with different capitalization on Windows to
   118  	// test case insensitivity
   119  	var dir2 string
   120  	if runtime.GOOS == "windows" {
   121  		dir = strings.ToLower(dir)
   122  		dir2 = strings.ToUpper(dir)
   123  	} else {
   124  		dir2 = dir
   125  	}
   126  
   127  	existingFile := filepath.Join(dir, "exists")
   128  	if err = os.MkdirAll(existingFile, 0755); err != nil {
   129  		t.Fatal(err)
   130  	}
   131  
   132  	nonExistingFile := filepath.Join(dir, "does_not_exists")
   133  
   134  	cases := []struct {
   135  		path   string
   136  		prefix string
   137  		want   bool
   138  		err    bool
   139  	}{
   140  		{existingFile, filepath.Join(dir2), true, false},
   141  		{nonExistingFile, filepath.Join(dir2), false, true},
   142  	}
   143  
   144  	for _, c := range cases {
   145  		got, err := HasFilepathPrefix(c.path, c.prefix)
   146  		if err != nil && !c.err {
   147  			t.Fatalf("unexpected error: %s", err)
   148  		}
   149  		if c.want != got {
   150  			t.Fatalf("dir: %q, prefix: %q, expected: %v, got: %v", c.path, c.prefix, c.want, got)
   151  		}
   152  	}
   153  }
   154  
   155  func TestEquivalentPaths(t *testing.T) {
   156  	h := test.NewHelper(t)
   157  	h.TempDir("dir")
   158  	h.TempDir("dir2")
   159  
   160  	h.TempFile("file", "")
   161  	h.TempFile("file2", "")
   162  
   163  	h.TempDir("DIR")
   164  	h.TempFile("FILE", "")
   165  
   166  	testcases := []struct {
   167  		p1, p2                   string
   168  		caseSensitiveEquivalent  bool
   169  		caseInensitiveEquivalent bool
   170  		err                      bool
   171  	}{
   172  		{h.Path("dir"), h.Path("dir"), true, true, false},
   173  		{h.Path("file"), h.Path("file"), true, true, false},
   174  		{h.Path("dir"), h.Path("dir2"), false, false, false},
   175  		{h.Path("file"), h.Path("file2"), false, false, false},
   176  		{h.Path("dir"), h.Path("file"), false, false, false},
   177  		{h.Path("dir"), h.Path("DIR"), false, true, false},
   178  		{strings.ToLower(h.Path("dir")), strings.ToUpper(h.Path("dir")), false, true, true},
   179  	}
   180  
   181  	caseSensitive, err := IsCaseSensitiveFilesystem(h.Path("dir"))
   182  	if err != nil {
   183  		t.Fatal("unexpcted error:", err)
   184  	}
   185  
   186  	for _, tc := range testcases {
   187  		got, err := EquivalentPaths(tc.p1, tc.p2)
   188  		if err != nil && !tc.err {
   189  			t.Error("unexpected error:", err)
   190  		}
   191  		if caseSensitive {
   192  			if tc.caseSensitiveEquivalent != got {
   193  				t.Errorf("expected EquivalentPaths(%q, %q) to be %t on case-sensitive filesystem, got %t", tc.p1, tc.p2, tc.caseSensitiveEquivalent, got)
   194  			}
   195  		} else {
   196  			if tc.caseInensitiveEquivalent != got {
   197  				t.Errorf("expected EquivalentPaths(%q, %q) to be %t on case-insensitive filesystem, got %t", tc.p1, tc.p2, tc.caseInensitiveEquivalent, got)
   198  			}
   199  		}
   200  	}
   201  }
   202  
   203  func TestRenameWithFallback(t *testing.T) {
   204  	dir, err := ioutil.TempDir("", "dep")
   205  	if err != nil {
   206  		t.Fatal(err)
   207  	}
   208  	defer os.RemoveAll(dir)
   209  
   210  	if err = RenameWithFallback(filepath.Join(dir, "does_not_exists"), filepath.Join(dir, "dst")); err == nil {
   211  		t.Fatal("expected an error for non existing file, but got nil")
   212  	}
   213  
   214  	srcpath := filepath.Join(dir, "src")
   215  
   216  	if srcf, err := os.Create(srcpath); err != nil {
   217  		t.Fatal(err)
   218  	} else {
   219  		srcf.Close()
   220  	}
   221  
   222  	if err = RenameWithFallback(srcpath, filepath.Join(dir, "dst")); err != nil {
   223  		t.Fatal(err)
   224  	}
   225  
   226  	srcpath = filepath.Join(dir, "a")
   227  	if err = os.MkdirAll(srcpath, 0777); err != nil {
   228  		t.Fatal(err)
   229  	}
   230  
   231  	dstpath := filepath.Join(dir, "b")
   232  	if err = os.MkdirAll(dstpath, 0777); err != nil {
   233  		t.Fatal(err)
   234  	}
   235  
   236  	if err = RenameWithFallback(srcpath, dstpath); err == nil {
   237  		t.Fatal("expected an error if dst is an existing directory, but got nil")
   238  	}
   239  }
   240  
   241  func TestIsCaseSensitiveFilesystem(t *testing.T) {
   242  	isLinux := runtime.GOOS == "linux"
   243  	isWindows := runtime.GOOS == "windows"
   244  	isMacOS := runtime.GOOS == "darwin"
   245  
   246  	if !isLinux && !isWindows && !isMacOS {
   247  		t.Skip("Run this test on Windows, Linux and macOS only")
   248  	}
   249  
   250  	dir, err := ioutil.TempDir("", "TestCaseSensitivity")
   251  	if err != nil {
   252  		t.Fatal(err)
   253  	}
   254  	defer os.RemoveAll(dir)
   255  
   256  	var want bool
   257  	if isLinux {
   258  		want = true
   259  	} else {
   260  		want = false
   261  	}
   262  
   263  	got, err := IsCaseSensitiveFilesystem(dir)
   264  
   265  	if err != nil {
   266  		t.Fatalf("unexpected error message: \n\t(GOT) %+v", err)
   267  	}
   268  
   269  	if want != got {
   270  		t.Fatalf("unexpected value returned: \n\t(GOT) %t\n\t(WNT) %t", got, want)
   271  	}
   272  }
   273  
   274  func TestReadActualFilenames(t *testing.T) {
   275  	// We are trying to skip this test on file systems which are case-sensiive. We could
   276  	// have used `fs.IsCaseSensitiveFilesystem` for this check. However, the code we are
   277  	// testing also relies on `fs.IsCaseSensitiveFilesystem`. So a bug in
   278  	// `fs.IsCaseSensitiveFilesystem` could prevent this test from being run. This is the
   279  	// only scenario where we prefer the OS heuristic over doing the actual work of
   280  	// validating filesystem case sensitivity via `fs.IsCaseSensitiveFilesystem`.
   281  	if runtime.GOOS != "windows" && runtime.GOOS != "darwin" {
   282  		t.Skip("skip this test on non-Windows, non-macOS")
   283  	}
   284  
   285  	h := test.NewHelper(t)
   286  	defer h.Cleanup()
   287  
   288  	h.TempDir("")
   289  	tmpPath := h.Path(".")
   290  
   291  	// First, check the scenarios for which we expect an error.
   292  	_, err := ReadActualFilenames(filepath.Join(tmpPath, "does_not_exists"), []string{""})
   293  	switch {
   294  	case err == nil:
   295  		t.Fatal("expected err for non-existing folder")
   296  	// use `errors.Cause` because the error is wrapped and returned
   297  	case !os.IsNotExist(errors.Cause(err)):
   298  		t.Fatalf("unexpected error: %+v", err)
   299  	}
   300  	h.TempFile("tmpFile", "")
   301  	_, err = ReadActualFilenames(h.Path("tmpFile"), []string{""})
   302  	switch {
   303  	case err == nil:
   304  		t.Fatal("expected err for passing file instead of directory")
   305  	case err != errPathNotDir:
   306  		t.Fatalf("unexpected error: %+v", err)
   307  	}
   308  
   309  	cases := []struct {
   310  		createFiles []string
   311  		names       []string
   312  		want        map[string]string
   313  	}{
   314  		// If we supply no filenames to the function, it should return an empty map.
   315  		{nil, nil, map[string]string{}},
   316  		// If the directory contains the given file with different case, it should return
   317  		// a map which has the given filename as the key and actual filename as the value.
   318  		{
   319  			[]string{"test1.txt"},
   320  			[]string{"Test1.txt"},
   321  			map[string]string{"Test1.txt": "test1.txt"},
   322  		},
   323  		// 1. If the given filename is same as the actual filename, map should have the
   324  		//    same key and value for the file.
   325  		// 2. If the given filename is present with different case for file extension,
   326  		//    it should return a map which has the given filename as the key and actual
   327  		//    filename as the value.
   328  		// 3. If the given filename is not present even with a different case, the map
   329  		//    returned should not have an entry for that filename.
   330  		{
   331  			[]string{"test2.txt", "test3.TXT"},
   332  			[]string{"test2.txt", "Test3.txt", "Test4.txt"},
   333  			map[string]string{
   334  				"test2.txt": "test2.txt",
   335  				"Test3.txt": "test3.TXT",
   336  			},
   337  		},
   338  	}
   339  	for _, c := range cases {
   340  		for _, file := range c.createFiles {
   341  			h.TempFile(file, "")
   342  		}
   343  		got, err := ReadActualFilenames(tmpPath, c.names)
   344  		if err != nil {
   345  			t.Fatalf("unexpected error: %+v", err)
   346  		}
   347  		if !reflect.DeepEqual(c.want, got) {
   348  			t.Fatalf("returned value does not match expected: \n\t(GOT) %v\n\t(WNT) %v",
   349  				got, c.want)
   350  		}
   351  	}
   352  }
   353  
   354  func TestGenTestFilename(t *testing.T) {
   355  	cases := []struct {
   356  		str  string
   357  		want string
   358  	}{
   359  		{"abc", "Abc"},
   360  		{"ABC", "aBC"},
   361  		{"AbC", "abC"},
   362  		{"αβγ", "Αβγ"},
   363  		{"123", "123"},
   364  		{"1a2", "1A2"},
   365  		{"12a", "12A"},
   366  		{"⌘", "⌘"},
   367  	}
   368  
   369  	for _, c := range cases {
   370  		got := genTestFilename(c.str)
   371  		if c.want != got {
   372  			t.Fatalf("str: %q, expected: %q, got: %q", c.str, c.want, got)
   373  		}
   374  	}
   375  }
   376  
   377  func BenchmarkGenTestFilename(b *testing.B) {
   378  	cases := []string{
   379  		strings.Repeat("a", 128),
   380  		strings.Repeat("A", 128),
   381  		strings.Repeat("α", 128),
   382  		strings.Repeat("1", 128),
   383  		strings.Repeat("⌘", 128),
   384  	}
   385  
   386  	for i := 0; i < b.N; i++ {
   387  		for _, str := range cases {
   388  			genTestFilename(str)
   389  		}
   390  	}
   391  }
   392  
   393  func TestCopyDir(t *testing.T) {
   394  	dir, err := ioutil.TempDir("", "dep")
   395  	if err != nil {
   396  		t.Fatal(err)
   397  	}
   398  	defer os.RemoveAll(dir)
   399  
   400  	srcdir := filepath.Join(dir, "src")
   401  	if err := os.MkdirAll(srcdir, 0755); err != nil {
   402  		t.Fatal(err)
   403  	}
   404  
   405  	files := []struct {
   406  		path     string
   407  		contents string
   408  		fi       os.FileInfo
   409  	}{
   410  		{path: "myfile", contents: "hello world"},
   411  		{path: filepath.Join("subdir", "file"), contents: "subdir file"},
   412  	}
   413  
   414  	// Create structure indicated in 'files'
   415  	for i, file := range files {
   416  		fn := filepath.Join(srcdir, file.path)
   417  		dn := filepath.Dir(fn)
   418  		if err = os.MkdirAll(dn, 0755); err != nil {
   419  			t.Fatal(err)
   420  		}
   421  
   422  		fh, err := os.Create(fn)
   423  		if err != nil {
   424  			t.Fatal(err)
   425  		}
   426  
   427  		if _, err = fh.Write([]byte(file.contents)); err != nil {
   428  			t.Fatal(err)
   429  		}
   430  		fh.Close()
   431  
   432  		files[i].fi, err = os.Stat(fn)
   433  		if err != nil {
   434  			t.Fatal(err)
   435  		}
   436  	}
   437  
   438  	destdir := filepath.Join(dir, "dest")
   439  	if err := CopyDir(srcdir, destdir); err != nil {
   440  		t.Fatal(err)
   441  	}
   442  
   443  	// Compare copy against structure indicated in 'files'
   444  	for _, file := range files {
   445  		fn := filepath.Join(srcdir, file.path)
   446  		dn := filepath.Dir(fn)
   447  		dirOK, err := IsDir(dn)
   448  		if err != nil {
   449  			t.Fatal(err)
   450  		}
   451  		if !dirOK {
   452  			t.Fatalf("expected %s to be a directory", dn)
   453  		}
   454  
   455  		got, err := ioutil.ReadFile(fn)
   456  		if err != nil {
   457  			t.Fatal(err)
   458  		}
   459  
   460  		if file.contents != string(got) {
   461  			t.Fatalf("expected: %s, got: %s", file.contents, string(got))
   462  		}
   463  
   464  		gotinfo, err := os.Stat(fn)
   465  		if err != nil {
   466  			t.Fatal(err)
   467  		}
   468  
   469  		if file.fi.Mode() != gotinfo.Mode() {
   470  			t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v",
   471  				file.path, file.fi.Mode(), fn, gotinfo.Mode())
   472  		}
   473  	}
   474  }
   475  
   476  func TestCopyDirFail_SrcInaccessible(t *testing.T) {
   477  	if runtime.GOOS == "windows" {
   478  		// XXX: setting permissions works differently in
   479  		// Microsoft Windows. Skipping this this until a
   480  		// compatible implementation is provided.
   481  		t.Skip("skipping on windows")
   482  	}
   483  
   484  	var srcdir, dstdir string
   485  
   486  	cleanup := setupInaccessibleDir(t, func(dir string) error {
   487  		srcdir = filepath.Join(dir, "src")
   488  		return os.MkdirAll(srcdir, 0755)
   489  	})
   490  	defer cleanup()
   491  
   492  	dir, err := ioutil.TempDir("", "dep")
   493  	if err != nil {
   494  		t.Fatal(err)
   495  	}
   496  	defer os.RemoveAll(dir)
   497  
   498  	dstdir = filepath.Join(dir, "dst")
   499  	if err = CopyDir(srcdir, dstdir); err == nil {
   500  		t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
   501  	}
   502  }
   503  
   504  func TestCopyDirFail_DstInaccessible(t *testing.T) {
   505  	if runtime.GOOS == "windows" {
   506  		// XXX: setting permissions works differently in
   507  		// Microsoft Windows. Skipping this this until a
   508  		// compatible implementation is provided.
   509  		t.Skip("skipping on windows")
   510  	}
   511  
   512  	var srcdir, dstdir string
   513  
   514  	dir, err := ioutil.TempDir("", "dep")
   515  	if err != nil {
   516  		t.Fatal(err)
   517  	}
   518  	defer os.RemoveAll(dir)
   519  
   520  	srcdir = filepath.Join(dir, "src")
   521  	if err = os.MkdirAll(srcdir, 0755); err != nil {
   522  		t.Fatal(err)
   523  	}
   524  
   525  	cleanup := setupInaccessibleDir(t, func(dir string) error {
   526  		dstdir = filepath.Join(dir, "dst")
   527  		return nil
   528  	})
   529  	defer cleanup()
   530  
   531  	if err := CopyDir(srcdir, dstdir); err == nil {
   532  		t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
   533  	}
   534  }
   535  
   536  func TestCopyDirFail_SrcIsNotDir(t *testing.T) {
   537  	var srcdir, dstdir string
   538  
   539  	dir, err := ioutil.TempDir("", "dep")
   540  	if err != nil {
   541  		t.Fatal(err)
   542  	}
   543  	defer os.RemoveAll(dir)
   544  
   545  	srcdir = filepath.Join(dir, "src")
   546  	if _, err = os.Create(srcdir); err != nil {
   547  		t.Fatal(err)
   548  	}
   549  
   550  	dstdir = filepath.Join(dir, "dst")
   551  
   552  	if err = CopyDir(srcdir, dstdir); err == nil {
   553  		t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
   554  	}
   555  
   556  	if err != errSrcNotDir {
   557  		t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errSrcNotDir, srcdir, dstdir, err)
   558  	}
   559  
   560  }
   561  
   562  func TestCopyDirFail_DstExists(t *testing.T) {
   563  	var srcdir, dstdir string
   564  
   565  	dir, err := ioutil.TempDir("", "dep")
   566  	if err != nil {
   567  		t.Fatal(err)
   568  	}
   569  	defer os.RemoveAll(dir)
   570  
   571  	srcdir = filepath.Join(dir, "src")
   572  	if err = os.MkdirAll(srcdir, 0755); err != nil {
   573  		t.Fatal(err)
   574  	}
   575  
   576  	dstdir = filepath.Join(dir, "dst")
   577  	if err = os.MkdirAll(dstdir, 0755); err != nil {
   578  		t.Fatal(err)
   579  	}
   580  
   581  	if err = CopyDir(srcdir, dstdir); err == nil {
   582  		t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
   583  	}
   584  
   585  	if err != errDstExist {
   586  		t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errDstExist, srcdir, dstdir, err)
   587  	}
   588  }
   589  
   590  func TestCopyDirFailOpen(t *testing.T) {
   591  	if runtime.GOOS == "windows" {
   592  		// XXX: setting permissions works differently in
   593  		// Microsoft Windows. os.Chmod(..., 0222) below is not
   594  		// enough for the file to be readonly, and os.Chmod(...,
   595  		// 0000) returns an invalid argument error. Skipping
   596  		// this this until a compatible implementation is
   597  		// provided.
   598  		t.Skip("skipping on windows")
   599  	}
   600  
   601  	var srcdir, dstdir string
   602  
   603  	dir, err := ioutil.TempDir("", "dep")
   604  	if err != nil {
   605  		t.Fatal(err)
   606  	}
   607  	defer os.RemoveAll(dir)
   608  
   609  	srcdir = filepath.Join(dir, "src")
   610  	if err = os.MkdirAll(srcdir, 0755); err != nil {
   611  		t.Fatal(err)
   612  	}
   613  
   614  	srcfn := filepath.Join(srcdir, "file")
   615  	srcf, err := os.Create(srcfn)
   616  	if err != nil {
   617  		t.Fatal(err)
   618  	}
   619  	srcf.Close()
   620  
   621  	// setup source file so that it cannot be read
   622  	if err = os.Chmod(srcfn, 0222); err != nil {
   623  		t.Fatal(err)
   624  	}
   625  
   626  	dstdir = filepath.Join(dir, "dst")
   627  
   628  	if err = CopyDir(srcdir, dstdir); err == nil {
   629  		t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir)
   630  	}
   631  }
   632  
   633  func TestCopyFile(t *testing.T) {
   634  	dir, err := ioutil.TempDir("", "dep")
   635  	if err != nil {
   636  		t.Fatal(err)
   637  	}
   638  	defer os.RemoveAll(dir)
   639  
   640  	srcf, err := os.Create(filepath.Join(dir, "srcfile"))
   641  	if err != nil {
   642  		t.Fatal(err)
   643  	}
   644  
   645  	want := "hello world"
   646  	if _, err := srcf.Write([]byte(want)); err != nil {
   647  		t.Fatal(err)
   648  	}
   649  	srcf.Close()
   650  
   651  	destf := filepath.Join(dir, "destf")
   652  	if err := copyFile(srcf.Name(), destf); err != nil {
   653  		t.Fatal(err)
   654  	}
   655  
   656  	got, err := ioutil.ReadFile(destf)
   657  	if err != nil {
   658  		t.Fatal(err)
   659  	}
   660  
   661  	if want != string(got) {
   662  		t.Fatalf("expected: %s, got: %s", want, string(got))
   663  	}
   664  
   665  	wantinfo, err := os.Stat(srcf.Name())
   666  	if err != nil {
   667  		t.Fatal(err)
   668  	}
   669  
   670  	gotinfo, err := os.Stat(destf)
   671  	if err != nil {
   672  		t.Fatal(err)
   673  	}
   674  
   675  	if wantinfo.Mode() != gotinfo.Mode() {
   676  		t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v", srcf.Name(), wantinfo.Mode(), destf, gotinfo.Mode())
   677  	}
   678  }
   679  
   680  func TestCopyFileSymlink(t *testing.T) {
   681  	h := test.NewHelper(t)
   682  	defer h.Cleanup()
   683  	h.TempDir(".")
   684  
   685  	testcases := map[string]string{
   686  		filepath.Join("./testdata/symlinks/file-symlink"):         filepath.Join(h.Path("."), "dst-file"),
   687  		filepath.Join("./testdata/symlinks/windows-file-symlink"): filepath.Join(h.Path("."), "windows-dst-file"),
   688  		filepath.Join("./testdata/symlinks/invalid-symlink"):      filepath.Join(h.Path("."), "invalid-symlink"),
   689  	}
   690  
   691  	for symlink, dst := range testcases {
   692  		t.Run(symlink, func(t *testing.T) {
   693  			var err error
   694  			if err = copyFile(symlink, dst); err != nil {
   695  				t.Fatalf("failed to copy symlink: %s", err)
   696  			}
   697  
   698  			var want, got string
   699  
   700  			if runtime.GOOS == "windows" {
   701  				// Creating symlinks on Windows require an additional permission
   702  				// regular users aren't granted usually. So we copy the file
   703  				// content as a fall back instead of creating a real symlink.
   704  				srcb, err := ioutil.ReadFile(symlink)
   705  				h.Must(err)
   706  				dstb, err := ioutil.ReadFile(dst)
   707  				h.Must(err)
   708  
   709  				want = string(srcb)
   710  				got = string(dstb)
   711  			} else {
   712  				want, err = os.Readlink(symlink)
   713  				h.Must(err)
   714  
   715  				got, err = os.Readlink(dst)
   716  				if err != nil {
   717  					t.Fatalf("could not resolve symlink: %s", err)
   718  				}
   719  			}
   720  
   721  			if want != got {
   722  				t.Fatalf("resolved path is incorrect. expected %s, got %s", want, got)
   723  			}
   724  		})
   725  	}
   726  }
   727  
   728  func TestCopyFileLongFilePath(t *testing.T) {
   729  	if runtime.GOOS != "windows" {
   730  		// We want to ensure the temporary fix actually fixes the issue with
   731  		// os.Chmod and long file paths. This is only applicable on Windows.
   732  		t.Skip("skipping on non-windows")
   733  	}
   734  
   735  	h := test.NewHelper(t)
   736  	h.TempDir(".")
   737  	defer h.Cleanup()
   738  
   739  	tmpPath := h.Path(".")
   740  
   741  	// Create a directory with a long-enough path name to cause the bug in #774.
   742  	dirName := ""
   743  	for len(tmpPath+string(os.PathSeparator)+dirName) <= 300 {
   744  		dirName += "directory"
   745  	}
   746  
   747  	h.TempDir(dirName)
   748  	h.TempFile(dirName+string(os.PathSeparator)+"src", "")
   749  
   750  	tmpDirPath := tmpPath + string(os.PathSeparator) + dirName + string(os.PathSeparator)
   751  
   752  	err := copyFile(tmpDirPath+"src", tmpDirPath+"dst")
   753  	if err != nil {
   754  		t.Fatalf("unexpected error while copying file: %v", err)
   755  	}
   756  }
   757  
   758  // C:\Users\appveyor\AppData\Local\Temp\1\gotest639065787\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890
   759  
   760  func TestCopyFileFail(t *testing.T) {
   761  	if runtime.GOOS == "windows" {
   762  		// XXX: setting permissions works differently in
   763  		// Microsoft Windows. Skipping this this until a
   764  		// compatible implementation is provided.
   765  		t.Skip("skipping on windows")
   766  	}
   767  
   768  	dir, err := ioutil.TempDir("", "dep")
   769  	if err != nil {
   770  		t.Fatal(err)
   771  	}
   772  	defer os.RemoveAll(dir)
   773  
   774  	srcf, err := os.Create(filepath.Join(dir, "srcfile"))
   775  	if err != nil {
   776  		t.Fatal(err)
   777  	}
   778  	srcf.Close()
   779  
   780  	var dstdir string
   781  
   782  	cleanup := setupInaccessibleDir(t, func(dir string) error {
   783  		dstdir = filepath.Join(dir, "dir")
   784  		return os.Mkdir(dstdir, 0777)
   785  	})
   786  	defer cleanup()
   787  
   788  	fn := filepath.Join(dstdir, "file")
   789  	if err := copyFile(srcf.Name(), fn); err == nil {
   790  		t.Fatalf("expected error for %s, got none", fn)
   791  	}
   792  }
   793  
   794  // setupInaccessibleDir creates a temporary location with a single
   795  // directory in it, in such a way that that directory is not accessible
   796  // after this function returns.
   797  //
   798  // op is called with the directory as argument, so that it can create
   799  // files or other test artifacts.
   800  //
   801  // If setupInaccessibleDir fails in its preparation, or op fails, t.Fatal
   802  // will be invoked.
   803  //
   804  // This function returns a cleanup function that removes all the temporary
   805  // files this function creates. It is the caller's responsibility to call
   806  // this function before the test is done running, whether there's an error or not.
   807  func setupInaccessibleDir(t *testing.T, op func(dir string) error) func() {
   808  	dir, err := ioutil.TempDir("", "dep")
   809  	if err != nil {
   810  		t.Fatal(err)
   811  		return nil // keep compiler happy
   812  	}
   813  
   814  	subdir := filepath.Join(dir, "dir")
   815  
   816  	cleanup := func() {
   817  		if err := os.Chmod(subdir, 0777); err != nil {
   818  			t.Error(err)
   819  		}
   820  		if err := os.RemoveAll(dir); err != nil {
   821  			t.Error(err)
   822  		}
   823  	}
   824  
   825  	if err := os.Mkdir(subdir, 0777); err != nil {
   826  		cleanup()
   827  		t.Fatal(err)
   828  		return nil
   829  	}
   830  
   831  	if err := op(subdir); err != nil {
   832  		cleanup()
   833  		t.Fatal(err)
   834  		return nil
   835  	}
   836  
   837  	if err := os.Chmod(subdir, 0666); err != nil {
   838  		cleanup()
   839  		t.Fatal(err)
   840  		return nil
   841  	}
   842  
   843  	return cleanup
   844  }
   845  
   846  func TestEnsureDir(t *testing.T) {
   847  	h := test.NewHelper(t)
   848  	defer h.Cleanup()
   849  	h.TempDir(".")
   850  	h.TempFile("file", "")
   851  
   852  	tmpPath := h.Path(".")
   853  
   854  	var dn string
   855  	cleanup := setupInaccessibleDir(t, func(dir string) error {
   856  		dn = filepath.Join(dir, "dir")
   857  		return os.Mkdir(dn, 0777)
   858  	})
   859  	defer cleanup()
   860  
   861  	tests := map[string]bool{
   862  		// [success] A dir already exists for the given path.
   863  		tmpPath: true,
   864  		// [success] Dir does not exist but parent dir exists, so should get created.
   865  		filepath.Join(tmpPath, "testdir"): true,
   866  		// [failure] Dir and parent dir do not exist, should return an error.
   867  		filepath.Join(tmpPath, "notexist", "testdir"): false,
   868  		// [failure] Regular file present at given path.
   869  		h.Path("file"): false,
   870  		// [failure] Path inaccessible.
   871  		dn: false,
   872  	}
   873  
   874  	if runtime.GOOS == "windows" {
   875  		// This test doesn't work on Microsoft Windows because
   876  		// of the differences in how file permissions are
   877  		// implemented. For this to work, the directory where
   878  		// the directory exists should be inaccessible.
   879  		delete(tests, dn)
   880  	}
   881  
   882  	for path, shouldEnsure := range tests {
   883  		err := EnsureDir(path, 0777)
   884  		if shouldEnsure {
   885  			if err != nil {
   886  				t.Fatalf("unexpected error %q for %q", err, path)
   887  			} else if ok, err := IsDir(path); !ok {
   888  				t.Fatalf("expected directory to be preset at %q", path)
   889  				t.Fatal(err)
   890  			}
   891  		} else if err == nil {
   892  			t.Fatalf("expected error for path %q, got none", path)
   893  		}
   894  	}
   895  }
   896  
   897  func TestIsRegular(t *testing.T) {
   898  	wd, err := os.Getwd()
   899  	if err != nil {
   900  		t.Fatal(err)
   901  	}
   902  
   903  	var fn string
   904  
   905  	cleanup := setupInaccessibleDir(t, func(dir string) error {
   906  		fn = filepath.Join(dir, "file")
   907  		fh, err := os.Create(fn)
   908  		if err != nil {
   909  			return err
   910  		}
   911  
   912  		return fh.Close()
   913  	})
   914  	defer cleanup()
   915  
   916  	tests := map[string]struct {
   917  		exists bool
   918  		err    bool
   919  	}{
   920  		wd:                            {false, true},
   921  		filepath.Join(wd, "testdata"): {false, true},
   922  		filepath.Join(wd, "testdata", "test.file"):          {true, false},
   923  		filepath.Join(wd, "this_file_does_not_exist.thing"): {false, false},
   924  		fn: {false, true},
   925  	}
   926  
   927  	if runtime.GOOS == "windows" {
   928  		// This test doesn't work on Microsoft Windows because
   929  		// of the differences in how file permissions are
   930  		// implemented. For this to work, the directory where
   931  		// the file exists should be inaccessible.
   932  		delete(tests, fn)
   933  	}
   934  
   935  	for f, want := range tests {
   936  		got, err := IsRegular(f)
   937  		if err != nil {
   938  			if want.exists != got {
   939  				t.Fatalf("expected %t for %s, got %t", want.exists, f, got)
   940  			}
   941  			if !want.err {
   942  				t.Fatalf("expected no error, got %v", err)
   943  			}
   944  		} else {
   945  			if want.err {
   946  				t.Fatalf("expected error for %s, got none", f)
   947  			}
   948  		}
   949  
   950  		if got != want.exists {
   951  			t.Fatalf("expected %t for %s, got %t", want, f, got)
   952  		}
   953  	}
   954  
   955  }
   956  
   957  func TestIsDir(t *testing.T) {
   958  	wd, err := os.Getwd()
   959  	if err != nil {
   960  		t.Fatal(err)
   961  	}
   962  
   963  	var dn string
   964  
   965  	cleanup := setupInaccessibleDir(t, func(dir string) error {
   966  		dn = filepath.Join(dir, "dir")
   967  		return os.Mkdir(dn, 0777)
   968  	})
   969  	defer cleanup()
   970  
   971  	tests := map[string]struct {
   972  		exists bool
   973  		err    bool
   974  	}{
   975  		wd:                            {true, false},
   976  		filepath.Join(wd, "testdata"): {true, false},
   977  		filepath.Join(wd, "main.go"):  {false, true},
   978  		filepath.Join(wd, "this_file_does_not_exist.thing"): {false, true},
   979  		dn: {false, true},
   980  	}
   981  
   982  	if runtime.GOOS == "windows" {
   983  		// This test doesn't work on Microsoft Windows because
   984  		// of the differences in how file permissions are
   985  		// implemented. For this to work, the directory where
   986  		// the directory exists should be inaccessible.
   987  		delete(tests, dn)
   988  	}
   989  
   990  	for f, want := range tests {
   991  		got, err := IsDir(f)
   992  		if err != nil && !want.err {
   993  			t.Fatalf("expected no error, got %v", err)
   994  		}
   995  
   996  		if got != want.exists {
   997  			t.Fatalf("expected %t for %s, got %t", want.exists, f, got)
   998  		}
   999  	}
  1000  }
  1001  
  1002  func TestIsNonEmptyDir(t *testing.T) {
  1003  	wd, err := os.Getwd()
  1004  	if err != nil {
  1005  		t.Fatal(err)
  1006  	}
  1007  
  1008  	h := test.NewHelper(t)
  1009  	defer h.Cleanup()
  1010  
  1011  	h.TempDir("empty")
  1012  
  1013  	testCases := []struct {
  1014  		path  string
  1015  		empty bool
  1016  		err   bool
  1017  	}{
  1018  		{wd, true, false},
  1019  		{"testdata", true, false},
  1020  		{filepath.Join(wd, "fs.go"), false, true},
  1021  		{filepath.Join(wd, "this_file_does_not_exist.thing"), false, false},
  1022  		{h.Path("empty"), false, false},
  1023  	}
  1024  
  1025  	// This test case doesn't work on Microsoft Windows because of the
  1026  	// differences in how file permissions are implemented.
  1027  	if runtime.GOOS != "windows" {
  1028  		var inaccessibleDir string
  1029  		cleanup := setupInaccessibleDir(t, func(dir string) error {
  1030  			inaccessibleDir = filepath.Join(dir, "empty")
  1031  			return os.Mkdir(inaccessibleDir, 0777)
  1032  		})
  1033  		defer cleanup()
  1034  
  1035  		testCases = append(testCases, struct {
  1036  			path  string
  1037  			empty bool
  1038  			err   bool
  1039  		}{inaccessibleDir, false, true})
  1040  	}
  1041  
  1042  	for _, want := range testCases {
  1043  		got, err := IsNonEmptyDir(want.path)
  1044  		if want.err && err == nil {
  1045  			if got {
  1046  				t.Fatalf("wanted false with error for %v, but got true", want.path)
  1047  			}
  1048  			t.Fatalf("wanted an error for %v, but it was nil", want.path)
  1049  		}
  1050  
  1051  		if got != want.empty {
  1052  			t.Fatalf("wanted %t for %v, but got %t", want.empty, want.path, got)
  1053  		}
  1054  	}
  1055  }
  1056  
  1057  func TestIsSymlink(t *testing.T) {
  1058  	dir, err := ioutil.TempDir("", "dep")
  1059  	if err != nil {
  1060  		t.Fatal(err)
  1061  	}
  1062  	defer os.RemoveAll(dir)
  1063  
  1064  	dirPath := filepath.Join(dir, "directory")
  1065  	if err = os.MkdirAll(dirPath, 0777); err != nil {
  1066  		t.Fatal(err)
  1067  	}
  1068  
  1069  	filePath := filepath.Join(dir, "file")
  1070  	f, err := os.Create(filePath)
  1071  	if err != nil {
  1072  		t.Fatal(err)
  1073  	}
  1074  	f.Close()
  1075  
  1076  	dirSymlink := filepath.Join(dir, "dirSymlink")
  1077  	fileSymlink := filepath.Join(dir, "fileSymlink")
  1078  
  1079  	if err = os.Symlink(dirPath, dirSymlink); err != nil {
  1080  		t.Fatal(err)
  1081  	}
  1082  	if err = os.Symlink(filePath, fileSymlink); err != nil {
  1083  		t.Fatal(err)
  1084  	}
  1085  
  1086  	var (
  1087  		inaccessibleFile    string
  1088  		inaccessibleSymlink string
  1089  	)
  1090  
  1091  	cleanup := setupInaccessibleDir(t, func(dir string) error {
  1092  		inaccessibleFile = filepath.Join(dir, "file")
  1093  		if fh, err := os.Create(inaccessibleFile); err != nil {
  1094  			return err
  1095  		} else if err = fh.Close(); err != nil {
  1096  			return err
  1097  		}
  1098  
  1099  		inaccessibleSymlink = filepath.Join(dir, "symlink")
  1100  		return os.Symlink(inaccessibleFile, inaccessibleSymlink)
  1101  	})
  1102  	defer cleanup()
  1103  
  1104  	tests := map[string]struct{ expected, err bool }{
  1105  		dirPath:             {false, false},
  1106  		filePath:            {false, false},
  1107  		dirSymlink:          {true, false},
  1108  		fileSymlink:         {true, false},
  1109  		inaccessibleFile:    {false, true},
  1110  		inaccessibleSymlink: {false, true},
  1111  	}
  1112  
  1113  	if runtime.GOOS == "windows" {
  1114  		// XXX: setting permissions works differently in Windows. Skipping
  1115  		// these cases until a compatible implementation is provided.
  1116  		delete(tests, inaccessibleFile)
  1117  		delete(tests, inaccessibleSymlink)
  1118  	}
  1119  
  1120  	for path, want := range tests {
  1121  		got, err := IsSymlink(path)
  1122  		if err != nil {
  1123  			if !want.err {
  1124  				t.Errorf("expected no error, got %v", err)
  1125  			}
  1126  		}
  1127  
  1128  		if got != want.expected {
  1129  			t.Errorf("expected %t for %s, got %t", want.expected, path, got)
  1130  		}
  1131  	}
  1132  }