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