github.com/saymoon/flop@v0.1.6-0.20201205092451-00912199cc96/copy_test.go (about)

     1  package flop
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/stretchr/testify/assert"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"testing"
    10  )
    11  
    12  // debug will perform advanced logging if set to true
    13  // set to false to keep test results more terse
    14  const debug = true
    15  
    16  func TestFileContentInCopy(t *testing.T) {
    17  	assert := assert.New(t)
    18  	tests := []struct {
    19  		name    string
    20  		inFile  string
    21  		outFile string
    22  	}{
    23  		//{
    24  		//	name:    "basic_file_copy",
    25  		//	inFile:  tmpFile(),
    26  		//	outFile: tmpFile(),
    27  		//},
    28  		{
    29  			name:    "unused_dst_file_path",
    30  			inFile:  tmpFile(),
    31  			outFile: tmpFilePathUnused(),
    32  		},
    33  	}
    34  	for _, tt := range tests {
    35  		t.Run(tt.name, func(t *testing.T) {
    36  			content := []byte("foo")
    37  
    38  			err := ioutil.WriteFile(tt.inFile, content, 0655)
    39  			assert.Nil(err)
    40  
    41  			err = SimpleCopy(tt.inFile, tt.outFile)
    42  			assert.Nil(err, "err is:", err)
    43  
    44  			outFileContent, err := ioutil.ReadFile(tt.outFile)
    45  			assert.Nil(err)
    46  			assert.Equal(content, outFileContent)
    47  		})
    48  	}
    49  }
    50  
    51  func TestErrorsInCopy(t *testing.T) {
    52  	assert := assert.New(t)
    53  	tests := []struct {
    54  		name                 string
    55  		inFile               string
    56  		outFile              string
    57  		options              Options
    58  		errExpected          bool
    59  		errSubstringExpected string
    60  	}{
    61  		{
    62  			name:                 "file_does_not_exist",
    63  			inFile:               tmpFilePathUnused(),
    64  			outFile:              tmpFile(),
    65  			errExpected:          true,
    66  			errSubstringExpected: ErrFileNotExist.Error(),
    67  		},
    68  		{
    69  			name:        "file_exist",
    70  			inFile:      tmpFile(),
    71  			outFile:     tmpFile(),
    72  			errExpected: false,
    73  		},
    74  		{
    75  			name:        "dst_path_which_cannot_be_opened_or_created",
    76  			inFile:      tmpFile(),
    77  			outFile:     "/path/that/is/inaccessible",
    78  			errExpected: true,
    79  			options:     Options{Atomic: false},
    80  			//errSubstringExpected: ErrCannotOpenOrCreateDstFile.Error(),
    81  		},
    82  		{
    83  			name:                 "atomic_dst_path_which_cannot_be_opened_or_created",
    84  			inFile:               tmpFile(),
    85  			outFile:              "/path/that/is/inaccessible",
    86  			errExpected:          true,
    87  			options:              Options{Atomic: true},
    88  			errSubstringExpected: ErrCannotCreateTmpFile.Error(),
    89  		},
    90  		{
    91  			name:    "src_directory_when_recurse_is_set_false",
    92  			inFile:  tmpDirPath(),
    93  			outFile: tmpDirPath(),
    94  			options: Options{
    95  				Recursive: false,
    96  			},
    97  			errExpected:          true,
    98  			errSubstringExpected: ErrOmittingDir.Error(),
    99  		},
   100  		{
   101  			name:                 "verify_cannot_overwrite_file_with_dir",
   102  			inFile:               tmpDirPath(),
   103  			outFile:              tmpFile(),
   104  			options:              Options{Recursive: true},
   105  			errExpected:          true,
   106  			errSubstringExpected: ErrCannotOverwriteNonDir.Error(),
   107  		},
   108  		{
   109  			name:                 "err_when_parents_true_but_dst_is_not_dir",
   110  			inFile:               tmpDirPath(),
   111  			outFile:              tmpFile(),
   112  			options:              Options{Parents: true},
   113  			errExpected:          true,
   114  			errSubstringExpected: ErrWithParentsDstMustBeDir.Error(),
   115  		},
   116  	}
   117  	for _, tt := range tests {
   118  		t.Run(tt.name, func(t *testing.T) {
   119  			err := Copy(tt.inFile, tt.outFile, tt.options)
   120  			if tt.errExpected {
   121  				assert.True(errContains(err, tt.errSubstringExpected), fmt.Sprintf("err '%s' does not contain '%s'", err, tt.errSubstringExpected))
   122  			} else {
   123  				assert.Nil(err)
   124  			}
   125  		})
   126  	}
   127  }
   128  
   129  func TestCopyingBasicFileContentsWhenDstFileExists(t *testing.T) {
   130  	assert := assert.New(t)
   131  	src, dst := tmpFile(), tmpFile()
   132  	content := []byte("foo")
   133  
   134  	err := ioutil.WriteFile(src, content, 0655)
   135  	assert.Nil(err)
   136  
   137  	err = SimpleCopy(src, dst)
   138  	assert.Nil(err)
   139  
   140  	b, err := ioutil.ReadFile(dst)
   141  	assert.Nil(err)
   142  	assert.Equal(content, b)
   143  }
   144  
   145  func TestCopyingBasicFileContentsWhenDstFileDoesNotExist(t *testing.T) {
   146  	assert := assert.New(t)
   147  	src := tmpFile()
   148  	dst := tmpFilePathUnused()
   149  	content := []byte("foo")
   150  	err := ioutil.WriteFile(src, content, 0655)
   151  	assert.Nil(err)
   152  
   153  	err = SimpleCopy(src, dst)
   154  	assert.Nil(err)
   155  
   156  	b, err := ioutil.ReadFile(dst)
   157  	assert.Nil(err)
   158  	assert.Equal(content, b)
   159  }
   160  
   161  func TestCopyingSameFileReturnsNoError(t *testing.T) {
   162  	assert := assert.New(t)
   163  	tmp := tmpFile()
   164  	err := SimpleCopy(tmp, tmp)
   165  	assert.Nil(err)
   166  }
   167  
   168  func TestCopySrcFileToDstDir(t *testing.T) {
   169  	assert := assert.New(t)
   170  	src := tmpFile()
   171  	content := []byte("foo")
   172  
   173  	tests := []struct {
   174  		name                 string
   175  		dst                  string
   176  		options              Options
   177  		errExpected          bool
   178  		errSubstringExpected string
   179  	}{
   180  		{
   181  			name:    "auto_write_files_to_dirs",
   182  			dst:     tmpDirPath(),
   183  			options: Options{AppendNameToPath: true},
   184  		},
   185  		{
   186  			name:                 "auto_write_files_to_dirs",
   187  			dst:                  tmpDirPath(),
   188  			options:              Options{AppendNameToPath: false},
   189  			errExpected:          true,
   190  			errSubstringExpected: ErrWritingFileToExistingDir.Error(),
   191  		},
   192  	}
   193  
   194  	for _, tt := range tests {
   195  		t.Run(tt.name, func(t *testing.T) {
   196  			err := ioutil.WriteFile(src, content, 0655)
   197  			assert.Nil(err)
   198  
   199  			// make sure we don't error
   200  			err = Copy(src, tt.dst, tt.options)
   201  			if tt.errExpected {
   202  				assert.True(errContains(err, tt.errSubstringExpected))
   203  			} else {
   204  				assert.Nil(err)
   205  				// make sure the dst inFile is in the dir where we expect it
   206  				b, err := ioutil.ReadFile(filepath.Join(tt.dst, filepath.Base(src)))
   207  				assert.Nil(err)
   208  				assert.Equal(content, b)
   209  			}
   210  		})
   211  	}
   212  }
   213  
   214  func TestCopyingDirToDirWhenDstDirDoesNotExist(t *testing.T) {
   215  	assert := assert.New(t)
   216  	src, dst := tmpDirPath(), tmpDirPathUnused()
   217  
   218  	// make files with content
   219  	file1 := filepath.Join(src, "file1.txt")
   220  	assert.Nil(os.Mkdir(filepath.Join(src, "subdir"), 0777))
   221  	file3 := filepath.Join(src, "subdir", "file2.txt")
   222  	files := []string{file1, file3}
   223  
   224  	// write content to files
   225  	content := []byte("foo")
   226  	for _, file := range files {
   227  		assert.Nil(ioutil.WriteFile(file, content, 0655))
   228  	}
   229  
   230  	// ensure src exists
   231  	_, err := os.Open(src)
   232  	assert.False(os.IsNotExist(err))
   233  
   234  	// copy directory to directory recursively
   235  	assert.Nil(Copy(src, dst, Options{Recursive: true}))
   236  
   237  	// verify the file contents
   238  	assert.Nil(err)
   239  	for _, file := range files {
   240  		b, err := ioutil.ReadFile(file)
   241  		assert.Nil(err)
   242  		assert.Equal(content, b)
   243  	}
   244  }
   245  
   246  func TestCopyingDirToDirWhenDstContainsTrailingSlash(t *testing.T) {
   247  	// at some point we try to determine if the dst directory's parent
   248  	// exists. when given /some/path the parent is /some.  when given
   249  	// /some/path/ the parent is /some/path.  this means the destination
   250  	// dir is not created when it should be.  this tests for that case.
   251  
   252  	assert := assert.New(t)
   253  	src, dst := tmpDirPath(), tmpDirPathUnused()+"/"
   254  
   255  	// make files with content
   256  	file1 := filepath.Join(src, "file1.txt")
   257  	assert.Nil(os.Mkdir(filepath.Join(src, "subdir"), 0777))
   258  	file3 := filepath.Join(src, "subdir", "file2.txt")
   259  	files := []string{file1, file3}
   260  
   261  	// write content to files
   262  	content := []byte("foo")
   263  	for _, file := range files {
   264  		assert.Nil(ioutil.WriteFile(file, content, 0655))
   265  	}
   266  
   267  	// ensure src exists
   268  	_, err := os.Open(src)
   269  	assert.False(os.IsNotExist(err))
   270  
   271  	// copy directory to directory recursively
   272  	assert.Nil(Copy(src, dst, Options{Recursive: true}))
   273  
   274  	// verify the file contents
   275  	assert.Nil(err)
   276  	for _, file := range files {
   277  		b, err := ioutil.ReadFile(file)
   278  		assert.Nil(err)
   279  		assert.Equal(content, b)
   280  	}
   281  }
   282  
   283  func TestCopyingFileWithParentsFlagCreatesParentDirs(t *testing.T) {
   284  	assert := assert.New(t)
   285  	workDir := tmpDirPath()
   286  	err := os.Chdir(workDir)
   287  	assert.Nil(err)
   288  
   289  	// create nested path
   290  	nestedPath := "nested/path/"
   291  	assert.Nil(os.MkdirAll(nestedPath, 0777))
   292  
   293  	// make destination dir
   294  	dst := "dest"
   295  	assert.Nil(os.Mkdir(dst, 0777))
   296  
   297  	// make files with content
   298  	content := []byte("foo")
   299  	fileName := "nestedFile.txt"
   300  	nestedFile := filepath.Join(nestedPath, fileName)
   301  
   302  	// write content to file
   303  	assert.Nil(ioutil.WriteFile(nestedFile, content, 0655))
   304  
   305  	// ensure nested exists just to be sure
   306  	_, err = os.Open(nestedFile)
   307  	assert.False(os.IsNotExist(err))
   308  
   309  	assert.Nil(Copy(nestedFile, dst, Options{
   310  		Recursive:    true,
   311  		Parents:      true,
   312  		DebugLogFunc: debugLogger,
   313  		InfoLogFunc:  infoLogger,
   314  	}))
   315  
   316  	expectedFile := filepath.Join(dst, nestedPath, fileName)
   317  
   318  	// ensure the file exists where we expect it to
   319  	var exists bool
   320  	if _, err = os.Stat(expectedFile); !os.IsNotExist(err) {
   321  		exists = true
   322  	}
   323  	assert.True(exists)
   324  }
   325  
   326  func TestCopyingDirWithParentsFlagCreatesParentDirs(t *testing.T) {
   327  	assert := assert.New(t)
   328  	workDir := tmpDirPath()
   329  	assert.Nil(os.Chdir(workDir))
   330  
   331  	nestedPath := "nested/path/"
   332  	assert.Nil(os.MkdirAll(nestedPath, 0777))
   333  
   334  	// make destination dir
   335  	dst := "dest"
   336  	assert.Nil(os.Mkdir(dst, 0777))
   337  
   338  	// make files with content
   339  	content := []byte("foo")
   340  	fileName := "nestedFile.txt"
   341  	nestedFile := filepath.Join(nestedPath, fileName)
   342  
   343  	// write content to file
   344  	assert.Nil(ioutil.WriteFile(nestedFile, content, 0655))
   345  
   346  	// ensure nested exists just to be sure
   347  	_, err := os.Open(nestedFile)
   348  	assert.False(os.IsNotExist(err))
   349  
   350  	assert.Nil(Copy(nestedPath, dst, Options{Recursive: true, Parents: true}))
   351  
   352  	expectedFile := filepath.Join(dst, nestedPath, fileName)
   353  
   354  	// ensure the file exists where we expect it to
   355  	var exists bool
   356  	if _, err = os.Stat(expectedFile); !os.IsNotExist(err) {
   357  		exists = true
   358  	}
   359  	assert.True(exists)
   360  	b, err := ioutil.ReadFile(expectedFile)
   361  	assert.Nil(err)
   362  	assert.Equal(content, b)
   363  }
   364  
   365  func TestNoClobberFile(t *testing.T) {
   366  	assert := assert.New(t)
   367  	tests := []struct {
   368  		name string
   369  		opts Options
   370  		// do we expect the dst file to be overwritten?
   371  		expectOverwrite bool
   372  	}{
   373  		{
   374  			name:            "expect_clobber",
   375  			opts:            Options{NoClobber: false},
   376  			expectOverwrite: true,
   377  		},
   378  		{
   379  			name:            "basic_no_clobber",
   380  			opts:            Options{NoClobber: true},
   381  			expectOverwrite: false,
   382  		},
   383  	}
   384  	for _, tt := range tests {
   385  		t.Run(tt.name, func(t *testing.T) {
   386  			srcContent := []byte("source")
   387  			dstContent := []byte("dest")
   388  
   389  			src, dst := tmpFile(), tmpFile()
   390  
   391  			assert.Nil(ioutil.WriteFile(src, srcContent, 0655))
   392  			assert.Nil(ioutil.WriteFile(dst, dstContent, 0655))
   393  
   394  			assert.Nil(Copy(src, dst, tt.opts))
   395  			b, err := ioutil.ReadFile(dst)
   396  			assert.Nil(err)
   397  			if tt.expectOverwrite {
   398  				assert.Equal(srcContent, b)
   399  			} else {
   400  				assert.Equal(dstContent, b)
   401  			}
   402  		})
   403  	}
   404  }
   405  
   406  func TestNoErrWhenCopyfileAlreadyExists(t *testing.T) {
   407  	assert := assert.New(t)
   408  	src, dst := tmpFile(), tmpFile()
   409  	dstDir := filepath.Dir(dst)
   410  
   411  	// use the known expected name for a copy file
   412  	copyFileName := "copyfile-"
   413  	copyFileFullyQualified := filepath.Join(dstDir, copyFileName)
   414  	assert.Nil(ioutil.WriteFile(copyFileFullyQualified, []byte("foo"), 0655))
   415  	// hold the copy file open to give our atomic copy code problems
   416  	f, err := os.Open(copyFileFullyQualified)
   417  	assert.Nil(err)
   418  
   419  	assert.Nil(Copy(src, dst, Options{Atomic: true}))
   420  	_ = f.Close()
   421  }
   422  
   423  func TestCreatingSimpleBackupFile(t *testing.T) {
   424  	assert := assert.New(t)
   425  	src, dst := tmpFile(), tmpFile()
   426  
   427  	content := []byte("foo")
   428  	assert.Nil(ioutil.WriteFile(dst, content, 0655))
   429  
   430  	assert.Nil(Copy(src, dst, Options{Backup: "simple"}))
   431  	expectedBkpFile := dst + "~"
   432  
   433  	_, err := os.Stat(expectedBkpFile)
   434  	assert.Nil(err)
   435  	assert.False(os.IsNotExist(err))
   436  
   437  	b, err := ioutil.ReadFile(expectedBkpFile)
   438  	assert.Nil(err)
   439  	assert.Equal(content, b)
   440  }
   441  
   442  func TestCreatingExistingBackupFilesWhenBackupIsNotPresent(t *testing.T) {
   443  	assert := assert.New(t)
   444  	src, dst := tmpFile(), tmpFile()
   445  
   446  	content := []byte("foo")
   447  	assert.Nil(ioutil.WriteFile(dst, content, 0655))
   448  
   449  	assert.Nil(Copy(src, dst, Options{Backup: "existing"}))
   450  
   451  	expectedBkpFile := dst + "~"
   452  	_, err := os.Stat(expectedBkpFile)
   453  	assert.Nil(err)
   454  	assert.False(os.IsNotExist(err))
   455  
   456  	b, err := ioutil.ReadFile(expectedBkpFile)
   457  	assert.Nil(err)
   458  	assert.Equal(content, b)
   459  }
   460  
   461  func TestNumberedBackupFileRegex(t *testing.T) {
   462  	assert := assert.New(t)
   463  	tests := []struct {
   464  		in          string
   465  		expectMatch bool
   466  		msg         string
   467  	}{
   468  		{"f.txt.~1~", true, ""},
   469  		{"f.txt~1~", false, "missing . before the ~"},
   470  		{"f.txt.~12~", true, ""},
   471  		{"f.txt.~12345~", true, ""},
   472  		{"f.txt.~123456789~", false, "we don't expect to see numbers this long"},
   473  		{"f.txt.~1x5~", false, "alpha in between numbers"},
   474  		{"f.txt~", false, ""},
   475  		{"f.t123xt", false, ""},
   476  		{"123", false, ""},
   477  	}
   478  	for _, tt := range tests {
   479  		t.Run(tt.in, func(t *testing.T) {
   480  			assert.Equal(tt.expectMatch, numberedBackupFile.MatchString(tt.in), tt.msg)
   481  		})
   482  	}
   483  }
   484  
   485  func TestNumberedBackupRegexSubstring(t *testing.T) {
   486  	assert := assert.New(t)
   487  	tests := []struct {
   488  		in  string
   489  		sub string // we expect to see this substring in index 1
   490  	}{
   491  		{"f.txt.~1~", "1"},
   492  		{"f.txt.~12~", "12"},
   493  		{"123.456.~78~", "78"},
   494  	}
   495  	for _, tt := range tests {
   496  		t.Run(tt.in, func(t *testing.T) {
   497  			sub := numberedBackupFile.FindStringSubmatch(tt.in)
   498  			if len(sub) == 0 {
   499  				assert.FailNow("substring has length 0")
   500  			}
   501  			if len(sub) <= 1 {
   502  				assert.FailNow("substring was not greater than 1 as we expect")
   503  			}
   504  			assert.Equal(string(sub[1]), tt.sub)
   505  		})
   506  	}
   507  }
   508  
   509  //noinspection ALL
   510  func TestCreatingNumberedBackupFilesWhenBackupFilesAreNumbered(t *testing.T) {
   511  	assert := assert.New(t)
   512  	tests := []struct {
   513  		name                  string
   514  		numberedFilesToCreate []int
   515  		expectedFileNum       int
   516  	}{
   517  		{"single_backup_file", []int{1}, 2},
   518  		{"multiple_files", []int{1, 2}, 3},
   519  		{"skip_files", []int{1, 3}, 4},
   520  		{"skip_first_num", []int{2, 3}, 4},
   521  		{"larger_numbers_just_for_good_measure", []int{5, 7, 5431}, 5432},
   522  	}
   523  	for _, tt := range tests {
   524  		t.Run(tt.name, func(t *testing.T) {
   525  			src, dst := tmpFile(), tmpFile()
   526  			content := []byte("foo")
   527  			assert.Nil(ioutil.WriteFile(dst, content, 0655))
   528  			defer os.Remove(dst)
   529  
   530  			// write all of the numbered files so we have a starting point
   531  			for _, num := range tt.numberedFilesToCreate {
   532  				numberedFile := fmt.Sprintf("%s.~%d~", dst, num)
   533  				assert.Nil(ioutil.WriteFile(numberedFile, content, 0655))
   534  				defer os.Remove(numberedFile)
   535  			}
   536  
   537  			assert.Nil(Copy(src, dst, Options{Backup: "numbered"}))
   538  			expectedBkpFile := fmt.Sprintf("%s.~%d~", dst, tt.expectedFileNum)
   539  			b, err := ioutil.ReadFile(expectedBkpFile)
   540  			assert.Nil(err)
   541  			assert.Equal(content, b)
   542  		})
   543  	}
   544  }