github.com/hauerwu/docker@v1.8.0-rc1/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", srcPath, dstPath)
   124  
   125  	return CopyResource(srcPath, dstPath)
   126  }
   127  
   128  // Basic assumptions about SRC and DST:
   129  // 1. SRC must exist.
   130  // 2. If SRC ends with a trailing separator, it must be a directory.
   131  // 3. DST parent directory must exist.
   132  // 4. If DST exists as a file, it must not end with a trailing separator.
   133  
   134  // First get these easy error cases out of the way.
   135  
   136  // Test for error when SRC does not exist.
   137  func TestCopyErrSrcNotExists(t *testing.T) {
   138  	tmpDirA, tmpDirB := getTestTempDirs(t)
   139  	defer removeAllPaths(tmpDirA, tmpDirB)
   140  
   141  	content, err := TarResource(filepath.Join(tmpDirA, "file1"))
   142  	if err == nil {
   143  		content.Close()
   144  		t.Fatal("expected IsNotExist error, but got nil instead")
   145  	}
   146  
   147  	if !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  	content, err := TarResource(joinTrailingSep(tmpDirA, "file1"))
   162  	if err == nil {
   163  		content.Close()
   164  		t.Fatal("expected IsNotDir error, but got nil instead")
   165  	}
   166  
   167  	if !isNotDir(err) {
   168  		t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
   169  	}
   170  }
   171  
   172  // Test for error when SRC is a valid file or directory,
   173  // but the DST parent directory does not exist.
   174  func TestCopyErrDstParentNotExists(t *testing.T) {
   175  	tmpDirA, tmpDirB := getTestTempDirs(t)
   176  	defer removeAllPaths(tmpDirA, tmpDirB)
   177  
   178  	// Load A with some sample files and directories.
   179  	createSampleDir(t, tmpDirA)
   180  
   181  	srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false}
   182  
   183  	// Try with a file source.
   184  	content, err := TarResource(srcInfo.Path)
   185  	if err != nil {
   186  		t.Fatalf("unexpected error %T: %s", err, err)
   187  	}
   188  	defer content.Close()
   189  
   190  	// Copy to a file whose parent does not exist.
   191  	if err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, "fakeParentDir", "file1")); err == nil {
   192  		t.Fatal("expected IsNotExist error, but got nil instead")
   193  	}
   194  
   195  	if !os.IsNotExist(err) {
   196  		t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
   197  	}
   198  
   199  	// Try with a directory source.
   200  	srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true}
   201  
   202  	content, err = TarResource(srcInfo.Path)
   203  	if err != nil {
   204  		t.Fatalf("unexpected error %T: %s", err, err)
   205  	}
   206  	defer content.Close()
   207  
   208  	// Copy to a directory whose parent does not exist.
   209  	if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "fakeParentDir", "fakeDstDir")); err == nil {
   210  		t.Fatal("expected IsNotExist error, but got nil instead")
   211  	}
   212  
   213  	if !os.IsNotExist(err) {
   214  		t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
   215  	}
   216  }
   217  
   218  // Test for error when DST ends in a trailing
   219  // path separator but exists as a file.
   220  func TestCopyErrDstNotDir(t *testing.T) {
   221  	tmpDirA, tmpDirB := getTestTempDirs(t)
   222  	defer removeAllPaths(tmpDirA, tmpDirB)
   223  
   224  	// Load A and B with some sample files and directories.
   225  	createSampleDir(t, tmpDirA)
   226  	createSampleDir(t, tmpDirB)
   227  
   228  	// Try with a file source.
   229  	srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false}
   230  
   231  	content, err := TarResource(srcInfo.Path)
   232  	if err != nil {
   233  		t.Fatalf("unexpected error %T: %s", err, err)
   234  	}
   235  	defer content.Close()
   236  
   237  	if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil {
   238  		t.Fatal("expected IsNotDir error, but got nil instead")
   239  	}
   240  
   241  	if !isNotDir(err) {
   242  		t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
   243  	}
   244  
   245  	// Try with a directory source.
   246  	srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true}
   247  
   248  	content, err = TarResource(srcInfo.Path)
   249  	if err != nil {
   250  		t.Fatalf("unexpected error %T: %s", err, err)
   251  	}
   252  	defer content.Close()
   253  
   254  	if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil {
   255  		t.Fatal("expected IsNotDir error, but got nil instead")
   256  	}
   257  
   258  	if !isNotDir(err) {
   259  		t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
   260  	}
   261  }
   262  
   263  // Possibilities are reduced to the remaining 10 cases:
   264  //
   265  //  case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action
   266  // ===================================================================================================
   267  //   A   |  no      |  -              |  no       |  -       |  no      |  create file
   268  //   B   |  no      |  -              |  no       |  -       |  yes     |  error
   269  //   C   |  no      |  -              |  yes      |  no      |  -       |  overwrite file
   270  //   D   |  no      |  -              |  yes      |  yes     |  -       |  create file in dst dir
   271  //   E   |  yes     |  no             |  no       |  -       |  -       |  create dir, copy contents
   272  //   F   |  yes     |  no             |  yes      |  no      |  -       |  error
   273  //   G   |  yes     |  no             |  yes      |  yes     |  -       |  copy dir and contents
   274  //   H   |  yes     |  yes            |  no       |  -       |  -       |  create dir, copy contents
   275  //   I   |  yes     |  yes            |  yes      |  no      |  -       |  error
   276  //   J   |  yes     |  yes            |  yes      |  yes     |  -       |  copy dir contents
   277  //
   278  
   279  // A. SRC specifies a file and DST (no trailing path separator) doesn't
   280  //    exist. This should create a file with the name DST and copy the
   281  //    contents of the source file into it.
   282  func TestCopyCaseA(t *testing.T) {
   283  	tmpDirA, tmpDirB := getTestTempDirs(t)
   284  	defer removeAllPaths(tmpDirA, tmpDirB)
   285  
   286  	// Load A with some sample files and directories.
   287  	createSampleDir(t, tmpDirA)
   288  
   289  	srcPath := filepath.Join(tmpDirA, "file1")
   290  	dstPath := filepath.Join(tmpDirB, "itWorks.txt")
   291  
   292  	var err error
   293  
   294  	if err = testCopyHelper(t, srcPath, dstPath); err != nil {
   295  		t.Fatalf("unexpected error %T: %s", err, err)
   296  	}
   297  
   298  	if err = fileContentsEqual(t, srcPath, dstPath); err != nil {
   299  		t.Fatal(err)
   300  	}
   301  }
   302  
   303  // B. SRC specifies a file and DST (with trailing path separator) doesn't
   304  //    exist. This should cause an error because the copy operation cannot
   305  //    create a directory when copying a single file.
   306  func TestCopyCaseB(t *testing.T) {
   307  	tmpDirA, tmpDirB := getTestTempDirs(t)
   308  	defer removeAllPaths(tmpDirA, tmpDirB)
   309  
   310  	// Load A with some sample files and directories.
   311  	createSampleDir(t, tmpDirA)
   312  
   313  	srcPath := filepath.Join(tmpDirA, "file1")
   314  	dstDir := joinTrailingSep(tmpDirB, "testDir")
   315  
   316  	var err error
   317  
   318  	if err = testCopyHelper(t, srcPath, dstDir); err == nil {
   319  		t.Fatal("expected ErrDirNotExists error, but got nil instead")
   320  	}
   321  
   322  	if err != ErrDirNotExists {
   323  		t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err)
   324  	}
   325  }
   326  
   327  // C. SRC specifies a file and DST exists as a file. This should overwrite
   328  //    the file at DST with the contents of the source file.
   329  func TestCopyCaseC(t *testing.T) {
   330  	tmpDirA, tmpDirB := getTestTempDirs(t)
   331  	defer removeAllPaths(tmpDirA, tmpDirB)
   332  
   333  	// Load A and B with some sample files and directories.
   334  	createSampleDir(t, tmpDirA)
   335  	createSampleDir(t, tmpDirB)
   336  
   337  	srcPath := filepath.Join(tmpDirA, "file1")
   338  	dstPath := filepath.Join(tmpDirB, "file2")
   339  
   340  	var err error
   341  
   342  	// Ensure they start out different.
   343  	if err = fileContentsEqual(t, srcPath, dstPath); err == nil {
   344  		t.Fatal("expected different file contents")
   345  	}
   346  
   347  	if err = testCopyHelper(t, srcPath, dstPath); err != nil {
   348  		t.Fatalf("unexpected error %T: %s", err, err)
   349  	}
   350  
   351  	if err = fileContentsEqual(t, srcPath, dstPath); err != nil {
   352  		t.Fatal(err)
   353  	}
   354  }
   355  
   356  // D. SRC specifies a file and DST exists as a directory. This should place
   357  //    a copy of the source file inside it using the basename from SRC. Ensure
   358  //    this works whether DST has a trailing path separator or not.
   359  func TestCopyCaseD(t *testing.T) {
   360  	tmpDirA, tmpDirB := getTestTempDirs(t)
   361  	defer removeAllPaths(tmpDirA, tmpDirB)
   362  
   363  	// Load A and B with some sample files and directories.
   364  	createSampleDir(t, tmpDirA)
   365  	createSampleDir(t, tmpDirB)
   366  
   367  	srcPath := filepath.Join(tmpDirA, "file1")
   368  	dstDir := filepath.Join(tmpDirB, "dir1")
   369  	dstPath := filepath.Join(dstDir, "file1")
   370  
   371  	var err error
   372  
   373  	// Ensure that dstPath doesn't exist.
   374  	if _, err = os.Stat(dstPath); !os.IsNotExist(err) {
   375  		t.Fatalf("did not expect dstPath %q to exist", dstPath)
   376  	}
   377  
   378  	if err = testCopyHelper(t, srcPath, dstDir); err != nil {
   379  		t.Fatalf("unexpected error %T: %s", err, err)
   380  	}
   381  
   382  	if err = fileContentsEqual(t, srcPath, dstPath); err != nil {
   383  		t.Fatal(err)
   384  	}
   385  
   386  	// Now try again but using a trailing path separator for dstDir.
   387  
   388  	if err = os.RemoveAll(dstDir); err != nil {
   389  		t.Fatalf("unable to remove dstDir: %s", err)
   390  	}
   391  
   392  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   393  		t.Fatalf("unable to make dstDir: %s", err)
   394  	}
   395  
   396  	dstDir = joinTrailingSep(tmpDirB, "dir1")
   397  
   398  	if err = testCopyHelper(t, srcPath, dstDir); err != nil {
   399  		t.Fatalf("unexpected error %T: %s", err, err)
   400  	}
   401  
   402  	if err = fileContentsEqual(t, srcPath, dstPath); err != nil {
   403  		t.Fatal(err)
   404  	}
   405  }
   406  
   407  // E. SRC specifies a directory and DST does not exist. This should create a
   408  //    directory at DST and copy the contents of the SRC directory into the DST
   409  //    directory. Ensure this works whether DST has a trailing path separator or
   410  //    not.
   411  func TestCopyCaseE(t *testing.T) {
   412  	tmpDirA, tmpDirB := getTestTempDirs(t)
   413  	defer removeAllPaths(tmpDirA, tmpDirB)
   414  
   415  	// Load A with some sample files and directories.
   416  	createSampleDir(t, tmpDirA)
   417  
   418  	srcDir := filepath.Join(tmpDirA, "dir1")
   419  	dstDir := filepath.Join(tmpDirB, "testDir")
   420  
   421  	var err error
   422  
   423  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   424  		t.Fatalf("unexpected error %T: %s", err, err)
   425  	}
   426  
   427  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   428  		t.Log("dir contents not equal")
   429  		logDirContents(t, tmpDirA)
   430  		logDirContents(t, tmpDirB)
   431  		t.Fatal(err)
   432  	}
   433  
   434  	// Now try again but using a trailing path separator for dstDir.
   435  
   436  	if err = os.RemoveAll(dstDir); err != nil {
   437  		t.Fatalf("unable to remove dstDir: %s", err)
   438  	}
   439  
   440  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   441  
   442  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   443  		t.Fatalf("unexpected error %T: %s", err, err)
   444  	}
   445  
   446  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   447  		t.Fatal(err)
   448  	}
   449  }
   450  
   451  // F. SRC specifies a directory and DST exists as a file. This should cause an
   452  //    error as it is not possible to overwrite a file with a directory.
   453  func TestCopyCaseF(t *testing.T) {
   454  	tmpDirA, tmpDirB := getTestTempDirs(t)
   455  	defer removeAllPaths(tmpDirA, tmpDirB)
   456  
   457  	// Load A and B with some sample files and directories.
   458  	createSampleDir(t, tmpDirA)
   459  	createSampleDir(t, tmpDirB)
   460  
   461  	srcDir := filepath.Join(tmpDirA, "dir1")
   462  	dstFile := filepath.Join(tmpDirB, "file1")
   463  
   464  	var err error
   465  
   466  	if err = testCopyHelper(t, srcDir, dstFile); err == nil {
   467  		t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   468  	}
   469  
   470  	if err != ErrCannotCopyDir {
   471  		t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   472  	}
   473  }
   474  
   475  // G. SRC specifies a directory and DST exists as a directory. This should copy
   476  //    the SRC directory and all its contents to the DST directory. Ensure this
   477  //    works whether DST has a trailing path separator or not.
   478  func TestCopyCaseG(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  	srcDir := filepath.Join(tmpDirA, "dir1")
   487  	dstDir := filepath.Join(tmpDirB, "dir2")
   488  	resultDir := filepath.Join(dstDir, "dir1")
   489  
   490  	var err error
   491  
   492  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   493  		t.Fatalf("unexpected error %T: %s", err, err)
   494  	}
   495  
   496  	if err = dirContentsEqual(t, resultDir, srcDir); err != nil {
   497  		t.Fatal(err)
   498  	}
   499  
   500  	// Now try again but using a trailing path separator for dstDir.
   501  
   502  	if err = os.RemoveAll(dstDir); err != nil {
   503  		t.Fatalf("unable to remove dstDir: %s", err)
   504  	}
   505  
   506  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   507  		t.Fatalf("unable to make dstDir: %s", err)
   508  	}
   509  
   510  	dstDir = joinTrailingSep(tmpDirB, "dir2")
   511  
   512  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   513  		t.Fatalf("unexpected error %T: %s", err, err)
   514  	}
   515  
   516  	if err = dirContentsEqual(t, resultDir, srcDir); err != nil {
   517  		t.Fatal(err)
   518  	}
   519  }
   520  
   521  // H. SRC specifies a directory's contents only and DST does not exist. This
   522  //    should create a directory at DST and copy the contents of the SRC
   523  //    directory (but not the directory itself) into the DST directory. Ensure
   524  //    this works whether DST has a trailing path separator or not.
   525  func TestCopyCaseH(t *testing.T) {
   526  	tmpDirA, tmpDirB := getTestTempDirs(t)
   527  	defer removeAllPaths(tmpDirA, tmpDirB)
   528  
   529  	// Load A with some sample files and directories.
   530  	createSampleDir(t, tmpDirA)
   531  
   532  	srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
   533  	dstDir := filepath.Join(tmpDirB, "testDir")
   534  
   535  	var err error
   536  
   537  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   538  		t.Fatalf("unexpected error %T: %s", err, err)
   539  	}
   540  
   541  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   542  		t.Log("dir contents not equal")
   543  		logDirContents(t, tmpDirA)
   544  		logDirContents(t, tmpDirB)
   545  		t.Fatal(err)
   546  	}
   547  
   548  	// Now try again but using a trailing path separator for dstDir.
   549  
   550  	if err = os.RemoveAll(dstDir); err != nil {
   551  		t.Fatalf("unable to remove dstDir: %s", err)
   552  	}
   553  
   554  	dstDir = joinTrailingSep(tmpDirB, "testDir")
   555  
   556  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   557  		t.Fatalf("unexpected error %T: %s", err, err)
   558  	}
   559  
   560  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   561  		t.Log("dir contents not equal")
   562  		logDirContents(t, tmpDirA)
   563  		logDirContents(t, tmpDirB)
   564  		t.Fatal(err)
   565  	}
   566  }
   567  
   568  // I. SRC specifies a direcotry's contents only and DST exists as a file. This
   569  //    should cause an error as it is not possible to overwrite a file with a
   570  //    directory.
   571  func TestCopyCaseI(t *testing.T) {
   572  	tmpDirA, tmpDirB := getTestTempDirs(t)
   573  	defer removeAllPaths(tmpDirA, tmpDirB)
   574  
   575  	// Load A and B with some sample files and directories.
   576  	createSampleDir(t, tmpDirA)
   577  	createSampleDir(t, tmpDirB)
   578  
   579  	srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
   580  	dstFile := filepath.Join(tmpDirB, "file1")
   581  
   582  	var err error
   583  
   584  	if err = testCopyHelper(t, srcDir, dstFile); err == nil {
   585  		t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
   586  	}
   587  
   588  	if err != ErrCannotCopyDir {
   589  		t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
   590  	}
   591  }
   592  
   593  // J. SRC specifies a directory's contents only and DST exists as a directory.
   594  //    This should copy the contents of the SRC directory (but not the directory
   595  //    itself) into the DST directory. Ensure this works whether DST has a
   596  //    trailing path separator or not.
   597  func TestCopyCaseJ(t *testing.T) {
   598  	tmpDirA, tmpDirB := getTestTempDirs(t)
   599  	defer removeAllPaths(tmpDirA, tmpDirB)
   600  
   601  	// Load A and B with some sample files and directories.
   602  	createSampleDir(t, tmpDirA)
   603  	createSampleDir(t, tmpDirB)
   604  
   605  	srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
   606  	dstDir := filepath.Join(tmpDirB, "dir5")
   607  
   608  	var err error
   609  
   610  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   611  		t.Fatalf("unexpected error %T: %s", err, err)
   612  	}
   613  
   614  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   615  		t.Fatal(err)
   616  	}
   617  
   618  	// Now try again but using a trailing path separator for dstDir.
   619  
   620  	if err = os.RemoveAll(dstDir); err != nil {
   621  		t.Fatalf("unable to remove dstDir: %s", err)
   622  	}
   623  
   624  	if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
   625  		t.Fatalf("unable to make dstDir: %s", err)
   626  	}
   627  
   628  	dstDir = joinTrailingSep(tmpDirB, "dir5")
   629  
   630  	if err = testCopyHelper(t, srcDir, dstDir); err != nil {
   631  		t.Fatalf("unexpected error %T: %s", err, err)
   632  	}
   633  
   634  	if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
   635  		t.Fatal(err)
   636  	}
   637  }