This forces all syscall functions, notably filesystem, to return numeric codes as opposed to mapping in two different areas. The result of this change is better consolidation in call sites of `sysfs.FS`, while further refactoring is needed to address consolidation of file errors. Signed-off-by: Adrian Cole <adrian@tetrate.io>
256 lines
6.6 KiB
Go
256 lines
6.6 KiB
Go
package platform_test
|
|
|
|
import (
|
|
"io/fs"
|
|
"os"
|
|
"path"
|
|
"runtime"
|
|
"sort"
|
|
"syscall"
|
|
"testing"
|
|
|
|
"github.com/tetratelabs/wazero/internal/fstest"
|
|
"github.com/tetratelabs/wazero/internal/platform"
|
|
"github.com/tetratelabs/wazero/internal/testing/require"
|
|
)
|
|
|
|
func TestReaddirnames(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
|
dirFS := os.DirFS(tmpDir)
|
|
|
|
tests := []struct {
|
|
name string
|
|
fs fs.FS
|
|
}{
|
|
{name: "os.DirFS", fs: dirFS}, // To test readdirnamesFile
|
|
{name: "fstest.MapFS", fs: fstest.FS}, // To test adaptation of ReadDirFile
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
tc := tc
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
dotF, err := tc.fs.Open(".")
|
|
require.NoError(t, err)
|
|
defer dotF.Close()
|
|
|
|
t.Run("dir", func(t *testing.T) {
|
|
names, errno := platform.Readdirnames(dotF, -1)
|
|
require.Zero(t, errno)
|
|
|
|
sort.Strings(names)
|
|
require.Equal(t, []string{"animals.txt", "dir", "empty.txt", "emptydir", "sub"}, names)
|
|
|
|
// read again even though it is exhausted
|
|
_, errno = platform.Readdirnames(dotF, 100)
|
|
require.Zero(t, errno)
|
|
})
|
|
|
|
// Don't err if something else closed the directory while reading.
|
|
t.Run("closed dir", func(t *testing.T) {
|
|
require.NoError(t, dotF.Close())
|
|
_, errno := platform.Readdir(dotF, -1)
|
|
require.Zero(t, errno)
|
|
})
|
|
|
|
dirF, err := tc.fs.Open("dir")
|
|
require.NoError(t, err)
|
|
defer dirF.Close()
|
|
|
|
t.Run("partial", func(t *testing.T) {
|
|
names1, errno := platform.Readdirnames(dirF, 1)
|
|
require.Zero(t, errno)
|
|
require.Equal(t, 1, len(names1))
|
|
|
|
names2, errno := platform.Readdirnames(dirF, 1)
|
|
require.Zero(t, errno)
|
|
require.Equal(t, 1, len(names2))
|
|
|
|
// read exactly the last entry
|
|
names3, errno := platform.Readdirnames(dirF, 1)
|
|
require.Zero(t, errno)
|
|
require.Equal(t, 1, len(names3))
|
|
|
|
names := []string{names1[0], names2[0], names3[0]}
|
|
sort.Strings(names)
|
|
|
|
require.Equal(t, []string{"-", "a-", "ab-"}, names)
|
|
|
|
// no error reading an exhausted directory
|
|
_, errno = platform.Readdirnames(dirF, 1)
|
|
require.Zero(t, errno)
|
|
})
|
|
|
|
fileF, err := tc.fs.Open("empty.txt")
|
|
require.NoError(t, err)
|
|
defer fileF.Close()
|
|
|
|
t.Run("file", func(t *testing.T) {
|
|
_, errno := platform.Readdirnames(fileF, -1)
|
|
require.EqualErrno(t, syscall.ENOTDIR, errno)
|
|
})
|
|
|
|
subdirF, err := tc.fs.Open("sub")
|
|
require.NoError(t, err)
|
|
defer subdirF.Close()
|
|
|
|
t.Run("subdir", func(t *testing.T) {
|
|
names, errno := platform.Readdirnames(subdirF, -1)
|
|
require.Zero(t, errno)
|
|
|
|
require.Equal(t, []string{"test.txt"}, names)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReaddir(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
require.NoError(t, fstest.WriteTestFiles(tmpDir))
|
|
dirFS := os.DirFS(tmpDir)
|
|
|
|
tests := []struct {
|
|
name string
|
|
fs fs.FS
|
|
expectIno bool
|
|
}{
|
|
{name: "os.DirFS", fs: dirFS, expectIno: runtime.GOOS != "windows"}, // To test readdirFile
|
|
{name: "fstest.MapFS", fs: fstest.FS, expectIno: false}, // To test adaptation of ReadDirFile
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
tc := tc
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
dotF, err := tc.fs.Open(".")
|
|
require.NoError(t, err)
|
|
defer dotF.Close()
|
|
|
|
t.Run("dir", func(t *testing.T) {
|
|
dirents, errno := platform.Readdir(dotF, -1)
|
|
require.Zero(t, errno) // no io.EOF when -1 is used
|
|
sort.Slice(dirents, func(i, j int) bool { return dirents[i].Name < dirents[j].Name })
|
|
|
|
requireIno(t, dirents, tc.expectIno)
|
|
|
|
require.Equal(t, []*platform.Dirent{
|
|
{Name: "animals.txt", Type: 0},
|
|
{Name: "dir", Type: fs.ModeDir},
|
|
{Name: "empty.txt", Type: 0},
|
|
{Name: "emptydir", Type: fs.ModeDir},
|
|
{Name: "sub", Type: fs.ModeDir},
|
|
}, dirents)
|
|
|
|
// read again even though it is exhausted
|
|
dirents, errno = platform.Readdir(dotF, 100)
|
|
require.Zero(t, errno)
|
|
require.Zero(t, len(dirents))
|
|
})
|
|
|
|
// Don't err if something else closed the directory while reading.
|
|
t.Run("closed dir", func(t *testing.T) {
|
|
require.NoError(t, dotF.Close())
|
|
_, errno := platform.Readdir(dotF, -1)
|
|
require.Zero(t, errno)
|
|
})
|
|
|
|
fileF, err := tc.fs.Open("empty.txt")
|
|
require.NoError(t, err)
|
|
defer fileF.Close()
|
|
|
|
t.Run("file", func(t *testing.T) {
|
|
_, errno := platform.Readdir(fileF, -1)
|
|
require.EqualErrno(t, syscall.ENOTDIR, errno)
|
|
})
|
|
|
|
dirF, err := tc.fs.Open("dir")
|
|
require.NoError(t, err)
|
|
defer dirF.Close()
|
|
|
|
t.Run("partial", func(t *testing.T) {
|
|
dirents1, errno := platform.Readdir(dirF, 1)
|
|
require.Zero(t, errno)
|
|
require.Equal(t, 1, len(dirents1))
|
|
|
|
dirents2, errno := platform.Readdir(dirF, 1)
|
|
require.Zero(t, errno)
|
|
require.Equal(t, 1, len(dirents2))
|
|
|
|
// read exactly the last entry
|
|
dirents3, errno := platform.Readdir(dirF, 1)
|
|
require.Zero(t, errno)
|
|
require.Equal(t, 1, len(dirents3))
|
|
|
|
dirents := []*platform.Dirent{dirents1[0], dirents2[0], dirents3[0]}
|
|
sort.Slice(dirents, func(i, j int) bool { return dirents[i].Name < dirents[j].Name })
|
|
|
|
requireIno(t, dirents, tc.expectIno)
|
|
|
|
require.Equal(t, []*platform.Dirent{
|
|
{Name: "-", Type: 0},
|
|
{Name: "a-", Type: fs.ModeDir},
|
|
{Name: "ab-", Type: 0},
|
|
}, dirents)
|
|
|
|
// no error reading an exhausted directory
|
|
_, errno = platform.Readdir(dirF, 1)
|
|
require.Zero(t, errno)
|
|
})
|
|
|
|
subdirF, err := tc.fs.Open("sub")
|
|
require.NoError(t, err)
|
|
defer subdirF.Close()
|
|
|
|
t.Run("subdir", func(t *testing.T) {
|
|
dirents, errno := platform.Readdir(subdirF, -1)
|
|
require.Zero(t, errno)
|
|
sort.Slice(dirents, func(i, j int) bool { return dirents[i].Name < dirents[j].Name })
|
|
|
|
require.Equal(t, 1, len(dirents))
|
|
require.Equal(t, "test.txt", dirents[0].Name)
|
|
require.Zero(t, dirents[0].Type)
|
|
})
|
|
})
|
|
}
|
|
|
|
// Don't err if something else removed the directory while reading.
|
|
t.Run("removed while open", func(t *testing.T) {
|
|
dirF, err := dirFS.Open("dir")
|
|
require.NoError(t, err)
|
|
defer dirF.Close()
|
|
|
|
dirents, errno := platform.Readdir(dirF, 1)
|
|
require.Zero(t, errno)
|
|
require.Equal(t, 1, len(dirents))
|
|
|
|
// Speculatively try to remove even if it won't likely work
|
|
// on windows.
|
|
err = os.RemoveAll(path.Join(tmpDir, "dir"))
|
|
if err != nil && runtime.GOOS == "windows" {
|
|
t.Skip()
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
_, errno = platform.Readdir(dirF, 1)
|
|
require.Zero(t, errno)
|
|
// don't validate the contents as due to caching it might be present.
|
|
})
|
|
}
|
|
|
|
func requireIno(t *testing.T, dirents []*platform.Dirent, expectIno bool) {
|
|
for _, e := range dirents {
|
|
if expectIno {
|
|
require.NotEqual(t, uint64(0), e.Ino, "%+v", e)
|
|
e.Ino = 0
|
|
} else {
|
|
require.Zero(t, e.Ino, "%+v", e)
|
|
}
|
|
}
|
|
}
|