github.com/ncdc/docker@v0.10.1-0.20160129113957-6c6729ef5b74/pkg/fileutils/fileutils_test.go (about)

     1  package fileutils
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path"
     7  	"path/filepath"
     8  	"runtime"
     9  	"strings"
    10  	"testing"
    11  )
    12  
    13  // CopyFile with invalid src
    14  func TestCopyFileWithInvalidSrc(t *testing.T) {
    15  	tempFolder, err := ioutil.TempDir("", "docker-fileutils-test")
    16  	defer os.RemoveAll(tempFolder)
    17  	if err != nil {
    18  		t.Fatal(err)
    19  	}
    20  	bytes, err := CopyFile("/invalid/file/path", path.Join(tempFolder, "dest"))
    21  	if err == nil {
    22  		t.Fatal("Should have fail to copy an invalid src file")
    23  	}
    24  	if bytes != 0 {
    25  		t.Fatal("Should have written 0 bytes")
    26  	}
    27  
    28  }
    29  
    30  // CopyFile with invalid dest
    31  func TestCopyFileWithInvalidDest(t *testing.T) {
    32  	tempFolder, err := ioutil.TempDir("", "docker-fileutils-test")
    33  	defer os.RemoveAll(tempFolder)
    34  	if err != nil {
    35  		t.Fatal(err)
    36  	}
    37  	src := path.Join(tempFolder, "file")
    38  	err = ioutil.WriteFile(src, []byte("content"), 0740)
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  	bytes, err := CopyFile(src, path.Join(tempFolder, "/invalid/dest/path"))
    43  	if err == nil {
    44  		t.Fatal("Should have fail to copy an invalid src file")
    45  	}
    46  	if bytes != 0 {
    47  		t.Fatal("Should have written 0 bytes")
    48  	}
    49  
    50  }
    51  
    52  // CopyFile with same src and dest
    53  func TestCopyFileWithSameSrcAndDest(t *testing.T) {
    54  	tempFolder, err := ioutil.TempDir("", "docker-fileutils-test")
    55  	defer os.RemoveAll(tempFolder)
    56  	if err != nil {
    57  		t.Fatal(err)
    58  	}
    59  	file := path.Join(tempFolder, "file")
    60  	err = ioutil.WriteFile(file, []byte("content"), 0740)
    61  	if err != nil {
    62  		t.Fatal(err)
    63  	}
    64  	bytes, err := CopyFile(file, file)
    65  	if err != nil {
    66  		t.Fatal(err)
    67  	}
    68  	if bytes != 0 {
    69  		t.Fatal("Should have written 0 bytes as it is the same file.")
    70  	}
    71  }
    72  
    73  // CopyFile with same src and dest but path is different and not clean
    74  func TestCopyFileWithSameSrcAndDestWithPathNameDifferent(t *testing.T) {
    75  	tempFolder, err := ioutil.TempDir("", "docker-fileutils-test")
    76  	defer os.RemoveAll(tempFolder)
    77  	if err != nil {
    78  		t.Fatal(err)
    79  	}
    80  	testFolder := path.Join(tempFolder, "test")
    81  	err = os.MkdirAll(testFolder, 0740)
    82  	if err != nil {
    83  		t.Fatal(err)
    84  	}
    85  	file := path.Join(testFolder, "file")
    86  	sameFile := testFolder + "/../test/file"
    87  	err = ioutil.WriteFile(file, []byte("content"), 0740)
    88  	if err != nil {
    89  		t.Fatal(err)
    90  	}
    91  	bytes, err := CopyFile(file, sameFile)
    92  	if err != nil {
    93  		t.Fatal(err)
    94  	}
    95  	if bytes != 0 {
    96  		t.Fatal("Should have written 0 bytes as it is the same file.")
    97  	}
    98  }
    99  
   100  func TestCopyFile(t *testing.T) {
   101  	tempFolder, err := ioutil.TempDir("", "docker-fileutils-test")
   102  	defer os.RemoveAll(tempFolder)
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  	src := path.Join(tempFolder, "src")
   107  	dest := path.Join(tempFolder, "dest")
   108  	ioutil.WriteFile(src, []byte("content"), 0777)
   109  	ioutil.WriteFile(dest, []byte("destContent"), 0777)
   110  	bytes, err := CopyFile(src, dest)
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  	if bytes != 7 {
   115  		t.Fatalf("Should have written %d bytes but wrote %d", 7, bytes)
   116  	}
   117  	actual, err := ioutil.ReadFile(dest)
   118  	if err != nil {
   119  		t.Fatal(err)
   120  	}
   121  	if string(actual) != "content" {
   122  		t.Fatalf("Dest content was '%s', expected '%s'", string(actual), "content")
   123  	}
   124  }
   125  
   126  // Reading a symlink to a directory must return the directory
   127  func TestReadSymlinkedDirectoryExistingDirectory(t *testing.T) {
   128  	var err error
   129  	if err = os.Mkdir("/tmp/testReadSymlinkToExistingDirectory", 0777); err != nil {
   130  		t.Errorf("failed to create directory: %s", err)
   131  	}
   132  
   133  	if err = os.Symlink("/tmp/testReadSymlinkToExistingDirectory", "/tmp/dirLinkTest"); err != nil {
   134  		t.Errorf("failed to create symlink: %s", err)
   135  	}
   136  
   137  	var path string
   138  	if path, err = ReadSymlinkedDirectory("/tmp/dirLinkTest"); err != nil {
   139  		t.Fatalf("failed to read symlink to directory: %s", err)
   140  	}
   141  
   142  	if path != "/tmp/testReadSymlinkToExistingDirectory" {
   143  		t.Fatalf("symlink returned unexpected directory: %s", path)
   144  	}
   145  
   146  	if err = os.Remove("/tmp/testReadSymlinkToExistingDirectory"); err != nil {
   147  		t.Errorf("failed to remove temporary directory: %s", err)
   148  	}
   149  
   150  	if err = os.Remove("/tmp/dirLinkTest"); err != nil {
   151  		t.Errorf("failed to remove symlink: %s", err)
   152  	}
   153  }
   154  
   155  // Reading a non-existing symlink must fail
   156  func TestReadSymlinkedDirectoryNonExistingSymlink(t *testing.T) {
   157  	var path string
   158  	var err error
   159  	if path, err = ReadSymlinkedDirectory("/tmp/test/foo/Non/ExistingPath"); err == nil {
   160  		t.Fatalf("error expected for non-existing symlink")
   161  	}
   162  
   163  	if path != "" {
   164  		t.Fatalf("expected empty path, but '%s' was returned", path)
   165  	}
   166  }
   167  
   168  // Reading a symlink to a file must fail
   169  func TestReadSymlinkedDirectoryToFile(t *testing.T) {
   170  	var err error
   171  	var file *os.File
   172  
   173  	if file, err = os.Create("/tmp/testReadSymlinkToFile"); err != nil {
   174  		t.Fatalf("failed to create file: %s", err)
   175  	}
   176  
   177  	file.Close()
   178  
   179  	if err = os.Symlink("/tmp/testReadSymlinkToFile", "/tmp/fileLinkTest"); err != nil {
   180  		t.Errorf("failed to create symlink: %s", err)
   181  	}
   182  
   183  	var path string
   184  	if path, err = ReadSymlinkedDirectory("/tmp/fileLinkTest"); err == nil {
   185  		t.Fatalf("ReadSymlinkedDirectory on a symlink to a file should've failed")
   186  	}
   187  
   188  	if path != "" {
   189  		t.Fatalf("path should've been empty: %s", path)
   190  	}
   191  
   192  	if err = os.Remove("/tmp/testReadSymlinkToFile"); err != nil {
   193  		t.Errorf("failed to remove file: %s", err)
   194  	}
   195  
   196  	if err = os.Remove("/tmp/fileLinkTest"); err != nil {
   197  		t.Errorf("failed to remove symlink: %s", err)
   198  	}
   199  }
   200  
   201  func TestWildcardMatches(t *testing.T) {
   202  	match, _ := Matches("fileutils.go", []string{"*"})
   203  	if match != true {
   204  		t.Errorf("failed to get a wildcard match, got %v", match)
   205  	}
   206  }
   207  
   208  // A simple pattern match should return true.
   209  func TestPatternMatches(t *testing.T) {
   210  	match, _ := Matches("fileutils.go", []string{"*.go"})
   211  	if match != true {
   212  		t.Errorf("failed to get a match, got %v", match)
   213  	}
   214  }
   215  
   216  // An exclusion followed by an inclusion should return true.
   217  func TestExclusionPatternMatchesPatternBefore(t *testing.T) {
   218  	match, _ := Matches("fileutils.go", []string{"!fileutils.go", "*.go"})
   219  	if match != true {
   220  		t.Errorf("failed to get true match on exclusion pattern, got %v", match)
   221  	}
   222  }
   223  
   224  // A folder pattern followed by an exception should return false.
   225  func TestPatternMatchesFolderExclusions(t *testing.T) {
   226  	match, _ := Matches("docs/README.md", []string{"docs", "!docs/README.md"})
   227  	if match != false {
   228  		t.Errorf("failed to get a false match on exclusion pattern, got %v", match)
   229  	}
   230  }
   231  
   232  // A folder pattern followed by an exception should return false.
   233  func TestPatternMatchesFolderWithSlashExclusions(t *testing.T) {
   234  	match, _ := Matches("docs/README.md", []string{"docs/", "!docs/README.md"})
   235  	if match != false {
   236  		t.Errorf("failed to get a false match on exclusion pattern, got %v", match)
   237  	}
   238  }
   239  
   240  // A folder pattern followed by an exception should return false.
   241  func TestPatternMatchesFolderWildcardExclusions(t *testing.T) {
   242  	match, _ := Matches("docs/README.md", []string{"docs/*", "!docs/README.md"})
   243  	if match != false {
   244  		t.Errorf("failed to get a false match on exclusion pattern, got %v", match)
   245  	}
   246  }
   247  
   248  // A pattern followed by an exclusion should return false.
   249  func TestExclusionPatternMatchesPatternAfter(t *testing.T) {
   250  	match, _ := Matches("fileutils.go", []string{"*.go", "!fileutils.go"})
   251  	if match != false {
   252  		t.Errorf("failed to get false match on exclusion pattern, got %v", match)
   253  	}
   254  }
   255  
   256  // A filename evaluating to . should return false.
   257  func TestExclusionPatternMatchesWholeDirectory(t *testing.T) {
   258  	match, _ := Matches(".", []string{"*.go"})
   259  	if match != false {
   260  		t.Errorf("failed to get false match on ., got %v", match)
   261  	}
   262  }
   263  
   264  // A single ! pattern should return an error.
   265  func TestSingleExclamationError(t *testing.T) {
   266  	_, err := Matches("fileutils.go", []string{"!"})
   267  	if err == nil {
   268  		t.Errorf("failed to get an error for a single exclamation point, got %v", err)
   269  	}
   270  }
   271  
   272  // A string preceded with a ! should return true from Exclusion.
   273  func TestExclusion(t *testing.T) {
   274  	exclusion := exclusion("!")
   275  	if !exclusion {
   276  		t.Errorf("failed to get true for a single !, got %v", exclusion)
   277  	}
   278  }
   279  
   280  // Matches with no patterns
   281  func TestMatchesWithNoPatterns(t *testing.T) {
   282  	matches, err := Matches("/any/path/there", []string{})
   283  	if err != nil {
   284  		t.Fatal(err)
   285  	}
   286  	if matches {
   287  		t.Fatalf("Should not have match anything")
   288  	}
   289  }
   290  
   291  // Matches with malformed patterns
   292  func TestMatchesWithMalformedPatterns(t *testing.T) {
   293  	matches, err := Matches("/any/path/there", []string{"["})
   294  	if err == nil {
   295  		t.Fatal("Should have failed because of a malformed syntax in the pattern")
   296  	}
   297  	if matches {
   298  		t.Fatalf("Should not have match anything")
   299  	}
   300  }
   301  
   302  // Test lots of variants of patterns & strings
   303  func TestMatches(t *testing.T) {
   304  	tests := []struct {
   305  		pattern string
   306  		text    string
   307  		pass    bool
   308  	}{
   309  		{"**", "file", true},
   310  		{"**", "file/", true},
   311  		{"**/", "file", true}, // weird one
   312  		{"**/", "file/", true},
   313  		{"**", "/", true},
   314  		{"**/", "/", true},
   315  		{"**", "dir/file", true},
   316  		{"**/", "dir/file", false},
   317  		{"**", "dir/file/", true},
   318  		{"**/", "dir/file/", true},
   319  		{"**/**", "dir/file", true},
   320  		{"**/**", "dir/file/", true},
   321  		{"dir/**", "dir/file", true},
   322  		{"dir/**", "dir/file/", true},
   323  		{"dir/**", "dir/dir2/file", true},
   324  		{"dir/**", "dir/dir2/file/", true},
   325  		{"**/dir2/*", "dir/dir2/file", true},
   326  		{"**/dir2/*", "dir/dir2/file/", false},
   327  		{"**/dir2/**", "dir/dir2/dir3/file", true},
   328  		{"**/dir2/**", "dir/dir2/dir3/file/", true},
   329  		{"**file", "file", true},
   330  		{"**file", "dir/file", true},
   331  		{"**/file", "dir/file", true},
   332  		{"**file", "dir/dir/file", true},
   333  		{"**/file", "dir/dir/file", true},
   334  		{"**/file*", "dir/dir/file", true},
   335  		{"**/file*", "dir/dir/file.txt", true},
   336  		{"**/file*txt", "dir/dir/file.txt", true},
   337  		{"**/file*.txt", "dir/dir/file.txt", true},
   338  		{"**/file*.txt*", "dir/dir/file.txt", true},
   339  		{"**/**/*.txt", "dir/dir/file.txt", true},
   340  		{"**/**/*.txt2", "dir/dir/file.txt", false},
   341  		{"**/*.txt", "file.txt", true},
   342  		{"**/**/*.txt", "file.txt", true},
   343  		{"a**/*.txt", "a/file.txt", true},
   344  		{"a**/*.txt", "a/dir/file.txt", true},
   345  		{"a**/*.txt", "a/dir/dir/file.txt", true},
   346  		{"a/*.txt", "a/dir/file.txt", false},
   347  		{"a/*.txt", "a/file.txt", true},
   348  		{"a/*.txt**", "a/file.txt", true},
   349  		{"a[b-d]e", "ae", false},
   350  		{"a[b-d]e", "ace", true},
   351  		{"a[b-d]e", "aae", false},
   352  		{"a[^b-d]e", "aze", true},
   353  		{".*", ".foo", true},
   354  		{".*", "foo", false},
   355  		{"abc.def", "abcdef", false},
   356  		{"abc.def", "abc.def", true},
   357  		{"abc.def", "abcZdef", false},
   358  		{"abc?def", "abcZdef", true},
   359  		{"abc?def", "abcdef", false},
   360  		{"a\\*b", "a*b", true},
   361  		{"a\\", "a", false},
   362  		{"a\\", "a\\", false},
   363  		{"a\\\\", "a\\", true},
   364  		{"**/foo/bar", "foo/bar", true},
   365  		{"**/foo/bar", "dir/foo/bar", true},
   366  		{"**/foo/bar", "dir/dir2/foo/bar", true},
   367  		{"abc/**", "abc", false},
   368  		{"abc/**", "abc/def", true},
   369  		{"abc/**", "abc/def/ghi", true},
   370  	}
   371  
   372  	for _, test := range tests {
   373  		res, _ := regexpMatch(test.pattern, test.text)
   374  		if res != test.pass {
   375  			t.Fatalf("Failed: %v - res:%v", test, res)
   376  		}
   377  	}
   378  }
   379  
   380  // An empty string should return true from Empty.
   381  func TestEmpty(t *testing.T) {
   382  	empty := empty("")
   383  	if !empty {
   384  		t.Errorf("failed to get true for an empty string, got %v", empty)
   385  	}
   386  }
   387  
   388  func TestCleanPatterns(t *testing.T) {
   389  	cleaned, _, _, _ := CleanPatterns([]string{"docs", "config"})
   390  	if len(cleaned) != 2 {
   391  		t.Errorf("expected 2 element slice, got %v", len(cleaned))
   392  	}
   393  }
   394  
   395  func TestCleanPatternsStripEmptyPatterns(t *testing.T) {
   396  	cleaned, _, _, _ := CleanPatterns([]string{"docs", "config", ""})
   397  	if len(cleaned) != 2 {
   398  		t.Errorf("expected 2 element slice, got %v", len(cleaned))
   399  	}
   400  }
   401  
   402  func TestCleanPatternsExceptionFlag(t *testing.T) {
   403  	_, _, exceptions, _ := CleanPatterns([]string{"docs", "!docs/README.md"})
   404  	if !exceptions {
   405  		t.Errorf("expected exceptions to be true, got %v", exceptions)
   406  	}
   407  }
   408  
   409  func TestCleanPatternsLeadingSpaceTrimmed(t *testing.T) {
   410  	_, _, exceptions, _ := CleanPatterns([]string{"docs", "  !docs/README.md"})
   411  	if !exceptions {
   412  		t.Errorf("expected exceptions to be true, got %v", exceptions)
   413  	}
   414  }
   415  
   416  func TestCleanPatternsTrailingSpaceTrimmed(t *testing.T) {
   417  	_, _, exceptions, _ := CleanPatterns([]string{"docs", "!docs/README.md  "})
   418  	if !exceptions {
   419  		t.Errorf("expected exceptions to be true, got %v", exceptions)
   420  	}
   421  }
   422  
   423  func TestCleanPatternsErrorSingleException(t *testing.T) {
   424  	_, _, _, err := CleanPatterns([]string{"!"})
   425  	if err == nil {
   426  		t.Errorf("expected error on single exclamation point, got %v", err)
   427  	}
   428  }
   429  
   430  func TestCleanPatternsFolderSplit(t *testing.T) {
   431  	_, dirs, _, _ := CleanPatterns([]string{"docs/config/CONFIG.md"})
   432  	if dirs[0][0] != "docs" {
   433  		t.Errorf("expected first element in dirs slice to be docs, got %v", dirs[0][1])
   434  	}
   435  	if dirs[0][1] != "config" {
   436  		t.Errorf("expected first element in dirs slice to be config, got %v", dirs[0][1])
   437  	}
   438  }
   439  
   440  func TestCreateIfNotExistsDir(t *testing.T) {
   441  	tempFolder, err := ioutil.TempDir("", "docker-fileutils-test")
   442  	if err != nil {
   443  		t.Fatal(err)
   444  	}
   445  	defer os.RemoveAll(tempFolder)
   446  
   447  	folderToCreate := filepath.Join(tempFolder, "tocreate")
   448  
   449  	if err := CreateIfNotExists(folderToCreate, true); err != nil {
   450  		t.Fatal(err)
   451  	}
   452  	fileinfo, err := os.Stat(folderToCreate)
   453  	if err != nil {
   454  		t.Fatalf("Should have create a folder, got %v", err)
   455  	}
   456  
   457  	if !fileinfo.IsDir() {
   458  		t.Fatalf("Should have been a dir, seems it's not")
   459  	}
   460  }
   461  
   462  func TestCreateIfNotExistsFile(t *testing.T) {
   463  	tempFolder, err := ioutil.TempDir("", "docker-fileutils-test")
   464  	if err != nil {
   465  		t.Fatal(err)
   466  	}
   467  	defer os.RemoveAll(tempFolder)
   468  
   469  	fileToCreate := filepath.Join(tempFolder, "file/to/create")
   470  
   471  	if err := CreateIfNotExists(fileToCreate, false); err != nil {
   472  		t.Fatal(err)
   473  	}
   474  	fileinfo, err := os.Stat(fileToCreate)
   475  	if err != nil {
   476  		t.Fatalf("Should have create a file, got %v", err)
   477  	}
   478  
   479  	if fileinfo.IsDir() {
   480  		t.Fatalf("Should have been a file, seems it's not")
   481  	}
   482  }
   483  
   484  // These matchTests are stolen from go's filepath Match tests.
   485  type matchTest struct {
   486  	pattern, s string
   487  	match      bool
   488  	err        error
   489  }
   490  
   491  var matchTests = []matchTest{
   492  	{"abc", "abc", true, nil},
   493  	{"*", "abc", true, nil},
   494  	{"*c", "abc", true, nil},
   495  	{"a*", "a", true, nil},
   496  	{"a*", "abc", true, nil},
   497  	{"a*", "ab/c", false, nil},
   498  	{"a*/b", "abc/b", true, nil},
   499  	{"a*/b", "a/c/b", false, nil},
   500  	{"a*b*c*d*e*/f", "axbxcxdxe/f", true, nil},
   501  	{"a*b*c*d*e*/f", "axbxcxdxexxx/f", true, nil},
   502  	{"a*b*c*d*e*/f", "axbxcxdxe/xxx/f", false, nil},
   503  	{"a*b*c*d*e*/f", "axbxcxdxexxx/fff", false, nil},
   504  	{"a*b?c*x", "abxbbxdbxebxczzx", true, nil},
   505  	{"a*b?c*x", "abxbbxdbxebxczzy", false, nil},
   506  	{"ab[c]", "abc", true, nil},
   507  	{"ab[b-d]", "abc", true, nil},
   508  	{"ab[e-g]", "abc", false, nil},
   509  	{"ab[^c]", "abc", false, nil},
   510  	{"ab[^b-d]", "abc", false, nil},
   511  	{"ab[^e-g]", "abc", true, nil},
   512  	{"a\\*b", "a*b", true, nil},
   513  	{"a\\*b", "ab", false, nil},
   514  	{"a?b", "a☺b", true, nil},
   515  	{"a[^a]b", "a☺b", true, nil},
   516  	{"a???b", "a☺b", false, nil},
   517  	{"a[^a][^a][^a]b", "a☺b", false, nil},
   518  	{"[a-ζ]*", "α", true, nil},
   519  	{"*[a-ζ]", "A", false, nil},
   520  	{"a?b", "a/b", false, nil},
   521  	{"a*b", "a/b", false, nil},
   522  	{"[\\]a]", "]", true, nil},
   523  	{"[\\-]", "-", true, nil},
   524  	{"[x\\-]", "x", true, nil},
   525  	{"[x\\-]", "-", true, nil},
   526  	{"[x\\-]", "z", false, nil},
   527  	{"[\\-x]", "x", true, nil},
   528  	{"[\\-x]", "-", true, nil},
   529  	{"[\\-x]", "a", false, nil},
   530  	{"[]a]", "]", false, filepath.ErrBadPattern},
   531  	{"[-]", "-", false, filepath.ErrBadPattern},
   532  	{"[x-]", "x", false, filepath.ErrBadPattern},
   533  	{"[x-]", "-", false, filepath.ErrBadPattern},
   534  	{"[x-]", "z", false, filepath.ErrBadPattern},
   535  	{"[-x]", "x", false, filepath.ErrBadPattern},
   536  	{"[-x]", "-", false, filepath.ErrBadPattern},
   537  	{"[-x]", "a", false, filepath.ErrBadPattern},
   538  	{"\\", "a", false, filepath.ErrBadPattern},
   539  	{"[a-b-c]", "a", false, filepath.ErrBadPattern},
   540  	{"[", "a", false, filepath.ErrBadPattern},
   541  	{"[^", "a", false, filepath.ErrBadPattern},
   542  	{"[^bc", "a", false, filepath.ErrBadPattern},
   543  	{"a[", "a", false, filepath.ErrBadPattern}, // was nil but IMO its wrong
   544  	{"a[", "ab", false, filepath.ErrBadPattern},
   545  	{"*x", "xxx", true, nil},
   546  }
   547  
   548  func errp(e error) string {
   549  	if e == nil {
   550  		return "<nil>"
   551  	}
   552  	return e.Error()
   553  }
   554  
   555  // TestMatch test's our version of filepath.Match, called regexpMatch.
   556  func TestMatch(t *testing.T) {
   557  	for _, tt := range matchTests {
   558  		pattern := tt.pattern
   559  		s := tt.s
   560  		if runtime.GOOS == "windows" {
   561  			if strings.Index(pattern, "\\") >= 0 {
   562  				// no escape allowed on windows.
   563  				continue
   564  			}
   565  			pattern = filepath.Clean(pattern)
   566  			s = filepath.Clean(s)
   567  		}
   568  		ok, err := regexpMatch(pattern, s)
   569  		if ok != tt.match || err != tt.err {
   570  			t.Fatalf("Match(%#q, %#q) = %v, %q want %v, %q", pattern, s, ok, errp(err), tt.match, errp(tt.err))
   571  		}
   572  	}
   573  }