okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

dawnAdd /cd: a folder picker that moves a session, and descendants follow3b601a9

main
2.9 KiB87 linesraw
1// Terminal color reply parsing and contrast across palettes cannot be
2// exercised by daemon E2E: they need a terminal that answers OSC queries, and
3// the e2e harness has no PTY.
4package tui
5
6import (
7	"math"
8	"testing"
9)
10
11func hexColor(t *testing.T, h string) rgb {
12	c := parseColors("\x1b]10;rgb:" + h[1:3] + "/" + h[3:5] + "/" + h[5:7] + "\x07").fg
13	if c == nil {
14		t.Fatalf("could not parse %s", h)
15	}
16	return *c
17}
18
19func TestParseColorReplies(t *testing.T) {
20	c := parseColors("\x1b]10;rgb:cdcd/d6d6/f4f4\x1b\\\x1b]11;rgb:1e/1e/2e\x07\x1b]4;4;rgb:8989/b4b4/fafa\x1b\\\x1b]4;5;rgb:f5f5/c2c2/e7e7\x1b\\\x1b[?62;22c")
21	if _, blue := c.palette[ansiBlue]; c.fg == nil || c.bg == nil || !blue || len(c.palette) != 2 {
22		t.Fatalf("missing colors: %+v", c)
23	}
24	if got := c.fg.hex(); got != "#cdd6f4" {
25		t.Fatalf("fg %s", got)
26	}
27	if got := c.bg.hex(); got != "#1e1e2e" {
28		t.Fatalf("bg %s", got)
29	}
30	if got := parseColors("\x1b[?62;22c"); got.fg != nil || got.bg != nil {
31		t.Fatal("a terminal that only answers device attributes reports no colors")
32	}
33}
34
35func TestInkHitsItsContrastTargets(t *testing.T) {
36	themes := map[string][2]string{
37		"mocha": {"#cdd6f4", "#1e1e2e"},
38		"latte": {"#4c4f69", "#eff1f5"},
39		"black": {"#ffffff", "#000000"},
40	}
41	for name, theme := range themes {
42		fg, bg := hexColor(t, theme[0]), hexColor(t, theme[1])
43		mixed, ok := mixInk(termColors{fg: &fg, bg: &bg})
44		if !ok {
45			t.Fatal("colors were reported")
46		}
47		for _, step := range []struct {
48			hex    string
49			target float64
50		}{{mixed.code, codeLc}, {mixed.secondary, secondaryLc}, {mixed.decor, decorLc}} {
51			if got := apca(hexColor(t, step.hex), bg); math.Abs(got-step.target) > 1.5 {
52				t.Errorf("%s: %s has Lc %.1f, want %v", name, step.hex, got, step.target)
53			}
54		}
55	}
56	soft, bg := hexColor(t, "#b3bbd7"), hexColor(t, "#1e1e2e")
57	if mixed, _ := mixInk(termColors{fg: &soft, bg: &bg}); mixed.code != soft.hex() {
58		t.Fatalf("text already under a target keeps its color, got %s", mixed.code)
59	}
60	black := hexColor(t, "#000000")
61	if _, ok := mixInk(termColors{fg: &black, bg: &bg}); ok {
62		t.Fatal("a text color barely apart from the background is not trusted")
63	}
64	if _, ok := mixInk(termColors{}); ok {
65		t.Fatal("no reply leaves the palette fallbacks in place")
66	}
67}
68
69func TestLanguageColorsReachTheirContrastOnEveryTheme(t *testing.T) {
70	colors := []string{"#00ADD8", "#3572A5", "#000080", "#f1e05a", "#555555", "#ffaff3"}
71	for _, hex := range languageHues {
72		colors = append(colors, hex)
73	}
74	for _, bgHex := range []string{"#1e1e2e", "#eff1f5", "#000000", "#ffffff"} {
75		bg := hexColor(t, bgHex)
76		for _, hex := range colors {
77			c := hexColor(t, hex)
78			got := legible(c, bg, languageLc)
79			if lc := apca(got, bg); lc < languageLc-1 {
80				t.Errorf("%s on %s became %s with Lc %.1f, want %d", hex, bgHex, got.hex(), lc, languageLc)
81			}
82			if apca(c, bg) >= languageLc && got != c {
83				t.Errorf("%s is legible on %s and should stay, got %s", hex, bgHex, got.hex())
84			}
85		}
86	}
87}