Akkusymbol folgt dem Ladestand

Ein Symbol, das bei 5 % genauso aussieht wie bei 95 %, ist Dekoration und
keine Anzeige. Wer in der Menüleiste nur das Glyph stehen hat, will es ohne
die Zahl daneben lesen können.

Fünf Füllstufen, gerundet zur nächsten: 60 % soll nicht halbleer aussehen
und 40 % nicht dreiviertel voll.

Beim Bauen fiel auf, dass die naheliegende Lösung nicht geht. Die erste
Fassung hängte beim Laden ein „.bolt" an den Füllstand — von diesen
kombinierten Symbolen existiert aber allein `battery.100percent.bolt`, und
`battery.slash` gibt es auch nicht. Die übrigen wären leere Felder in der
Menüleiste gewesen. Nachgemessen statt angenommen, und ein Test prüft jetzt
für jeden erzeugten Namen, dass es ihn wirklich gibt.

Den Füllstand beim Laden gegen einen Blitz einzutauschen wäre die falsche
Wahl gewesen: dass geladen wird, sieht man am Netzstecker; wie weit es ist,
sieht man nirgends sonst. Also bleibt der Füllstand, und der Blitz kommt als
kleines Zeichen darüber.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-08-11 13:30:13 +02:00
parent 667fb61c96
commit 4b5e86b3f4
5 changed files with 1434 additions and 1308 deletions

View File

@@ -0,0 +1,52 @@
import Testing
import AppKit
@testable import MetricsProvider
// Ein Akkusymbol, das immer gleich aussieht, ist Dekoration und keine
// Anzeige es beantwortet die eine Frage nicht, für die es da ist.
@Suite("Akkusymbol")
struct BatterySymbolTests {
@Test("Der Füllstand steht im Symbol")
func fillFollowsCharge() {
#expect(BatterySymbol.name(charge: 0.05) == "battery.0percent")
#expect(BatterySymbol.name(charge: 0.25) == "battery.25percent")
#expect(BatterySymbol.name(charge: 0.50) == "battery.50percent")
#expect(BatterySymbol.name(charge: 0.75) == "battery.75percent")
#expect(BatterySymbol.name(charge: 1.00) == "battery.100percent")
}
@Test("Die Stufen liegen zwischen den Marken, nicht darauf")
func stepsRoundToNearest() {
// 60 % soll nicht als halbleer erscheinen, 40 % nicht als dreiviertel
// voll. Gerundet wird zur nächsten Stufe.
#expect(BatterySymbol.name(charge: 0.60) == "battery.50percent")
#expect(BatterySymbol.name(charge: 0.64) == "battery.75percent")
#expect(BatterySymbol.name(charge: 0.40) == "battery.50percent")
}
@Test("Leer und voll bleiben in ihren Grenzen")
func extremesAreClamped() {
// Ein Messfehler darf keinen Symbolnamen erzeugen, den es nicht gibt.
#expect(BatterySymbol.name(charge: -1) == "battery.0percent")
#expect(BatterySymbol.name(charge: 5) == "battery.100percent")
}
@Test("Jeder erzeugte Name existiert auch wirklich")
func everyNameResolves() {
// Der Grund für diesen Test: die erste Fassung setzte beim Laden ein
// .bolt" an den Füllstand an. Von diesen kombinierten Symbolen gibt es
// aber nur `battery.100percent.bolt` der Rest wäre ein leeres Feld
// in der Menüleiste gewesen.
for percent in stride(from: 0.0, through: 1.0, by: 0.05) {
let name = BatterySymbol.name(charge: percent)
#expect(NSImage(systemSymbolName: name, accessibilityDescription: nil) != nil,
"\(name) gibt es nicht")
}
#expect(NSImage(systemSymbolName: BatterySymbol.unavailable,
accessibilityDescription: nil) != nil)
#expect(NSImage(systemSymbolName: BatterySymbol.chargingBolt,
accessibilityDescription: nil) != nil)
}
}