aboutsummaryrefslogtreecommitdiffstats
path: root/internal/notify/classify_test.go
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-15 18:31:30 +0200
committerDanilo M. <danix@danix.xyz>2026-09-15 18:31:30 +0200
commit8087086bc0a996e7dc020ba8bcf1332dd843f034 (patch)
treef0fade4044cd29681670e54f7b9e80af8fce2f23 /internal/notify/classify_test.go
parentd5812ffa717cad99e12752b719a024f06b72b6af (diff)
downloadnotifyd-8087086bc0a996e7dc020ba8bcf1332dd843f034.tar.gz
notifyd-8087086bc0a996e7dc020ba8bcf1332dd843f034.zip
fix(notify): route icon-like content images to the app-icon slot
Clients like kitty, mail and opencode send their icon through the content-image hint, so it drew as a large balloon preview. A theme-name source, an SVG, or a raster small on both sides now fills the app-icon slot instead; a larger raster (a screenshot) stays the preview. An app_icon already present keeps the slot.
Diffstat (limited to 'internal/notify/classify_test.go')
-rw-r--r--internal/notify/classify_test.go95
1 files changed, 95 insertions, 0 deletions
diff --git a/internal/notify/classify_test.go b/internal/notify/classify_test.go
new file mode 100644
index 0000000..6234008
--- /dev/null
+++ b/internal/notify/classify_test.go
@@ -0,0 +1,95 @@
+// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+package notify
+
+import (
+ "image"
+ "image/png"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "github.com/godbus/dbus/v5"
+)
+
+func writeTestPNG(t *testing.T, path string, w, h int) {
+ t.Helper()
+ f, err := os.Create(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ if err := png.Encode(f, image.NewRGBA(image.Rect(0, 0, w, h))); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestIsIconImage(t *testing.T) {
+ dir := t.TempDir()
+ small := filepath.Join(dir, "logo.png")
+ large := filepath.Join(dir, "shot.png")
+ svg := filepath.Join(dir, "icon.svg")
+ writeTestPNG(t, small, 32, 32)
+ writeTestPNG(t, large, 800, 600)
+ if err := os.WriteFile(svg, []byte("<svg/>"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ cases := []struct {
+ name string
+ raw, resolved string
+ want bool
+ }{
+ {"theme name", "utilities-terminal", "/x/apps/16/utilities-terminal.svg", true},
+ {"svg", svg, svg, true},
+ {"small raster", small, small, true},
+ {"large raster", large, large, false},
+ {"missing file", "/no/such.png", "/no/such.png", false},
+ {"empty", "", "", false},
+ }
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ if got := IsIconImage(c.raw, c.resolved); got != c.want {
+ t.Fatalf("IsIconImage(%q, %q) = %v, want %v", c.raw, c.resolved, got, c.want)
+ }
+ })
+ }
+}
+
+func TestNotifyRoutesContentImageByKind(t *testing.T) {
+ dir := t.TempDir()
+ small := filepath.Join(dir, "logo.png")
+ large := filepath.Join(dir, "shot.png")
+ writeTestPNG(t, small, 32, 32)
+ writeTestPNG(t, large, 800, 600)
+
+ run := func(path string) Popup {
+ var live []Popup
+ s := &Service{dir: t.TempDir(), timers: map[uint32]*time.Timer{}}
+ s.store = NewStore(s.emitClosed, func(l, _ []Popup) { live = l }, s.removeImage)
+ hints := map[string]dbus.Variant{"image-path": dbus.MakeVariant(path)}
+ if _, err := s.Notify("app", 0, "", "s", "b", nil, hints, -1); err != nil {
+ t.Fatal(err)
+ }
+ if len(live) != 1 {
+ t.Fatalf("published %d popups", len(live))
+ }
+ return live[0]
+ }
+
+ if p := run(small); p.Icon != small || p.Image != "" {
+ t.Fatalf("small image: icon=%q image=%q, want icon only", p.Icon, p.Image)
+ }
+ if p := run(large); p.Icon != "" || p.Image != large {
+ t.Fatalf("large image: icon=%q image=%q, want image only", p.Icon, p.Image)
+ }
+}