wasi: backfills errno conversion tests (#1109)

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2023-02-08 13:55:12 -05:00
committed by GitHub
parent 39983c4080
commit 7badba4ea8
2 changed files with 191 additions and 22 deletions

View File

@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io/fs" "io/fs"
"os"
"syscall" "syscall"
) )
@@ -266,31 +267,60 @@ var errnoToString = [...]string{
// error codes. For example, wasi-filesystem and GOOS=js don't map to these // error codes. For example, wasi-filesystem and GOOS=js don't map to these
// Errno. // Errno.
func ToErrno(err error) Errno { func ToErrno(err error) Errno {
if pe, ok := err.(*os.PathError); ok {
err = pe.Unwrap()
}
if se, ok := err.(syscall.Errno); ok {
return errnoFromSyscall(se)
}
// Below are all the fs.ErrXXX in fs.go. errors.Is is more expensive, so
// try it last. Note: Once we have our own file type, we should never see
// these.
switch { switch {
case errors.Is(err, syscall.ENOTDIR): case errors.Is(err, fs.ErrInvalid):
return ErrnoNotdir
case errors.Is(err, syscall.EBADF), errors.Is(err, fs.ErrClosed):
return ErrnoBadf
case errors.Is(err, syscall.EINVAL), errors.Is(err, fs.ErrInvalid):
return ErrnoInval return ErrnoInval
case errors.Is(err, syscall.EISDIR): case errors.Is(err, fs.ErrPermission):
return ErrnoIsdir return ErrnoPerm
case errors.Is(err, syscall.ENOTEMPTY): case errors.Is(err, fs.ErrExist):
return ErrnoNotempty return ErrnoExist
case errors.Is(err, syscall.EEXIST), errors.Is(err, fs.ErrExist): case errors.Is(err, fs.ErrNotExist):
return ErrnoExist return ErrnoNoent
case errors.Is(err, syscall.ENOENT), errors.Is(err, fs.ErrNotExist): case errors.Is(err, fs.ErrClosed):
return ErrnoNoent return ErrnoBadf
case errors.Is(err, syscall.ENOSYS): default:
return ErrnoNosys return ErrnoIo
case errors.Is(err, syscall.ENOTSUP): }
return ErrnoNotsup }
case errors.Is(err, syscall.ENOTDIR):
return ErrnoNotdir func errnoFromSyscall(errno syscall.Errno) Errno {
case errors.Is(err, syscall.EPERM), errors.Is(err, fs.ErrPermission): // The below Errno have references in existing WASI code.
switch errno {
case syscall.EBADF:
return ErrnoBadf
case syscall.EEXIST:
return ErrnoExist
case syscall.EINVAL:
return ErrnoInval
case syscall.EIO:
return ErrnoIo
case syscall.EISDIR:
return ErrnoIsdir
case syscall.ELOOP:
return ErrnoLoop
case syscall.ENAMETOOLONG:
return ErrnoNametoolong
case syscall.ENOENT:
return ErrnoNoent
case syscall.ENOSYS:
return ErrnoNosys
case syscall.ENOTDIR:
return ErrnoNotdir
case syscall.ENOTEMPTY:
return ErrnoNotempty
case syscall.ENOTSUP:
return ErrnoNotsup
case syscall.EPERM:
return ErrnoPerm return ErrnoPerm
case errors.Is(err, syscall.ELOOP):
return ErrnoLoop
default: default:
return ErrnoIo return ErrnoIo
} }

View File

@@ -0,0 +1,139 @@
package wasi_snapshot_preview1
import (
"errors"
"fmt"
"io/fs"
"os"
"syscall"
"testing"
"github.com/tetratelabs/wazero/internal/testing/require"
)
func TestToErrno(t *testing.T) {
tests := []struct {
name string
input error
expected Errno
}{
{
name: "syscall.EBADF",
input: syscall.EBADF,
expected: ErrnoBadf,
},
{
name: "syscall.EEXIST",
input: syscall.EEXIST,
expected: ErrnoExist,
},
{
name: "syscall.EINVAL",
input: syscall.EINVAL,
expected: ErrnoInval,
},
{
name: "syscall.EIO",
input: syscall.EIO,
expected: ErrnoIo,
},
{
name: "syscall.EISDIR",
input: syscall.EISDIR,
expected: ErrnoIsdir,
},
{
name: "syscall.ELOOP",
input: syscall.ELOOP,
expected: ErrnoLoop,
},
{
name: "syscall.ENAMETOOLONG",
input: syscall.ENAMETOOLONG,
expected: ErrnoNametoolong,
},
{
name: "syscall.ENOENT",
input: syscall.ENOENT,
expected: ErrnoNoent,
},
{
name: "syscall.ENOSYS",
input: syscall.ENOSYS,
expected: ErrnoNosys,
},
{
name: "syscall.ENOTDIR",
input: syscall.ENOTDIR,
expected: ErrnoNotdir,
},
{
name: "syscall.ENOTEMPTY",
input: syscall.ENOTEMPTY,
expected: ErrnoNotempty,
},
{
name: "syscall.ENOTSUP",
input: syscall.ENOTSUP,
expected: ErrnoNotsup,
},
{
name: "syscall.EPERM",
input: syscall.EPERM,
expected: ErrnoPerm,
},
{
name: "syscall.Errno unexpected == ErrnoIo",
input: syscall.Errno(0xfe),
expected: ErrnoIo,
},
{
name: "PathError ErrInvalid",
input: &os.PathError{Err: fs.ErrInvalid},
expected: ErrnoInval,
},
{
name: "PathError ErrPermission",
input: &os.PathError{Err: fs.ErrPermission},
expected: ErrnoPerm,
},
{
name: "PathError ErrExist",
input: &os.PathError{Err: fs.ErrExist},
expected: ErrnoExist,
},
{
name: "PathError ErrNotExist",
input: &os.PathError{Err: fs.ErrNotExist},
expected: ErrnoNoent,
},
{
name: "PathError ErrClosed",
input: &os.PathError{Err: fs.ErrClosed},
expected: ErrnoBadf,
},
{
name: "PathError unknown == ErrnoIo",
input: &os.PathError{Err: errors.New("ice cream")},
expected: ErrnoIo,
},
{
name: "unknown == ErrnoIo",
input: errors.New("ice cream"),
expected: ErrnoIo,
},
{
name: "very wrapped unknown == ErrnoIo",
input: fmt.Errorf("%w", fmt.Errorf("%w", fmt.Errorf("%w", errors.New("ice cream")))),
expected: ErrnoIo,
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
errno := ToErrno(tc.input)
require.Equal(t, tc.expected, errno)
})
}
}