1// Copyright © 2014 Steve Francia <spf@spf13.com>.
2// Copyright 2009 The Go Authors. All rights reserved.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package afero
16
17import (
18	"bytes"
19	"fmt"
20	"io"
21	"io/ioutil"
22	"os"
23	"path/filepath"
24	"runtime"
25	"strings"
26	"syscall"
27	"testing"
28)
29
30var testName = "test.txt"
31var Fss = []Fs{&MemMapFs{}, &OsFs{}}
32
33var testRegistry map[Fs][]string = make(map[Fs][]string)
34
35func testDir(fs Fs) string {
36	name, err := TempDir(fs, "", "afero")
37	if err != nil {
38		panic(fmt.Sprint("unable to work with test dir", err))
39	}
40	testRegistry[fs] = append(testRegistry[fs], name)
41
42	return name
43}
44
45func tmpFile(fs Fs) File {
46	x, err := TempFile(fs, "", "afero")
47
48	if err != nil {
49		panic(fmt.Sprint("unable to work with temp file", err))
50	}
51
52	testRegistry[fs] = append(testRegistry[fs], x.Name())
53
54	return x
55}
56
57//Read with length 0 should not return EOF.
58func TestRead0(t *testing.T) {
59	for _, fs := range Fss {
60		f := tmpFile(fs)
61		defer f.Close()
62		f.WriteString("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
63
64		var b []byte
65		// b := make([]byte, 0)
66		n, err := f.Read(b)
67		if n != 0 || err != nil {
68			t.Errorf("%v: Read(0) = %d, %v, want 0, nil", fs.Name(), n, err)
69		}
70		f.Seek(0, 0)
71		b = make([]byte, 100)
72		n, err = f.Read(b)
73		if n <= 0 || err != nil {
74			t.Errorf("%v: Read(100) = %d, %v, want >0, nil", fs.Name(), n, err)
75		}
76	}
77}
78
79func TestOpenFile(t *testing.T) {
80	defer removeAllTestFiles(t)
81	for _, fs := range Fss {
82		tmp := testDir(fs)
83		path := filepath.Join(tmp, testName)
84
85		f, err := fs.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
86		if err != nil {
87			t.Error(fs.Name(), "OpenFile (O_CREATE) failed:", err)
88			continue
89		}
90		io.WriteString(f, "initial")
91		f.Close()
92
93		f, err = fs.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0600)
94		if err != nil {
95			t.Error(fs.Name(), "OpenFile (O_APPEND) failed:", err)
96			continue
97		}
98		io.WriteString(f, "|append")
99		f.Close()
100
101		f, err = fs.OpenFile(path, os.O_RDONLY, 0600)
102		contents, _ := ioutil.ReadAll(f)
103		expectedContents := "initial|append"
104		if string(contents) != expectedContents {
105			t.Errorf("%v: appending, expected '%v', got: '%v'", fs.Name(), expectedContents, string(contents))
106		}
107		f.Close()
108
109		f, err = fs.OpenFile(path, os.O_RDWR|os.O_TRUNC, 0600)
110		if err != nil {
111			t.Error(fs.Name(), "OpenFile (O_TRUNC) failed:", err)
112			continue
113		}
114		contents, _ = ioutil.ReadAll(f)
115		if string(contents) != "" {
116			t.Errorf("%v: expected truncated file, got: '%v'", fs.Name(), string(contents))
117		}
118		f.Close()
119	}
120}
121
122func TestCreate(t *testing.T) {
123	defer removeAllTestFiles(t)
124	for _, fs := range Fss {
125		tmp := testDir(fs)
126		path := filepath.Join(tmp, testName)
127
128		f, err := fs.Create(path)
129		if err != nil {
130			t.Error(fs.Name(), "Create failed:", err)
131			f.Close()
132			continue
133		}
134		io.WriteString(f, "initial")
135		f.Close()
136
137		f, err = fs.Create(path)
138		if err != nil {
139			t.Error(fs.Name(), "Create failed:", err)
140			f.Close()
141			continue
142		}
143		secondContent := "second create"
144		io.WriteString(f, secondContent)
145		f.Close()
146
147		f, err = fs.Open(path)
148		if err != nil {
149			t.Error(fs.Name(), "Open failed:", err)
150			f.Close()
151			continue
152		}
153		buf, err := ReadAll(f)
154		if err != nil {
155			t.Error(fs.Name(), "ReadAll failed:", err)
156			f.Close()
157			continue
158		}
159		if string(buf) != secondContent {
160			t.Error(fs.Name(), "Content should be", "\""+secondContent+"\" but is \""+string(buf)+"\"")
161			f.Close()
162			continue
163		}
164		f.Close()
165	}
166}
167
168func TestMemFileRead(t *testing.T) {
169	f := tmpFile(new(MemMapFs))
170	// f := MemFileCreate("testfile")
171	f.WriteString("abcd")
172	f.Seek(0, 0)
173	b := make([]byte, 8)
174	n, err := f.Read(b)
175	if n != 4 {
176		t.Errorf("didn't read all bytes: %v %v %v", n, err, b)
177	}
178	if err != nil {
179		t.Errorf("err is not nil: %v %v %v", n, err, b)
180	}
181	n, err = f.Read(b)
182	if n != 0 {
183		t.Errorf("read more bytes: %v %v %v", n, err, b)
184	}
185	if err != io.EOF {
186		t.Errorf("error is not EOF: %v %v %v", n, err, b)
187	}
188}
189
190func TestRename(t *testing.T) {
191	defer removeAllTestFiles(t)
192	for _, fs := range Fss {
193		tDir := testDir(fs)
194		from := filepath.Join(tDir, "/renamefrom")
195		to := filepath.Join(tDir, "/renameto")
196		exists := filepath.Join(tDir, "/renameexists")
197		file, err := fs.Create(from)
198		if err != nil {
199			t.Fatalf("%s: open %q failed: %v", fs.Name(), to, err)
200		}
201		if err = file.Close(); err != nil {
202			t.Errorf("%s: close %q failed: %v", fs.Name(), to, err)
203		}
204		file, err = fs.Create(exists)
205		if err != nil {
206			t.Fatalf("%s: open %q failed: %v", fs.Name(), to, err)
207		}
208		if err = file.Close(); err != nil {
209			t.Errorf("%s: close %q failed: %v", fs.Name(), to, err)
210		}
211		err = fs.Rename(from, to)
212		if err != nil {
213			t.Fatalf("%s: rename %q, %q failed: %v", fs.Name(), to, from, err)
214		}
215		file, err = fs.Create(from)
216		if err != nil {
217			t.Fatalf("%s: open %q failed: %v", fs.Name(), to, err)
218		}
219		if err = file.Close(); err != nil {
220			t.Errorf("%s: close %q failed: %v", fs.Name(), to, err)
221		}
222		err = fs.Rename(from, exists)
223		if err != nil {
224			t.Errorf("%s: rename %q, %q failed: %v", fs.Name(), exists, from, err)
225		}
226		names, err := readDirNames(fs, tDir)
227		if err != nil {
228			t.Errorf("%s: readDirNames error: %v", fs.Name(), err)
229		}
230		found := false
231		for _, e := range names {
232			if e == "renamefrom" {
233				t.Error("File is still called renamefrom")
234			}
235			if e == "renameto" {
236				found = true
237			}
238		}
239		if !found {
240			t.Error("File was not renamed to renameto")
241		}
242
243		_, err = fs.Stat(to)
244		if err != nil {
245			t.Errorf("%s: stat %q failed: %v", fs.Name(), to, err)
246		}
247	}
248}
249
250func TestRemove(t *testing.T) {
251	for _, fs := range Fss {
252
253		x, err := TempFile(fs, "", "afero")
254		if err != nil {
255			t.Error(fmt.Sprint("unable to work with temp file", err))
256		}
257
258		path := x.Name()
259		x.Close()
260
261		tDir := filepath.Dir(path)
262
263		err = fs.Remove(path)
264		if err != nil {
265			t.Errorf("%v: Remove() failed: %v", fs.Name(), err)
266			continue
267		}
268
269		_, err = fs.Stat(path)
270		if !os.IsNotExist(err) {
271			t.Errorf("%v: Remove() didn't remove file", fs.Name())
272			continue
273		}
274
275		// Deleting non-existent file should raise error
276		err = fs.Remove(path)
277		if !os.IsNotExist(err) {
278			t.Errorf("%v: Remove() didn't raise error for non-existent file", fs.Name())
279		}
280
281		f, err := fs.Open(tDir)
282		if err != nil {
283			t.Error("TestDir should still exist:", err)
284		}
285
286		names, err := f.Readdirnames(-1)
287		if err != nil {
288			t.Error("Readdirnames failed:", err)
289		}
290
291		for _, e := range names {
292			if e == testName {
293				t.Error("File was not removed from parent directory")
294			}
295		}
296	}
297}
298
299func TestTruncate(t *testing.T) {
300	defer removeAllTestFiles(t)
301	for _, fs := range Fss {
302		f := tmpFile(fs)
303		defer f.Close()
304
305		checkSize(t, f, 0)
306		f.Write([]byte("hello, world\n"))
307		checkSize(t, f, 13)
308		f.Truncate(10)
309		checkSize(t, f, 10)
310		f.Truncate(1024)
311		checkSize(t, f, 1024)
312		f.Truncate(0)
313		checkSize(t, f, 0)
314		_, err := f.Write([]byte("surprise!"))
315		if err == nil {
316			checkSize(t, f, 13+9) // wrote at offset past where hello, world was.
317		}
318	}
319}
320
321func TestSeek(t *testing.T) {
322	defer removeAllTestFiles(t)
323	for _, fs := range Fss {
324		f := tmpFile(fs)
325		defer f.Close()
326
327		const data = "hello, world\n"
328		io.WriteString(f, data)
329
330		type test struct {
331			in     int64
332			whence int
333			out    int64
334		}
335		var tests = []test{
336			{0, 1, int64(len(data))},
337			{0, 0, 0},
338			{5, 0, 5},
339			{0, 2, int64(len(data))},
340			{0, 0, 0},
341			{-1, 2, int64(len(data)) - 1},
342			{1 << 33, 0, 1 << 33},
343			{1 << 33, 2, 1<<33 + int64(len(data))},
344		}
345		for i, tt := range tests {
346			off, err := f.Seek(tt.in, tt.whence)
347			if off != tt.out || err != nil {
348				if e, ok := err.(*os.PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 {
349					// Reiserfs rejects the big seeks.
350					// http://code.google.com/p/go/issues/detail?id=91
351					break
352				}
353				t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out)
354			}
355		}
356	}
357}
358
359func TestReadAt(t *testing.T) {
360	defer removeAllTestFiles(t)
361	for _, fs := range Fss {
362		f := tmpFile(fs)
363		defer f.Close()
364
365		const data = "hello, world\n"
366		io.WriteString(f, data)
367
368		b := make([]byte, 5)
369		n, err := f.ReadAt(b, 7)
370		if err != nil || n != len(b) {
371			t.Fatalf("ReadAt 7: %d, %v", n, err)
372		}
373		if string(b) != "world" {
374			t.Fatalf("ReadAt 7: have %q want %q", string(b), "world")
375		}
376	}
377}
378
379func TestWriteAt(t *testing.T) {
380	defer removeAllTestFiles(t)
381	for _, fs := range Fss {
382		f := tmpFile(fs)
383		defer f.Close()
384
385		const data = "hello, world\n"
386		io.WriteString(f, data)
387
388		n, err := f.WriteAt([]byte("WORLD"), 7)
389		if err != nil || n != 5 {
390			t.Fatalf("WriteAt 7: %d, %v", n, err)
391		}
392
393		f2, err := fs.Open(f.Name())
394		if err != nil {
395			t.Fatalf("%v: ReadFile %s: %v", fs.Name(), f.Name(), err)
396		}
397		defer f2.Close()
398		buf := new(bytes.Buffer)
399		buf.ReadFrom(f2)
400		b := buf.Bytes()
401		if string(b) != "hello, WORLD\n" {
402			t.Fatalf("after write: have %q want %q", string(b), "hello, WORLD\n")
403		}
404
405	}
406}
407
408func setupTestDir(t *testing.T, fs Fs) string {
409	path := testDir(fs)
410	return setupTestFiles(t, fs, path)
411}
412
413func setupTestDirRoot(t *testing.T, fs Fs) string {
414	path := testDir(fs)
415	setupTestFiles(t, fs, path)
416	return path
417}
418
419func setupTestDirReusePath(t *testing.T, fs Fs, path string) string {
420	testRegistry[fs] = append(testRegistry[fs], path)
421	return setupTestFiles(t, fs, path)
422}
423
424func setupTestFiles(t *testing.T, fs Fs, path string) string {
425	testSubDir := filepath.Join(path, "more", "subdirectories", "for", "testing", "we")
426	err := fs.MkdirAll(testSubDir, 0700)
427	if err != nil && !os.IsExist(err) {
428		t.Fatal(err)
429	}
430
431	f, err := fs.Create(filepath.Join(testSubDir, "testfile1"))
432	if err != nil {
433		t.Fatal(err)
434	}
435	f.WriteString("Testfile 1 content")
436	f.Close()
437
438	f, err = fs.Create(filepath.Join(testSubDir, "testfile2"))
439	if err != nil {
440		t.Fatal(err)
441	}
442	f.WriteString("Testfile 2 content")
443	f.Close()
444
445	f, err = fs.Create(filepath.Join(testSubDir, "testfile3"))
446	if err != nil {
447		t.Fatal(err)
448	}
449	f.WriteString("Testfile 3 content")
450	f.Close()
451
452	f, err = fs.Create(filepath.Join(testSubDir, "testfile4"))
453	if err != nil {
454		t.Fatal(err)
455	}
456	f.WriteString("Testfile 4 content")
457	f.Close()
458	return testSubDir
459}
460
461func TestReaddirnames(t *testing.T) {
462	defer removeAllTestFiles(t)
463	for _, fs := range Fss {
464		testSubDir := setupTestDir(t, fs)
465		tDir := filepath.Dir(testSubDir)
466
467		root, err := fs.Open(tDir)
468		if err != nil {
469			t.Fatal(fs.Name(), tDir, err)
470		}
471		defer root.Close()
472
473		namesRoot, err := root.Readdirnames(-1)
474		if err != nil {
475			t.Fatal(fs.Name(), namesRoot, err)
476		}
477
478		sub, err := fs.Open(testSubDir)
479		if err != nil {
480			t.Fatal(err)
481		}
482		defer sub.Close()
483
484		namesSub, err := sub.Readdirnames(-1)
485		if err != nil {
486			t.Fatal(fs.Name(), namesSub, err)
487		}
488
489		findNames(fs, t, tDir, testSubDir, namesRoot, namesSub)
490	}
491}
492
493func TestReaddirSimple(t *testing.T) {
494	defer removeAllTestFiles(t)
495	for _, fs := range Fss {
496		testSubDir := setupTestDir(t, fs)
497		tDir := filepath.Dir(testSubDir)
498
499		root, err := fs.Open(tDir)
500		if err != nil {
501			t.Fatal(err)
502		}
503		defer root.Close()
504
505		rootInfo, err := root.Readdir(1)
506		if err != nil {
507			t.Log(myFileInfo(rootInfo))
508			t.Error(err)
509		}
510
511		rootInfo, err = root.Readdir(5)
512		if err != io.EOF {
513			t.Log(myFileInfo(rootInfo))
514			t.Error(err)
515		}
516
517		sub, err := fs.Open(testSubDir)
518		if err != nil {
519			t.Fatal(err)
520		}
521		defer sub.Close()
522
523		subInfo, err := sub.Readdir(5)
524		if err != nil {
525			t.Log(myFileInfo(subInfo))
526			t.Error(err)
527		}
528	}
529}
530
531func TestReaddir(t *testing.T) {
532	defer removeAllTestFiles(t)
533	for num := 0; num < 6; num++ {
534		outputs := make([]string, len(Fss))
535		infos := make([]string, len(Fss))
536		for i, fs := range Fss {
537			testSubDir := setupTestDir(t, fs)
538			//tDir := filepath.Dir(testSubDir)
539			root, err := fs.Open(testSubDir)
540			if err != nil {
541				t.Fatal(err)
542			}
543			defer root.Close()
544
545			for j := 0; j < 6; j++ {
546				info, err := root.Readdir(num)
547				outputs[i] += fmt.Sprintf("%v  Error: %v\n", myFileInfo(info), err)
548				infos[i] += fmt.Sprintln(len(info), err)
549			}
550		}
551
552		fail := false
553		for i, o := range infos {
554			if i == 0 {
555				continue
556			}
557			if o != infos[i-1] {
558				fail = true
559				break
560			}
561		}
562		if fail {
563			t.Log("Readdir outputs not equal for Readdir(", num, ")")
564			for i, o := range outputs {
565				t.Log(Fss[i].Name())
566				t.Log(o)
567			}
568			t.Fail()
569		}
570	}
571}
572
573// https://github.com/spf13/afero/issues/169
574func TestReaddirRegularFile(t *testing.T) {
575	defer removeAllTestFiles(t)
576	for _, fs := range Fss {
577		f := tmpFile(fs)
578		defer f.Close()
579
580		_, err := f.Readdirnames(-1)
581		if err == nil {
582			t.Fatal("Expected error")
583		}
584
585		_, err = f.Readdir(-1)
586		if err == nil {
587			t.Fatal("Expected error")
588		}
589	}
590}
591
592type myFileInfo []os.FileInfo
593
594func (m myFileInfo) String() string {
595	out := "Fileinfos:\n"
596	for _, e := range m {
597		out += "  " + e.Name() + "\n"
598	}
599	return out
600}
601
602func TestReaddirAll(t *testing.T) {
603	defer removeAllTestFiles(t)
604	for _, fs := range Fss {
605		testSubDir := setupTestDir(t, fs)
606		tDir := filepath.Dir(testSubDir)
607
608		root, err := fs.Open(tDir)
609		if err != nil {
610			t.Fatal(err)
611		}
612		defer root.Close()
613
614		rootInfo, err := root.Readdir(-1)
615		if err != nil {
616			t.Fatal(err)
617		}
618		var namesRoot = []string{}
619		for _, e := range rootInfo {
620			namesRoot = append(namesRoot, e.Name())
621		}
622
623		sub, err := fs.Open(testSubDir)
624		if err != nil {
625			t.Fatal(err)
626		}
627		defer sub.Close()
628
629		subInfo, err := sub.Readdir(-1)
630		if err != nil {
631			t.Fatal(err)
632		}
633		var namesSub = []string{}
634		for _, e := range subInfo {
635			namesSub = append(namesSub, e.Name())
636		}
637
638		findNames(fs, t, tDir, testSubDir, namesRoot, namesSub)
639	}
640}
641
642func findNames(fs Fs, t *testing.T, tDir, testSubDir string, root, sub []string) {
643	var foundRoot bool
644	for _, e := range root {
645		f, err := fs.Open(filepath.Join(tDir, e))
646		if err != nil {
647			t.Error("Open", filepath.Join(tDir, e), ":", err)
648		}
649		defer f.Close()
650
651		if equal(e, "we") {
652			foundRoot = true
653		}
654	}
655	if !foundRoot {
656		t.Logf("Names root: %v", root)
657		t.Logf("Names sub: %v", sub)
658		t.Error("Didn't find subdirectory we")
659	}
660
661	var found1, found2 bool
662	for _, e := range sub {
663		f, err := fs.Open(filepath.Join(testSubDir, e))
664		if err != nil {
665			t.Error("Open", filepath.Join(testSubDir, e), ":", err)
666		}
667		defer f.Close()
668
669		if equal(e, "testfile1") {
670			found1 = true
671		}
672		if equal(e, "testfile2") {
673			found2 = true
674		}
675	}
676
677	if !found1 {
678		t.Logf("Names root: %v", root)
679		t.Logf("Names sub: %v", sub)
680		t.Error("Didn't find testfile1")
681	}
682	if !found2 {
683		t.Logf("Names root: %v", root)
684		t.Logf("Names sub: %v", sub)
685		t.Error("Didn't find testfile2")
686	}
687}
688
689func removeAllTestFiles(t *testing.T) {
690	for fs, list := range testRegistry {
691		for _, path := range list {
692			if err := fs.RemoveAll(path); err != nil {
693				t.Error(fs.Name(), err)
694			}
695		}
696	}
697	testRegistry = make(map[Fs][]string)
698}
699
700func equal(name1, name2 string) (r bool) {
701	switch runtime.GOOS {
702	case "windows":
703		r = strings.ToLower(name1) == strings.ToLower(name2)
704	default:
705		r = name1 == name2
706	}
707	return
708}
709
710func checkSize(t *testing.T, f File, size int64) {
711	dir, err := f.Stat()
712	if err != nil {
713		t.Fatalf("Stat %q (looking for size %d): %s", f.Name(), size, err)
714	}
715	if dir.Size() != size {
716		t.Errorf("Stat %q: size %d want %d", f.Name(), dir.Size(), size)
717	}
718}
719