aboutsummaryrefslogtreecommitdiffstats
path: root/internal/notify/icons_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/notify/icons_test.go')
-rw-r--r--internal/notify/icons_test.go88
1 files changed, 88 insertions, 0 deletions
diff --git a/internal/notify/icons_test.go b/internal/notify/icons_test.go
index 527b330..9a6824a 100644
--- a/internal/notify/icons_test.go
+++ b/internal/notify/icons_test.go
@@ -68,3 +68,91 @@ func TestResolveIconThemeName(t *testing.T) {
t.Fatalf("missing name must be empty, got %q", got)
}
}
+
+// iconEnv builds a throwaway XDG tree, points qt6ct at theme, and returns the
+// root. HOME, XDG_DATA_HOME and XDG_DATA_DIRS are restored after the test.
+func iconEnv(t *testing.T, theme string) string {
+ t.Helper()
+ root := t.TempDir()
+ home := filepath.Join(root, "home")
+ if err := os.MkdirAll(filepath.Join(home, ".config", "qt6ct"), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(filepath.Join(home, ".config", "qt6ct", "qt6ct.conf"), []byte("icon_theme="+theme+"\n"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ t.Setenv("XDG_DATA_HOME", root)
+ t.Setenv("XDG_DATA_DIRS", "")
+ t.Setenv("HOME", home)
+ return root
+}
+
+// putIcon creates a placeholder icon file, and its parents.
+func putIcon(t *testing.T, path string) {
+ t.Helper()
+ if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(path, []byte("<svg/>"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// writeIndex writes a theme's index.theme with a single Inherits value.
+func writeIndex(t *testing.T, dir, inherits string) {
+ t.Helper()
+ if err := os.MkdirAll(dir, 0o755); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(filepath.Join(dir, "index.theme"), []byte("[Icon Theme]\nInherits="+inherits+"\n"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestResolveIconFreedesktopLayout(t *testing.T) {
+ root := iconEnv(t, "Flat")
+ putIcon(t, filepath.Join(root, "icons", "Flat", "scalable", "apps", "firefox.svg"))
+ putIcon(t, filepath.Join(root, "icons", "Flat", "48x48", "apps", "firefox.png"))
+ want := filepath.Join(root, "icons", "Flat", "scalable", "apps", "firefox.svg")
+ if got := ResolveIcon("firefox"); got != want {
+ t.Fatalf("freedesktop scalable: got %q want %q", got, want)
+ }
+
+ root = iconEnv(t, "Grid")
+ putIcon(t, filepath.Join(root, "icons", "Grid", "48x48", "apps", "gimp.png"))
+ want = filepath.Join(root, "icons", "Grid", "48x48", "apps", "gimp.png")
+ if got := ResolveIcon("gimp"); got != want {
+ t.Fatalf("freedesktop raster: got %q want %q", got, want)
+ }
+}
+
+func TestResolveIconSizePreference(t *testing.T) {
+ root := iconEnv(t, "Sized")
+ base := filepath.Join(root, "icons", "Sized")
+ putIcon(t, filepath.Join(base, "apps", "48", "app.png"))
+ putIcon(t, filepath.Join(base, "apps", "32@2x", "app.png"))
+ want := filepath.Join(base, "apps", "32@2x", "app.png")
+ if got := ResolveIcon("app"); got != want {
+ t.Fatalf("@2x beats plain 48 (64px vs 48px): got %q want %q", got, want)
+ }
+
+ root = iconEnv(t, "Tied")
+ base = filepath.Join(root, "icons", "Tied")
+ putIcon(t, filepath.Join(base, "32x32@2x", "apps", "app.png"))
+ putIcon(t, filepath.Join(base, "64x64", "apps", "app.png"))
+ want = filepath.Join(base, "32x32@2x", "apps", "app.png")
+ if got := ResolveIcon("app"); got != want {
+ t.Fatalf("equal 64px must break ties deterministically: got %q want %q", got, want)
+ }
+}
+
+func TestResolveIconInheritsCycle(t *testing.T) {
+ root := iconEnv(t, "LoopA")
+ writeIndex(t, filepath.Join(root, "icons", "LoopA"), "LoopB")
+ writeIndex(t, filepath.Join(root, "icons", "LoopB"), "LoopA")
+ putIcon(t, filepath.Join(root, "icons", "hicolor", "48x48", "apps", "app.png"))
+ want := filepath.Join(root, "icons", "hicolor", "48x48", "apps", "app.png")
+ if got := ResolveIcon("app"); got != want {
+ t.Fatalf("cycle must terminate and fall through to hicolor: got %q want %q", got, want)
+ }
+}