Skip to content

Commit 13989d1

Browse files
author
mobus
committed
fs mmap moudle
1 parent ea52ffb commit 13989d1

16 files changed

+1136
-0
lines changed

fs/fs.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
Package fs provides a file system interface.
3+
*/
4+
package fs
5+
6+
import (
7+
"errors"
8+
"io"
9+
"os"
10+
)
11+
12+
var (
13+
errAppendModeNotSupported = errors.New("append mode is not supported")
14+
)
15+
16+
// File is the interface compatible with os.File.
17+
type File interface {
18+
io.Closer
19+
io.Reader
20+
io.ReaderAt
21+
io.Seeker
22+
io.Writer
23+
io.WriterAt
24+
25+
// Stat returns os.FileInfo describing the file.
26+
Stat() (os.FileInfo, error)
27+
28+
// Sync commits the current contents of the file.
29+
Sync() error
30+
31+
// Truncate changes the size of the file.
32+
Truncate(size int64) error
33+
34+
// Slice reads and returns the contents of file from offset start to offset end.
35+
Slice(start int64, end int64) ([]byte, error)
36+
}
37+
38+
// LockFile represents a lock file.
39+
type LockFile interface {
40+
// Unlock and removes the lock file.
41+
Unlock() error
42+
}
43+
44+
// FileSystem represents a file system.
45+
type FileSystem interface {
46+
// OpenFile opens the file with specified flag.
47+
OpenFile(name string, flag int, perm os.FileMode) (File, error)
48+
49+
// Stat returns os.FileInfo describing the file.
50+
Stat(name string) (os.FileInfo, error)
51+
52+
// Remove removes the file.
53+
Remove(name string) error
54+
55+
// Rename renames oldpath to newpath.
56+
Rename(oldpath, newpath string) error
57+
58+
// ReadDir reads the directory and returns a list of directory entries.
59+
ReadDir(name string) ([]os.FileInfo, error)
60+
61+
// CreateLockFile creates a lock file.
62+
CreateLockFile(name string, perm os.FileMode) (LockFile, bool, error)
63+
}

fs/fs_test.go

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
package fs
2+
3+
import (
4+
"io"
5+
"os"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
const (
12+
lockTestPath = "test.lock"
13+
)
14+
15+
var (
16+
lockTestMode = os.FileMode(0666)
17+
)
18+
19+
func testLockFile(t *testing.T, fs FileSystem) {
20+
_ = fs.Remove(lockTestPath)
21+
lock, acquiredExisting, err := fs.CreateLockFile(lockTestPath, lockTestMode)
22+
if lock == nil || acquiredExisting || err != nil {
23+
t.Fatal(lock, err, acquiredExisting)
24+
}
25+
lock2, acquiredExisting2, err2 := fs.CreateLockFile(lockTestPath, lockTestMode)
26+
if lock2 != nil || acquiredExisting2 || err2 != os.ErrExist {
27+
t.Fatal(lock2, acquiredExisting2, err2)
28+
}
29+
30+
err = lock.Unlock()
31+
assert.Nil(t, err)
32+
33+
_, err = fs.Stat(lockTestPath)
34+
assert.NotNil(t, err)
35+
}
36+
37+
func touchFile(fs FileSystem, path string) error {
38+
f, err := fs.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.FileMode(0666))
39+
if err != nil {
40+
return err
41+
}
42+
return f.Close()
43+
}
44+
45+
func testLockFileAcquireExisting(t *testing.T, fs FileSystem) {
46+
err := touchFile(fs, lockTestPath)
47+
assert.Nil(t, err)
48+
49+
lock, acquiredExisting, err := fs.CreateLockFile(lockTestPath, lockTestMode)
50+
if lock == nil || !acquiredExisting || err != nil {
51+
t.Fatal(lock, err, acquiredExisting)
52+
}
53+
54+
err = lock.Unlock()
55+
assert.Nil(t, err)
56+
57+
_, err = fs.Stat(lockTestPath)
58+
assert.NotNil(t, err)
59+
}
60+
61+
func testFS(t *testing.T, fsys FileSystem) {
62+
f, err := fsys.OpenFile("test", os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.FileMode(0666))
63+
assert.Nil(t, err)
64+
65+
buf := make([]byte, 10)
66+
67+
t.Run("Empty file", func(t *testing.T) {
68+
off, err := f.Seek(0, io.SeekCurrent)
69+
assert.Nil(t, err)
70+
assert.Equal(t, int64(0), off)
71+
72+
n, err := f.Read(buf)
73+
assert.Equal(t, 0, n)
74+
assert.Equal(t, io.EOF, err)
75+
76+
n, err = f.ReadAt(buf, 0)
77+
assert.Equal(t, 0, n)
78+
assert.Equal(t, io.EOF, err)
79+
80+
n, err = f.ReadAt(buf, 10)
81+
assert.Equal(t, 0, n)
82+
assert.Equal(t, io.EOF, err)
83+
84+
b, err := f.Slice(1, 10)
85+
assert.Equal(t, io.EOF, err)
86+
assert.Nil(t, b)
87+
})
88+
89+
testData := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
90+
91+
t.Run("Write", func(t *testing.T) {
92+
n, err := f.Write(testData[:9])
93+
assert.Nil(t, err)
94+
assert.Equal(t, 9, n)
95+
96+
off, err := f.Seek(0, io.SeekCurrent)
97+
assert.Nil(t, err)
98+
assert.Equal(t, int64(9), off)
99+
})
100+
101+
t.Run("Write beyond EOF", func(t *testing.T) {
102+
off, err := f.Seek(2, io.SeekStart)
103+
assert.Nil(t, err)
104+
assert.Equal(t, int64(2), off)
105+
106+
n, err := f.Write(testData[2:])
107+
assert.Nil(t, err)
108+
assert.Equal(t, 8, n)
109+
110+
off, err = f.Seek(0, io.SeekCurrent)
111+
assert.Nil(t, err)
112+
assert.Equal(t, int64(10), off)
113+
})
114+
115+
t.Run("Slice", func(t *testing.T) {
116+
b, err := f.Slice(1, 9)
117+
assert.Nil(t, err)
118+
assert.Equal(t, testData[1:9], b)
119+
120+
b, err = f.Slice(0, 10)
121+
assert.Nil(t, err)
122+
assert.Equal(t, testData, b)
123+
124+
// Offset larger than mapping.
125+
b, err = f.Slice(0, 12)
126+
assert.Equal(t, io.EOF, err)
127+
assert.Nil(t, b)
128+
})
129+
130+
t.Run("WriteAt", func(t *testing.T) {
131+
n, err := f.WriteAt(testData[1:4], 1)
132+
assert.Nil(t, err)
133+
assert.Equal(t, 3, n)
134+
135+
// WriteAt doesn't move offset.
136+
off, err := f.Seek(0, io.SeekCurrent)
137+
assert.Nil(t, err)
138+
assert.Equal(t, int64(10), off)
139+
})
140+
141+
t.Run("Sync", func(t *testing.T) {
142+
// Not tested yet, just make sure it doesn't return an error.
143+
assert.Nil(t, f.Sync())
144+
})
145+
146+
t.Run("ReadAt", func(t *testing.T) {
147+
n, err := f.ReadAt(buf, 0)
148+
assert.Nil(t, err)
149+
assert.Equal(t, len(testData), n)
150+
assert.Equal(t, testData, buf)
151+
})
152+
153+
t.Run("Read EOF", func(t *testing.T) {
154+
n, err := f.Read(buf)
155+
assert.Equal(t, io.EOF, err)
156+
assert.Equal(t, 0, n)
157+
})
158+
159+
t.Run("Read", func(t *testing.T) {
160+
// SeekEnd and Read
161+
off, err := f.Seek(0, io.SeekEnd)
162+
assert.Nil(t, err)
163+
assert.Equal(t, int64(len(testData)), off)
164+
165+
n, err := f.Read(buf)
166+
assert.Equal(t, io.EOF, err)
167+
assert.Equal(t, 0, n)
168+
169+
// SeekStart and Read
170+
off, err = f.Seek(0, io.SeekStart)
171+
assert.Nil(t, err)
172+
assert.Equal(t, int64(0), off)
173+
174+
n, err = f.Read(buf)
175+
assert.Nil(t, err)
176+
assert.Equal(t, len(testData), n)
177+
assert.Equal(t, testData, buf)
178+
179+
off, err = f.Seek(0, io.SeekCurrent)
180+
assert.Equal(t, int64(n), off)
181+
assert.Nil(t, err)
182+
183+
// SeekStart 2 and Read
184+
testOff := int64(2)
185+
lbuf := make([]byte, 8)
186+
off, err = f.Seek(testOff, io.SeekStart)
187+
assert.Nil(t, err)
188+
assert.Equal(t, testOff, off)
189+
190+
n, err = f.Read(lbuf)
191+
assert.Nil(t, err)
192+
assert.Equal(t, len(testData)-int(testOff), n)
193+
assert.Equal(t, testData[testOff:], lbuf)
194+
})
195+
196+
t.Run("Read larger than file", func(t *testing.T) {
197+
off, err := f.Seek(0, io.SeekStart)
198+
assert.Nil(t, err)
199+
assert.Equal(t, int64(0), off)
200+
201+
lbuf := make([]byte, 4096)
202+
n, err := f.Read(lbuf)
203+
assert.Nil(t, err)
204+
assert.Equal(t, len(testData), n)
205+
assert.Equal(t, testData, lbuf[:n])
206+
207+
n, err = f.Read(lbuf)
208+
assert.Equal(t, io.EOF, err)
209+
assert.Equal(t, 0, n)
210+
})
211+
212+
t.Run("Close and Open again", func(t *testing.T) {
213+
assert.Nil(t, f.Close())
214+
215+
f, err = fsys.OpenFile("test", os.O_RDWR, os.FileMode(0666))
216+
assert.Nil(t, err)
217+
218+
b, err := f.Slice(1, 10)
219+
assert.Nil(t, err)
220+
assert.Equal(t, testData[1:], b)
221+
})
222+
223+
t.Run("Truncate extend", func(t *testing.T) {
224+
err := f.Truncate(11)
225+
assert.Nil(t, err)
226+
227+
lbuf := make([]byte, 11)
228+
n, err := f.ReadAt(lbuf, 0)
229+
assert.Nil(t, err)
230+
assert.Equal(t, 11, n)
231+
assert.Equal(t, testData, lbuf[:10])
232+
233+
b, err := f.Slice(0, 11)
234+
assert.Nil(t, err)
235+
assert.Equal(t, testData, b[:10])
236+
237+
fi, err := f.Stat()
238+
assert.Nil(t, err)
239+
assert.Equal(t, int64(11), fi.Size())
240+
})
241+
242+
t.Run("Truncate shrink", func(t *testing.T) {
243+
err := f.Truncate(1)
244+
assert.Nil(t, err)
245+
246+
lbuf := make([]byte, 1)
247+
n, err := f.ReadAt(lbuf, 0)
248+
assert.Nil(t, err)
249+
assert.Equal(t, 1, n)
250+
assert.Equal(t, testData[:1], lbuf)
251+
252+
b, err := f.Slice(0, 1)
253+
assert.Nil(t, err)
254+
assert.Equal(t, testData[:1], b)
255+
256+
b, err = f.Slice(0, 10)
257+
assert.Equal(t, io.EOF, err)
258+
assert.Nil(t, b)
259+
260+
fi, err := f.Stat()
261+
assert.Nil(t, err)
262+
assert.Equal(t, int64(1), fi.Size())
263+
})
264+
265+
t.Run("Truncate shrink to zero", func(t *testing.T) {
266+
err := f.Truncate(0)
267+
assert.Nil(t, err)
268+
269+
n, err := f.ReadAt(buf, 0)
270+
assert.Equal(t, io.EOF, err)
271+
assert.Equal(t, 0, n)
272+
273+
b, err := f.Slice(0, 1)
274+
assert.Equal(t, io.EOF, err)
275+
assert.Nil(t, b)
276+
277+
fi, err := f.Stat()
278+
assert.Nil(t, err)
279+
assert.Equal(t, int64(0), fi.Size())
280+
})
281+
282+
t.Run("Close", func(t *testing.T) {
283+
assert.Nil(t, f.Close())
284+
285+
err := f.Close()
286+
assert.NotNil(t, err)
287+
288+
_, err = f.Seek(1, io.SeekStart)
289+
assert.NotNil(t, err)
290+
})
291+
292+
t.Run("Rename", func(t *testing.T) {
293+
err := fsys.Rename("foobar", "baz")
294+
assert.NotNil(t, err)
295+
296+
assert.Nil(t, fsys.Rename("test", "test2"))
297+
fi, err := fsys.Stat("test2")
298+
assert.Nil(t, err)
299+
assert.Equal(t, int64(0), fi.Size())
300+
assert.Equal(t, "test2", fi.Name())
301+
})
302+
303+
t.Run("ReadDir", func(t *testing.T) {
304+
fis, err := fsys.ReadDir(".")
305+
assert.Nil(t, err)
306+
307+
var hasTestFile bool
308+
for _, fi := range fis {
309+
if fi.Name() == "test2" {
310+
hasTestFile = true
311+
}
312+
}
313+
assert.Equal(t, true, hasTestFile)
314+
})
315+
316+
t.Run("Remove", func(t *testing.T) {
317+
err := fsys.Remove("test2")
318+
assert.Nil(t, err)
319+
320+
_, err = fsys.Stat("test2")
321+
assert.NotNil(t, err)
322+
})
323+
}

0 commit comments

Comments
 (0)