github.com/lazyboychen7/engine@v17.12.1-ce-rc2+incompatible/pkg/archive/copy_unix_test.go (about)

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