Files
8bitMyGoUI/pixel/palette_test.go
T

118 lines
3.1 KiB
Go

package pixel_test
import (
"math"
"slices"
"testing"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
func luminance(c ui.Color) float64 {
lin := func(v uint8) float64 {
s := float64(v) / 255
if s <= 0.04045 {
return s / 12.92
}
return math.Pow((s+0.055)/1.055, 2.4)
}
return 0.2126*lin(c.R) + 0.7152*lin(c.G) + 0.0722*lin(c.B)
}
func contrast(a, b ui.Color) float64 {
la, lb := luminance(a), luminance(b)
return (max(la, lb) + 0.05) / (min(la, lb) + 0.05)
}
func TestPaletteContrast(t *testing.T) {
rules := []struct {
fg, bg pixel.Slot
min float64
}{
{pixel.Ink, pixel.Face, 4.5},
{pixel.Ink, pixel.Back, 4.5},
{pixel.Ink, pixel.Well, 4.5},
{pixel.AccentInk, pixel.Accent, 4.5},
{pixel.Faint, pixel.Face, 3},
{pixel.Edge, pixel.Back, 3},
}
for _, p := range pixel.Palettes {
for _, r := range rules {
if got := contrast(p.C(r.fg), p.C(r.bg)); got < r.min {
t.Errorf("%s: slot %d on %d contrast %.2f, want ≥ %.1f", p.Name, r.fg, r.bg, got, r.min)
}
}
}
}
func TestPaletteColorsDistinctAndOpaque(t *testing.T) {
if len(pixel.Palettes) != 8 {
t.Fatalf("got %d palettes, want 8", len(pixel.Palettes))
}
for _, p := range pixel.Palettes {
seen := map[ui.Color]pixel.Slot{}
for s, c := range p.Colors {
if c.A != 255 {
t.Errorf("%s slot %d alpha %d", p.Name, s, c.A)
}
if prev, ok := seen[c]; ok {
t.Errorf("%s slots %d and %d share %v", p.Name, prev, s, c)
}
seen[c] = pixel.Slot(s)
}
}
}
func TestPaletteBevelOrder(t *testing.T) {
for _, p := range pixel.Palettes {
face := luminance(p.C(pixel.Face))
if luminance(p.C(pixel.Lit)) <= face || luminance(p.C(pixel.Shade)) >= face {
t.Errorf("%s: Lit must be lighter and Shade darker than Face", p.Name)
}
if p.Night != (luminance(p.C(pixel.Back)) < luminance(p.C(pixel.Ink))) {
t.Errorf("%s: night palettes are dark, day palettes light", p.Name)
}
}
}
func TestFind(t *testing.T) {
seen := map[*pixel.Palette]bool{}
for s := pixel.Spring; s <= pixel.Winter; s++ {
for _, night := range []bool{false, true} {
p := pixel.Find(s, night)
if p.Season != s || p.Night != night {
t.Errorf("Find(%d, %v) = %s", s, night, p.Name)
}
seen[p] = true
}
}
if len(seen) != 8 {
t.Fatalf("Find reached %d palettes, want 8", len(seen))
}
}
func TestCycle(t *testing.T) {
band := []pixel.Slot{pixel.Info, pixel.Accent, pixel.Lit}
cases := []struct {
step int
want []pixel.Slot
}{
{0, []pixel.Slot{pixel.Info, pixel.Accent, pixel.Lit}},
{1, []pixel.Slot{pixel.Accent, pixel.Lit, pixel.Info}},
{-1, []pixel.Slot{pixel.Lit, pixel.Info, pixel.Accent}},
{7, []pixel.Slot{pixel.Accent, pixel.Lit, pixel.Info}},
{math.MaxInt, []pixel.Slot{pixel.Accent, pixel.Lit, pixel.Info}},
{math.MinInt, []pixel.Slot{pixel.Accent, pixel.Lit, pixel.Info}},
}
for _, c := range cases {
if got := pixel.Cycle(band, c.step); !slices.Equal(got, c.want) {
t.Errorf("Cycle(step %d) = %v, want %v", c.step, got, c.want)
}
}
if !slices.Equal(band, []pixel.Slot{pixel.Info, pixel.Accent, pixel.Lit}) {
t.Fatalf("Cycle changed its input: %v", band)
}
}