import Testing @testable import OnyxHelperProtocol // Gemessen am 11.08.2026 auf Mac17,9, rein lesend, gegen macOS' eigene // Ladebegrenzung: // // Limit 80 % → CHLT = 50 01 00 // Limit 95 % → CHLT = 5F 01 00 // Limit 85 % → CHLT = 55 01 00 // // Erstes Byte ist die Prozentzahl, zweites offenbar „Limit aktiv", drittes // unbenutzt. Was hier steht, ist gemessen — nicht abgeleitet. @Suite("Ladelimit — Kodierung") struct ChargeEncodingTests { @Test("Die drei gemessenen Werte werden richtig kodiert") func encodesMeasuredValues() { #expect(ChargeSafety.encode(80) == [0x50, 0x01, 0x00]) #expect(ChargeSafety.encode(95) == [0x5F, 0x01, 0x00]) #expect(ChargeSafety.encode(85) == [0x55, 0x01, 0x00]) } @Test("Und wieder gelesen ergeben sie dasselbe") func decodesMeasuredValues() { #expect(ChargeSafety.decode([0x50, 0x01, 0x00]) == 80) #expect(ChargeSafety.decode([0x5F, 0x01, 0x00]) == 95) #expect(ChargeSafety.decode([0x55, 0x01, 0x00]) == 85) } @Test("Ein abgeschaltetes Limit liest sich als kein Limit") func disabledReadsAsNoLimit() { // Zweites Byte auf null: macOS schreibt das zwar nicht selbst, aber // fremde Werkzeuge könnten es. Dann gilt kein Limit, egal was im // ersten Byte steht. #expect(ChargeSafety.decode([0x50, 0x00, 0x00]) == nil) } @Test("Zu kurze Daten ergeben kein Ergebnis statt eines falschen") func shortDataIsRefused() { #expect(ChargeSafety.decode([0x50]) == nil) #expect(ChargeSafety.decode([]) == nil) } @Test("Ein unsinniger Wert im Speicher wird nicht als Limit ausgegeben") func nonsenseIsRefused() { // 200 % ist kein Ladestand. Lieber „unbekannt" melden, als eine Zahl // anzuzeigen, die es nicht geben kann. #expect(ChargeSafety.decode([200, 0x01, 0x00]) == nil) } } @Suite("Ladelimit — Sicherheitsnetz") struct ChargeSafetyTests { @Test("Der gemessene Bereich wird eingehalten") func staysInMeasuredRange() { // macOS selbst lässt nur 80 bis 100 zu. Darunter ist ungemessenes // Gebiet, und im Lade-Subsystem wird nicht geraten: das ist die eine // Stelle in diesem Projekt, an der ein falscher Wert die Hardware // dauerhaft beschädigen kann. #expect(ChargeSafety.minimumPercent == 80) #expect(ChargeSafety.maximumPercent == 100) } @Test("Zu kleine Werte werden angehoben, nicht abgelehnt") func lowValuesAreClamped() { // Anheben statt ablehnen: ein Regler, der bei einer Fehleingabe gar // nichts tut, wirkt kaputt. #expect(ChargeSafety.clamp(50) == 80) #expect(ChargeSafety.clamp(0) == 80) #expect(ChargeSafety.clamp(-10) == 80) } @Test("Zu große Werte werden gedeckelt") func highValuesAreClamped() { #expect(ChargeSafety.clamp(120) == 100) #expect(ChargeSafety.clamp(255) == 100) } @Test("Werte im Bereich bleiben unverändert") func validValuesPass() { for percent in 80...100 { #expect(ChargeSafety.clamp(percent) == percent) } } @Test("Kein Limit heißt hundert Prozent") func noLimitIsHundred() { // Und nicht: das Aktiv-Byte auf null setzen. Diese Kodierung hat macOS // nie geschrieben, also ist sie ungemessen — hundert Prozent erreicht // dasselbe innerhalb dessen, was wir belegt haben. #expect(ChargeSafety.encode(ChargeSafety.noLimit) == [0x64, 0x01, 0x00]) #expect(ChargeSafety.noLimit == 100) } @Test("Geschriebene Bytes behalten immer die Form, die macOS benutzt") func writtenShapeIsAlwaysTheSame() { // Nur das erste Byte wird verändert. Alles andere ist ungemessen, und // ungemessene Bytes im Ladepfad werden nicht angefasst. for percent in [80, 85, 90, 95, 100] { let bytes = ChargeSafety.encode(percent) #expect(bytes.count == 3) #expect(bytes[1] == 0x01) #expect(bytes[2] == 0x00) } } @Test("Was geschrieben wurde, muss auch zurückgelesen werden können") func writeIsVerifiable() { // Der Helfer prüft nach dem Schreiben nach. Bleibt der Wert aus, hat // der SMC ihn abgelehnt — und das muss auffallen, statt als Erfolg // gemeldet zu werden. let bytes = ChargeSafety.encode(85) #expect(ChargeSafety.decode(bytes) == 85) } }