system: optimise string formatting
Some checks failed
Test / Create distribution (push) Successful in 26s
Test / Fortify (push) Successful in 2m35s
Test / Data race detector (push) Successful in 4m38s
Test / Fpkg (push) Failing after 17m17s
Test / Flake checks (push) Has been skipped

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-03-25 04:42:30 +09:00
parent b66cfd9a63
commit 292715b0f6
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
16 changed files with 173 additions and 154 deletions

View File

@ -41,7 +41,7 @@ type appInfo struct {
// passed through to [fst.Config] // passed through to [fst.Config]
SessionBus *dbus.Config `json:"session_bus,omitempty"` SessionBus *dbus.Config `json:"session_bus,omitempty"`
// passed through to [fst.Config] // passed through to [fst.Config]
Enablements system.Enablements `json:"enablements"` Enablements system.Enablement `json:"enablements"`
// passed through to [fst.Config] // passed through to [fst.Config]
Multiarch bool `json:"multiarch,omitempty"` Multiarch bool `json:"multiarch,omitempty"`

View File

@ -48,7 +48,7 @@ type ConfinementConfig struct {
SessionBus *dbus.Config `json:"session_bus,omitempty"` SessionBus *dbus.Config `json:"session_bus,omitempty"`
// system resources to expose to the container // system resources to expose to the container
Enablements system.Enablements `json:"enablements"` Enablements system.Enablement `json:"enablements"`
} }
type ExtraPermConfig struct { type ExtraPermConfig struct {
@ -153,7 +153,7 @@ func Template() *Config {
Log: false, Log: false,
Filter: true, Filter: true,
}, },
Enablements: system.EWayland.Mask() | system.EDBus.Mask() | system.EPulse.Mask(), Enablements: system.EWayland | system.EDBus | system.EPulse,
}, },
} }
} }

View File

@ -45,7 +45,7 @@ var testCasesNixos = []sealTestCase{
Call: map[string]string{}, Broadcast: map[string]string{}, Call: map[string]string{}, Broadcast: map[string]string{},
Filter: true, Filter: true,
}, },
Enablements: system.EWayland.Mask() | system.EDBus.Mask() | system.EPulse.Mask(), Enablements: system.EWayland | system.EDBus | system.EPulse,
}, },
}, },
fst.ID{ fst.ID{

View File

@ -196,7 +196,7 @@ var testCasesPd = []sealTestCase{
}, },
Filter: true, Filter: true,
}, },
Enablements: system.EWayland.Mask() | system.EDBus.Mask() | system.EPulse.Mask(), Enablements: system.EWayland | system.EDBus | system.EPulse,
}, },
}, },
fst.ID{ fst.ID{

View File

@ -5,7 +5,6 @@ import (
"errors" "errors"
"log" "log"
"os/exec" "os/exec"
"strings"
"time" "time"
"git.gensokyo.uk/security/fortify/fst" "git.gensokyo.uk/security/fortify/fst"
@ -54,17 +53,16 @@ func (seal *outcome) Run(rs *fst.RunState) error {
revert app setup transaction revert app setup transaction
*/ */
rt, ec := new(system.Enablements), new(system.Criteria) var rt system.Enablement
ec.Enablements = new(system.Enablements) ec := system.Process
ec.Set(system.Process)
if states, err := c.Load(); err != nil { if states, err := c.Load(); err != nil {
// revert per-process state here to limit damage // revert per-process state here to limit damage
storeErr.OpErr = err storeErr.OpErr = err
return seal.sys.Revert(ec) return seal.sys.Revert((*system.Criteria)(&ec))
} else { } else {
if l := len(states); l == 0 { if l := len(states); l == 0 {
fmsg.Verbose("no other launchers active, will clean up globals") fmsg.Verbose("no other launchers active, will clean up globals")
ec.Set(system.User) ec |= system.User
} else { } else {
fmsg.Verbosef("found %d active launchers, cleaning up without globals", l) fmsg.Verbosef("found %d active launchers, cleaning up without globals", l)
} }
@ -72,31 +70,20 @@ func (seal *outcome) Run(rs *fst.RunState) error {
// accumulate enablements of remaining launchers // accumulate enablements of remaining launchers
for i, s := range states { for i, s := range states {
if s.Config != nil { if s.Config != nil {
*rt |= s.Config.Confinement.Enablements rt |= s.Config.Confinement.Enablements
} else { } else {
log.Printf("state entry %d does not contain config", i) log.Printf("state entry %d does not contain config", i)
} }
} }
} }
// invert accumulated enablements for cleanup ec |= rt ^ (system.EWayland | system.EX11 | system.EDBus | system.EPulse)
for i := system.Enablement(0); i < system.Enablement(system.ELen); i++ {
if !rt.Has(i) {
ec.Set(i)
}
}
if fmsg.Load() { if fmsg.Load() {
labels := make([]string, 0, system.ELen+1) if ec > 0 {
for i := system.Enablement(0); i < system.Enablement(system.ELen+2); i++ { fmsg.Verbose("reverting operations type", system.TypeString(ec))
if ec.Has(i) {
labels = append(labels, system.TypeString(i))
}
}
if len(labels) > 0 {
fmsg.Verbose("reverting operations type", strings.Join(labels, ", "))
} }
} }
return seal.sys.Revert(ec) return seal.sys.Revert((*system.Criteria)(&ec))
}() }()
}) })
storeErr.save([]error{revertErr, store.Close()}) storeErr.save([]error{revertErr, store.Close()})

View File

@ -224,7 +224,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
conf.Cover = append(conf.Cover, nscd) conf.Cover = append(conf.Cover, nscd)
} }
// bind GPU stuff // bind GPU stuff
if config.Confinement.Enablements.Has(system.EX11) || config.Confinement.Enablements.Has(system.EWayland) { if config.Confinement.Enablements&(system.EX11|system.EWayland) != 0 {
conf.Filesystem = append(conf.Filesystem, &fst.FilesystemConfig{Src: "/dev/dri", Device: true}) conf.Filesystem = append(conf.Filesystem, &fst.FilesystemConfig{Src: "/dev/dri", Device: true})
} }
// opportunistically bind kvm // opportunistically bind kvm
@ -338,7 +338,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
seal.env[term] = t seal.env[term] = t
} }
if config.Confinement.Enablements.Has(system.EWayland) { if config.Confinement.Enablements&system.EWayland != 0 {
// outer wayland socket (usually `/run/user/%d/wayland-%d`) // outer wayland socket (usually `/run/user/%d/wayland-%d`)
var socketPath string var socketPath string
if name, ok := sys.LookupEnv(wl.WaylandDisplay); !ok { if name, ok := sys.LookupEnv(wl.WaylandDisplay); !ok {
@ -371,7 +371,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
} }
if config.Confinement.Enablements.Has(system.EX11) { if config.Confinement.Enablements&system.EX11 != 0 {
if d, ok := sys.LookupEnv(display); !ok { if d, ok := sys.LookupEnv(display); !ok {
return fmsg.WrapError(ErrXDisplay, return fmsg.WrapError(ErrXDisplay,
"DISPLAY is not set") "DISPLAY is not set")
@ -386,7 +386,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
PulseAudio server and authentication PulseAudio server and authentication
*/ */
if config.Confinement.Enablements.Has(system.EPulse) { if config.Confinement.Enablements&system.EPulse != 0 {
// PulseAudio runtime directory (usually `/run/user/%d/pulse`) // PulseAudio runtime directory (usually `/run/user/%d/pulse`)
pulseRuntimeDir := path.Join(sc.RuntimePath, "pulse") pulseRuntimeDir := path.Join(sc.RuntimePath, "pulse")
// PulseAudio socket (usually `/run/user/%d/pulse/native`) // PulseAudio socket (usually `/run/user/%d/pulse/native`)
@ -439,7 +439,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
D-Bus proxy D-Bus proxy
*/ */
if config.Confinement.Enablements.Has(system.EDBus) { if config.Confinement.Enablements&system.EDBus != 0 {
// ensure dbus session bus defaults // ensure dbus session bus defaults
if config.Confinement.SessionBus == nil { if config.Confinement.SessionBus == nil {
config.Confinement.SessionBus = dbus.NewConfig(config.ID, true, true) config.Confinement.SessionBus = dbus.NewConfig(config.ID, true, true)

39
main.go
View File

@ -96,12 +96,13 @@ func buildCommand(out io.Writer) command.Command {
mpris bool mpris bool
dbusVerbose bool dbusVerbose bool
fid string fid string
aid int aid int
groups command.RepeatableFlag groups command.RepeatableFlag
homeDir string homeDir string
userName string userName string
enablements [system.ELen]bool
wayland, x11, dBus, pulse bool
) )
c.NewCommand("run", "Configure and start a permissive default sandbox", func(args []string) error { c.NewCommand("run", "Configure and start a permissive default sandbox", func(args []string) error {
@ -158,15 +159,21 @@ func buildCommand(out io.Writer) command.Command {
config.Confinement.Outer = homeDir config.Confinement.Outer = homeDir
config.Confinement.Username = userName config.Confinement.Username = userName
// enablements from flags if wayland {
for i := system.Enablement(0); i < system.Enablement(system.ELen); i++ { config.Confinement.Enablements |= system.EWayland
if enablements[i] { }
config.Confinement.Enablements.Set(i) if x11 {
} config.Confinement.Enablements |= system.EX11
}
if dBus {
config.Confinement.Enablements |= system.EDBus
}
if pulse {
config.Confinement.Enablements |= system.EPulse
} }
// parse D-Bus config file from flags if applicable // parse D-Bus config file from flags if applicable
if enablements[system.EDBus] { if dBus {
if dbusConfigSession == "builtin" { if dbusConfigSession == "builtin" {
config.Confinement.SessionBus = dbus.NewConfig(fid, true, mpris) config.Confinement.SessionBus = dbus.NewConfig(fid, true, mpris)
} else { } else {
@ -215,13 +222,13 @@ func buildCommand(out io.Writer) command.Command {
"Application home directory"). "Application home directory").
Flag(&userName, "u", command.StringFlag("chronos"), Flag(&userName, "u", command.StringFlag("chronos"),
"Passwd name within sandbox"). "Passwd name within sandbox").
Flag(&enablements[system.EWayland], "wayland", command.BoolFlag(false), Flag(&wayland, "wayland", command.BoolFlag(false),
"Allow Wayland connections"). "Allow Wayland connections").
Flag(&enablements[system.EX11], "X", command.BoolFlag(false), Flag(&x11, "X", command.BoolFlag(false),
"Share X11 socket and allow connection"). "Share X11 socket and allow connection").
Flag(&enablements[system.EDBus], "dbus", command.BoolFlag(false), Flag(&dBus, "dbus", command.BoolFlag(false),
"Proxy D-Bus connection"). "Proxy D-Bus connection").
Flag(&enablements[system.EPulse], "pulse", command.BoolFlag(false), Flag(&pulse, "pulse", command.BoolFlag(false),
"Share PulseAudio socket and cookie") "Share PulseAudio socket and cookie")
} }

View File

@ -37,7 +37,7 @@ func Test_printShowInstance(t *testing.T) {
}{ }{
{"config", nil, fst.Template(), false, false, `App {"config", nil, fst.Template(), false, false, `App
ID: 9 (org.chromium.Chromium) ID: 9 (org.chromium.Chromium)
Enablements: Wayland, D-Bus, PulseAudio Enablements: wayland, dbus, pulseaudio
Groups: ["video"] Groups: ["video"]
Directory: /var/lib/persist/home/org.chromium.Chromium Directory: /var/lib/persist/home/org.chromium.Chromium
Hostname: "localhost" Hostname: "localhost"
@ -74,14 +74,14 @@ System bus
App App
ID: 0 ID: 0
Enablements: (No enablements) Enablements: (no enablements)
Directory: Directory:
Command: Command:
`}, `},
{"config flag none", nil, &fst.Config{Confinement: fst.ConfinementConfig{Sandbox: new(fst.SandboxConfig)}}, false, false, `App {"config flag none", nil, &fst.Config{Confinement: fst.ConfinementConfig{Sandbox: new(fst.SandboxConfig)}}, false, false, `App
ID: 0 ID: 0
Enablements: (No enablements) Enablements: (no enablements)
Directory: Directory:
Flags: none Flags: none
Etc: /etc Etc: /etc
@ -90,7 +90,7 @@ App
`}, `},
{"config nil entries", nil, &fst.Config{Confinement: fst.ConfinementConfig{Sandbox: &fst.SandboxConfig{Filesystem: make([]*fst.FilesystemConfig, 1)}, ExtraPerms: make([]*fst.ExtraPermConfig, 1)}}, false, false, `App {"config nil entries", nil, &fst.Config{Confinement: fst.ConfinementConfig{Sandbox: &fst.SandboxConfig{Filesystem: make([]*fst.FilesystemConfig, 1)}, ExtraPerms: make([]*fst.ExtraPermConfig, 1)}}, false, false, `App
ID: 0 ID: 0
Enablements: (No enablements) Enablements: (no enablements)
Directory: Directory:
Flags: none Flags: none
Etc: /etc Etc: /etc
@ -105,7 +105,7 @@ Extra ACL
App App
ID: 0 ID: 0
Enablements: (No enablements) Enablements: (no enablements)
Directory: Directory:
Command: Command:
@ -121,7 +121,7 @@ Session bus
App App
ID: 9 (org.chromium.Chromium) ID: 9 (org.chromium.Chromium)
Enablements: Wayland, D-Bus, PulseAudio Enablements: wayland, dbus, pulseaudio
Groups: ["video"] Groups: ["video"]
Directory: /var/lib/persist/home/org.chromium.Chromium Directory: /var/lib/persist/home/org.chromium.Chromium
Hostname: "localhost" Hostname: "localhost"
@ -162,7 +162,7 @@ State
App App
ID: 0 ID: 0
Enablements: (No enablements) Enablements: (no enablements)
Directory: Directory:
Command: Command:
@ -469,8 +469,8 @@ func Test_printPs(t *testing.T) {
`}, `},
{"valid", state.Entries{testID: testState}, false, false, ` Instance PID App Uptime Enablements Command {"valid", state.Entries{testID: testState}, false, false, ` Instance PID App Uptime Enablements Command
8e2c76b0 3735928559 9 1h2m32s Wayland, D-Bus, PulseAudio ["chromium" "--ignore-gpu-blocklist" "--disable-smooth-scrolling" "--enable-features=UseOzonePlatform" "--ozone-platform=wayland"] 8e2c76b0 3735928559 9 1h2m32s wayland, dbus, pulseaudio ["chromium" "--ignore-gpu-blocklist" "--disable-smooth-scrolling" "--enable-features=UseOzonePlatform" "--ozone-platform=wayland"]
`}, `},
{"valid short", state.Entries{testID: testState}, true, false, `8e2c76b0 {"valid short", state.Entries{testID: testState}, true, false, `8e2c76b0

View File

@ -46,20 +46,20 @@ func TestUpdatePermType(t *testing.T) {
} }
} }
func TestACL_String(t *testing.T) { func TestACLString(t *testing.T) {
testCases := []struct { testCases := []struct {
want string want string
et Enablement et Enablement
perms []acl.Perm perms []acl.Perm
}{ }{
{`--- type: Process path: "/nonexistent"`, Process, []acl.Perm{}}, {`--- type: process path: "/nonexistent"`, Process, []acl.Perm{}},
{`r-- type: User path: "/nonexistent"`, User, []acl.Perm{acl.Read}}, {`r-- type: user path: "/nonexistent"`, User, []acl.Perm{acl.Read}},
{`-w- type: Wayland path: "/nonexistent"`, EWayland, []acl.Perm{acl.Write}}, {`-w- type: wayland path: "/nonexistent"`, EWayland, []acl.Perm{acl.Write}},
{`--x type: X11 path: "/nonexistent"`, EX11, []acl.Perm{acl.Execute}}, {`--x type: x11 path: "/nonexistent"`, EX11, []acl.Perm{acl.Execute}},
{`rw- type: D-Bus path: "/nonexistent"`, EDBus, []acl.Perm{acl.Read, acl.Write}}, {`rw- type: dbus path: "/nonexistent"`, EDBus, []acl.Perm{acl.Read, acl.Write}},
{`r-x type: PulseAudio path: "/nonexistent"`, EPulse, []acl.Perm{acl.Read, acl.Execute}}, {`r-x type: pulseaudio path: "/nonexistent"`, EPulse, []acl.Perm{acl.Read, acl.Execute}},
{`rwx type: User path: "/nonexistent"`, User, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, {`rwx type: user path: "/nonexistent"`, User, []acl.Perm{acl.Read, acl.Write, acl.Execute}},
{`rwx type: Process path: "/nonexistent"`, Process, []acl.Perm{acl.Read, acl.Write, acl.Write, acl.Execute}}, {`rwx type: process path: "/nonexistent"`, Process, []acl.Perm{acl.Read, acl.Write, acl.Write, acl.Execute}},
} }
for _, tc := range testCases { for _, tc := range testCases {

View File

@ -1,68 +1,47 @@
package system package system
import ( import (
"fmt"
"strings" "strings"
) )
type ( // Enablement represents optional system resources.
// Enablement represents an optional system resource type Enablement byte
Enablement uint8
// Enablements represents optional system resources to share
Enablements uint64
)
const ( const (
EWayland Enablement = iota EWayland Enablement = 1 << iota
EX11 EX11
EDBus EDBus
EPulse EPulse
EM
) )
var enablementString = [...]string{
EWayland: "Wayland",
EX11: "X11",
EDBus: "D-Bus",
EPulse: "PulseAudio",
}
const ELen = len(enablementString)
func (e Enablement) String() string { func (e Enablement) String() string {
if int(e) >= ELen { switch e {
return "<invalid enablement>" case 0:
} return "(no enablements)"
return enablementString[e] case EWayland:
} return "wayland"
case EX11:
return "x11"
case EDBus:
return "dbus"
case EPulse:
return "pulseaudio"
default:
buf := new(strings.Builder)
buf.Grow(32)
func (e Enablement) Mask() Enablements { for i := Enablement(1); i < EM; i <<= 1 {
return 1 << e if e&i != 0 {
} buf.WriteString(", " + i.String())
}
// Has returns whether a feature is enabled
func (es *Enablements) Has(e Enablement) bool {
return *es&e.Mask() != 0
}
// Set enables a feature
func (es *Enablements) Set(e Enablement) {
if es.Has(e) {
panic("enablement " + e.String() + " set twice")
}
*es |= e.Mask()
}
func (es *Enablements) String() string {
buf := new(strings.Builder)
for i := Enablement(0); i < Enablement(ELen); i++ {
if es.Has(i) {
buf.WriteString(", " + i.String())
} }
}
if buf.Len() == 0 { if buf.Len() == 0 {
buf.WriteString("(No enablements)") return fmt.Sprintf("e%x", byte(e))
}
return strings.TrimPrefix(buf.String(), ", ")
} }
return strings.TrimPrefix(buf.String(), ", ")
} }

43
system/enablement_test.go Normal file
View File

@ -0,0 +1,43 @@
package system_test
import (
"testing"
"git.gensokyo.uk/security/fortify/system"
)
func TestEnablementString(t *testing.T) {
testCases := []struct {
flags system.Enablement
want string
}{
{0, "(no enablements)"},
{system.EWayland, "wayland"},
{system.EX11, "x11"},
{system.EDBus, "dbus"},
{system.EPulse, "pulseaudio"},
{system.EWayland | system.EX11, "wayland, x11"},
{system.EWayland | system.EDBus, "wayland, dbus"},
{system.EWayland | system.EPulse, "wayland, pulseaudio"},
{system.EX11 | system.EDBus, "x11, dbus"},
{system.EX11 | system.EPulse, "x11, pulseaudio"},
{system.EDBus | system.EPulse, "dbus, pulseaudio"},
{system.EWayland | system.EX11 | system.EDBus, "wayland, x11, dbus"},
{system.EWayland | system.EX11 | system.EPulse, "wayland, x11, pulseaudio"},
{system.EWayland | system.EDBus | system.EPulse, "wayland, dbus, pulseaudio"},
{system.EX11 | system.EDBus | system.EPulse, "x11, dbus, pulseaudio"},
{system.EWayland | system.EX11 | system.EDBus | system.EPulse, "wayland, x11, dbus, pulseaudio"},
{1 << 5, "e20"},
{1 << 6, "e40"},
{1 << 7, "e80"},
}
for _, tc := range testCases {
t.Run(tc.want, func(t *testing.T) {
if got := tc.flags.String(); got != tc.want {
t.Errorf("String: %q, want %q", got, tc.want)
}
})
}
}

View File

@ -78,7 +78,7 @@ func (m *Mkdir) Path() string {
} }
func (m *Mkdir) String() string { func (m *Mkdir) String() string {
t := "Ensure" t := "ensure"
if m.ephemeral { if m.ephemeral {
t = TypeString(m.Type()) t = TypeString(m.Type())
} }

View File

@ -41,20 +41,20 @@ func TestEphemeral(t *testing.T) {
} }
} }
func TestMkdir_String(t *testing.T) { func TestMkdirString(t *testing.T) {
testCases := []struct { testCases := []struct {
want string want string
ephemeral bool ephemeral bool
et Enablement et Enablement
}{ }{
{"Ensure", false, User}, {"ensure", false, User},
{"Ensure", false, Process}, {"ensure", false, Process},
{"Ensure", false, EWayland}, {"ensure", false, EWayland},
{"Wayland", true, EWayland}, {"wayland", true, EWayland},
{"X11", true, EX11}, {"x11", true, EX11},
{"D-Bus", true, EDBus}, {"dbus", true, EDBus},
{"PulseAudio", true, EPulse}, {"pulseaudio", true, EPulse},
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.want, func(t *testing.T) { t.Run(tc.want, func(t *testing.T) {

View File

@ -5,28 +5,29 @@ import (
"context" "context"
"errors" "errors"
"log" "log"
"strings"
"sync" "sync"
) )
const ( const (
// User type is reverted at final launcher exit. // User type is reverted at final launcher exit.
User = Enablement(ELen) User = EM << iota
// Process type is unconditionally reverted on exit. // Process type is unconditionally reverted on exit.
Process = Enablement(ELen + 1) Process
CM
) )
// Criteria specifies types of Op to revert. // Criteria specifies types of Op to revert.
type Criteria struct { type Criteria Enablement
*Enablements
}
func (ec *Criteria) hasType(o Op) bool { func (ec *Criteria) hasType(o Op) bool {
// nil criteria: revert everything except User // nil criteria: revert everything except User
if ec.Enablements == nil { if ec == nil {
return o.Type() != User return o.Type() != User
} }
return ec.Has(o.Type()) return Enablement(*ec)&o.Type() != 0
} }
// Op is a reversible system operation. // Op is a reversible system operation.
@ -48,11 +49,22 @@ type Op interface {
func TypeString(e Enablement) string { func TypeString(e Enablement) string {
switch e { switch e {
case User: case User:
return "User" return "user"
case Process: case Process:
return "Process" return "process"
default: default:
return e.String() buf := new(strings.Builder)
buf.Grow(48)
if v := e &^ User &^ Process; v != 0 {
buf.WriteString(v.String())
}
for i := User; i < CM; i <<= 1 {
if e&i != 0 {
buf.WriteString(", " + TypeString(i))
}
}
return strings.TrimPrefix(buf.String(), ", ")
} }
} }
@ -110,7 +122,7 @@ func (sys *I) Commit(ctx context.Context) error {
if sp != nil { if sp != nil {
// rollback partial commit // rollback partial commit
msg.Verbosef("commit faulted after %d ops, rolling back partial commit", len(sp.ops)) msg.Verbosef("commit faulted after %d ops, rolling back partial commit", len(sp.ops))
if err := sp.Revert(&Criteria{nil}); err != nil { if err := sp.Revert(nil); err != nil {
log.Println("errors returned reverting partial commit:", err) log.Println("errors returned reverting partial commit:", err)
} }
} }

View File

@ -47,10 +47,10 @@ func (ptc tcOp) test(t *testing.T, gotOps []Op, wantOps []Op, fn string) {
ec *Criteria ec *Criteria
want bool want bool
}{ }{
{"nil", newCriteria(), ptc.et != User}, {"nil", nil, ptc.et != User},
{"self", newCriteria(ptc.et), true}, {"self", newCriteria(ptc.et), true},
{"all", newCriteria(EWayland, EX11, EDBus, EPulse, User, Process), true}, {"all", newCriteria(EWayland | EX11 | EDBus | EPulse | User | Process), true},
{"enablements", newCriteria(EWayland, EX11, EDBus, EPulse), ptc.et != User && ptc.et != Process}, {"enablements", newCriteria(EWayland | EX11 | EDBus | EPulse), ptc.et != User && ptc.et != Process},
} }
for _, tc := range testCases { for _, tc := range testCases {
@ -65,15 +65,4 @@ func (ptc tcOp) test(t *testing.T, gotOps []Op, wantOps []Op, fn string) {
} }
} }
func newCriteria(labels ...Enablement) *Criteria { func newCriteria(e Enablement) *Criteria { return (*Criteria)(&e) }
ec := new(Criteria)
if len(labels) == 0 {
return ec
}
ec.Enablements = new(Enablements)
for _, e := range labels {
ec.Set(e)
}
return ec
}

View File

@ -37,15 +37,17 @@ func TestTypeString(t *testing.T) {
{system.EX11, system.EX11.String()}, {system.EX11, system.EX11.String()},
{system.EDBus, system.EDBus.String()}, {system.EDBus, system.EDBus.String()},
{system.EPulse, system.EPulse.String()}, {system.EPulse, system.EPulse.String()},
{system.User, "User"}, {system.User, "user"},
{system.Process, "Process"}, {system.Process, "process"},
{system.User | system.Process, "user, process"},
{system.EWayland | system.User | system.Process, "wayland, user, process"},
{system.EX11 | system.Process, "x11, process"},
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run("label type string "+tc.want, func(t *testing.T) { t.Run("label type string "+tc.want, func(t *testing.T) {
if got := system.TypeString(tc.e); got != tc.want { if got := system.TypeString(tc.e); got != tc.want {
t.Errorf("TypeString(%d) = %v, want %v", t.Errorf("TypeString: %q, want %q",
tc.e,
got, tc.want) got, tc.want)
} }
}) })