1package afero
2
3import (
4	"os"
5	"path/filepath"
6	"strings"
7	"testing"
8)
9
10func TestSymlinkIfPossible(t *testing.T) {
11	wd, _ := os.Getwd()
12	defer func() {
13		os.Chdir(wd)
14	}()
15
16	osFs := &OsFs{}
17
18	workDir, err := TempDir(osFs, "", "afero-symlink")
19	if err != nil {
20		t.Fatal(err)
21	}
22
23	defer func() {
24		osFs.RemoveAll(workDir)
25	}()
26
27	memWorkDir := "/sym"
28
29	memFs := NewMemMapFs()
30	overlayFs1 := &CopyOnWriteFs{base: osFs, layer: memFs}
31	overlayFs2 := &CopyOnWriteFs{base: memFs, layer: osFs}
32	overlayFsMemOnly := &CopyOnWriteFs{base: memFs, layer: NewMemMapFs()}
33	basePathFs := &BasePathFs{source: osFs, path: workDir}
34	basePathFsMem := &BasePathFs{source: memFs, path: memWorkDir}
35	roFs := &ReadOnlyFs{source: osFs}
36	roFsMem := &ReadOnlyFs{source: memFs}
37
38	pathFileMem := filepath.Join(memWorkDir, "aferom.txt")
39	osPath := filepath.Join(workDir, "afero.txt")
40
41	WriteFile(osFs, osPath, []byte("Hi, Afero!"), 0777)
42	WriteFile(memFs, filepath.Join(pathFileMem), []byte("Hi, Afero!"), 0777)
43
44	testLink := func(l Linker, source, destination string, output *string) {
45		if fs, ok := l.(Fs); ok {
46			dir := filepath.Dir(destination)
47			if dir != "" {
48				fs.MkdirAll(dir, 0777)
49			}
50		}
51
52		err := l.SymlinkIfPossible(source, destination)
53		if (err == nil) && (output != nil) {
54			t.Fatalf("Error creating symlink, succeeded when expecting error %v", *output)
55		} else if (err != nil) && (output == nil) {
56			t.Fatalf("Error creating symlink, expected success, got %v", err)
57		} else if err != nil && err.Error() != *output && !strings.HasSuffix(err.Error(), *output) {
58			t.Fatalf("Error creating symlink, expected error '%v', instead got output '%v'", *output, err)
59		} else {
60			// test passed, if expecting a successful link, check the link with lstat if able
61			if output == nil {
62				if lst, ok := l.(Lstater); ok {
63					_, ok, err := lst.LstatIfPossible(destination)
64					if !ok {
65						if err != nil {
66							t.Fatalf("Error calling lstat on file after successful link, got: %v", err)
67						} else {
68							t.Fatalf("Error calling lstat on file after successful link, result didn't use lstat (not link)")
69						}
70						return
71					}
72				}
73			}
74		}
75	}
76
77	notSupported := ErrNoSymlink.Error()
78
79	testLink(osFs, osPath, filepath.Join(workDir, "os/link.txt"), nil)
80	testLink(overlayFs1, osPath, filepath.Join(workDir, "overlay/link1.txt"), &notSupported)
81	testLink(overlayFs2, pathFileMem, filepath.Join(workDir, "overlay2/link2.txt"), nil)
82	testLink(overlayFsMemOnly, pathFileMem, filepath.Join(memWorkDir, "overlay3/link.txt"), &notSupported)
83	testLink(basePathFs, "afero.txt", "basepath/link.txt", nil)
84	testLink(basePathFsMem, pathFileMem, "link/file.txt", &notSupported)
85	testLink(roFs, osPath, filepath.Join(workDir, "ro/link.txt"), &notSupported)
86	testLink(roFsMem, pathFileMem, filepath.Join(memWorkDir, "ro/link.txt"), &notSupported)
87}
88
89func TestReadlinkIfPossible(t *testing.T) {
90	wd, _ := os.Getwd()
91	defer func() {
92		os.Chdir(wd)
93	}()
94
95	osFs := &OsFs{}
96
97	workDir, err := TempDir(osFs, "", "afero-readlink")
98	if err != nil {
99		t.Fatal(err)
100	}
101
102	defer func() {
103		osFs.RemoveAll(workDir)
104	}()
105
106	memWorkDir := "/read"
107
108	memFs := NewMemMapFs()
109	overlayFs1 := &CopyOnWriteFs{base: osFs, layer: memFs}
110	overlayFs2 := &CopyOnWriteFs{base: memFs, layer: osFs}
111	overlayFsMemOnly := &CopyOnWriteFs{base: memFs, layer: NewMemMapFs()}
112	basePathFs := &BasePathFs{source: osFs, path: workDir}
113	basePathFsMem := &BasePathFs{source: memFs, path: memWorkDir}
114	roFs := &ReadOnlyFs{source: osFs}
115	roFsMem := &ReadOnlyFs{source: memFs}
116
117	pathFileMem := filepath.Join(memWorkDir, "aferom.txt")
118	osPath := filepath.Join(workDir, "afero.txt")
119
120	WriteFile(osFs, osPath, []byte("Hi, Afero!"), 0777)
121	WriteFile(memFs, filepath.Join(pathFileMem), []byte("Hi, Afero!"), 0777)
122
123	createLink := func(l Linker, source, destination string) error {
124		if fs, ok := l.(Fs); ok {
125			dir := filepath.Dir(destination)
126			if dir != "" {
127				fs.MkdirAll(dir, 0777)
128			}
129		}
130
131		return l.SymlinkIfPossible(source, destination)
132	}
133
134	testRead := func(r LinkReader, name string, output *string) {
135		_, err := r.ReadlinkIfPossible(name)
136		if (err != nil) && (output == nil) {
137			t.Fatalf("Error reading link, expected success, got error: %v", err)
138		} else if (err == nil) && (output != nil) {
139			t.Fatalf("Error reading link, succeeded when expecting error: %v", *output)
140		} else if err != nil && err.Error() != *output && !strings.HasSuffix(err.Error(), *output) {
141			t.Fatalf("Error reading link, expected error '%v', instead received '%v'", *output, err)
142		}
143	}
144
145	notSupported := ErrNoReadlink.Error()
146
147	err = createLink(osFs, osPath, filepath.Join(workDir, "os/link.txt"))
148	if err != nil {
149		t.Fatal("Error creating test link: ", err)
150	}
151
152	testRead(osFs, filepath.Join(workDir, "os/link.txt"), nil)
153	testRead(overlayFs1, filepath.Join(workDir, "os/link.txt"), nil)
154	testRead(overlayFs2, filepath.Join(workDir, "os/link.txt"), nil)
155	testRead(overlayFsMemOnly, pathFileMem, &notSupported)
156	testRead(basePathFs, "os/link.txt", nil)
157	testRead(basePathFsMem, pathFileMem, &notSupported)
158	testRead(roFs, filepath.Join(workDir, "os/link.txt"), nil)
159	testRead(roFsMem, pathFileMem, &notSupported)
160}
161