Finish writing tests

This commit is contained in:
Melon 2023-10-08 16:24:09 +01:00
parent 3bdbd3be80
commit 35ae69993c
Signed by: melon
GPG Key ID: 6C9D970C50D26A25
8 changed files with 53 additions and 24 deletions

1
a/a-test.txt Normal file
View File

@ -0,0 +1 @@
Nothing

1
a/example.md Normal file
View File

@ -0,0 +1 @@
# This is an example

View File

@ -2,15 +2,16 @@ package overlapfs
import ( import (
"errors" "errors"
"fmt"
"io/fs" "io/fs"
"slices"
"strings"
) )
// OverlapFS layers B on top of A // OverlapFS layers B on top of A
// //
// Open and Stat reimplemented to show B unless ErrNotExist is returned then show A. // Open and Stat reimplemented to show B unless ErrNotExist is returned then show A
// //
// Glob and ReadDir are reimplemented to uniquely merge the returned lists. // Glob and ReadDir are reimplemented to uniquely merge and sort the returned slices
// //
// For ReadFile and Sub use the standard fs.ReadFile and fs.Sub implementations // For ReadFile and Sub use the standard fs.ReadFile and fs.Sub implementations
type OverlapFS struct { type OverlapFS struct {
@ -24,8 +25,9 @@ var _ interface {
fs.ReadDirFS fs.ReadDirFS
} = &OverlapFS{} } = &OverlapFS{}
// Open implements fs.FS, the file named will be read from B if the file exists,
// otherwise the file named will be read from A
func (o OverlapFS) Open(name string) (fs.File, error) { func (o OverlapFS) Open(name string) (fs.File, error) {
fmt.Println("Open", name)
open, err := o.B.Open(name) open, err := o.B.Open(name)
switch { switch {
case err == nil: case err == nil:
@ -36,6 +38,8 @@ func (o OverlapFS) Open(name string) (fs.File, error) {
return nil, err return nil, err
} }
// Stat implements fs.StatFS, the file named will have stats read from B if the
// file exists, otherwise the file named will have stats read from A
func (o OverlapFS) Stat(name string) (fs.FileInfo, error) { func (o OverlapFS) Stat(name string) (fs.FileInfo, error) {
stat, err := fs.Stat(o.B, name) stat, err := fs.Stat(o.B, name)
switch { switch {
@ -47,36 +51,39 @@ func (o OverlapFS) Stat(name string) (fs.FileInfo, error) {
return nil, err return nil, err
} }
// Glob implements fs.GlobFS, the pattern will be globed in B and A and the
// result uniquely merged and sorted
func (o OverlapFS) Glob(pattern string) ([]string, error) { func (o OverlapFS) Glob(pattern string) ([]string, error) {
aSlice, err := fs.Glob(o.B, pattern) bSlice, err := fs.Glob(o.B, pattern)
if err != nil { if err != nil {
return nil, err return nil, err
} }
bSlice, err := fs.Glob(o.A, pattern) aSlice, err := fs.Glob(o.A, pattern)
if err != nil { if err != nil {
return nil, err return nil, err
} }
mergeUnique(aSlice, bSlice, func(s string) string { return s }) return mergeUnique(aSlice, bSlice, func(s string) string { return s }), nil
return aSlice, nil
} }
// ReadDir implements fs.ReadDirFS, the directory named will be read in B and A
// and the result uniquely merged and sorted
func (o OverlapFS) ReadDir(name string) ([]fs.DirEntry, error) { func (o OverlapFS) ReadDir(name string) ([]fs.DirEntry, error) {
aSlice, err := fs.ReadDir(o.B, name) bSlice, err := fs.ReadDir(o.B, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
bSlice, err := fs.ReadDir(o.A, name) aSlice, err := fs.ReadDir(o.A, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
mergeUnique(aSlice, bSlice, func(entry fs.DirEntry) string { return entry.Name() }) return mergeUnique(aSlice, bSlice, func(entry fs.DirEntry) string { return entry.Name() }), nil
return aSlice, nil
} }
// mergeUnique puts unique values from b into a // mergeUnique puts unique values from b into a and sorts the resulting merged
func mergeUnique[T any](a, b []T, key func(T) string) { // slice
func mergeUnique[T any](a, b []T, key func(T) string) []T {
m := make(map[string]struct{}) m := make(map[string]struct{})
for _, i := range a { for _, i := range a {
m[key(i)] = struct{}{} m[key(i)] = struct{}{}
@ -86,4 +93,8 @@ func mergeUnique[T any](a, b []T, key func(T) string) {
a = append(a, i) a = append(a, i)
} }
} }
slices.SortFunc(a, func(a, b T) int {
return strings.Compare(key(a), key(b))
})
return a
} }

View File

@ -16,16 +16,32 @@ func TestOverlapFS_Open(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "hello.txt", stat.Name()) assert.Equal(t, "hello.txt", stat.Name())
bAll, err := io.ReadAll(open) bAll, err := io.ReadAll(open)
assert.Equal(t, "Hello World!", string(bAll)) assert.Equal(t, "Hello World!\n", string(bAll))
} }
func TestOverlapFS_Stat(t *testing.T) { func TestOverlapFS_Stat(t *testing.T) {
o := OverlapFS{A: a.Embed, B: b.Embed} o := OverlapFS{A: a.Embed, B: b.Embed}
open, err := o.Stat("hello.txt") stat, err := o.Stat("hello.txt")
assert.NoError(t, err)
stat, err := open.Stat()
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "hello.txt", stat.Name()) assert.Equal(t, "hello.txt", stat.Name())
bAll, err := io.ReadAll(open) assert.Equal(t, int64(13), stat.Size())
assert.Equal(t, "Hello World!", string(bAll)) assert.False(t, stat.IsDir())
}
func TestOverlapFS_Glob(t *testing.T) {
o := OverlapFS{A: a.Embed, B: b.Embed}
glob, err := o.Glob("*.txt")
assert.NoError(t, err)
assert.Equal(t, []string{"a-test.txt", "hello.txt", "just-a.txt", "just-b.txt"}, glob)
}
func TestOverlapFS_ReadDir(t *testing.T) {
o := OverlapFS{A: a.Embed, B: b.Embed}
dir, err := o.ReadDir(".")
assert.NoError(t, err)
z := make([]string, 0, len(dir))
for _, i := range dir {
z = append(z, i.Name())
}
assert.Equal(t, []string{"a-test.txt", "a.go", "b.go", "example.md", "hello.txt", "just-a.txt", "just-b.txt"}, z)
} }