forked from security/hakurei
The struct turned out not necessary during initial implementation but was not unwrapped into its single string field. This change replaces it with the underlying string and removes the indirection. Signed-off-by: Ophestra <cat@gensokyo.uk>
180 lines
4.5 KiB
Go
180 lines
4.5 KiB
Go
package container
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"reflect"
|
|
"strconv"
|
|
"syscall"
|
|
"testing"
|
|
|
|
"hakurei.app/container/check"
|
|
"hakurei.app/container/stub"
|
|
"hakurei.app/container/vfs"
|
|
)
|
|
|
|
func TestMessageFromError(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
err error
|
|
want string
|
|
wantOk bool
|
|
}{
|
|
{"mount", &MountError{
|
|
Source: SourceTmpfsEphemeral,
|
|
Target: "/sysroot/tmp",
|
|
Fstype: FstypeTmpfs,
|
|
Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
|
|
Data: zeroString,
|
|
Errno: syscall.EINVAL,
|
|
}, "cannot mount tmpfs on /sysroot/tmp: invalid argument", true},
|
|
|
|
{"path", &os.PathError{
|
|
Op: "mount",
|
|
Path: "/sysroot",
|
|
Err: stub.UniqueError(0xdeadbeef),
|
|
}, "cannot mount /sysroot: unique error 3735928559 injected by the test suite", true},
|
|
|
|
{"absolute", check.AbsoluteError("etc/mtab"),
|
|
`path "etc/mtab" is not absolute`, true},
|
|
|
|
{"repeat", OpRepeatError("autoetc"),
|
|
"autoetc is not repeatable", true},
|
|
|
|
{"state", OpStateError("overlay"),
|
|
"impossible overlay state reached", true},
|
|
|
|
{"vfs parse", &vfs.DecoderError{Op: "parse", Line: 0xdead, Err: &strconv.NumError{Func: "Atoi", Num: "meow", Err: strconv.ErrSyntax}},
|
|
`cannot parse mountinfo at line 57005: numeric field "meow" invalid syntax`, true},
|
|
|
|
{"tmpfs", TmpfsSizeError(-1),
|
|
"tmpfs size -1 out of bounds", true},
|
|
|
|
{"unsupported", stub.UniqueError(0xdeadbeef), zeroString, false},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
got, ok := messageFromError(tc.err)
|
|
if got != tc.want {
|
|
t.Errorf("messageFromError: %q, want %q", got, tc.want)
|
|
}
|
|
if ok != tc.wantOk {
|
|
t.Errorf("messageFromError: ok = %v, want %v", ok, tc.wantOk)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMountError(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
err error
|
|
errno syscall.Errno
|
|
want string
|
|
}{
|
|
{"bind", &MountError{
|
|
Source: "/host/nix/store",
|
|
Target: "/sysroot/nix/store",
|
|
Fstype: FstypeNULL,
|
|
Flags: syscall.MS_SILENT | syscall.MS_BIND | syscall.MS_REC,
|
|
Data: zeroString,
|
|
Errno: syscall.ENOSYS,
|
|
}, syscall.ENOSYS,
|
|
"bind /host/nix/store on /sysroot/nix/store: function not implemented"},
|
|
|
|
{"remount", &MountError{
|
|
Source: SourceNone,
|
|
Target: "/sysroot/nix/store",
|
|
Fstype: FstypeNULL,
|
|
Flags: syscall.MS_SILENT | syscall.MS_BIND | syscall.MS_REMOUNT,
|
|
Data: zeroString,
|
|
Errno: syscall.EPERM,
|
|
}, syscall.EPERM,
|
|
"remount /sysroot/nix/store: operation not permitted"},
|
|
|
|
{"overlay", &MountError{
|
|
Source: SourceOverlay,
|
|
Target: sysrootPath,
|
|
Fstype: FstypeOverlay,
|
|
Data: `lowerdir=/host/var/lib/planterette/base/debian\:f92c9052`,
|
|
Errno: syscall.EINVAL,
|
|
}, syscall.EINVAL,
|
|
"mount overlay on /sysroot: invalid argument"},
|
|
|
|
{"fallback", &MountError{
|
|
Source: SourceNone,
|
|
Target: sysrootPath,
|
|
Fstype: FstypeNULL,
|
|
Errno: syscall.ENOTRECOVERABLE,
|
|
}, syscall.ENOTRECOVERABLE,
|
|
"mount /sysroot: state not recoverable"},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("is", func(t *testing.T) {
|
|
if !errors.Is(tc.err, tc.errno) {
|
|
t.Errorf("Is: %#v is not %v", tc.err, tc.errno)
|
|
}
|
|
})
|
|
t.Run("error", func(t *testing.T) {
|
|
if got := tc.err.Error(); got != tc.want {
|
|
t.Errorf("Error: %q, want %q", got, tc.want)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
t.Run("zero", func(t *testing.T) {
|
|
t.Parallel()
|
|
if errors.Is(new(MountError), syscall.Errno(0)) {
|
|
t.Errorf("Is: zero MountError unexpected true")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestErrnoFallback(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
err error
|
|
wantErrno syscall.Errno
|
|
wantPath *os.PathError
|
|
}{
|
|
{"mount", &MountError{
|
|
Errno: syscall.ENOTRECOVERABLE,
|
|
}, syscall.ENOTRECOVERABLE, nil},
|
|
|
|
{"path errno", &os.PathError{
|
|
Err: syscall.ETIMEDOUT,
|
|
}, syscall.ETIMEDOUT, nil},
|
|
|
|
{"fallback", stub.UniqueError(0xcafebabe), 0, &os.PathError{
|
|
Op: "fallback",
|
|
Path: "/proc/nonexistent",
|
|
Err: stub.UniqueError(0xcafebabe),
|
|
}},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
errno, err := errnoFallback(tc.name, Nonexistent, tc.err)
|
|
if errno != tc.wantErrno {
|
|
t.Errorf("errnoFallback: errno = %v, want %v", errno, tc.wantErrno)
|
|
}
|
|
if !reflect.DeepEqual(err, tc.wantPath) {
|
|
t.Errorf("errnoFallback: pathError = %#v, want %#v", err, tc.wantPath)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// InternalMessageFromError exports messageFromError for other tests.
|
|
func InternalMessageFromError(err error) (string, bool) { return messageFromError(err) }
|