wasi: backfills errno conversion tests (#1109)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
139
internal/wasi_snapshot_preview1/errno_test.go
Normal file
139
internal/wasi_snapshot_preview1/errno_test.go
Normal 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user