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

synfs_test.gno

4.33 Kb · 174 lines
  1package synfs
  2
  3import (
  4	"strconv"
  5	"strings"
  6	"testing"
  7
  8	ninep "gno.land/p/moul/x/plan9/ninep/v0"
  9)
 10
 11func fixedClock(h int64) func() int64 { return func() int64 { return h } }
 12
 13func TestTreeShape(t *testing.T) {
 14	tr := New("dev", "sys", fixedClock(42))
 15	tr.Root().
 16		Add("sysname", func() string { return "gnoland-1" }).
 17		Add("height", func() string { return "42" })
 18
 19	st := tr.Root().Stat()
 20	if st.Name != "dev" {
 21		t.Errorf("root name %q", st.Name)
 22	}
 23	if !st.IsDir() {
 24		t.Error("root should be a directory")
 25	}
 26	if st.Mtime != 42 {
 27		t.Errorf("mtime %d, want 42", st.Mtime)
 28	}
 29	if st.Mode.String() != "dr-xr-xr-x" {
 30		t.Errorf("mode %q, want dr-xr-xr-x", st.Mode.String())
 31	}
 32}
 33
 34func TestReadComputed(t *testing.T) {
 35	height := int64(100)
 36	tr := New("dev", "sys", func() int64 { return height })
 37	tr.Root().Add("height", func() string { return strconv.FormatInt(height, 10) })
 38
 39	f, err := tr.Root().Walk("height")
 40	if err != nil {
 41		t.Fatalf("walk: %v", err)
 42	}
 43	got, _ := ninep.ReadAll(f)
 44	if got != "100" {
 45		t.Errorf("got %q", got)
 46	}
 47	// The generator runs on every read, so the file follows the chain.
 48	height = 101
 49	got, _ = ninep.ReadAll(f)
 50	if got != "101" {
 51		t.Errorf("after the block advanced: got %q", got)
 52	}
 53	if v := f.Stat().Qid.Version; v != 0 {
 54		t.Errorf("a synthetic file pins version 0, got %d", v)
 55	}
 56}
 57
 58func TestReadWindow(t *testing.T) {
 59	tr := New("dev", "sys", nil)
 60	tr.Root().Add("msg", func() string { return "hello world" })
 61	f, _ := tr.Root().Walk("msg")
 62
 63	tests := []struct {
 64		off, count int64
 65		want       string
 66	}{
 67		{0, -1, "hello world"},
 68		{0, 5, "hello"},
 69		{6, 5, "world"},
 70		{99, 5, ""},
 71	}
 72	for _, tt := range tests {
 73		got, err := f.Read(tt.off, tt.count)
 74		if err != nil {
 75			t.Fatalf("read: %v", err)
 76		}
 77		if got != tt.want {
 78			t.Errorf("Read(%d,%d): got %q, want %q", tt.off, tt.count, got, tt.want)
 79		}
 80	}
 81}
 82
 83func TestAddRangeEndlessFile(t *testing.T) {
 84	// /dev/zero has no end: it must serve the window rather than a value.
 85	tr := New("dev", "sys", nil)
 86	tr.Root().AddRange("zero", 0444, func(off, count int64) (string, error) {
 87		if count < 0 {
 88			count = 0
 89		}
 90		return strings.Repeat("0", int(count)), nil
 91	})
 92	f, _ := tr.Root().Walk("zero")
 93	got, _ := f.Read(0, 4)
 94	if got != "0000" {
 95		t.Errorf("got %q", got)
 96	}
 97	got, _ = f.Read(0, -1)
 98	if got != "" {
 99		t.Errorf("an unbounded read of an endless file must not hang or grow: got %q", got)
100	}
101}
102
103func TestReadDirIsOrdered(t *testing.T) {
104	tr := New("dev", "sys", fixedClock(1))
105	tr.Root().
106		Add("zero", func() string { return "" }).
107		Add("null", func() string { return "" }).
108		Add("time", func() string { return "" })
109
110	ents, err := tr.Root().ReadDir()
111	if err != nil {
112		t.Fatalf("readdir: %v", err)
113	}
114	want := []string{"null", "time", "zero"}
115	if len(ents) != len(want) {
116		t.Fatalf("got %d entries", len(ents))
117	}
118	for i, w := range want {
119		if ents[i].Name != w {
120			t.Errorf("entry %d: got %q, want %q", i, ents[i].Name, w)
121		}
122	}
123	if got := tr.Root().Names(); len(got) != 3 || got[0] != "null" {
124		t.Errorf("Names: %v", got)
125	}
126}
127
128func TestSubdirectories(t *testing.T) {
129	tr := New("proc", "sys", fixedClock(7))
130	sub := tr.NewDir("1")
131	sub.Add("status", func() string { return "running" })
132	tr.Root().AddDir(sub)
133
134	f, err := ninep.Walk(tr.Root(), ninep.Elems("/1/status"))
135	if err != nil {
136		t.Fatalf("walk: %v", err)
137	}
138	got, _ := ninep.ReadAll(f)
139	if got != "running" {
140		t.Errorf("got %q", got)
141	}
142}
143
144func TestErrors(t *testing.T) {
145	tr := New("dev", "sys", nil)
146	tr.Root().Add("null", func() string { return "" })
147
148	if _, err := tr.Root().Read(0, -1); err != ninep.ErrIsDir {
149		t.Errorf("read a directory: got %v", err)
150	}
151	if _, err := tr.Root().Walk("absent"); err != ninep.ErrNotExist {
152		t.Errorf("walk missing: got %v", err)
153	}
154	if _, err := tr.Root().Walk("a/b"); err != ninep.ErrBadName {
155		t.Errorf("walk bad name: got %v", err)
156	}
157	f, _ := tr.Root().Walk("null")
158	if _, err := f.Walk("x"); err != ninep.ErrNotDir {
159		t.Errorf("walk through a file: got %v", err)
160	}
161	if _, err := f.ReadDir(); err != ninep.ErrNotDir {
162		t.Errorf("readdir a file: got %v", err)
163	}
164}
165
166func TestSyntheticTreeIsReadOnly(t *testing.T) {
167	// The whole point: a synthetic tree implements File and NOT Mutable, so
168	// it is safe to hand to a namespace owned by somebody else.
169	tr := New("dev", "sys", nil)
170	var f ninep.File = tr.Root()
171	if _, ok := f.(ninep.Mutable); ok {
172		t.Error("a synthetic tree must not be Mutable")
173	}
174}