github.com/kunnos/engine@v1.13.1/pkg/archive/copy_unix_test.go (about)

     1  // +build !windows
     2  
     3  // TODO Windows: Some of these tests may be salvagable and portable to Windows.
     4  
     5  package archive
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/sha256"
    10  	"encoding/hex"
    11  	"fmt"
    12  	"io"
    13  	"io/ioutil"
    14  	"os"
    15  	"path/filepath"
    16  	"strings"
    17  	"testing"
    18  )
    19  
    20  func removeAllPaths(paths ...string) {
    21  	for _, path := range paths {
    22  		os.RemoveAll(path)
    23  	}
    24  }
    25  
    26  func getTestTempDirs(t *testing.T) (tmpDirA, tmpDirB string) {
    27  	var err error
    28  
    29  	if tmpDirA, err = ioutil.TempDir("", "archive-copy-test"); err != nil {
    30  		t.Fatal(err)
    31  	}
    32  
    33  	if tmpDirB, err = ioutil.TempDir("", "archive-copy-test"); err != nil {
    34  		t.Fatal(err)
    35  	}
    36  
    37  	return
    38  }
    39  
    40  func isNotDir(err error) bool {
    41  	return strings.Contains(err.Error(), "not a directory")
    42  }
    43  
    44  func joinTrailingSep(pathElements ...string) string {
    45  	joined := filepath.Join(pathElements...)
    46  
    47  	return fmt.Sprintf("%s%c", joined, filepath.Separator)
    48  }
    49  
    50  func fileContentsEqual(t *testing.T, filenameA, filenameB string) (err error) {
    51  	t.Logf("checking for equal file contents: %q and %q\n", filenameA, filenameB)
    52  
    53  	fileA, err := os.Open(filenameA)
    54  	if err != nil {
    55  		return
    56  	}
    57  	defer fileA.Close()
    58  
    59  	fileB, err := os.Open(filenameB)
    60  	if err != nil {
    61  		return
    62  	}
    63  	defer fileB.Close()
    64  
    65  	hasher := sha256.New()
    66  
    67  	if _, err = io.Copy(hasher, fileA); err != nil {
    68  		return
    69  	}
    70  
    71  	hashA := hasher.Sum(nil)
    72  	hasher.Reset()
    73  
    74  	if _, err = io.Copy(hasher, fileB); err != nil {
    75  		return
    76  	}
    77  
    78  	hashB := hasher.Sum(nil)
    79  
    80  	if !bytes.Equal(hashA, hashB) {
    81  		err = fmt.Errorf("file content hashes not equal - expected %s, got %s", hex.EncodeToString(hashA), hex.EncodeToString(hashB))
    82  	}
    83  
    84  	return
    85  }
    86  
    87  func dirContentsEqual(t *testing.T, newDir, oldDir string) (err error) {
    88  	t.Logf("checking for equal directory contents: %q and %q\n", newDir, oldDir)
    89  
    90  	var changes []Change
    91  
    92  	if changes, err = ChangesDirs(newDir, oldDir); err != nil {
    93  		return
    94  	}
    95  
    96  	if len(changes) != 0 {
    97  		err = fmt.Errorf("expected no changes between directories, but got: %v", changes)
    98  	}
    99  
   100  	return
   101  }
   102  
   103  func logDirContents(t *testing.T, dirPath string) {
   104  	logWalkedPaths := filepath.WalkFunc(func(path string, info os.FileInfo, err error) error {
   105  		if err != nil {
   106  			t.Errorf("stat error for path %q: %s", path, err)
   107  			return nil
   108  		}
   109  
   110  		if info.IsDir() {
   111  			path = joinTrailingSep(path)
   112  		}
   113  
   114  		t.Logf("\t%s", path)
   115  
   116  		return nil
   117  	})
   118  
   119  	t.Logf("logging directory contents: %q", dirPath)
   120  
   121  	if err := filepath.Walk(dirPath, logWalkedPaths); err != nil {
   122  		t.Fatal(err)
   123  	}
   124  }
   125  
   126  func testCopyHelper(t *testing.T, srcPath, dstPath string) (err error) {
   127  	t.Logf("copying from %q to %q (not follow symbol link)", srcPath, dstPath)
   128  
   129  	return CopyResource(srcPath, dstPath, false)
   130  }
   131  
   132  func testCopyHelperFSym(t *testing.T, srcPath, dstPath string) (err error) {
   133  	t.Logf("copying from %q to %q (follow symbol link)", srcPath, dstPath)
   134  
   135  	return CopyResource(srcPath, dstPath, true)
   136  }
   137  
   138  // Basic assumptions about SRC and DST:
   139  // 1. SRC must exist.
   140  // 2. If SRC ends with a trailing separator, it must be a directory.
   141  // 3. DST parent directory must exist.
   142  // 4. If DST exists as a file, it must not end with a trailing separator.
   143  
   144  // First get these easy error cases out of the way.
   145  
   146  // Test for error when SRC does not exist.
   147  func TestCopyErrSrcNotExists(t *testing.T) {
   148  	tmpDirA, tmpDirB := getTestTempDirs(t)
   149  	defer removeAllPaths(tmpDirA, tmpDirB)
   150  
   151  	if _, err := CopyInfoSourcePath(filepath.Join(tmpDirA, "file1"), false); !os.IsNotExist(err) {
   152  		t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
   153  	}
   154  }
   155  
   156  // Test for error when SRC ends in a trailing
   157  // path separator but it exists as a file.
   158  func TestCopyErrSrcNotDir(t *testing.T) {
   159  	tmpDirA, tmpDirB := getTestTempDirs(t)
   160  	defer removeAllPaths(tmpDirA, tmpDirB)
   161  
   162  	// Load A with some sample files and directories.
   163  	createSampleDir(t, tmpDirA)
   164  
   165  	if _, err := CopyInfoSourcePath(joinTrailingSep(tmpDirA, "file1"), false); !isNotDir(err) {
   166  		t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
   167  	}
   168  }
   169  
   170  // Test for error when SRC is a valid file or directory,
   171  // but the DST parent directory does not exist.
   172  func TestCopyErrDstParentNotExists(t *testing.T) {
   173  	tmpDirA, tmpDirB := getTestTempDirs(t)
   174  	defer removeAllPaths(tmpDirA, tmpDirB)
   175  
   176  	// Load A with some sample files and directories.
   177  	createSampleDir(t, tmpDirA)
   178  
   179  	srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false}
   180  
   181  	// Try with a file source.
   182  	content, err := TarResource(srcInfo)
   183  	if err != nil {
   184  		t.Fatalf("unexpected error %T: %s", err, err)
   185  	}
   186  	defer content.Close()
   187  
   188  	// Copy to a file whose parent does not exist.
   189  	if err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, "fakeParentDir", "file1")); err == nil {
   190  		t.Fatal("expected IsNotExist error, but got nil instead")
   191  	}
   192  
   193  	if !os.IsNotExist(err) {
   194  		t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
   195  	}
   196  
   197  	// Try with a directory source.
   198  	srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true}
   199  
   200  	content, err = TarResource(srcInfo)
   201  	if err != nil {
   202  		t.Fatalf("unexpected error %T: %s", err, err)
   203  	}
   204  	defer content.Close()
   205  
   206  	// Copy to a directory whose parent does not exist.
   207  	if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "fakeParentDir", "fakeDstDir")); err == nil {
   208  		t.Fatal("expected IsNotExist error, but got nil instead")
   209  	}
   210  
   211  	if !os.IsNotExist(err) {
   212  		t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
   213  	}
   214  }
   215  
   216  // Test for error when DST ends in a trailing
   217  // path separator but exists as a file.
   218  func TestCopyErrDstNotDir(t *testing.T) {
   219  	tmpDirA, tmpDirB := getTestTempDirs(t)
   220  	defer removeAllPaths(tmpDirA, tmpDirB)
   221  
   222  	// Load A and B with some sample files and directories.
   223  	createSampleDir(t, tmpDirA)
   224  	createSampleDir(t, tmpDirB)
   225  
   226  	// Try with a file source.
   227  	srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false}
   228  
   229  	content, err := TarResource(srcInfo)
   230  	if err != nil {
   231  		t.Fatalf("unexpected error %T: %s", err, err)
   232  	}
   233  	defer content.Close()
   234  
   235  	if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil {
   236  		t.Fatal("expected IsNotDir error, but got nil instead")
   237  	}
   238  
   239  	if !isNotDir(err) {
   240  		t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
   241  	}
   242  
   243  	// Try with a directory source.
   244  	srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true}
   245  
   246  	content, err = TarResource(srcInfo)
   247  	if err != nil {
   248  		t.Fatalf("unexpected error %T: %s", err, err)
   249  	}
   250  	defer content.Close()
   251  
   252  	if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil {
   253  		t.Fatal("expected IsNotDir error, but got nil instead")
   254  	}
   255  
   256  	if !isNotDir(err) {
   257  		t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
   258  	}
   259  }
   260  
   261  // Possibilities are reduced to the remaining 10 cases:
   262  //
   263  //  case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action
   264  // ===================================================================================================
   265  //   A   |  no      |  -              |  no       |  -       |  no      |  create file
   266  //   B   |  no      |  -              |  no       |  -       |  yes     |  error
   267  //   C   |  no      |  -              |  yes      |  no      |  -       |  overwrite file
   268  //   D   |  no      |  -              |  yes      |  yes     |  -       |  create file in dst dir
   269  //   E   |  yes     |  no             |  no       |  -       |  -       |  create dir, copy contents
   270  //   F   |  yes     |  no             |  yes      |  no      |  -       |  error
   271  //   G   |  yes     |  no             |  yes      |  yes     |  -       |  copy dir and contents
   272  //   H   |  yes     |  yes            |  no       |  -       |  -       |  create dir, copy contents
   273  //   I   |  yes     |  yes            |  yes      |  no      |  -       |  error
   274  //   J   |  yes     |  yes            |  yes      |  yes     |  -       |  copy dir contents
   275  //
   276  
   277  // A. SRC specifies a file and DST (no trailing path separator) doesn't
   278  //    exist. This should create a file with the name DST and copy the
   279  //    contents of the source file into it.
   280  func TestCopyCaseA(t *testing.T) {
   281  	tmpDirA, tmpDirB := getTestTempDirs(t)
   282  	defer removeAllPaths(tmpDirA, tmpDirB)
   283  
   284  	// Load A with some sample files and directories.
   285  	createSampleDir(t, tmpDirA)
   286  
   287  	srcPath := filepath.Join(tmpDirA, "file1")
   288  	dstPath := filepath.Join(tmpDirB, "itWorks.txt")
   289  
   290  	var err error
   291  
   292  	if err = testCopyHelper(t, srcPath, dstPath); err != nil {
   293  		t.Fatalf("unexpected error %T: %s", err, err)
   294  	}
   295  
   296  	if err = fileContentsEqual(t, srcPath, dstPath); err != nil {
   297  		t.Fatal(err)
   298  	}
   299  	os.Remove(dstPath)
   300  
   301  	symlinkPath := filepath.Join(tmpDirA, "symlink3")
   302  	symlinkPath1 := filepath.Join(tmpDirA, "symlink4")
   303  	linkTarget := filepath.Join(tmpDirA, "file1")
   304  
   305  	if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil {
   306  		t.Fatalf("unexpected error %T: %s", err, err)
   307  	}
   308  
   309  	if err = fileContentsEqual(t, linkTarget, dstPath); err != nil {
   310  		t.Fatal(err)
   311  	}
   312  	os.Remove(dstPath)
   313  	if err = testCopyHelperFSym(t, symlinkPath1, dstPath); err != nil {
   314  		t.Fatalf("unexpected error %T: %s", err, err)
   315  	}
   316  
   317  	if err = fileContentsEqual(t, linkTarget, dstPath); err != nil {
   318  		t.Fatal(err)
   319  	}
   320  }
   321  
   322  // B. SRC specifies a file and DST (with trailing path separator) doesn't
   323  //    exist. This should cause an error because the copy operation cannot
   324  //    create a directory when copying a single file.
   325  func TestCopyCaseB(t *testing.T) {
   326  	tmpDirA, tmpDirB := getTestTempDirs(t)
   327  	defer removeAllPaths(tmpDirA, tmpDirB)
   328  
   329  	// Load A with some sample files and directories.
   330  	createSampleDir(t, tmpDirA)
   331  
   332  	srcPath := filepath.Join(tmpDirA, "file1")
   333  	dstDir := joinTrailingSep(tmpDirB, "testDir")
   334  
   335  	var err error
   336  
   337  	if err = testCopyHelper(t, srcPath, dstDir); err == nil {
   338  		t.Fatal("expected ErrDirNotExists error, but got nil instead")
   339  	}
   340  
   341  	if err != ErrDirNotExists {
   342  		t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err)
   343  	}
   344  
   345  	symlinkPath := filepath.Join(tmpDirA, "symlink3")
   346  
   347  	if err = testCopyHelperFSym(t, symlinkPath, dstDir); err == nil {
   348  		t.Fatal("expected ErrDirNotExists error, but got nil instead")
   349  	}
   350  	if err != ErrDirNotExists {
   351  		t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err)
   352  	}
   353  
   354  }
   355  
   356  // C. SRC specifies a file and DST exists as a file. This should overwrite
   357  //    the file at DST with the contents of the source file.
   358  func TestCopyCaseC(t *testing.T) {
   359  	tmpDirA, tmpDirB := getTestTempDirs(t)
   360  	defer removeAllPaths(tmpDirA, tmpDirB)
   361  
   362  	// Load A and B with some sample files and directories.
   363  	createSampleDir(t, tmpDirA)
   364  	createSampleDir(t, tmpDirB)
   365  
   366  	srcPath := filepath.Join(tmpDirA, "file1")
   367  	dstPath := filepath.Join(tmpDirB, "file2")
   368  
   369  	var err error
   370  
   371  	// Ensure they start out different.
   372  	if err = fileContentsEqual(t, srcPath, dstPath); err == nil {
   373  		t.Fatal("expected different file contents")
   374  	}
   375  
   376  	if err = testCopyHelper(t, srcPath, dstPath); err != nil {
   377  		t.Fatalf("unexpected error %T: %s", err, err)
   378  	}
   379  
   380  	if err = fileContentsEqual(t, srcPath, dstPath); err != nil {
   381  		t.Fatal(err)
   382  	}
   383  }
   384  
   385  // C. Symbol link following version:
   386  //    SRC specifies a file and DST exists as a file. This should overwrite
   387  //    the file at DST with the contents of the source file.
   388  func TestCopyCaseCFSym(t *testing.T) {
   389  	tmpDirA, tmpDirB := getTestTempDirs(t)
   390  	defer removeAllPaths(tmpDirA, tmpDirB)
   391  
   392  	// Load A and B with some sample files and directories.
   393  	createSampleDir(t, tmpDirA)
   394  	createSampleDir(t, tmpDirB)
   395  
   396  	symlinkPathBad := filepath.Join(tmpDirA, "symlink1")
   397  	symlinkPath := filepath.Join(tmpDirA, "symlink3")
   398  	linkTarget := filepath.Join(tmpDirA, "file1")
   399  	dstPath := filepath.Join(tmpDirB, "file2")
   400  
   401  	var err error
   402  
   403  	// first to test broken link
   404  	if err = testCopyHelperFSym(t, symlinkPathBad, dstPath); err == nil {
   405  		t.Fatalf("unexpected error %T: %s", err, err)
   406  	}
   407  
   408  	// test symbol link -> symbol link -> target
   409  	// Ensure they start out different.
   410  	if err = fileContentsEqual(t, linkTarget, dstPath); err == nil {
   411  		t.Fatal("expected different file contents")
   412  	}
   413  
   414  	if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil {
   415  		t.Fatalf("unexpected error %T: %s", err, err)
   416  	}
   417  
   418  	if err = fileContentsEqual(t, linkTarget, dstPath); err != nil {
   419  		t.Fatal(err)
   420  	}
   421  }
   422  
   423  // D. SRC specifies a file and DST exists as a directory. This should place
   424  //    a copy of the source file inside it using the basename from SRC. Ensure
   425  //    this works whether DST has a trailing path separator or not.
   426  func TestCopyCaseD(t *testing.T) {
   427  	tmpDirA, tmpDirB := getTestTempDirs(t)
   428  	defer removeAllPaths(tmpDirA, tmpDirB)
   429  
   430  	// Load A and B with some sample files and directories.
   431  	createSampleDir(t, tmpDirA)
   432  	createSampleDir(t, tmpDirB)
   433  
   434  	srcPath := filepath.Join(tmpDirA, "file1")
   435  	dstDir := filepath.Join(tmpDirB, "dir1")
   436  	dstPath := filepath.Join(dstDir, "file1")
   437  
   438  	var err error
   439  
   440  	// Ensure that dstPath doesn't exist.
   441  	if _, err = os.Stat(dstPath); !os.IsNotExist(err) {
   442  		t.Fatalf("did not expect dstPath %q to exist", dstPath)
   443  	}
   444  
   445  	if err = testCopyHelper(t, srcPath, dstDir); err != nil {
   446  		t.Fatalf("unexpected error %T: %s", err, err)
   447  	}
   448  
   449  	if err = fileContentsEqual(t, srcPath, dstPath); err != nil {
   450  		t.Fatal(err)
   451  	}
   452  
   453  	// Now try again but using a trailing path separator for dstDir.
   454  
   455  	if err = os.RemoveAll(dstDir); err != nil {
   456  		t.Fatalf("unable to remove dstDir: %s", err)
   457  	}
   458  
   459  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   460  		t.Fatalf("unable to make dstDir: %s", err)
   461  	}
   462  
   463  	dstDir = joinTrailingSep(tmpDirB, "dir1")
   464  
   465  	if err = testCopyHelper(t, srcPath, dstDir); err != nil {
   466  		t.Fatalf("unexpected error %T: %s", err, err)
   467  	}
   468  
   469  	if err = fileContentsEqual(t, srcPath, dstPath); err != nil {
   470  		t.Fatal(err)
   471  	}
   472  }
   473  
   474  // D. Symbol link following version:
   475  //    SRC specifies a file and DST exists as a directory. This should place
   476  //    a copy of the source file inside it using the basename from SRC. Ensure
   477  //    this works whether DST has a trailing path separator or not.
   478  func TestCopyCaseDFSym(t *testing.T) {
   479  	tmpDirA, tmpDirB := getTestTempDirs(t)
   480  	defer removeAllPaths(tmpDirA, tmpDirB)
   481  
   482  	// Load A and B with some sample files and directories.
   483  	createSampleDir(t, tmpDirA)
   484  	createSampleDir(t, tmpDirB)
   485  
   486  	srcPath := filepath.Join(tmpDirA, "symlink4")
   487  	linkTarget := filepath.Join(tmpDirA, "file1")
   488  	dstDir := filepath.Join(tmpDirB, "dir1")
   489  	dstPath := filepath.Join(dstDir, "symlink4")
   490  
   491  	var err error
   492  
   493  	// Ensure that dstPath doesn't exist.
   494  	if _, err = os.Stat(dstPath); !os.IsNotExist(err) {
   495  		t.Fatalf("did not expect dstPath %q to exist", dstPath)
   496  	}
   497  
   498  	if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil {
   499  		t.Fatalf("unexpected error %T: %s", err, err)
   500  	}
   501  
   502  	if err = fileContentsEqual(t, linkTarget, dstPath); err != nil {
   503  		t.Fatal(err)
   504  	}
   505  
   506  	// Now try again but using a trailing path separator for dstDir.
   507  
   508  	if err = os.RemoveAll(dstDir); err != nil {
   509  		t.Fatalf("unable to remove dstDir: %s", err)
   510  	}
   511  
   512  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   513  		t.Fatalf("unable to make dstDir: %s", err)
   514  	}
   515  
   516  	dstDir = joinTrailingSep(tmpDirB, "dir1")
   517  
   518  	if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil {
   519  		t.Fatalf("unexpected error %T: %s", err, err)
   520  	}
   521  
   522  	if err = fileContentsEqual(t, linkTarget, dstPath); err != nil {
   523  		t.Fatal(err)
   524  	}
   525  }
   526  
   527  // E. SRC specifies a directory and DST does not exist. This should create a
   528  //    directory at DST and copy the contents of the SRC directory into the DST
   529  //    directory. Ensure this works whether DST has a trailing path separator or
   530  //    not.
   531  func TestCopyCaseE(t *testing.T) {
   532  	tmpDirA, tmpDirB := getTestTempDirs(t)
   533  	defer removeAllPaths(tmpDirA, tmpDirB)
   534  
   535  	// Load A with some sample files and directories.
   536  	createSampleDir(t, tmpDirA)
   537  
   538  	srcDir := filepath.Join(tmpDirA, "dir1")
   539  	dstDir := filepath.Join(tmpDirB, "testDir")
   540  
   541  	var err error
   542  
   543  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   544  		t.Fatalf("unexpected error %T: %s", err, err)
   545  	}
   546  
   547  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   548  		t.Log("dir contents not equal")
   549  		logDirContents(t, tmpDirA)
   550  		logDirContents(t, tmpDirB)
   551  		t.Fatal(err)
   552  	}
   553  
   554  	// Now try again but using a trailing path separator for dstDir.
   555  
   556  	if err = os.RemoveAll(dstDir); err != nil {
   557  		t.Fatalf("unable to remove dstDir: %s", err)
   558  	}
   559  
   560  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   561  
   562  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   563  		t.Fatalf("unexpected error %T: %s", err, err)
   564  	}
   565  
   566  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   567  		t.Fatal(err)
   568  	}
   569  }
   570  
   571  // E. Symbol link following version:
   572  //    SRC specifies a directory and DST does not exist. This should create a
   573  //    directory at DST and copy the contents of the SRC directory into the DST
   574  //    directory. Ensure this works whether DST has a trailing path separator or
   575  //    not.
   576  func TestCopyCaseEFSym(t *testing.T) {
   577  	tmpDirA, tmpDirB := getTestTempDirs(t)
   578  	defer removeAllPaths(tmpDirA, tmpDirB)
   579  
   580  	// Load A with some sample files and directories.
   581  	createSampleDir(t, tmpDirA)
   582  
   583  	srcDir := filepath.Join(tmpDirA, "dirSymlink")
   584  	linkTarget := filepath.Join(tmpDirA, "dir1")
   585  	dstDir := filepath.Join(tmpDirB, "testDir")
   586  
   587  	var err error
   588  
   589  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   590  		t.Fatalf("unexpected error %T: %s", err, err)
   591  	}
   592  
   593  	if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
   594  		t.Log("dir contents not equal")
   595  		logDirContents(t, tmpDirA)
   596  		logDirContents(t, tmpDirB)
   597  		t.Fatal(err)
   598  	}
   599  
   600  	// Now try again but using a trailing path separator for dstDir.
   601  
   602  	if err = os.RemoveAll(dstDir); err != nil {
   603  		t.Fatalf("unable to remove dstDir: %s", err)
   604  	}
   605  
   606  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   607  
   608  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   609  		t.Fatalf("unexpected error %T: %s", err, err)
   610  	}
   611  
   612  	if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
   613  		t.Fatal(err)
   614  	}
   615  }
   616  
   617  // F. SRC specifies a directory and DST exists as a file. This should cause an
   618  //    error as it is not possible to overwrite a file with a directory.
   619  func TestCopyCaseF(t *testing.T) {
   620  	tmpDirA, tmpDirB := getTestTempDirs(t)
   621  	defer removeAllPaths(tmpDirA, tmpDirB)
   622  
   623  	// Load A and B with some sample files and directories.
   624  	createSampleDir(t, tmpDirA)
   625  	createSampleDir(t, tmpDirB)
   626  
   627  	srcDir := filepath.Join(tmpDirA, "dir1")
   628  	symSrcDir := filepath.Join(tmpDirA, "dirSymlink")
   629  	dstFile := filepath.Join(tmpDirB, "file1")
   630  
   631  	var err error
   632  
   633  	if err = testCopyHelper(t, srcDir, dstFile); err == nil {
   634  		t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   635  	}
   636  
   637  	if err != ErrCannotCopyDir {
   638  		t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   639  	}
   640  
   641  	// now test with symbol link
   642  	if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil {
   643  		t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   644  	}
   645  
   646  	if err != ErrCannotCopyDir {
   647  		t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   648  	}
   649  }
   650  
   651  // G. SRC specifies a directory and DST exists as a directory. This should copy
   652  //    the SRC directory and all its contents to the DST directory. Ensure this
   653  //    works whether DST has a trailing path separator or not.
   654  func TestCopyCaseG(t *testing.T) {
   655  	tmpDirA, tmpDirB := getTestTempDirs(t)
   656  	defer removeAllPaths(tmpDirA, tmpDirB)
   657  
   658  	// Load A and B with some sample files and directories.
   659  	createSampleDir(t, tmpDirA)
   660  	createSampleDir(t, tmpDirB)
   661  
   662  	srcDir := filepath.Join(tmpDirA, "dir1")
   663  	dstDir := filepath.Join(tmpDirB, "dir2")
   664  	resultDir := filepath.Join(dstDir, "dir1")
   665  
   666  	var err error
   667  
   668  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   669  		t.Fatalf("unexpected error %T: %s", err, err)
   670  	}
   671  
   672  	if err = dirContentsEqual(t, resultDir, srcDir); err != nil {
   673  		t.Fatal(err)
   674  	}
   675  
   676  	// Now try again but using a trailing path separator for dstDir.
   677  
   678  	if err = os.RemoveAll(dstDir); err != nil {
   679  		t.Fatalf("unable to remove dstDir: %s", err)
   680  	}
   681  
   682  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   683  		t.Fatalf("unable to make dstDir: %s", err)
   684  	}
   685  
   686  	dstDir = joinTrailingSep(tmpDirB, "dir2")
   687  
   688  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   689  		t.Fatalf("unexpected error %T: %s", err, err)
   690  	}
   691  
   692  	if err = dirContentsEqual(t, resultDir, srcDir); err != nil {
   693  		t.Fatal(err)
   694  	}
   695  }
   696  
   697  // G. Symbol link version:
   698  //    SRC specifies a directory and DST exists as a directory. This should copy
   699  //    the SRC directory and all its contents to the DST directory. Ensure this
   700  //    works whether DST has a trailing path separator or not.
   701  func TestCopyCaseGFSym(t *testing.T) {
   702  	tmpDirA, tmpDirB := getTestTempDirs(t)
   703  	defer removeAllPaths(tmpDirA, tmpDirB)
   704  
   705  	// Load A and B with some sample files and directories.
   706  	createSampleDir(t, tmpDirA)
   707  	createSampleDir(t, tmpDirB)
   708  
   709  	srcDir := filepath.Join(tmpDirA, "dirSymlink")
   710  	linkTarget := filepath.Join(tmpDirA, "dir1")
   711  	dstDir := filepath.Join(tmpDirB, "dir2")
   712  	resultDir := filepath.Join(dstDir, "dirSymlink")
   713  
   714  	var err error
   715  
   716  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   717  		t.Fatalf("unexpected error %T: %s", err, err)
   718  	}
   719  
   720  	if err = dirContentsEqual(t, resultDir, linkTarget); err != nil {
   721  		t.Fatal(err)
   722  	}
   723  
   724  	// Now try again but using a trailing path separator for dstDir.
   725  
   726  	if err = os.RemoveAll(dstDir); err != nil {
   727  		t.Fatalf("unable to remove dstDir: %s", err)
   728  	}
   729  
   730  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   731  		t.Fatalf("unable to make dstDir: %s", err)
   732  	}
   733  
   734  	dstDir = joinTrailingSep(tmpDirB, "dir2")
   735  
   736  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   737  		t.Fatalf("unexpected error %T: %s", err, err)
   738  	}
   739  
   740  	if err = dirContentsEqual(t, resultDir, linkTarget); err != nil {
   741  		t.Fatal(err)
   742  	}
   743  }
   744  
   745  // H. SRC specifies a directory's contents only and DST does not exist. This
   746  //    should create a directory at DST and copy the contents of the SRC
   747  //    directory (but not the directory itself) into the DST directory. Ensure
   748  //    this works whether DST has a trailing path separator or not.
   749  func TestCopyCaseH(t *testing.T) {
   750  	tmpDirA, tmpDirB := getTestTempDirs(t)
   751  	defer removeAllPaths(tmpDirA, tmpDirB)
   752  
   753  	// Load A with some sample files and directories.
   754  	createSampleDir(t, tmpDirA)
   755  
   756  	srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
   757  	dstDir := filepath.Join(tmpDirB, "testDir")
   758  
   759  	var err error
   760  
   761  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   762  		t.Fatalf("unexpected error %T: %s", err, err)
   763  	}
   764  
   765  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   766  		t.Log("dir contents not equal")
   767  		logDirContents(t, tmpDirA)
   768  		logDirContents(t, tmpDirB)
   769  		t.Fatal(err)
   770  	}
   771  
   772  	// Now try again but using a trailing path separator for dstDir.
   773  
   774  	if err = os.RemoveAll(dstDir); err != nil {
   775  		t.Fatalf("unable to remove dstDir: %s", err)
   776  	}
   777  
   778  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   779  
   780  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   781  		t.Fatalf("unexpected error %T: %s", err, err)
   782  	}
   783  
   784  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   785  		t.Log("dir contents not equal")
   786  		logDirContents(t, tmpDirA)
   787  		logDirContents(t, tmpDirB)
   788  		t.Fatal(err)
   789  	}
   790  }
   791  
   792  // H. Symbol link following version:
   793  //    SRC specifies a directory's contents only and DST does not exist. This
   794  //    should create a directory at DST and copy the contents of the SRC
   795  //    directory (but not the directory itself) into the DST directory. Ensure
   796  //    this works whether DST has a trailing path separator or not.
   797  func TestCopyCaseHFSym(t *testing.T) {
   798  	tmpDirA, tmpDirB := getTestTempDirs(t)
   799  	defer removeAllPaths(tmpDirA, tmpDirB)
   800  
   801  	// Load A with some sample files and directories.
   802  	createSampleDir(t, tmpDirA)
   803  
   804  	srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "."
   805  	linkTarget := filepath.Join(tmpDirA, "dir1")
   806  	dstDir := filepath.Join(tmpDirB, "testDir")
   807  
   808  	var err error
   809  
   810  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   811  		t.Fatalf("unexpected error %T: %s", err, err)
   812  	}
   813  
   814  	if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
   815  		t.Log("dir contents not equal")
   816  		logDirContents(t, tmpDirA)
   817  		logDirContents(t, tmpDirB)
   818  		t.Fatal(err)
   819  	}
   820  
   821  	// Now try again but using a trailing path separator for dstDir.
   822  
   823  	if err = os.RemoveAll(dstDir); err != nil {
   824  		t.Fatalf("unable to remove dstDir: %s", err)
   825  	}
   826  
   827  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   828  
   829  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   830  		t.Fatalf("unexpected error %T: %s", err, err)
   831  	}
   832  
   833  	if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
   834  		t.Log("dir contents not equal")
   835  		logDirContents(t, tmpDirA)
   836  		logDirContents(t, tmpDirB)
   837  		t.Fatal(err)
   838  	}
   839  }
   840  
   841  // I. SRC specifies a directory's contents only and DST exists as a file. This
   842  //    should cause an error as it is not possible to overwrite a file with a
   843  //    directory.
   844  func TestCopyCaseI(t *testing.T) {
   845  	tmpDirA, tmpDirB := getTestTempDirs(t)
   846  	defer removeAllPaths(tmpDirA, tmpDirB)
   847  
   848  	// Load A and B with some sample files and directories.
   849  	createSampleDir(t, tmpDirA)
   850  	createSampleDir(t, tmpDirB)
   851  
   852  	srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
   853  	symSrcDir := filepath.Join(tmpDirB, "dirSymlink")
   854  	dstFile := filepath.Join(tmpDirB, "file1")
   855  
   856  	var err error
   857  
   858  	if err = testCopyHelper(t, srcDir, dstFile); err == nil {
   859  		t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   860  	}
   861  
   862  	if err != ErrCannotCopyDir {
   863  		t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   864  	}
   865  
   866  	// now try with symbol link of dir
   867  	if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil {
   868  		t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   869  	}
   870  
   871  	if err != ErrCannotCopyDir {
   872  		t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   873  	}
   874  }
   875  
   876  // J. SRC specifies a directory's contents only and DST exists as a directory.
   877  //    This should copy the contents of the SRC directory (but not the directory
   878  //    itself) into the DST directory. Ensure this works whether DST has a
   879  //    trailing path separator or not.
   880  func TestCopyCaseJ(t *testing.T) {
   881  	tmpDirA, tmpDirB := getTestTempDirs(t)
   882  	defer removeAllPaths(tmpDirA, tmpDirB)
   883  
   884  	// Load A and B with some sample files and directories.
   885  	createSampleDir(t, tmpDirA)
   886  	createSampleDir(t, tmpDirB)
   887  
   888  	srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
   889  	dstDir := filepath.Join(tmpDirB, "dir5")
   890  
   891  	var err error
   892  
   893  	// first to create an empty dir
   894  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   895  		t.Fatalf("unable to make dstDir: %s", err)
   896  	}
   897  
   898  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   899  		t.Fatalf("unexpected error %T: %s", err, err)
   900  	}
   901  
   902  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   903  		t.Fatal(err)
   904  	}
   905  
   906  	// Now try again but using a trailing path separator for dstDir.
   907  
   908  	if err = os.RemoveAll(dstDir); err != nil {
   909  		t.Fatalf("unable to remove dstDir: %s", err)
   910  	}
   911  
   912  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   913  		t.Fatalf("unable to make dstDir: %s", err)
   914  	}
   915  
   916  	dstDir = joinTrailingSep(tmpDirB, "dir5")
   917  
   918  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   919  		t.Fatalf("unexpected error %T: %s", err, err)
   920  	}
   921  
   922  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   923  		t.Fatal(err)
   924  	}
   925  }
   926  
   927  // J. Symbol link following version:
   928  //    SRC specifies a directory's contents only and DST exists as a directory.
   929  //    This should copy the contents of the SRC directory (but not the directory
   930  //    itself) into the DST directory. Ensure this works whether DST has a
   931  //    trailing path separator or not.
   932  func TestCopyCaseJFSym(t *testing.T) {
   933  	tmpDirA, tmpDirB := getTestTempDirs(t)
   934  	defer removeAllPaths(tmpDirA, tmpDirB)
   935  
   936  	// Load A and B with some sample files and directories.
   937  	createSampleDir(t, tmpDirA)
   938  	createSampleDir(t, tmpDirB)
   939  
   940  	srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "."
   941  	linkTarget := filepath.Join(tmpDirA, "dir1")
   942  	dstDir := filepath.Join(tmpDirB, "dir5")
   943  
   944  	var err error
   945  
   946  	// first to create an empty dir
   947  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   948  		t.Fatalf("unable to make dstDir: %s", err)
   949  	}
   950  
   951  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   952  		t.Fatalf("unexpected error %T: %s", err, err)
   953  	}
   954  
   955  	if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
   956  		t.Fatal(err)
   957  	}
   958  
   959  	// Now try again but using a trailing path separator for dstDir.
   960  
   961  	if err = os.RemoveAll(dstDir); err != nil {
   962  		t.Fatalf("unable to remove dstDir: %s", err)
   963  	}
   964  
   965  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   966  		t.Fatalf("unable to make dstDir: %s", err)
   967  	}
   968  
   969  	dstDir = joinTrailingSep(tmpDirB, "dir5")
   970  
   971  	if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
   972  		t.Fatalf("unexpected error %T: %s", err, err)
   973  	}
   974  
   975  	if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
   976  		t.Fatal(err)
   977  	}
   978  }