package budget import ( "errors" "io" "reflect" "strings" "testing" ) func TestApply(t *testing.T) { f := newFake() f.units = []unit{ {Name: "app-browser-123.scope", Active: "active"}, {Name: "app-org.example.Terminal.service", Active: "active"}, {Name: "containerd.service", Active: "active"}, {Name: "session-ui.service", Active: "active"}, {Name: "app-closed-123.scope", Active: "inactive"}, } events := []string{} f.events = &events a := application{testConfig(), homePaths(t.TempDir()), f, func(args ...string) error { events = append(events, strings.Join(args, " ")) return nil }, io.Discard} if err := a.apply(); err != nil { t.Fatal(err) } want := []string{"daemon-reload", "set app-browser-123.scope", "set app-org.example.Terminal.service", "set containerd.service", "enable budget.service", "restart budget.service"} if !reflect.DeepEqual(events, want) { t.Fatalf("got %v, want %v", events, want) } if values(f.sets["app-browser-123.scope"])["MemoryMax"].Value() != uint64(8589934592) { t.Fatal("default ceiling missing") } if len(f.sets) != 3 { t.Fatal("session infrastructure or inactive apps were changed") } f.setErr = errors.New("set failed") events = nil if err := a.apply(); err != f.setErr { t.Fatal(err) } if len(events) != 2 { t.Fatal("continued after failure", events) } } func TestSharedOverride(t *testing.T) { f := newFake() f.units = []unit{ {Name: "app-editor@123.service", Active: "active"}, {Name: `app-editor\x2duri\x2dhandler@456.service`, Active: "active"}, {Name: "budget-d123abc.scope", Active: "active"}, {Name: "app-other-123.scope", Active: "active"}, {Name: "app-editor@closed.service", Active: "inactive"}, } f.groups = map[string]string{ "app-editor.slice": "/app.slice/app-editor.slice", "app-editor@123.service": "/app.slice/app-editor.slice/app-editor@123.service", `app-editor\x2duri\x2dhandler@456.service`: "/app.slice/app-editor.slice/uri.service", "budget-d123abc.scope": "/app.slice/app-editor.slice/budget-d123abc.scope", "app-other-123.scope": "/app.slice/app-editor.slice-neighbor/app-other-123.scope", } a := application{config: testConfig(), ctl: f, out: io.Discard} if err := a.override("compute", "editor"); err != nil { t.Fatal(err) } want := []string{"app-editor.slice", "app-editor@123.service", `app-editor\x2duri\x2dhandler@456.service`, "budget-d123abc.scope"} if !reflect.DeepEqual(keys(f.sets), want) { t.Fatalf("got %v, want %v", keys(f.sets), want) } for name, props := range f.sets { if values(props)["MemoryMax"].Value() != uint64(17179869184) { t.Fatal(name, props) } } if err := a.override("default", "app-editor.slice"); err != nil { t.Fatal(err) } for name, props := range f.sets { if values(props)["MemoryMax"].Value() != uint64(8589934592) { t.Fatal(name, props) } } } func TestOverrideTargets(t *testing.T) { f := newFake() f.units = []unit{{Name: "app-flatpak-org.example.Download-123.scope", Active: "active"}} a := application{config: testConfig(), ctl: f, out: io.Discard} if err := a.override("unrestricted", "org.example.Download"); err != nil { t.Fatal(err) } if len(f.sets) != 1 { t.Fatal(f.sets) } if err := a.override("default", "no-such-app"); err == nil { t.Fatal("missing app accepted") } if err := a.override("missing", "org.example.Download"); err == nil { t.Fatal("missing profile accepted") } } func TestAppOutput(t *testing.T) { want := errors.New("output failed") f := newFake() a := application{ config: testConfig(), paths: homePaths(t.TempDir()), ctl: f, run: func(...string) error { return nil }, out: failWriter{want}, } if err := a.apply(); !errors.Is(err, want) { t.Fatal(err) } if err := a.override("default", "app-example.service"); !errors.Is(err, want) { t.Fatal(err) } }