Add systemd resource budgets with TOML profiles

This commit is contained in:
ms2k 2026-10-05 19:19:05 +09:00
commit be8749a384

View file

@ -0,0 +1,100 @@
package budget
import (
"strings"
"testing"
)
func TestEscape(t *testing.T) {
for input, want := range map[string]string{
"editor-uri-handler": `editor\x2duri\x2dhandler`,
".hidden": `\x2ehidden`, "a/b c": `a-b\x20c`, "λ": `\xce\xbb`,
"org.example.Terminal": "org.example.Terminal", "a:b_c": "a:b_c",
} {
if got := escape(input); got != want {
t.Errorf("%q: got %q, want %q", input, got, want)
}
}
}
func TestUnitProfiles(t *testing.T) {
c := testConfig()
for name, want := range map[string]string{
"app-terminal-surface-transient-123.scope": "unrestricted",
"app-org.example.Terminal.service": "unrestricted",
"[email protected]": "unrestricted",
"containerd.service": "unrestricted", "worker.service": "unrestricted",
"[email protected]": "compute",
"app-flatpak-org.example.Game-123.scope": "compute",
"app-flatpak-org.example.Download-123.scope": "bulk",
`app-flatpak-org.example.Browser\x2dcommunity-123.scope`: "default",
"[email protected]": "default", "app-editor.slice": "default",
"search.slice": "search", "session-ui.service": "",
"budget.service": "", "session.slice": "", "work-123.scope": "",
} {
t.Run(name, func(t *testing.T) {
if got := c.unitProfile(name); got != want {
t.Fatalf("got %q, want %q", got, want)
}
})
}
}
func TestRender(t *testing.T) {
c := testConfig()
files, err := c.render()
if err != nil {
t.Fatal(err)
}
for _, name := range []string{"app-.scope.d/50-budget.conf", "app-.service.d/50-budget.conf"} {
if !strings.Contains(files[name], "MemoryMax=8G\n") {
t.Error(name)
}
}
for _, app := range []string{"editor", "editor-uri-handler"} {
for _, directory := range appDirs(app) {
if !strings.Contains(files[directory+"/50-budget.conf"], "Slice=app-editor.slice\n") {
t.Error(directory)
}
}
}
if !strings.Contains(files["search.slice"], "CPUQuota=200%\n") || !strings.Contains(files["search.slice"], "IOReadBandwidthMax=/dev/mock 50M\n") {
t.Fatal(files["search.slice"])
}
if !strings.Contains(files["app-editor.slice"], "MemoryMax=8G\n") {
t.Fatal(files["app-editor.slice"])
}
if !strings.Contains(files["app-terminal-.scope.d/50-budget.conf"], "CPUQuota=\n") {
t.Fatal("terminal ceiling wasn't removed")
}
if !strings.Contains(files["app-flatpak-org.example.Game-.scope.d/50-budget.conf"], "MemoryMax=16G\n") {
t.Fatal("compute profile missing")
}
if files["budget.service"] != serviceText {
t.Fatal("wrong service command")
}
c.Shared["editor"] = "missing"
if _, err := c.render(); err == nil {
t.Fatal("unknown shared profile accepted")
}
}
func TestNoSharedApps(t *testing.T) {
c := testConfig()
c.Shared = nil
if got := c.sharedSlice(); got != "" {
t.Fatal(got)
}
files, err := c.render()
if err != nil {
t.Fatal(err)
}
for name := range files {
if strings.HasPrefix(name, "app-editor") {
t.Fatal("unconfigured shared slice remains", name)
}
}
if got := c.unitProfile(""); got != "" {
t.Fatal(got)
}
}