0.2.76 - Icon Packs
This commit is contained in:
@@ -77,7 +77,7 @@ func TestEasterWindowIsTheLongWeekend(t *testing.T) {
|
||||
// A season is the one thing on this feature nobody on a television can decline, so the
|
||||
// tests that matter most are the ones asserting that no argument suppresses it.
|
||||
func TestSeasonOutranksTheViewer(t *testing.T) {
|
||||
resolved := resolveTheme(themePlum, nil, true, true, date(2026, time.December, 20))
|
||||
resolved := resolveTheme(themePlum, "", nil, true, true, date(2026, time.December, 20))
|
||||
if resolved.ID != themeChristmas {
|
||||
t.Fatalf("applied theme = %q, want %q", resolved.ID, themeChristmas)
|
||||
}
|
||||
@@ -98,7 +98,7 @@ func TestSeasonOutranksTheViewer(t *testing.T) {
|
||||
// grantable per person, and an operator restricting a viewer to one palette must not be a
|
||||
// way of exempting them from Christmas.
|
||||
func TestAllowlistDoesNotApplyToSeasons(t *testing.T) {
|
||||
resolved := resolveTheme(themePlum, []string{themeEmber}, true, true, date(2026, time.October, 31))
|
||||
resolved := resolveTheme(themePlum, "", []string{themeEmber}, true, true, date(2026, time.October, 31))
|
||||
if resolved.ID != themeHalloween {
|
||||
t.Fatalf("applied theme = %q, want %q", resolved.ID, themeHalloween)
|
||||
}
|
||||
@@ -110,7 +110,7 @@ func TestAllowlistDoesNotApplyToSeasons(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSeasonsOffLeavesTheViewersChoice(t *testing.T) {
|
||||
resolved := resolveTheme(themeEmber, nil, false, true, date(2026, time.December, 20))
|
||||
resolved := resolveTheme(themeEmber, "", nil, false, true, date(2026, time.December, 20))
|
||||
if resolved.ID != themeEmber {
|
||||
t.Fatalf("applied theme = %q, want %q", resolved.ID, themeEmber)
|
||||
}
|
||||
@@ -138,7 +138,7 @@ func TestResolveThemeFallbacks(t *testing.T) {
|
||||
}
|
||||
for _, testCase := range cases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
got := resolveTheme(testCase.chosen, testCase.allowed, true, true, ordinary)
|
||||
got := resolveTheme(testCase.chosen, "", testCase.allowed, true, true, ordinary)
|
||||
if got.ID != testCase.want {
|
||||
t.Fatalf("resolveTheme(%q, %v) = %q, want %q",
|
||||
testCase.chosen, testCase.allowed, got.ID, testCase.want)
|
||||
@@ -150,14 +150,14 @@ func TestResolveThemeFallbacks(t *testing.T) {
|
||||
// The revision is the whole delivery mechanism: a television refetches the palette only when
|
||||
// this moves. If it did not move when a season began, no set in the house would repaint.
|
||||
func TestThemeRevisionTracksTheAnswer(t *testing.T) {
|
||||
ordinary := resolveTheme(themePlum, nil, true, true, date(2026, time.June, 14))
|
||||
christmas := resolveTheme(themePlum, nil, true, true, date(2026, time.December, 20))
|
||||
ordinary := resolveTheme(themePlum, "", nil, true, true, date(2026, time.June, 14))
|
||||
christmas := resolveTheme(themePlum, "", nil, true, true, date(2026, time.December, 20))
|
||||
if ordinary.Revision == christmas.Revision {
|
||||
t.Fatal("the revision must change when the season does, or nothing refetches")
|
||||
}
|
||||
// And it must be stable, or every poll would look like a change and every set would
|
||||
// fetch the palette six times a minute.
|
||||
again := resolveTheme(themePlum, nil, true, true, date(2026, time.June, 15))
|
||||
again := resolveTheme(themePlum, "", nil, true, true, date(2026, time.June, 15))
|
||||
if ordinary.Revision != again.Revision {
|
||||
t.Fatalf("the revision moved on an ordinary day: %s then %s", ordinary.Revision, again.Revision)
|
||||
}
|
||||
@@ -168,10 +168,10 @@ func TestThemeRevisionTracksTheAnswer(t *testing.T) {
|
||||
// every day of the year.
|
||||
func TestDecorationsAreSeasonalAndSeparatelySwitchable(t *testing.T) {
|
||||
christmas := date(2026, time.December, 20)
|
||||
if got := resolveTheme(themePlum, nil, true, true, christmas); got.Decoration != decorationSnow {
|
||||
if got := resolveTheme(themePlum, "", nil, true, true, christmas); got.Decoration != decorationSnow {
|
||||
t.Fatalf("decoration = %q, want %q", got.Decoration, decorationSnow)
|
||||
}
|
||||
off := resolveTheme(themePlum, nil, true, false, christmas)
|
||||
off := resolveTheme(themePlum, "", nil, true, false, christmas)
|
||||
if off.Decoration != "" {
|
||||
t.Fatalf("decorations off still returned %q", off.Decoration)
|
||||
}
|
||||
@@ -179,10 +179,10 @@ func TestDecorationsAreSeasonalAndSeparatelySwitchable(t *testing.T) {
|
||||
t.Fatal("turning decorations off must keep the seasonal palette")
|
||||
}
|
||||
// The revision has to move, or a set already snowing is never told to stop.
|
||||
if off.Revision == resolveTheme(themePlum, nil, true, true, christmas).Revision {
|
||||
if off.Revision == resolveTheme(themePlum, "", nil, true, true, christmas).Revision {
|
||||
t.Fatal("the revision must change when the decoration does")
|
||||
}
|
||||
ordinary := resolveTheme(themePlum, nil, true, true, date(2026, time.June, 14))
|
||||
ordinary := resolveTheme(themePlum, "", nil, true, true, date(2026, time.June, 14))
|
||||
if ordinary.Decoration != "" {
|
||||
t.Fatalf("a chosen theme carries a decoration: %q", ordinary.Decoration)
|
||||
}
|
||||
@@ -277,3 +277,88 @@ func TestNormalizeThemeAllowlist(t *testing.T) {
|
||||
t.Fatalf("allowlist is not in catalogue order: %v", ordered)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Icon packs ---------------------------------------------------------------------------
|
||||
|
||||
// The gateway's half of the wire contract with ui/theme/MembyIconPacks.kt. Its own
|
||||
// MembyIconPackTest pins the same three slugs from the television's end; change one
|
||||
// without the other and a household is sent marks nothing can draw.
|
||||
func TestIconPackOptionsAreTheKnownPacks(t *testing.T) {
|
||||
for _, option := range iconPackOptions() {
|
||||
if !knownIconPack(option.Value) {
|
||||
t.Fatalf("offered icon pack %q is not one the resolver will accept", option.Value)
|
||||
}
|
||||
}
|
||||
if !knownIconPack(defaultIconPackID) {
|
||||
t.Fatalf("the default icon pack %q is not selectable", defaultIconPackID)
|
||||
}
|
||||
}
|
||||
|
||||
// A slug the resolver does not recognise must never reach a television. The set would fall
|
||||
// back on its own, but a gateway echoing a retired pack would make every screen in the
|
||||
// house look wrong in the same way while reporting it had done what it was asked.
|
||||
func TestUnknownIconPackFallsBackToTheShippedMarks(t *testing.T) {
|
||||
ordinary := time.Date(2026, time.May, 3, 12, 0, 0, 0, time.UTC)
|
||||
for _, chosen := range []string{"", "tabler", "material "} {
|
||||
got := resolveTheme(themeMidnight, chosen, nil, true, true, ordinary)
|
||||
if got.IconSet != defaultIconPackID {
|
||||
t.Fatalf("resolveTheme(icon %q) = %q, want %q", chosen, got.IconSet, defaultIconPackID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The viewer's marks stand on every scheme they can choose, and only a season may replace
|
||||
// them — the same asymmetry decorations have, and for the same reason: a season is a look,
|
||||
// where a scheme somebody picked to live with all year must not silently take their marks
|
||||
// away with no way to tell which choice did it.
|
||||
func TestOnlyASeasonMayReplaceTheViewersMarks(t *testing.T) {
|
||||
ordinary := time.Date(2026, time.May, 3, 12, 0, 0, 0, time.UTC)
|
||||
if got := resolveTheme(themePlum, iconPackLucide, nil, true, true, ordinary); got.IconSet != iconPackLucide {
|
||||
t.Fatalf("an ordinary day = %q, want the viewer's %q", got.IconSet, iconPackLucide)
|
||||
}
|
||||
|
||||
halloween := time.Date(2026, time.October, 30, 20, 0, 0, 0, time.UTC)
|
||||
got := resolveTheme(themePlum, iconPackLucide, nil, true, true, halloween)
|
||||
if !got.Seasonal {
|
||||
t.Fatalf("expected the Halloween window to be seasonal")
|
||||
}
|
||||
if got.IconSet != iconPackFontAwesome {
|
||||
t.Fatalf("Halloween = %q, want its own %q", got.IconSet, iconPackFontAwesome)
|
||||
}
|
||||
if got.Chosen != themePlum {
|
||||
t.Fatalf("the viewer's own choice should survive underneath a season, got %q", got.Chosen)
|
||||
}
|
||||
|
||||
// Seasons off: the viewer keeps both halves.
|
||||
if off := resolveTheme(themePlum, iconPackLucide, nil, false, true, halloween); off.IconSet != iconPackLucide {
|
||||
t.Fatalf("with seasons off = %q, want the viewer's %q", off.IconSet, iconPackLucide)
|
||||
}
|
||||
}
|
||||
|
||||
// Only seasonal themes may declare marks of their own, the rule that holds the asymmetry
|
||||
// above in place by construction rather than by a check at the point of use.
|
||||
func TestOnlySeasonalThemesDeclareAnIconSet(t *testing.T) {
|
||||
for _, theme := range themeCatalogue {
|
||||
if theme.Seasonal {
|
||||
if theme.IconSet != "" && !knownIconPack(theme.IconSet) {
|
||||
t.Fatalf("%s names an icon pack %q nothing can draw", theme.ID, theme.IconSet)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if theme.IconSet != "" {
|
||||
t.Fatalf("selectable theme %s takes the viewer's marks away", theme.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The revision is the entire delivery mechanism: televisions compare it and refetch only
|
||||
// when it moves. A pack change the hash did not notice would reach nobody until something
|
||||
// else about the theme happened to change.
|
||||
func TestThemeRevisionTracksTheIconSet(t *testing.T) {
|
||||
ordinary := time.Date(2026, time.May, 3, 12, 0, 0, 0, time.UTC)
|
||||
material := resolveTheme(themeMidnight, iconPackMaterial, nil, true, true, ordinary)
|
||||
lucide := resolveTheme(themeMidnight, iconPackLucide, nil, true, true, ordinary)
|
||||
if material.Revision == lucide.Revision {
|
||||
t.Fatalf("the same revision %q for two different icon packs", material.Revision)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user