1package afero
2
3import (
4	"bytes"
5	"fmt"
6	"io"
7	"io/ioutil"
8	"os"
9	"testing"
10	"time"
11)
12
13var tempDirs []string
14
15func NewTempOsBaseFs(t *testing.T) Fs {
16	name, err := TempDir(NewOsFs(), "", "")
17	if err != nil {
18		t.Error("error creating tempDir", err)
19	}
20
21	tempDirs = append(tempDirs, name)
22
23	return NewBasePathFs(NewOsFs(), name)
24}
25
26func CleanupTempDirs(t *testing.T) {
27	osfs := NewOsFs()
28	type ev struct {
29		path string
30		e    error
31	}
32
33	errs := []ev{}
34
35	for _, x := range tempDirs {
36		err := osfs.RemoveAll(x)
37		if err != nil {
38			errs = append(errs, ev{path: x, e: err})
39		}
40	}
41
42	for _, e := range errs {
43		fmt.Println("error removing tempDir", e.path, e.e)
44	}
45
46	if len(errs) > 0 {
47		t.Error("error cleaning up tempDirs")
48	}
49	tempDirs = []string{}
50}
51
52func TestUnionCreateExisting(t *testing.T) {
53	base := &MemMapFs{}
54	roBase := &ReadOnlyFs{source: base}
55	ufs := NewCopyOnWriteFs(roBase, &MemMapFs{})
56
57	base.MkdirAll("/home/test", 0777)
58	fh, _ := base.Create("/home/test/file.txt")
59	fh.WriteString("This is a test")
60	fh.Close()
61
62	fh, err := ufs.OpenFile("/home/test/file.txt", os.O_RDWR, 0666)
63	if err != nil {
64		t.Errorf("Failed to open file r/w: %s", err)
65	}
66
67	_, err = fh.Write([]byte("####"))
68	if err != nil {
69		t.Errorf("Failed to write file: %s", err)
70	}
71	fh.Seek(0, 0)
72	data, err := ioutil.ReadAll(fh)
73	if err != nil {
74		t.Errorf("Failed to read file: %s", err)
75	}
76	if string(data) != "#### is a test" {
77		t.Errorf("Got wrong data")
78	}
79	fh.Close()
80
81	fh, _ = base.Open("/home/test/file.txt")
82	data, err = ioutil.ReadAll(fh)
83	if string(data) != "This is a test" {
84		t.Errorf("Got wrong data in base file")
85	}
86	fh.Close()
87
88	fh, err = ufs.Create("/home/test/file.txt")
89	switch err {
90	case nil:
91		if fi, _ := fh.Stat(); fi.Size() != 0 {
92			t.Errorf("Create did not truncate file")
93		}
94		fh.Close()
95	default:
96		t.Errorf("Create failed on existing file")
97	}
98
99}
100
101func TestUnionMergeReaddir(t *testing.T) {
102	base := &MemMapFs{}
103	roBase := &ReadOnlyFs{source: base}
104
105	ufs := &CopyOnWriteFs{base: roBase, layer: &MemMapFs{}}
106
107	base.MkdirAll("/home/test", 0777)
108	fh, _ := base.Create("/home/test/file.txt")
109	fh.WriteString("This is a test")
110	fh.Close()
111
112	fh, _ = ufs.Create("/home/test/file2.txt")
113	fh.WriteString("This is a test")
114	fh.Close()
115
116	fh, _ = ufs.Open("/home/test")
117	files, err := fh.Readdirnames(-1)
118	if err != nil {
119		t.Errorf("Readdirnames failed")
120	}
121	if len(files) != 2 {
122		t.Errorf("Got wrong number of files: %v", files)
123	}
124}
125
126func TestExistingDirectoryCollisionReaddir(t *testing.T) {
127	base := &MemMapFs{}
128	roBase := &ReadOnlyFs{source: base}
129	overlay := &MemMapFs{}
130
131	ufs := &CopyOnWriteFs{base: roBase, layer: overlay}
132
133	base.MkdirAll("/home/test", 0777)
134	fh, _ := base.Create("/home/test/file.txt")
135	fh.WriteString("This is a test")
136	fh.Close()
137
138	overlay.MkdirAll("home/test", 0777)
139	fh, _ = overlay.Create("/home/test/file2.txt")
140	fh.WriteString("This is a test")
141	fh.Close()
142
143	fh, _ = ufs.Create("/home/test/file3.txt")
144	fh.WriteString("This is a test")
145	fh.Close()
146
147	fh, _ = ufs.Open("/home/test")
148	files, err := fh.Readdirnames(-1)
149	if err != nil {
150		t.Errorf("Readdirnames failed")
151	}
152	if len(files) != 3 {
153		t.Errorf("Got wrong number of files in union: %v", files)
154	}
155
156	fh, _ = overlay.Open("/home/test")
157	files, err = fh.Readdirnames(-1)
158	if err != nil {
159		t.Errorf("Readdirnames failed")
160	}
161	if len(files) != 2 {
162		t.Errorf("Got wrong number of files in overlay: %v", files)
163	}
164}
165
166func TestNestedDirBaseReaddir(t *testing.T) {
167	base := &MemMapFs{}
168	roBase := &ReadOnlyFs{source: base}
169	overlay := &MemMapFs{}
170
171	ufs := &CopyOnWriteFs{base: roBase, layer: overlay}
172
173	base.MkdirAll("/home/test/foo/bar", 0777)
174	fh, _ := base.Create("/home/test/file.txt")
175	fh.WriteString("This is a test")
176	fh.Close()
177
178	fh, _ = base.Create("/home/test/foo/file2.txt")
179	fh.WriteString("This is a test")
180	fh.Close()
181	fh, _ = base.Create("/home/test/foo/bar/file3.txt")
182	fh.WriteString("This is a test")
183	fh.Close()
184
185	overlay.MkdirAll("/", 0777)
186
187	// Opening something only in the base
188	fh, _ = ufs.Open("/home/test/foo")
189	list, err := fh.Readdir(-1)
190	if err != nil {
191		t.Errorf("Readdir failed %s", err)
192	}
193	if len(list) != 2 {
194		for _, x := range list {
195			fmt.Println(x.Name())
196		}
197		t.Errorf("Got wrong number of files in union: %v", len(list))
198	}
199}
200
201func TestNestedDirOverlayReaddir(t *testing.T) {
202	base := &MemMapFs{}
203	roBase := &ReadOnlyFs{source: base}
204	overlay := &MemMapFs{}
205
206	ufs := &CopyOnWriteFs{base: roBase, layer: overlay}
207
208	base.MkdirAll("/", 0777)
209	overlay.MkdirAll("/home/test/foo/bar", 0777)
210	fh, _ := overlay.Create("/home/test/file.txt")
211	fh.WriteString("This is a test")
212	fh.Close()
213	fh, _ = overlay.Create("/home/test/foo/file2.txt")
214	fh.WriteString("This is a test")
215	fh.Close()
216	fh, _ = overlay.Create("/home/test/foo/bar/file3.txt")
217	fh.WriteString("This is a test")
218	fh.Close()
219
220	// Opening nested dir only in the overlay
221	fh, _ = ufs.Open("/home/test/foo")
222	list, err := fh.Readdir(-1)
223	if err != nil {
224		t.Errorf("Readdir failed %s", err)
225	}
226	if len(list) != 2 {
227		for _, x := range list {
228			fmt.Println(x.Name())
229		}
230		t.Errorf("Got wrong number of files in union: %v", len(list))
231	}
232}
233
234func TestNestedDirOverlayOsFsReaddir(t *testing.T) {
235	defer CleanupTempDirs(t)
236	base := NewTempOsBaseFs(t)
237	roBase := &ReadOnlyFs{source: base}
238	overlay := NewTempOsBaseFs(t)
239
240	ufs := &CopyOnWriteFs{base: roBase, layer: overlay}
241
242	base.MkdirAll("/", 0777)
243	overlay.MkdirAll("/home/test/foo/bar", 0777)
244	fh, _ := overlay.Create("/home/test/file.txt")
245	fh.WriteString("This is a test")
246	fh.Close()
247	fh, _ = overlay.Create("/home/test/foo/file2.txt")
248	fh.WriteString("This is a test")
249	fh.Close()
250	fh, _ = overlay.Create("/home/test/foo/bar/file3.txt")
251	fh.WriteString("This is a test")
252	fh.Close()
253
254	// Opening nested dir only in the overlay
255	fh, _ = ufs.Open("/home/test/foo")
256	list, err := fh.Readdir(-1)
257	fh.Close()
258	if err != nil {
259		t.Errorf("Readdir failed %s", err)
260	}
261	if len(list) != 2 {
262		for _, x := range list {
263			fmt.Println(x.Name())
264		}
265		t.Errorf("Got wrong number of files in union: %v", len(list))
266	}
267}
268
269func TestCopyOnWriteFsWithOsFs(t *testing.T) {
270	defer CleanupTempDirs(t)
271	base := NewTempOsBaseFs(t)
272	roBase := &ReadOnlyFs{source: base}
273	overlay := NewTempOsBaseFs(t)
274
275	ufs := &CopyOnWriteFs{base: roBase, layer: overlay}
276
277	base.MkdirAll("/home/test", 0777)
278	fh, _ := base.Create("/home/test/file.txt")
279	fh.WriteString("This is a test")
280	fh.Close()
281
282	overlay.MkdirAll("home/test", 0777)
283	fh, _ = overlay.Create("/home/test/file2.txt")
284	fh.WriteString("This is a test")
285	fh.Close()
286
287	fh, _ = ufs.Create("/home/test/file3.txt")
288	fh.WriteString("This is a test")
289	fh.Close()
290
291	fh, _ = ufs.Open("/home/test")
292	files, err := fh.Readdirnames(-1)
293	fh.Close()
294	if err != nil {
295		t.Errorf("Readdirnames failed")
296	}
297	if len(files) != 3 {
298		t.Errorf("Got wrong number of files in union: %v", files)
299	}
300
301	fh, _ = overlay.Open("/home/test")
302	files, err = fh.Readdirnames(-1)
303	fh.Close()
304	if err != nil {
305		t.Errorf("Readdirnames failed")
306	}
307	if len(files) != 2 {
308		t.Errorf("Got wrong number of files in overlay: %v", files)
309	}
310}
311
312func TestUnionCacheWrite(t *testing.T) {
313	base := &MemMapFs{}
314	layer := &MemMapFs{}
315
316	ufs := NewCacheOnReadFs(base, layer, 0)
317
318	base.Mkdir("/data", 0777)
319
320	fh, err := ufs.Create("/data/file.txt")
321	if err != nil {
322		t.Errorf("Failed to create file")
323	}
324	_, err = fh.Write([]byte("This is a test"))
325	if err != nil {
326		t.Errorf("Failed to write file")
327	}
328
329	fh.Seek(0, os.SEEK_SET)
330	buf := make([]byte, 4)
331	_, err = fh.Read(buf)
332	fh.Write([]byte(" IS A"))
333	fh.Close()
334
335	baseData, _ := ReadFile(base, "/data/file.txt")
336	layerData, _ := ReadFile(layer, "/data/file.txt")
337	if string(baseData) != string(layerData) {
338		t.Errorf("Different data: %s <=> %s", baseData, layerData)
339	}
340}
341
342func TestUnionCacheExpire(t *testing.T) {
343	base := &MemMapFs{}
344	layer := &MemMapFs{}
345	ufs := &CacheOnReadFs{base: base, layer: layer, cacheTime: 1 * time.Second}
346
347	base.Mkdir("/data", 0777)
348
349	fh, err := ufs.Create("/data/file.txt")
350	if err != nil {
351		t.Errorf("Failed to create file")
352	}
353	_, err = fh.Write([]byte("This is a test"))
354	if err != nil {
355		t.Errorf("Failed to write file")
356	}
357	fh.Close()
358
359	fh, _ = base.Create("/data/file.txt")
360	// sleep some time, so we really get a different time.Now() on write...
361	time.Sleep(2 * time.Second)
362	fh.WriteString("Another test")
363	fh.Close()
364
365	data, _ := ReadFile(ufs, "/data/file.txt")
366	if string(data) != "Another test" {
367		t.Errorf("cache time failed: <%s>", data)
368	}
369}
370
371func TestCacheOnReadFsNotInLayer(t *testing.T) {
372	base := NewMemMapFs()
373	layer := NewMemMapFs()
374	fs := NewCacheOnReadFs(base, layer, 0)
375
376	fh, err := base.Create("/file.txt")
377	if err != nil {
378		t.Fatal("unable to create file: ", err)
379	}
380
381	txt := []byte("This is a test")
382	fh.Write(txt)
383	fh.Close()
384
385	fh, err = fs.Open("/file.txt")
386	if err != nil {
387		t.Fatal("could not open file: ", err)
388	}
389
390	b, err := ReadAll(fh)
391	fh.Close()
392
393	if err != nil {
394		t.Fatal("could not read file: ", err)
395	} else if !bytes.Equal(txt, b) {
396		t.Fatalf("wanted file text %q, got %q", txt, b)
397	}
398
399	fh, err = layer.Open("/file.txt")
400	if err != nil {
401		t.Fatal("could not open file from layer: ", err)
402	}
403	fh.Close()
404}
405
406// #194
407func TestUnionFileReaddirEmpty(t *testing.T) {
408	osFs := NewOsFs()
409
410	base := NewMemMapFs()
411	overlay := NewMemMapFs()
412	ufs := &CopyOnWriteFs{base: base, layer: overlay}
413	mem := NewMemMapFs()
414
415	// The OS file will return io.EOF on end of directory.
416	for _, fs := range []Fs{osFs, ufs, mem} {
417		baseDir, err := TempDir(fs, "", "empty-dir")
418		if err != nil {
419			t.Fatal(err)
420		}
421
422		f, err := fs.Open(baseDir)
423		if err != nil {
424			t.Fatal(err)
425		}
426
427		names, err := f.Readdirnames(1)
428		if err != io.EOF {
429			t.Fatal(err)
430		}
431
432		if len(names) != 0 {
433			t.Fatal("should be empty")
434		}
435
436		f.Close()
437
438		fs.RemoveAll(baseDir)
439	}
440}
441
442// #197
443func TestUnionFileReaddirDuplicateEmpty(t *testing.T) {
444	base := NewMemMapFs()
445	dir, err := TempDir(base, "", "empty-dir")
446	if err != nil {
447		t.Fatal(err)
448	}
449
450	// Overlay shares same empty directory as base
451	overlay := NewMemMapFs()
452	err = overlay.Mkdir(dir, 0700)
453	if err != nil {
454		t.Fatal(err)
455	}
456
457	ufs := &CopyOnWriteFs{base: base, layer: overlay}
458
459	f, err := ufs.Open(dir)
460	if err != nil {
461		t.Fatal(err)
462	}
463	defer f.Close()
464
465	names, err := f.Readdirnames(0)
466
467	if err == io.EOF {
468		t.Errorf("unexpected io.EOF error")
469	}
470
471	if len(names) != 0 {
472		t.Fatal("should be empty")
473	}
474}
475
476func TestUnionFileReaddirAskForTooMany(t *testing.T) {
477	base := &MemMapFs{}
478	overlay := &MemMapFs{}
479
480	for i := 0; i < 5; i++ {
481		WriteFile(base, fmt.Sprintf("file%d.txt", i), []byte("afero"), 0777)
482	}
483
484	ufs := &CopyOnWriteFs{base: base, layer: overlay}
485
486	f, err := ufs.Open("")
487	if err != nil {
488		t.Fatal(err)
489	}
490
491	defer f.Close()
492
493	names, err := f.Readdirnames(6)
494	if err != nil {
495		t.Fatal(err)
496	}
497
498	if len(names) != 5 {
499		t.Fatal(names)
500	}
501
502	// End of directory
503	_, err = f.Readdirnames(3)
504	if err != io.EOF {
505		t.Fatal(err)
506	}
507}
508