Dir.Create, Dir.Mkdir and Dir.Rename joined whatever name they were
given onto the directory's path. A name of "..", "." or one containing
a "/" isn't a child of the directory, and at the VFS root
path.Join("", "..") preserves the ".." so the new node's path pointed
outside the root. Backends which join the Fs root with the remote then
resolved it to a location above the directory the VFS was created on.
serve nfs was exploitable through this. The go-nfs library refuses a
name of exactly "." or ".." in MKDIR only, doesn't check the name in
CREATE, RENAME or SYMLINK, and never refuses a name containing a "/".
serve webdav was exploitable in the same way and has its own fix. The
other serve front-ends rely on their own or their protocol library's
path cleaning to keep such names out of the VFS.
Reject such names with EINVAL in the VFS itself, so that every
front-end and mount is protected regardless of what the layer above it
checks. This is the fix for serve nfs and defence in depth for the
others. The serve webdav check is kept as the front-end is the trust
boundary and gives the client a clear error. The source name of a
rename is only used to look the node up so it isn't checked.
VFS.Mkdir of the root, which previously went through Dir.Mkdir with an
empty name and added a nameless child node to the root, now makes the
root directory on the backend directly, still honouring --read-only.
serve webdav and serve ftp made directories through Dir.Mkdir rather
than VFS.Mkdir, so they are switched to VFS.Mkdir to keep "MKCOL /" and
"MKD /" working.
583 lines
15 KiB
Go
583 lines
15 KiB
Go
// Test suite for vfs
|
|
|
|
package vfs
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rclone/rclone/fs"
|
|
"github.com/rclone/rclone/fstest"
|
|
"github.com/rclone/rclone/vfs/vfscommon"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Some times used in the tests
|
|
var (
|
|
t1 = fstest.Time("2001-02-03T04:05:06.499999999Z")
|
|
t2 = fstest.Time("2011-12-25T12:59:59.123456789Z")
|
|
t3 = fstest.Time("2011-12-30T12:59:59.000000000Z")
|
|
)
|
|
|
|
// Constants uses in the tests
|
|
const (
|
|
writeBackDelay = fs.Duration(100 * time.Millisecond) // A short writeback delay for testing
|
|
waitForWritersDelay = 30 * time.Second // time to wait for existing writers
|
|
)
|
|
|
|
// TestMain drives the tests
|
|
func TestMain(m *testing.M) {
|
|
fstest.TestMain(m)
|
|
}
|
|
|
|
// Clean up a test VFS
|
|
func cleanupVFS(t *testing.T, vfs *VFS) {
|
|
vfs.WaitForWriters(waitForWritersDelay)
|
|
err := vfs.CleanUp()
|
|
require.NoError(t, err)
|
|
vfs.Shutdown()
|
|
}
|
|
|
|
// Create a new VFS
|
|
func newTestVFSOpt(t *testing.T, opt *vfscommon.Options) (r *fstest.Run, vfs *VFS) {
|
|
r = fstest.NewRun(t)
|
|
vfs = New(context.Background(), r.Fremote, opt)
|
|
t.Cleanup(func() {
|
|
cleanupVFS(t, vfs)
|
|
})
|
|
return r, vfs
|
|
}
|
|
|
|
// Create a new VFS with default options
|
|
func newTestVFS(t *testing.T) (r *fstest.Run, vfs *VFS) {
|
|
return newTestVFSOpt(t, nil)
|
|
}
|
|
|
|
// Check baseHandle performs as advertised
|
|
func TestVFSbaseHandle(t *testing.T) {
|
|
fh := baseHandle{}
|
|
|
|
err := fh.Chdir()
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
err = fh.Chmod(0)
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
err = fh.Chown(0, 0)
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
err = fh.Close()
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
fd := fh.Fd()
|
|
assert.Equal(t, uintptr(0), fd)
|
|
|
|
name := fh.Name()
|
|
assert.Equal(t, "", name)
|
|
|
|
_, err = fh.Read(nil)
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
_, err = fh.ReadAt(nil, 0)
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
_, err = fh.Readdir(0)
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
_, err = fh.Readdirnames(0)
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
_, err = fh.Seek(0, io.SeekStart)
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
_, err = fh.Stat()
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
err = fh.Sync()
|
|
assert.Equal(t, nil, err)
|
|
|
|
err = fh.Truncate(0)
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
_, err = fh.Write(nil)
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
_, err = fh.WriteAt(nil, 0)
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
_, err = fh.WriteString("")
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
err = fh.Flush()
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
err = fh.Release()
|
|
assert.Equal(t, ENOSYS, err)
|
|
|
|
node := fh.Node()
|
|
assert.Nil(t, node)
|
|
}
|
|
|
|
// TestVFSNew sees if the New command works properly
|
|
func TestVFSNew(t *testing.T) {
|
|
// Check active cache has this many entries
|
|
checkActiveCacheEntries := func(i int) {
|
|
_, count := activeCacheEntries()
|
|
assert.Equal(t, i, count)
|
|
}
|
|
|
|
checkActiveCacheEntries(0)
|
|
|
|
r, vfs := newTestVFS(t)
|
|
|
|
// Check making a VFS with nil options
|
|
var defaultOpt = vfscommon.Opt
|
|
defaultOpt.Init(context.Background())
|
|
|
|
checkActiveCacheEntries(1)
|
|
|
|
// Check that we get the same VFS if we ask for it again with
|
|
// the same options
|
|
vfs2 := New(context.Background(), r.Fremote, nil)
|
|
assert.Equal(t, fmt.Sprintf("%p", vfs), fmt.Sprintf("%p", vfs2))
|
|
|
|
checkActiveCacheEntries(1)
|
|
|
|
// Shut the new VFS down and check the cache still has stuff in
|
|
vfs2.Shutdown()
|
|
|
|
checkActiveCacheEntries(1)
|
|
|
|
cleanupVFS(t, vfs)
|
|
|
|
checkActiveCacheEntries(0)
|
|
}
|
|
|
|
// TestVFSHold checks a held VFS isn't shut down until it is released
|
|
// and can't be held once shut down.
|
|
func TestVFSHold(t *testing.T) {
|
|
r := fstest.NewRun(t)
|
|
vfs := New(context.Background(), r.Fremote, nil)
|
|
|
|
require.True(t, vfs.Hold())
|
|
vfs.Shutdown()
|
|
assert.NoError(t, vfs.ctx.Err(), "VFS shut down while held")
|
|
|
|
vfs.Shutdown()
|
|
assert.Error(t, vfs.ctx.Err(), "VFS not shut down when released")
|
|
assert.False(t, vfs.Hold(), "VFS held after being shut down")
|
|
}
|
|
|
|
// TestVFSNewShuttingDown checks New doesn't reuse a VFS which is being
|
|
// shut down but hasn't yet removed itself from the active cache.
|
|
func TestVFSNewShuttingDown(t *testing.T) {
|
|
r := fstest.NewRun(t)
|
|
vfs := New(context.Background(), r.Fremote, nil)
|
|
|
|
// The state Shutdown leaves the VFS in while it waits for activeMu
|
|
vfs.inUse.Store(0)
|
|
vfs2 := New(context.Background(), r.Fremote, nil)
|
|
assert.NotSame(t, vfs, vfs2, "reused a VFS being shut down")
|
|
assert.Equal(t, int32(0), vfs.inUse.Load())
|
|
|
|
vfs.Shutdown()
|
|
vfs2.Shutdown()
|
|
}
|
|
|
|
// TestVFSBusy checks Busy reports files open for write and cached
|
|
// files which haven't been uploaded yet.
|
|
func TestVFSBusy(t *testing.T) {
|
|
opt := vfscommon.Opt
|
|
opt.CacheMode = vfscommon.CacheModeWrites
|
|
opt.WriteBack = 10 * writeBackDelay
|
|
_, vfs := newTestVFSOpt(t, &opt)
|
|
assert.False(t, vfs.Busy())
|
|
|
|
fh, err := vfs.Create("file.txt")
|
|
require.NoError(t, err)
|
|
assert.True(t, vfs.Busy(), "not busy with a file open for write")
|
|
|
|
_, err = fh.WriteString("hello")
|
|
require.NoError(t, err)
|
|
require.NoError(t, fh.Close())
|
|
assert.True(t, vfs.Busy(), "not busy with a file waiting to be uploaded")
|
|
|
|
vfs.WaitForWriters(waitForWritersDelay)
|
|
assert.False(t, vfs.Busy(), "busy after the file was uploaded")
|
|
}
|
|
|
|
// TestVFSNewWithOpts sees if the New command works properly
|
|
func TestVFSNewWithOpts(t *testing.T) {
|
|
var opt = vfscommon.Opt
|
|
opt.DirPerms = 0777
|
|
opt.FilePerms = 0666
|
|
opt.Umask = 0002
|
|
_, vfs := newTestVFSOpt(t, &opt)
|
|
|
|
assert.Equal(t, vfscommon.FileMode(0775)|vfscommon.FileMode(os.ModeDir), vfs.Opt.DirPerms)
|
|
assert.Equal(t, vfscommon.FileMode(0664), vfs.Opt.FilePerms)
|
|
}
|
|
|
|
// TestVFSRoot checks root directory is present and correct
|
|
func TestVFSRoot(t *testing.T) {
|
|
_, vfs := newTestVFS(t)
|
|
|
|
root, err := vfs.Root()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, vfs.root, root)
|
|
assert.True(t, root.IsDir())
|
|
assert.Equal(t, os.FileMode(vfs.Opt.DirPerms).Perm(), root.Mode().Perm())
|
|
}
|
|
|
|
func TestVFSStat(t *testing.T) {
|
|
r, vfs := newTestVFS(t)
|
|
|
|
file1 := r.WriteObject(context.Background(), "file1", "file1 contents", t1)
|
|
file2 := r.WriteObject(context.Background(), "dir/file2", "file2 contents", t2)
|
|
r.CheckRemoteItems(t, file1, file2)
|
|
|
|
node, err := vfs.Stat("file1")
|
|
require.NoError(t, err)
|
|
assert.True(t, node.IsFile())
|
|
assert.Equal(t, "file1", node.Name())
|
|
|
|
node, err = vfs.Stat("dir")
|
|
require.NoError(t, err)
|
|
assert.True(t, node.IsDir())
|
|
assert.Equal(t, "dir", node.Name())
|
|
|
|
node, err = vfs.Stat("dir/file2")
|
|
require.NoError(t, err)
|
|
assert.True(t, node.IsFile())
|
|
assert.Equal(t, "file2", node.Name())
|
|
|
|
_, err = vfs.Stat("not found")
|
|
assert.Equal(t, os.ErrNotExist, err)
|
|
|
|
_, err = vfs.Stat("dir/not found")
|
|
assert.Equal(t, os.ErrNotExist, err)
|
|
|
|
_, err = vfs.Stat("not found/not found")
|
|
assert.Equal(t, os.ErrNotExist, err)
|
|
|
|
_, err = vfs.Stat("file1/under a file")
|
|
assert.Equal(t, os.ErrNotExist, err)
|
|
}
|
|
|
|
func TestVFSStatParent(t *testing.T) {
|
|
r, vfs := newTestVFS(t)
|
|
|
|
file1 := r.WriteObject(context.Background(), "file1", "file1 contents", t1)
|
|
file2 := r.WriteObject(context.Background(), "dir/file2", "file2 contents", t2)
|
|
r.CheckRemoteItems(t, file1, file2)
|
|
|
|
node, leaf, err := vfs.StatParent("file1")
|
|
require.NoError(t, err)
|
|
assert.True(t, node.IsDir())
|
|
assert.Equal(t, "/", node.Name())
|
|
assert.Equal(t, "file1", leaf)
|
|
|
|
node, leaf, err = vfs.StatParent("dir/file2")
|
|
require.NoError(t, err)
|
|
assert.True(t, node.IsDir())
|
|
assert.Equal(t, "dir", node.Name())
|
|
assert.Equal(t, "file2", leaf)
|
|
|
|
node, leaf, err = vfs.StatParent("not found")
|
|
require.NoError(t, err)
|
|
assert.True(t, node.IsDir())
|
|
assert.Equal(t, "/", node.Name())
|
|
assert.Equal(t, "not found", leaf)
|
|
|
|
_, _, err = vfs.StatParent("not found dir/not found")
|
|
assert.Equal(t, os.ErrNotExist, err)
|
|
|
|
_, _, err = vfs.StatParent("file1/under a file")
|
|
assert.Equal(t, os.ErrExist, err)
|
|
}
|
|
|
|
func TestVFSAddVirtual(t *testing.T) {
|
|
_, vfs := newTestVFS(t)
|
|
|
|
require.NoError(t, vfs.AddVirtual("file", 17, false))
|
|
node, err := vfs.Stat("file")
|
|
require.NoError(t, err)
|
|
assert.True(t, node.IsFile())
|
|
assert.Equal(t, int64(17), node.Size())
|
|
|
|
require.NoError(t, vfs.AddVirtual("dir/", 0, true))
|
|
node, err = vfs.Stat("dir")
|
|
require.NoError(t, err)
|
|
assert.True(t, node.IsDir())
|
|
|
|
require.NoError(t, vfs.AddVirtual("dir/file2", 18, false))
|
|
node, err = vfs.Stat("dir/file2")
|
|
require.NoError(t, err)
|
|
assert.True(t, node.IsFile())
|
|
assert.Equal(t, int64(18), node.Size())
|
|
}
|
|
|
|
func TestVFSOpenFile(t *testing.T) {
|
|
r, vfs := newTestVFS(t)
|
|
|
|
file1 := r.WriteObject(context.Background(), "file1", "file1 contents", t1)
|
|
file2 := r.WriteObject(context.Background(), "dir/file2", "file2 contents", t2)
|
|
r.CheckRemoteItems(t, file1, file2)
|
|
|
|
fd, err := vfs.OpenFile("file1", os.O_RDONLY, 0777)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, fd)
|
|
require.NoError(t, fd.Close())
|
|
|
|
fd, err = vfs.OpenFile("dir", os.O_RDONLY, 0777)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, fd)
|
|
require.NoError(t, fd.Close())
|
|
|
|
fd, err = vfs.OpenFile("dir/new_file.txt", os.O_RDONLY, 0777)
|
|
assert.Equal(t, os.ErrNotExist, err)
|
|
assert.Nil(t, fd)
|
|
|
|
fd, err = vfs.OpenFile("dir/new_file.txt", os.O_WRONLY|os.O_CREATE, 0777)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, fd)
|
|
err = fd.Close()
|
|
if !errors.Is(err, fs.ErrorCantUploadEmptyFiles) {
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
fd, err = vfs.OpenFile("not found/new_file.txt", os.O_WRONLY|os.O_CREATE, 0777)
|
|
assert.Equal(t, os.ErrNotExist, err)
|
|
assert.Nil(t, fd)
|
|
}
|
|
|
|
func TestVFSRename(t *testing.T) {
|
|
r, vfs := newTestVFS(t)
|
|
|
|
features := r.Fremote.Features()
|
|
if features.Move == nil && features.Copy == nil {
|
|
t.Skip("skip as can't rename files")
|
|
}
|
|
|
|
file1 := r.WriteObject(context.Background(), "dir/file2", "file2 contents", t2)
|
|
r.CheckRemoteItems(t, file1)
|
|
|
|
err := vfs.Rename("dir/file2", "dir/file1")
|
|
require.NoError(t, err)
|
|
file1.Path = "dir/file1"
|
|
r.CheckRemoteItems(t, file1)
|
|
|
|
err = vfs.Rename("dir/file1", "file0")
|
|
require.NoError(t, err)
|
|
file1.Path = "file0"
|
|
r.CheckRemoteItems(t, file1)
|
|
|
|
err = vfs.Rename("not found/file0", "file0")
|
|
assert.Equal(t, os.ErrNotExist, err)
|
|
|
|
err = vfs.Rename("file0", "not found/file0")
|
|
assert.Equal(t, os.ErrNotExist, err)
|
|
}
|
|
|
|
func TestVFSStatfs(t *testing.T) {
|
|
r, vfs := newTestVFS(t)
|
|
|
|
// pre-conditions
|
|
assert.Nil(t, vfs.usage)
|
|
assert.True(t, vfs.usageTime.IsZero())
|
|
|
|
aboutSupported := r.Fremote.Features().About != nil
|
|
|
|
// read
|
|
total, used, free := vfs.Statfs()
|
|
if !aboutSupported {
|
|
assert.Equal(t, int64(unknownFreeBytes), total)
|
|
assert.Equal(t, int64(unknownFreeBytes), free)
|
|
assert.Equal(t, int64(0), used)
|
|
return // can't test anything else if About not supported
|
|
}
|
|
require.NotNil(t, vfs.usage)
|
|
assert.False(t, vfs.usageTime.IsZero())
|
|
if vfs.usage.Total != nil {
|
|
assert.Equal(t, *vfs.usage.Total, total)
|
|
} else {
|
|
assert.True(t, total >= int64(unknownFreeBytes))
|
|
}
|
|
if vfs.usage.Free != nil {
|
|
assert.Equal(t, *vfs.usage.Free, free)
|
|
} else {
|
|
if vfs.usage.Total != nil && vfs.usage.Used != nil {
|
|
assert.Equal(t, free, total-used)
|
|
} else {
|
|
assert.True(t, free >= int64(unknownFreeBytes))
|
|
}
|
|
}
|
|
if vfs.usage.Used != nil {
|
|
assert.Equal(t, *vfs.usage.Used, used)
|
|
} else {
|
|
assert.Equal(t, int64(0), used)
|
|
}
|
|
|
|
// read cached
|
|
oldUsage := vfs.usage
|
|
oldTime := vfs.usageTime
|
|
total2, used2, free2 := vfs.Statfs()
|
|
assert.Equal(t, oldUsage, vfs.usage)
|
|
assert.Equal(t, total, total2)
|
|
assert.Equal(t, used, used2)
|
|
assert.Equal(t, free, free2)
|
|
assert.Equal(t, oldTime, vfs.usageTime)
|
|
}
|
|
|
|
func TestVFSMkdir(t *testing.T) {
|
|
r, vfs := newTestVFS(t)
|
|
|
|
if !r.Fremote.Features().CanHaveEmptyDirectories {
|
|
return // can't test if can't have empty directories
|
|
}
|
|
|
|
r.CheckRemoteListing(t, nil, []string{})
|
|
|
|
// Try making the root
|
|
err := vfs.Mkdir("", 0777)
|
|
require.NoError(t, err)
|
|
r.CheckRemoteListing(t, nil, []string{})
|
|
|
|
// Try making a sub directory
|
|
err = vfs.Mkdir("a", 0777)
|
|
require.NoError(t, err)
|
|
|
|
r.CheckRemoteListing(t, nil, []string{"a"})
|
|
|
|
// Try making an existing directory
|
|
err = vfs.Mkdir("a", 0777)
|
|
require.NoError(t, err)
|
|
|
|
r.CheckRemoteListing(t, nil, []string{"a"})
|
|
|
|
// Try making a new directory
|
|
err = vfs.Mkdir("b/", 0777)
|
|
require.NoError(t, err)
|
|
|
|
r.CheckRemoteListing(t, nil, []string{"a", "b"})
|
|
|
|
// Try making a new directory
|
|
err = vfs.Mkdir("/c", 0777)
|
|
require.NoError(t, err)
|
|
|
|
r.CheckRemoteListing(t, nil, []string{"a", "b", "c"})
|
|
|
|
// Try making a new directory
|
|
err = vfs.Mkdir("/d/", 0777)
|
|
require.NoError(t, err)
|
|
|
|
r.CheckRemoteListing(t, nil, []string{"a", "b", "c", "d"})
|
|
|
|
vfs.Opt.ReadOnly = true
|
|
err = vfs.Mkdir("", 0777)
|
|
assert.Equal(t, EROFS, err)
|
|
err = vfs.Mkdir("e", 0777)
|
|
assert.Equal(t, EROFS, err)
|
|
}
|
|
|
|
func TestVFSMkdirAll(t *testing.T) {
|
|
r, vfs := newTestVFS(t)
|
|
|
|
if !r.Fremote.Features().CanHaveEmptyDirectories {
|
|
return // can't test if can't have empty directories
|
|
}
|
|
|
|
r.CheckRemoteListing(t, nil, []string{})
|
|
|
|
// Try making the root
|
|
err := vfs.MkdirAll("", 0777)
|
|
require.NoError(t, err)
|
|
r.CheckRemoteListing(t, nil, []string{})
|
|
|
|
// Try making a sub directory
|
|
err = vfs.MkdirAll("a/b/c/d", 0777)
|
|
require.NoError(t, err)
|
|
|
|
r.CheckRemoteListing(t, nil, []string{"a", "a/b", "a/b/c", "a/b/c/d"})
|
|
|
|
// Try making an existing directory
|
|
err = vfs.MkdirAll("a/b/c", 0777)
|
|
require.NoError(t, err)
|
|
|
|
r.CheckRemoteListing(t, nil, []string{"a", "a/b", "a/b/c", "a/b/c/d"})
|
|
|
|
// Try making an existing directory
|
|
err = vfs.MkdirAll("/a/b/c/", 0777)
|
|
require.NoError(t, err)
|
|
|
|
r.CheckRemoteListing(t, nil, []string{"a", "a/b", "a/b/c", "a/b/c/d"})
|
|
}
|
|
|
|
func TestFillInMissingSizes(t *testing.T) {
|
|
const unknownFree = 10
|
|
for _, test := range []struct {
|
|
total, free, used int64
|
|
wantTotal, wantUsed, wantFree int64
|
|
}{
|
|
{
|
|
total: 20, free: 5, used: 15,
|
|
wantTotal: 20, wantFree: 5, wantUsed: 15,
|
|
},
|
|
{
|
|
total: 20, free: 5, used: -1,
|
|
wantTotal: 20, wantFree: 5, wantUsed: 15,
|
|
},
|
|
{
|
|
total: 20, free: -1, used: 15,
|
|
wantTotal: 20, wantFree: 5, wantUsed: 15,
|
|
},
|
|
{
|
|
total: 20, free: -1, used: -1,
|
|
wantTotal: 20, wantFree: 20, wantUsed: 0,
|
|
},
|
|
{
|
|
total: -1, free: 5, used: 15,
|
|
wantTotal: 20, wantFree: 5, wantUsed: 15,
|
|
},
|
|
{
|
|
total: -1, free: 15, used: -1,
|
|
wantTotal: 15, wantFree: 15, wantUsed: 0,
|
|
},
|
|
{
|
|
total: -1, free: -1, used: 15,
|
|
wantTotal: 25, wantFree: 10, wantUsed: 15,
|
|
},
|
|
{
|
|
total: -1, free: -1, used: -1,
|
|
wantTotal: 10, wantFree: 10, wantUsed: 0,
|
|
},
|
|
} {
|
|
t.Run(fmt.Sprintf("total=%d,free=%d,used=%d", test.total, test.free, test.used), func(t *testing.T) {
|
|
gotTotal, gotUsed, gotFree := fillInMissingSizes(test.total, test.used, test.free, unknownFree)
|
|
assert.Equal(t, test.wantTotal, gotTotal, "total")
|
|
assert.Equal(t, test.wantUsed, gotUsed, "used")
|
|
assert.Equal(t, test.wantFree, gotFree, "free")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVFSIsMetadataFile(t *testing.T) {
|
|
_, vfs := newTestVFS(t)
|
|
|
|
rawName, found := vfs.isMetadataFile("leaf.metadata")
|
|
assert.Equal(t, "leaf.metadata", rawName)
|
|
assert.Equal(t, false, found)
|
|
|
|
vfs.Opt.MetadataExtension = ".metadata"
|
|
|
|
rawName, found = vfs.isMetadataFile("leaf.metadata")
|
|
assert.Equal(t, "leaf", rawName)
|
|
assert.Equal(t, true, found)
|
|
}
|