diff --git a/container/autoetc.go b/container/autoetc.go index 44b5d76..0334ad1 100644 --- a/container/autoetc.go +++ b/container/autoetc.go @@ -21,6 +21,7 @@ func (f *Ops) Etc(host *Absolute, prefix string) *Ops { type AutoEtcOp struct{ Prefix string } +func (e *AutoEtcOp) Valid() bool { return e != nil } func (e *AutoEtcOp) early(*setupState) error { return nil } func (e *AutoEtcOp) apply(state *setupState) error { if state.nonrepeatable&nrAutoEtc != 0 { @@ -66,7 +67,7 @@ func (e *AutoEtcOp) hostRel() string { return ".host/" + e.Prefix } func (e *AutoEtcOp) Is(op Op) bool { ve, ok := op.(*AutoEtcOp) - return ok && ((e == nil && ve == nil) || (e != nil && ve != nil && *e == *ve)) + return ok && e.Valid() && ve.Valid() && *e == *ve } func (*AutoEtcOp) prefix() string { return "setting up" } func (e *AutoEtcOp) String() string { return fmt.Sprintf("auto etc %s", e.Prefix) } diff --git a/container/autoetc_test.go b/container/autoetc_test.go index 4040fb0..efb4b3b 100644 --- a/container/autoetc_test.go +++ b/container/autoetc_test.go @@ -3,6 +3,12 @@ package container import "testing" func TestAutoEtcOp(t *testing.T) { + checkOpsValid(t, []opValidTestCase{ + {"nil", (*AutoEtcOp)(nil), false}, + {"zero", new(AutoEtcOp), true}, + {"populated", &AutoEtcOp{Prefix: ":3"}, true}, + }) + checkOpsBuilder(t, []opsBuilderTestCase{ {"pd", new(Ops).Etc(MustAbs("/etc/"), "048090b6ed8f9ebb10e275ff5d8c0659"), Ops{ &MkdirOp{Path: MustAbs("/etc/"), Perm: 0755}, diff --git a/container/autoroot.go b/container/autoroot.go index a7a3f33..0de6bce 100644 --- a/container/autoroot.go +++ b/container/autoroot.go @@ -28,11 +28,9 @@ type AutoRootOp struct { resolved []Op } -func (r *AutoRootOp) early(state *setupState) error { - if r.Host == nil { - return syscall.EBADE - } +func (r *AutoRootOp) Valid() bool { return r != nil && r.Host != nil } +func (r *AutoRootOp) early(state *setupState) error { if d, err := os.ReadDir(r.Host.String()); err != nil { return wrapErrSelf(err) } else { @@ -72,9 +70,10 @@ func (r *AutoRootOp) apply(state *setupState) error { func (r *AutoRootOp) Is(op Op) bool { vr, ok := op.(*AutoRootOp) - return ok && ((r == nil && vr == nil) || (r != nil && vr != nil && - r.Host != nil && vr.Host != nil && r.Host.Is(vr.Host) && - r.Prefix == vr.Prefix && r.Flags == vr.Flags)) + return ok && r.Valid() && vr.Valid() && + r.Host.Is(vr.Host) && + r.Prefix == vr.Prefix && + r.Flags == vr.Flags } func (*AutoRootOp) prefix() string { return "setting up" } func (r *AutoRootOp) String() string { diff --git a/container/autoroot_test.go b/container/autoroot_test.go index 5a77435..03df7c8 100644 --- a/container/autoroot_test.go +++ b/container/autoroot_test.go @@ -3,6 +3,12 @@ package container import "testing" func TestAutoRootOp(t *testing.T) { + checkOpsValid(t, []opValidTestCase{ + {"nil", (*AutoRootOp)(nil), false}, + {"zero", new(AutoRootOp), false}, + {"valid", &AutoRootOp{Host: MustAbs("/")}, true}, + }) + checkOpsBuilder(t, []opsBuilderTestCase{ {"pd", new(Ops).Root(MustAbs("/"), "048090b6ed8f9ebb10e275ff5d8c0659", BindWritable), Ops{ &AutoRootOp{ diff --git a/container/init.go b/container/init.go index d516a3c..663bbc8 100644 --- a/container/init.go +++ b/container/init.go @@ -52,6 +52,7 @@ type ( prefix() string Is(op Op) bool + Valid() bool fmt.Stringer } @@ -167,8 +168,8 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) { via library functions after pivot_root, and implementations are expected to avoid changing the state of the mount namespace */ for i, op := range *params.Ops { - if op == nil { - log.Fatalf("invalid op %d", i) + if op == nil || !op.Valid() { + log.Fatalf("invalid op at index %d", i) } if err := op.early(state); err != nil { diff --git a/container/initbind.go b/container/initbind.go index 0b34851..e19cb5d 100644 --- a/container/initbind.go +++ b/container/initbind.go @@ -24,6 +24,8 @@ type BindMountOp struct { Flags int } +func (b *BindMountOp) Valid() bool { return b != nil && b.Source != nil && b.Target != nil } + const ( // BindOptional skips nonexistent host paths. BindOptional = 1 << iota @@ -34,10 +36,6 @@ const ( ) func (b *BindMountOp) early(*setupState) error { - if b.Source == nil || b.Target == nil { - return EBADE - } - if pathname, err := filepath.EvalSymlinks(b.Source.String()); err != nil { if os.IsNotExist(err) && b.Flags&BindOptional != 0 { // leave sourceFinal as nil @@ -87,10 +85,10 @@ func (b *BindMountOp) apply(*setupState) error { func (b *BindMountOp) Is(op Op) bool { vb, ok := op.(*BindMountOp) - return ok && ((b == nil && vb == nil) || (b != nil && vb != nil && - b.Source != nil && vb.Source != nil && b.Source.Is(vb.Source) && - b.Target != nil && vb.Target != nil && b.Target.Is(vb.Target) && - b.Flags == vb.Flags)) + return ok && b.Valid() && vb.Valid() && + b.Source.Is(vb.Source) && + b.Target.Is(vb.Target) && + b.Flags == vb.Flags } func (*BindMountOp) prefix() string { return "mounting" } func (b *BindMountOp) String() string { diff --git a/container/initbind_test.go b/container/initbind_test.go index f079261..bae33f8 100644 --- a/container/initbind_test.go +++ b/container/initbind_test.go @@ -3,6 +3,14 @@ package container import "testing" func TestBindMountOp(t *testing.T) { + checkOpsValid(t, []opValidTestCase{ + {"nil", (*BindMountOp)(nil), false}, + {"zero", new(BindMountOp), false}, + {"nil source", &BindMountOp{Target: MustAbs("/")}, false}, + {"nil target", &BindMountOp{Source: MustAbs("/")}, false}, + {"valid", &BindMountOp{Source: MustAbs("/"), Target: MustAbs("/")}, true}, + }) + checkOpsBuilder(t, []opsBuilderTestCase{ {"autoetc", new(Ops).Bind( MustAbs("/etc/"), diff --git a/container/initdev.go b/container/initdev.go index 71c30f4..48d70f3 100644 --- a/container/initdev.go +++ b/container/initdev.go @@ -33,11 +33,9 @@ type MountDevOp struct { Write bool } +func (d *MountDevOp) Valid() bool { return d != nil && d.Target != nil } func (d *MountDevOp) early(*setupState) error { return nil } func (d *MountDevOp) apply(state *setupState) error { - if d.Target == nil { - return EBADE - } target := toSysroot(d.Target.String()) if err := mountTmpfs(SourceTmpfsDevtmpfs, target, MS_NOSUID|MS_NODEV, 0, state.ParentPerm); err != nil { @@ -128,9 +126,10 @@ func (d *MountDevOp) apply(state *setupState) error { func (d *MountDevOp) Is(op Op) bool { vd, ok := op.(*MountDevOp) - return ok && ((d == nil && vd == nil) || (d != nil && vd != nil && - d.Target != nil && vd.Target != nil && d.Target.Is(vd.Target) && - d.Mqueue == vd.Mqueue && d.Write == vd.Write)) + return ok && d.Valid() && vd.Valid() && + d.Target.Is(vd.Target) && + d.Mqueue == vd.Mqueue && + d.Write == vd.Write } func (*MountDevOp) prefix() string { return "mounting" } func (d *MountDevOp) String() string { diff --git a/container/initdev_test.go b/container/initdev_test.go index 0d6ea5a..4e23e6b 100644 --- a/container/initdev_test.go +++ b/container/initdev_test.go @@ -3,6 +3,12 @@ package container import "testing" func TestMountDevOp(t *testing.T) { + checkOpsValid(t, []opValidTestCase{ + {"nil", (*MountDevOp)(nil), false}, + {"zero", new(MountDevOp), false}, + {"valid", &MountDevOp{Target: MustAbs("/dev/")}, true}, + }) + checkOpsBuilder(t, []opsBuilderTestCase{ {"dev", new(Ops).Dev(MustAbs("/dev/"), true), Ops{ &MountDevOp{ diff --git a/container/initmkdir.go b/container/initmkdir.go index 6153106..54aa4f7 100644 --- a/container/initmkdir.go +++ b/container/initmkdir.go @@ -4,7 +4,6 @@ import ( "encoding/gob" "fmt" "os" - "syscall" ) func init() { gob.Register(new(MkdirOp)) } @@ -21,19 +20,17 @@ type MkdirOp struct { Perm os.FileMode } +func (m *MkdirOp) Valid() bool { return m != nil && m.Path != nil } func (m *MkdirOp) early(*setupState) error { return nil } func (m *MkdirOp) apply(*setupState) error { - if m.Path == nil { - return syscall.EBADE - } return wrapErrSelf(os.MkdirAll(toSysroot(m.Path.String()), m.Perm)) } func (m *MkdirOp) Is(op Op) bool { vm, ok := op.(*MkdirOp) - return ok && ((m == nil && vm == nil) || (m != nil && vm != nil && - m.Path != nil && vm.Path != nil && m.Path.Is(vm.Path) && - m.Perm == vm.Perm)) + return ok && m.Valid() && vm.Valid() && + m.Path.Is(vm.Path) && + m.Perm == vm.Perm } func (*MkdirOp) prefix() string { return "creating" } func (m *MkdirOp) String() string { return fmt.Sprintf("directory %q perm %s", m.Path, m.Perm) } diff --git a/container/initmkdir_test.go b/container/initmkdir_test.go index d5c6f0b..ad32624 100644 --- a/container/initmkdir_test.go +++ b/container/initmkdir_test.go @@ -3,6 +3,12 @@ package container import "testing" func TestMkdirOp(t *testing.T) { + checkOpsValid(t, []opValidTestCase{ + {"nil", (*MkdirOp)(nil), false}, + {"zero", new(MkdirOp), false}, + {"valid", &MkdirOp{Path: MustAbs("/.hakurei")}, true}, + }) + checkOpsBuilder(t, []opsBuilderTestCase{ {"etc", new(Ops).Mkdir(MustAbs("/etc/"), 0), Ops{ &MkdirOp{Path: MustAbs("/etc/")}, diff --git a/container/initoverlay.go b/container/initoverlay.go index 81b00b9..6822274 100644 --- a/container/initoverlay.go +++ b/container/initoverlay.go @@ -53,10 +53,10 @@ type MountOverlayOp struct { lower []string // The upperdir is normally on a writable filesystem. // - // If Work is nil and Upper holds the special value [FHSRoot], + // If Work is nil and Upper holds the special value [AbsFHSRoot], // an ephemeral upperdir and workdir will be set up. // - // If both Work and Upper are empty strings, upperdir and workdir is omitted and the overlay is mounted readonly. + // If both Work and Upper are nil, upperdir and workdir is omitted and the overlay is mounted readonly. Upper *Absolute // formatted for [OptionOverlayUpperdir], resolved, prefixed and escaped during early upper string @@ -68,6 +68,19 @@ type MountOverlayOp struct { ephemeral bool } +func (o *MountOverlayOp) Valid() bool { + if o == nil { + return false + } + if o.Work != nil && o.Upper == nil { + return false + } + if slices.Contains(o.Lower, nil) { + return false + } + return o.Target != nil +} + func (o *MountOverlayOp) early(*setupState) error { if o.Work == nil && o.Upper != nil { switch o.Upper.String() { @@ -104,11 +117,7 @@ func (o *MountOverlayOp) early(*setupState) error { } o.lower = make([]string, len(o.Lower)) - for i, a := range o.Lower { - if a == nil { - return EBADE - } - + for i, a := range o.Lower { // nil checked in Valid if v, err := filepath.EvalSymlinks(a.String()); err != nil { return wrapErrSelf(err) } else { @@ -119,9 +128,6 @@ func (o *MountOverlayOp) early(*setupState) error { } func (o *MountOverlayOp) apply(state *setupState) error { - if o.Target == nil { - return EBADE - } target := toSysroot(o.Target.String()) if err := os.MkdirAll(target, state.ParentPerm); err != nil { return wrapErrSelf(err) @@ -163,10 +169,10 @@ func (o *MountOverlayOp) apply(state *setupState) error { func (o *MountOverlayOp) Is(op Op) bool { vo, ok := op.(*MountOverlayOp) - return ok && ((o == nil && vo == nil) || (o != nil && vo != nil && - o.Target != nil && vo.Target != nil && o.Target.Is(vo.Target) && + return ok && o.Valid() && vo.Valid() && + o.Target.Is(vo.Target) && slices.EqualFunc(o.Lower, vo.Lower, func(a *Absolute, v *Absolute) bool { return a.Is(v) }) && - o.Upper.Is(vo.Upper) && o.Work.Is(vo.Work))) + o.Upper.Is(vo.Upper) && o.Work.Is(vo.Work) } func (*MountOverlayOp) prefix() string { return "mounting" } func (o *MountOverlayOp) String() string { diff --git a/container/initoverlay_test.go b/container/initoverlay_test.go index ae4cd1b..68377dd 100644 --- a/container/initoverlay_test.go +++ b/container/initoverlay_test.go @@ -3,6 +3,15 @@ package container import "testing" func TestMountOverlayOp(t *testing.T) { + checkOpsValid(t, []opValidTestCase{ + {"nil", (*MountOverlayOp)(nil), false}, + {"zero", new(MountOverlayOp), false}, + {"nil lower", &MountOverlayOp{Target: MustAbs("/"), Lower: []*Absolute{nil}}, false}, + {"ro", &MountOverlayOp{Target: MustAbs("/"), Lower: []*Absolute{MustAbs("/")}}, true}, + {"ro work", &MountOverlayOp{Target: MustAbs("/"), Work: MustAbs("/tmp/")}, false}, + {"rw", &MountOverlayOp{Target: MustAbs("/"), Lower: []*Absolute{MustAbs("/")}, Upper: MustAbs("/"), Work: MustAbs("/")}, true}, + }) + checkOpsBuilder(t, []opsBuilderTestCase{ {"full", new(Ops).Overlay( MustAbs("/nix/store"), diff --git a/container/initplace.go b/container/initplace.go index 9114694..2d98d94 100644 --- a/container/initplace.go +++ b/container/initplace.go @@ -35,12 +35,9 @@ type TmpfileOp struct { Data []byte } +func (t *TmpfileOp) Valid() bool { return t != nil && t.Path != nil } func (t *TmpfileOp) early(*setupState) error { return nil } func (t *TmpfileOp) apply(state *setupState) error { - if t.Path == nil { - return EBADE - } - var tmpPath string if f, err := os.CreateTemp(FHSRoot, intermediatePatternTmpfile); err != nil { return wrapErrSelf(err) @@ -72,7 +69,8 @@ func (t *TmpfileOp) apply(state *setupState) error { func (t *TmpfileOp) Is(op Op) bool { vt, ok := op.(*TmpfileOp) - return ok && t.Path != nil && vt.Path != nil && t.Path.Is(vt.Path) && + return ok && t.Valid() && vt.Valid() && + t.Path.Is(vt.Path) && string(t.Data) == string(vt.Data) } func (*TmpfileOp) prefix() string { return "placing" } diff --git a/container/initplace_test.go b/container/initplace_test.go index e1d8163..db3d955 100644 --- a/container/initplace_test.go +++ b/container/initplace_test.go @@ -3,6 +3,12 @@ package container import "testing" func TestTmpfileOp(t *testing.T) { + checkOpsValid(t, []opValidTestCase{ + {"nil", (*TmpfileOp)(nil), false}, + {"zero", new(TmpfileOp), false}, + {"valid", &TmpfileOp{Path: MustAbs("/etc/passwd")}, true}, + }) + checkOpsBuilder(t, []opsBuilderTestCase{ {"noref", new(Ops).Place(MustAbs("/etc/passwd"), []byte(`chronos:x:65534:65534:Hakurei:/var/empty:/bin/zsh`)), Ops{ &TmpfileOp{ diff --git a/container/initproc.go b/container/initproc.go index d3c3753..9b9769c 100644 --- a/container/initproc.go +++ b/container/initproc.go @@ -16,15 +16,11 @@ func (f *Ops) Proc(target *Absolute) *Ops { } // MountProcOp mounts a new instance of [FstypeProc] on container path Target. -type MountProcOp struct { - Target *Absolute -} +type MountProcOp struct{ Target *Absolute } +func (p *MountProcOp) Valid() bool { return p != nil && p.Target != nil } func (p *MountProcOp) early(*setupState) error { return nil } func (p *MountProcOp) apply(state *setupState) error { - if p.Target == nil { - return EBADE - } target := toSysroot(p.Target.String()) if err := os.MkdirAll(target, state.ParentPerm); err != nil { return wrapErrSelf(err) @@ -35,8 +31,8 @@ func (p *MountProcOp) apply(state *setupState) error { func (p *MountProcOp) Is(op Op) bool { vp, ok := op.(*MountProcOp) - return ok && ((p == nil && vp == nil) || - (p.Target != nil && vp.Target != nil && p.Target.Is(vp.Target))) + return ok && p.Valid() && vp.Valid() && + p.Target.Is(vp.Target) } func (*MountProcOp) prefix() string { return "mounting" } func (p *MountProcOp) String() string { return fmt.Sprintf("proc on %q", p.Target) } diff --git a/container/initproc_test.go b/container/initproc_test.go index fe37732..eb55e4e 100644 --- a/container/initproc_test.go +++ b/container/initproc_test.go @@ -3,6 +3,12 @@ package container import "testing" func TestMountProcOp(t *testing.T) { + checkOpsValid(t, []opValidTestCase{ + {"nil", (*MountProcOp)(nil), false}, + {"zero", new(MountProcOp), false}, + {"valid", &MountProcOp{Target: MustAbs("/proc/")}, true}, + }) + checkOpsBuilder(t, []opsBuilderTestCase{ {"proc", new(Ops).Proc(MustAbs("/proc/")), Ops{ &MountProcOp{Target: MustAbs("/proc/")}, diff --git a/container/initremount.go b/container/initremount.go index 77efc21..74bea09 100644 --- a/container/initremount.go +++ b/container/initremount.go @@ -3,7 +3,6 @@ package container import ( "encoding/gob" "fmt" - "syscall" ) func init() { gob.Register(new(RemountOp)) } @@ -20,20 +19,18 @@ type RemountOp struct { Flags uintptr } +func (r *RemountOp) Valid() bool { return r != nil && r.Target != nil } func (*RemountOp) early(*setupState) error { return nil } func (r *RemountOp) apply(*setupState) error { - if r.Target == nil { - return syscall.EBADE - } return wrapErrSuffix(hostProc.remount(toSysroot(r.Target.String()), r.Flags), fmt.Sprintf("cannot remount %q:", r.Target)) } func (r *RemountOp) Is(op Op) bool { vr, ok := op.(*RemountOp) - return ok && ((r == nil && vr == nil) || - (r.Target != nil && vr.Target != nil && r.Target.Is(vr.Target)) && - r.Flags == vr.Flags) + return ok && r.Valid() && vr.Valid() && + r.Target.Is(vr.Target) && + r.Flags == vr.Flags } func (*RemountOp) prefix() string { return "remounting" } func (r *RemountOp) String() string { return fmt.Sprintf("%q flags %#x", r.Target, r.Flags) } diff --git a/container/initremount_test.go b/container/initremount_test.go index a59160b..db5476e 100644 --- a/container/initremount_test.go +++ b/container/initremount_test.go @@ -6,6 +6,12 @@ import ( ) func TestRemountOp(t *testing.T) { + checkOpsValid(t, []opValidTestCase{ + {"nil", (*RemountOp)(nil), false}, + {"zero", new(RemountOp), false}, + {"valid", &RemountOp{Target: MustAbs("/"), Flags: syscall.MS_RDONLY}, true}, + }) + checkOpsBuilder(t, []opsBuilderTestCase{ {"root", new(Ops).Remount(MustAbs("/"), syscall.MS_RDONLY), Ops{ &RemountOp{ diff --git a/container/initsymlink.go b/container/initsymlink.go index 3580554..3060817 100644 --- a/container/initsymlink.go +++ b/container/initsymlink.go @@ -26,6 +26,8 @@ type SymlinkOp struct { Dereference bool } +func (l *SymlinkOp) Valid() bool { return l != nil && l.Target != nil && l.LinkName != zeroString } + func (l *SymlinkOp) early(*setupState) error { if l.Dereference { if !isAbs(l.LinkName) { @@ -41,9 +43,6 @@ func (l *SymlinkOp) early(*setupState) error { } func (l *SymlinkOp) apply(state *setupState) error { - if l.Target == nil { - return syscall.EBADE - } target := toSysroot(l.Target.String()) if err := os.MkdirAll(path.Dir(target), state.ParentPerm); err != nil { return wrapErrSelf(err) @@ -56,9 +55,10 @@ func (l *SymlinkOp) apply(state *setupState) error { func (l *SymlinkOp) Is(op Op) bool { vl, ok := op.(*SymlinkOp) - return ok && ((l == nil && vl == nil) || - (l.Target != nil && vl.Target != nil && l.Target.Is(vl.Target)) && - l.LinkName == vl.LinkName && l.Dereference == vl.Dereference) + return ok && l.Valid() && vl.Valid() && + l.Target.Is(vl.Target) && + l.LinkName == vl.LinkName && + l.Dereference == vl.Dereference } func (*SymlinkOp) prefix() string { return "creating" } func (l *SymlinkOp) String() string { diff --git a/container/initsymlink_test.go b/container/initsymlink_test.go index 1051beb..4ae2da8 100644 --- a/container/initsymlink_test.go +++ b/container/initsymlink_test.go @@ -3,6 +3,14 @@ package container import "testing" func TestSymlinkOp(t *testing.T) { + checkOpsValid(t, []opValidTestCase{ + {"nil", (*SymlinkOp)(nil), false}, + {"zero", new(SymlinkOp), false}, + {"nil target", &SymlinkOp{LinkName: "/run/current-system"}, false}, + {"zero linkname", &SymlinkOp{Target: MustAbs("/run/current-system")}, false}, + {"valid", &SymlinkOp{Target: MustAbs("/run/current-system"), LinkName: "/run/current-system", Dereference: true}, true}, + }) + checkOpsBuilder(t, []opsBuilderTestCase{ {"current-system", new(Ops).Link( MustAbs("/run/current-system"), diff --git a/container/inittmpfs.go b/container/inittmpfs.go index 332fec8..d313a11 100644 --- a/container/inittmpfs.go +++ b/container/inittmpfs.go @@ -31,11 +31,9 @@ type MountTmpfsOp struct { Perm os.FileMode } +func (t *MountTmpfsOp) Valid() bool { return t != nil && t.Path != nil && t.FSName != zeroString } func (t *MountTmpfsOp) early(*setupState) error { return nil } func (t *MountTmpfsOp) apply(*setupState) error { - if t.Path == nil { - return EBADE - } if t.Size < 0 || t.Size > math.MaxUint>>1 { return msg.WrapErr(EBADE, fmt.Sprintf("size %d out of bounds", t.Size)) } @@ -44,9 +42,12 @@ func (t *MountTmpfsOp) apply(*setupState) error { func (t *MountTmpfsOp) Is(op Op) bool { vt, ok := op.(*MountTmpfsOp) - return ok && ((t == nil && vt == nil) || - (t.Path != nil && vt.Path != nil && t.Path.Is(vt.Path)) && - t.FSName == vt.FSName && t.Flags == vt.Flags && t.Size == vt.Size && t.Perm == vt.Perm) + return ok && t.Valid() && vt.Valid() && + t.FSName == vt.FSName && + t.Path.Is(vt.Path) && + t.Flags == vt.Flags && + t.Size == vt.Size && + t.Perm == vt.Perm } func (*MountTmpfsOp) prefix() string { return "mounting" } func (t *MountTmpfsOp) String() string { return fmt.Sprintf("tmpfs on %q size %d", t.Path, t.Size) } diff --git a/container/inittmpfs_test.go b/container/inittmpfs_test.go index c36820c..c924586 100644 --- a/container/inittmpfs_test.go +++ b/container/inittmpfs_test.go @@ -6,6 +6,14 @@ import ( ) func TestMountTmpfsOp(t *testing.T) { + checkOpsValid(t, []opValidTestCase{ + {"nil", (*MountTmpfsOp)(nil), false}, + {"zero", new(MountTmpfsOp), false}, + {"nil path", &MountTmpfsOp{FSName: "tmpfs"}, false}, + {"zero fsname", &MountTmpfsOp{Path: MustAbs("/tmp/")}, false}, + {"valid", &MountTmpfsOp{FSName: "tmpfs", Path: MustAbs("/tmp/")}, true}, + }) + checkOpsBuilder(t, []opsBuilderTestCase{ {"runtime", new(Ops).Tmpfs( MustAbs("/run/user"), diff --git a/container/mount_test.go b/container/mount_test.go index 4c598b3..64cac28 100644 --- a/container/mount_test.go +++ b/container/mount_test.go @@ -49,6 +49,24 @@ func TestEscapeOverlayDataSegment(t *testing.T) { } } +type opValidTestCase struct { + name string + op Op + want bool +} + +func checkOpsValid(t *testing.T, testCases []opValidTestCase) { + t.Run("valid", func(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if got := tc.op.Valid(); got != tc.want { + t.Errorf("Valid: %v, want %v", got, tc.want) + } + }) + } + }) +} + type opsBuilderTestCase struct { name string ops *Ops