github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/pkg/archive/copy_test.go (about)

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