Files
Behavision/desktop/icons_test.go
Suriyakumarvijayanayagam 4c62fc0ef3 The Loyaly mark everywhere a person sees the product
Brand assets in brand/ (the 512px mark, sizes for each surface, a
multi-size .ico). Windows executables carry it as a compiled-in
resource (rsrc_windows_amd64.syso from go-winres) so Explorer, the
taskbar and the installer show it; installer/build.ps1 therefore uses a
plain go build rather than wails build, which would add a second copy
and fail the link. The tray icon is the mark with a state dot over its
corner - a plain coloured circle read as a generic status light among
other icons - rendered from the embedded PNG at 32px so it survives
150% scaling. The desktop app's login, setup and sidebar marks, the
head-office web app's mark and favicon, and the engine dashboard's
favicon are the same file.

Also found while packaging: no wheel so far shipped static/, so the
engine's own dashboard at :8010 on a Windows source install would have
failed with a missing file. package-data now includes it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KGcjxF1cNLcuwc3DAPcnfj
2026-09-19 12:54:19 +05:30

95 lines
3.2 KiB
Go

package main
import (
"bytes"
"encoding/binary"
"image/png"
"runtime"
"testing"
)
// The tray icon shipped as a PNG for a while. systray hands the bytes to
// LoadImageW, which decodes .ico only, so Windows logged one line and drew
// nothing — and nothing on a Mac could notice. These tests are the substitute
// for the Windows box we do not have.
func TestEncodeICOIsAValidIconFile(t *testing.T) {
b := encodeICO(trayImage(colorFor("ok")))
if len(b) < 22 {
t.Fatalf("far too short: %d bytes", len(b))
}
if got := b[:6]; !bytes.Equal(got, []byte{0, 0, 1, 0, 1, 0}) {
t.Errorf("ICONDIR = % x, want 00 00 01 00 01 00", got)
}
if b[6] != iconSize || b[7] != iconSize {
t.Errorf("entry is %dx%d, want %dx%d", b[6], b[7], iconSize, iconSize)
}
if bpp := binary.LittleEndian.Uint16(b[12:14]); bpp != 32 {
t.Errorf("bits per pixel = %d, want 32", bpp)
}
// A wrong length here loads as a truncated or garbage icon rather than
// failing outright, which is the harder version of this bug to spot.
size := binary.LittleEndian.Uint32(b[14:18])
off := binary.LittleEndian.Uint32(b[18:22])
if off != 22 {
t.Errorf("image offset = %d, want 22", off)
}
if int(off)+int(size) != len(b) {
t.Errorf("entry claims %d bytes at %d, file is %d", size, off, len(b))
}
if h := binary.LittleEndian.Uint32(b[22:26]); h != 40 {
t.Errorf("BITMAPINFOHEADER size = %d, want 40", h)
}
// Height must be doubled for the implied AND mask or Windows draws the
// bottom half of the icon stretched over the whole square.
if hh := int32(binary.LittleEndian.Uint32(b[30:34])); hh != int32(iconSize*2) {
t.Errorf("biHeight = %d, want %d", hh, iconSize*2)
}
}
func TestEncodeICOPixelsAreBGRABottomUp(t *testing.T) {
want := colorFor("error") // red: distinguishable from B and G if swapped
img := trayImage(want)
b := encodeICO(img)
// Centre of the state dot, which is solid fill. Bottom-up means image row
// y lands at DIB row iconSize-1-y counting from the start.
x, y := iconSize-6-1, iconSize-6-1
row := iconSize - 1 - y
i := 22 + 40 + (row*iconSize+x)*4
got := b[i : i+4]
if !bytes.Equal(got, []byte{want.B, want.G, want.R, 0xFF}) {
t.Errorf("centre pixel = % x, want % x (BGRA)",
got, []byte{want.B, want.G, want.R, 0xFF})
}
}
func TestIconForMatchesThePlatformDecoder(t *testing.T) {
b := iconFor("ok")
pngMagic := []byte{0x89, 'P', 'N', 'G'}
if runtime.GOOS == "windows" {
if bytes.HasPrefix(b, pngMagic) {
t.Fatal("Windows tray icon is a PNG; LoadImageW will refuse it")
}
if !bytes.HasPrefix(b, []byte{0, 0, 1, 0}) {
t.Fatalf("Windows tray icon is not an ICO: % x", b[:4])
}
return
}
if _, err := png.Decode(bytes.NewReader(b)); err != nil {
t.Fatalf("non-Windows tray icon is not a decodable PNG: %v", err)
}
}
// The four states exist to be told apart at a glance; identical bytes would
// mean the icon never changes and the amber "running but blind" state — the
// one that otherwise goes unnoticed for weeks — looks exactly like healthy.
func TestEveryStateLooksDifferent(t *testing.T) {
seen := map[string]string{}
for _, s := range []string{"ok", "warn", "error", "stopped"} {
k := string(iconFor(s))
if prev, dup := seen[k]; dup {
t.Errorf("%q and %q render identically", prev, s)
}
seen[k] = s
}
}