import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:melo/player/cover_farbe.dart'; import 'package:melo/shared/theme.dart'; /// Baut ein RGBA-Bild aus Farben — je Farbe [wiederholungen] Bildpunkte. Uint8List _bild(List<(Color, int)> teile) { final bytes = []; for (final (farbe, anzahl) in teile) { for (var i = 0; i < anzahl; i++) { bytes.addAll([ (farbe.r * 255).round(), (farbe.g * 255).round(), (farbe.b * 255).round(), (farbe.a * 255).round(), ]); } } return Uint8List.fromList(bytes); } /// Der Player war für jeden Titel gleich schwarz. Diese Regel holt die /// prägende Farbe aus dem Coverbild. void main() { group('dominanteFarbe', () { test('ein einfarbiges Bild ergibt seine Farbe', () { const rot = Color(0xFFD32F2F); final farbe = dominanteFarbe(_bild([(rot, 100)])); // Grob gerundet — die Farben werden in Töpfe einsortiert. expect((farbe.r * 255).round(), closeTo(211, 16)); expect((farbe.g * 255).round(), closeTo(47, 16)); expect((farbe.b * 255).round(), closeTo(47, 16)); }); test('eine kräftige Farbe schlägt viel Grau', () { // Cover sind oft überwiegend dunkel oder grau. Die häufigste Farbe wäre // dann ein Grauton, und der Player sähe für jeden Titel gleich aus. final farbe = dominanteFarbe(_bild([ (const Color(0xFF808080), 900), (const Color(0xFF00A0FF), 100), ])); expect(farbe.b, greaterThan(farbe.r)); }); test('ein rein schwarzes Bild fällt auf die Markenfarbe zurück', () { expect(dominanteFarbe(_bild([(const Color(0xFF000000), 100)])), MeloTheme.red); }); test('ein rein weißes Bild fällt ebenfalls zurück', () { expect(dominanteFarbe(_bild([(const Color(0xFFFFFFFF), 100)])), MeloTheme.red); }); test('durchsichtige Bildpunkte zählen nicht mit', () { final farbe = dominanteFarbe(_bild([ (const Color(0x0000FF00), 900), (const Color(0xFFD32F2F), 100), ])); expect(farbe.r, greaterThan(farbe.g)); }); test('ein leeres Bild fällt auf die Markenfarbe zurück', () { expect(dominanteFarbe(Uint8List(0)), MeloTheme.red); }); test('unvollständige Bildpunkte am Ende stören nicht', () { // Abgeschnittene Daten dürfen keinen Bereichsfehler auslösen. final bytes = _bild([(const Color(0xFFD32F2F), 10)]).toList() ..addAll([12, 34]); expect(() => dominanteFarbe(Uint8List.fromList(bytes)), returnsNormally); }); }); group('lesbarAuf', () { test('dunkle Farben bleiben dunkel genug für weiße Schrift', () { final farbe = lesbarAuf(const Color(0xFFFFEB3B)); // Ein knalliges Gelb muss abgedunkelt werden, sonst ist weiße Schrift // darauf nicht mehr lesbar. expect(farbe.computeLuminance(), lessThan(0.35)); }); test('eine bereits dunkle Farbe bleibt weitgehend erhalten', () { const dunkel = Color(0xFF1A237E); expect(lesbarAuf(dunkel).computeLuminance(), closeTo(dunkel.computeLuminance(), 0.1)); }); }); }