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