import 'package:flutter_test/flutter_test.dart'; import 'package:melo/player/lrc.dart'; /// Der Songtext war eine Textwand ohne Bezug zur Wiedergabe. Diese Regeln /// machen aus einem LRC-Text mitlaufende Zeilen. void main() { group('parseLrc', () { test('liest Zeitmarke und Text', () { final zeilen = parseLrc('[00:12.50]Erste Zeile'); expect(zeilen, hasLength(1)); expect(zeilen.single.zeit, const Duration(seconds: 12, milliseconds: 500)); expect(zeilen.single.text, 'Erste Zeile'); }); test('versteht Hundertstel und Tausendstel', () { expect(parseLrc('[00:01.5]A').single.zeit, const Duration(seconds: 1, milliseconds: 500)); expect(parseLrc('[00:01.05]A').single.zeit, const Duration(seconds: 1, milliseconds: 50)); expect(parseLrc('[00:01.050]A').single.zeit, const Duration(seconds: 1, milliseconds: 50)); }); test('kommt auch ohne Bruchteile aus', () { expect(parseLrc('[01:30]A').single.zeit, const Duration(minutes: 1, seconds: 30)); }); test('sortiert nach Zeit, auch wenn die Datei es nicht tut', () { final zeilen = parseLrc('[00:20.00]Zwei\n[00:10.00]Eins'); expect(zeilen.map((z) => z.text), ['Eins', 'Zwei']); }); test('eine Zeile mit mehreren Marken erscheint mehrfach', () { // Im Refrain üblich: dieselbe Zeile für mehrere Stellen. final zeilen = parseLrc('[00:10.00][01:10.00]Refrain'); expect(zeilen, hasLength(2)); expect(zeilen.first.zeit, const Duration(seconds: 10)); expect(zeilen.last.zeit, const Duration(minutes: 1, seconds: 10)); }); test('überspringt Kopfzeilen wie [ar:] und [ti:]', () { final zeilen = parseLrc('[ar:Neoni]\n[ti:Roses]\n[00:05.00]Los'); expect(zeilen.map((z) => z.text), ['Los']); }); test('leere Textzeilen bleiben erhalten — sie sind Pausen', () { final zeilen = parseLrc('[00:05.00]A\n[00:08.00]\n[00:12.00]B'); expect(zeilen, hasLength(3)); expect(zeilen[1].text, isEmpty); }); test('gewöhnlicher Text ohne Marken ergibt nichts', () { expect(parseLrc('Nur eine Textwand\nohne Zeitangaben'), isEmpty); }); test('leerer Text ergibt nichts', () { expect(parseLrc(''), isEmpty); }); }); group('istSynchron', () { test('erkennt einen LRC-Text', () { expect(istSynchron('[00:12.50]Zeile'), isTrue); }); test('eine Textwand ist nicht synchron', () { expect(istSynchron('Roses are red\nViolets are blue'), isFalse); }); }); group('aktiveZeile', () { final zeilen = parseLrc('[00:00.00]Null\n[00:10.00]Zehn\n[00:20.00]Zwanzig'); test('vor der ersten Marke gilt die erste Zeile', () { expect(aktiveZeile(zeilen, Duration.zero), 0); }); test('genau auf einer Marke gilt diese Zeile', () { expect(aktiveZeile(zeilen, const Duration(seconds: 10)), 1); }); test('zwischen zwei Marken gilt die frühere', () { expect(aktiveZeile(zeilen, const Duration(seconds: 15)), 1); }); test('nach der letzten Marke gilt die letzte Zeile', () { expect(aktiveZeile(zeilen, const Duration(minutes: 5)), 2); }); test('ohne Zeilen gibt es keine aktive', () { expect(aktiveZeile(const [], Duration.zero), isNull); }); }); }