Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

memfs_test.gno

7.38 Kb · 279 lines
  1package memfs
  2
  3import (
  4	"testing"
  5
  6	ninep "gno.land/p/moul/x/plan9/ninep/v0"
  7)
  8
  9func TestRootStat(t *testing.T) {
 10	fs := New("glenda", 100)
 11	s := fs.Root().Stat()
 12	if s.Name != "/" {
 13		t.Errorf("root name: got %q", s.Name)
 14	}
 15	if !s.IsDir() {
 16		t.Error("root should be a directory")
 17	}
 18	if s.Length != 0 {
 19		t.Errorf("a directory reports length 0 in 9P, got %d", s.Length)
 20	}
 21	if s.Uid != "glenda" || s.Gid != "glenda" {
 22		t.Errorf("owner: got %q/%q", s.Uid, s.Gid)
 23	}
 24	if s.Mtime != 100 {
 25		t.Errorf("mtime: got %d, want 100", s.Mtime)
 26	}
 27}
 28
 29func TestCreateWalkRead(t *testing.T) {
 30	fs := New("glenda", 1)
 31	root := fs.RootMutable()
 32
 33	f, err := root.Create("greeting", ninep.FilePerm, 2)
 34	if err != nil {
 35		t.Fatalf("create: %v", err)
 36	}
 37	m := f.(ninep.Mutable)
 38	n, err := m.Write(0, "hello world", 3)
 39	if err != nil {
 40		t.Fatalf("write: %v", err)
 41	}
 42	if n != 11 {
 43		t.Errorf("wrote %d bytes, want 11", n)
 44	}
 45
 46	got, err := root.Walk("greeting")
 47	if err != nil {
 48		t.Fatalf("walk: %v", err)
 49	}
 50	data, err := ninep.ReadAll(got)
 51	if err != nil {
 52		t.Fatalf("read: %v", err)
 53	}
 54	if data != "hello world" {
 55		t.Errorf("read back %q", data)
 56	}
 57	if l := got.Stat().Length; l != 11 {
 58		t.Errorf("length %d, want 11", l)
 59	}
 60}
 61
 62func TestWalkErrors(t *testing.T) {
 63	fs := New("glenda", 1)
 64	root := fs.RootMutable()
 65	root.Create("file", ninep.FilePerm, 1)
 66
 67	if _, err := root.Walk("absent"); err != ninep.ErrNotExist {
 68		t.Errorf("missing name: got %v, want ErrNotExist", err)
 69	}
 70	if _, err := root.Walk("a/b"); err != ninep.ErrBadName {
 71		t.Errorf("slash in name: got %v, want ErrBadName", err)
 72	}
 73	f, _ := root.Walk("file")
 74	if _, err := f.Walk("x"); err != ninep.ErrNotDir {
 75		t.Errorf("walk through a file: got %v, want ErrNotDir", err)
 76	}
 77	if _, err := f.ReadDir(); err != ninep.ErrNotDir {
 78		t.Errorf("readdir of a file: got %v, want ErrNotDir", err)
 79	}
 80	if _, err := root.Read(0, -1); err != ninep.ErrIsDir {
 81		t.Errorf("read of a directory: got %v, want ErrIsDir", err)
 82	}
 83}
 84
 85func TestReadDirIsOrdered(t *testing.T) {
 86	fs := New("glenda", 1)
 87	root := fs.RootMutable()
 88	for _, name := range []string{"zulu", "alpha", "mike", "bravo"} {
 89		if _, err := root.Create(name, ninep.FilePerm, 1); err != nil {
 90			t.Fatalf("create %s: %v", name, err)
 91		}
 92	}
 93	ents, err := root.ReadDir()
 94	if err != nil {
 95		t.Fatalf("readdir: %v", err)
 96	}
 97	want := []string{"alpha", "bravo", "mike", "zulu"}
 98	if len(ents) != len(want) {
 99		t.Fatalf("got %d entries, want %d", len(ents), len(want))
100	}
101	for i, w := range want {
102		if ents[i].Name != w {
103			t.Errorf("entry %d: got %q, want %q", i, ents[i].Name, w)
104		}
105	}
106}
107
108func TestCreateDuplicate(t *testing.T) {
109	fs := New("glenda", 1)
110	root := fs.RootMutable()
111	root.Create("x", ninep.FilePerm, 1)
112	if _, err := root.Create("x", ninep.FilePerm, 1); err != ninep.ErrExist {
113		t.Errorf("got %v, want ErrExist", err)
114	}
115	if _, err := root.Create("", ninep.FilePerm, 1); err != ninep.ErrBadName {
116		t.Errorf("empty name: got %v, want ErrBadName", err)
117	}
118	if _, err := root.Create("..", ninep.FilePerm, 1); err != ninep.ErrBadName {
119		t.Errorf("dotdot: got %v, want ErrBadName", err)
120	}
121}
122
123func TestRemove(t *testing.T) {
124	fs := New("glenda", 1)
125	root := fs.RootMutable()
126	root.Create("file", ninep.FilePerm, 1)
127	d, _ := root.Create("dir", ninep.DirPerm, 1)
128	d.(ninep.Mutable).Create("inner", ninep.FilePerm, 1)
129
130	if err := root.Remove("absent"); err != ninep.ErrNotExist {
131		t.Errorf("remove missing: got %v", err)
132	}
133	if err := root.Remove("dir"); err != ninep.ErrNotEmpty {
134		t.Errorf("remove non-empty dir: got %v, want ErrNotEmpty", err)
135	}
136	if err := d.(ninep.Mutable).Remove("inner"); err != nil {
137		t.Fatalf("remove inner: %v", err)
138	}
139	if err := root.Remove("dir"); err != nil {
140		t.Fatalf("remove emptied dir: %v", err)
141	}
142	if err := root.Remove("file"); err != nil {
143		t.Fatalf("remove file: %v", err)
144	}
145	ents, _ := root.ReadDir()
146	if len(ents) != 0 {
147		t.Errorf("root should be empty, has %d entries", len(ents))
148	}
149}
150
151func TestWriteOffsets(t *testing.T) {
152	tests := []struct {
153		name  string
154		start string
155		off   int64
156		data  string
157		want  string
158	}{
159		{"append at end", "abc", 3, "def", "abcdef"},
160		{"overwrite middle", "abcdef", 2, "XY", "abXYef"},
161		{"overwrite past end", "abc", 2, "XYZ", "abXYZ"},
162		{"from zero", "abcdef", 0, "ZZ", "ZZcdef"},
163		{"gap is nul filled", "ab", 4, "Z", "ab\x00\x00Z"},
164		{"negative offset clamps", "abc", -2, "Z", "Zbc"},
165	}
166	for _, tt := range tests {
167		fs := New("glenda", 1)
168		root := fs.RootMutable()
169		f, _ := root.Create("f", ninep.FilePerm, 1)
170		m := f.(ninep.Mutable)
171		m.Write(0, tt.start, 1)
172		m.Write(tt.off, tt.data, 2)
173		got, _ := ninep.ReadAll(f)
174		if got != tt.want {
175			t.Errorf("%s: got %q, want %q", tt.name, got, tt.want)
176		}
177	}
178}
179
180func TestAppendOnlyIgnoresOffset(t *testing.T) {
181	fs := New("glenda", 1)
182	root := fs.RootMutable()
183	f, _ := root.Create("log", ninep.DMAPPEND|0644, 1)
184	m := f.(ninep.Mutable)
185	m.Write(0, "one\n", 1)
186	m.Write(0, "two\n", 2) // offset 0, but append-only pins it to the end
187	got, _ := ninep.ReadAll(f)
188	if got != "one\ntwo\n" {
189		t.Errorf("got %q", got)
190	}
191}
192
193func TestTruncate(t *testing.T) {
194	fs := New("glenda", 1)
195	root := fs.RootMutable()
196	f, _ := root.Create("f", ninep.FilePerm, 1)
197	m := f.(ninep.Mutable)
198	m.Write(0, "abcdef", 1)
199
200	m.Truncate(3, 2)
201	if got, _ := ninep.ReadAll(f); got != "abc" {
202		t.Errorf("shrink: got %q", got)
203	}
204	m.Truncate(5, 3)
205	if got, _ := ninep.ReadAll(f); got != "abc\x00\x00" {
206		t.Errorf("grow: got %q", got)
207	}
208	m.Truncate(0, 4)
209	if got, _ := ninep.ReadAll(f); got != "" {
210		t.Errorf("zero: got %q", got)
211	}
212}
213
214func TestQidVersionAndPath(t *testing.T) {
215	fs := New("glenda", 1)
216	root := fs.RootMutable()
217	a, _ := root.Create("a", ninep.FilePerm, 1)
218	b, _ := root.Create("b", ninep.FilePerm, 1)
219
220	if a.Stat().Qid.Path == b.Stat().Qid.Path {
221		t.Error("two files share a qid path")
222	}
223	v0 := a.Stat().Qid.Version
224	a.(ninep.Mutable).Write(0, "x", 2)
225	if a.Stat().Qid.Version != v0+1 {
226		t.Errorf("version did not advance on write: %d -> %d", v0, a.Stat().Qid.Version)
227	}
228	if a.Stat().Mtime != 2 {
229		t.Errorf("mtime: got %d, want 2", a.Stat().Mtime)
230	}
231}
232
233func TestMkdirAllAndWriteFile(t *testing.T) {
234	fs := New("glenda", 1)
235	if _, err := fs.MkdirAll("/usr/glenda/lib", 1); err != nil {
236		t.Fatalf("mkdirall: %v", err)
237	}
238	// idempotent
239	if _, err := fs.MkdirAll("/usr/glenda", 1); err != nil {
240		t.Fatalf("mkdirall again: %v", err)
241	}
242	if err := fs.WriteFile("/usr/glenda/lib/profile", "font=pelm\n", 2); err != nil {
243		t.Fatalf("writefile: %v", err)
244	}
245	f, err := ninep.Walk(fs.Root(), ninep.Elems("/usr/glenda/lib/profile"))
246	if err != nil {
247		t.Fatalf("walk: %v", err)
248	}
249	if got, _ := ninep.ReadAll(f); got != "font=pelm\n" {
250		t.Errorf("got %q", got)
251	}
252	// rewriting replaces rather than appends
253	if err := fs.WriteFile("/usr/glenda/lib/profile", "font=lucida\n", 3); err != nil {
254		t.Fatalf("rewrite: %v", err)
255	}
256	f, _ = ninep.Walk(fs.Root(), ninep.Elems("/usr/glenda/lib/profile"))
257	if got, _ := ninep.ReadAll(f); got != "font=lucida\n" {
258		t.Errorf("rewrite got %q", got)
259	}
260}
261
262func TestMkdirAllThroughAFileFails(t *testing.T) {
263	fs := New("glenda", 1)
264	fs.WriteFile("/etc", "not a directory", 1)
265	if _, err := fs.MkdirAll("/etc/passwd", 2); err != ninep.ErrNotDir {
266		t.Errorf("got %v, want ErrNotDir", err)
267	}
268}
269
270func TestWalkDepthLimit(t *testing.T) {
271	fs := New("glenda", 1)
272	elems := []string{}
273	for i := 0; i <= ninep.MaxDepth; i++ {
274		elems = append(elems, "x")
275	}
276	if _, err := ninep.Walk(fs.Root(), elems); err != ninep.ErrTooDeep {
277		t.Errorf("got %v, want ErrTooDeep", err)
278	}
279}