v2.48.5 — Tests für Player-Kern (Shuffle/Repeat/Queue/Sleep-Timer)

## test/player_service_test.dart (neu, 17 Tests)
- Warteschlange: setWarteschlange, unveränderliche Kopie, spieleAlsNaechstes,
  amEndeHinzufuegen, verschiebeInWarteschlange (Index-Anpassung)
- Repeat: aus (Ende → null), titel (immer aktuell), playlist (Wrap von vorn)
- Shuffle: alle anderen Songs genau einmal ohne Wiederholung, kein aktueller
  Song; shuffle+playlist mischt nach Durchlauf neu; Einzel-Song-Queue;
  Ausschluss des aktuellen Index nach Umsortieren
- Sleep-Timer: aktivieren, Restzeit, abbrechen, Ersetzen

flutter analyze 0 Issues, Tests 21/21
This commit is contained in:
Dustin
2026-08-04 16:27:36 +02:00
parent 82003c55d6
commit b97914699e
+187
View File
@@ -0,0 +1,187 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:melo_app/models/song.dart';
import 'package:melo_app/services/player_service.dart';
Song _song(int id, String titel) => Song(
id: id,
titel: titel,
kuenstler: 'Künstler $id',
dauerSekunden: 100,
dateiPfad: '',
);
void main() {
// PlayerService ist ein Singleton — Zustand vor jedem Test zurücksetzen
setUp(() {
final p = PlayerService();
p.setZufallsmodus(false);
p.setWiederholmodus(Wiederholmodus.aus);
p.cancelSleepTimer();
p.setWarteschlange([]);
});
group('Warteschlange', () {
test('setWarteschlange + Getter', () {
final p = PlayerService();
p.setWarteschlange([_song(1, 'A'), _song(2, 'B'), _song(3, 'C')]);
expect(p.warteschlange.length, 3);
expect(p.aktuellerIndex, 0);
expect(p.aktuellerSong?.titel, 'A');
});
test('warteschlange ist eine unveränderliche Kopie', () {
final p = PlayerService();
p.setWarteschlange([_song(1, 'A'), _song(2, 'B')]);
expect(() => p.warteschlange.add(_song(3, 'C')), throwsUnsupportedError);
expect(p.warteschlange.length, 2);
});
test('spieleAlsNaechstes fügt nach dem aktuellen Song ein', () {
final p = PlayerService();
p.setWarteschlange([_song(1, 'A'), _song(2, 'B'), _song(3, 'C')]);
p.spieleAlsNaechstes(_song(9, 'X'));
expect(p.warteschlange.map((s) => s.id).toList(), [1, 9, 2, 3]);
expect(p.aktuellerIndex, 0);
});
test('spieleAlsNaechstes ohne Warteschlange hängt an', () {
final p = PlayerService();
p.spieleAlsNaechstes(_song(1, 'A'));
expect(p.warteschlange.length, 1);
expect(p.aktuellerIndex, -1);
});
test('amEndeHinzufuegen hängt ans Ende an', () {
final p = PlayerService();
p.setWarteschlange([_song(1, 'A')]);
p.amEndeHinzufuegen(_song(2, 'B'));
expect(p.warteschlange.map((s) => s.id).toList(), [1, 2]);
});
test('verschiebeInWarteschlange passt aktuellen Index an (Song danach)', () {
final p = PlayerService();
p.setWarteschlange([_song(1, 'A'), _song(2, 'B'), _song(3, 'C')]);
p.verschiebeInWarteschlange(2, 0); // C nach vorne
expect(p.warteschlange.map((s) => s.id).toList(), [3, 1, 2]);
expect(p.aktuellerIndex, 1); // aktuell A (war 0, ist jetzt 1)
});
test('verschiebeInWarteschlange passt aktuellen Index an (Song davor)', () {
final p = PlayerService();
p.setWarteschlange([_song(1, 'A'), _song(2, 'B'), _song(3, 'C')]);
p.verschiebeInWarteschlange(0, 2); // aktueller Song selbst wandert
expect(p.warteschlange.map((s) => s.id).toList(), [2, 3, 1]);
expect(p.aktuellerIndex, 2);
});
});
group('Repeat (Wiederholmodus)', () {
test('aus: naechsterIndex null am Ende der Warteschlange', () {
final p = PlayerService();
p.setWarteschlange([_song(1, 'A'), _song(2, 'B'), _song(3, 'C')], startIndex: 2);
p.setWiederholmodus(Wiederholmodus.aus);
expect(p.naechsterIndex(), isNull);
});
test('titel: immer der aktuelle Song', () {
final p = PlayerService();
p.setWarteschlange([_song(1, 'A'), _song(2, 'B'), _song(3, 'C')], startIndex: 1);
p.setWiederholmodus(Wiederholmodus.titel);
expect(p.naechsterIndex(), 1);
expect(p.naechsterIndex(), 1);
});
test('playlist: nach dem letzten Song wieder von vorn', () {
final p = PlayerService();
p.setWarteschlange([_song(1, 'A'), _song(2, 'B'), _song(3, 'C')]);
p.setWiederholmodus(Wiederholmodus.playlist);
// Mittendrin: nächster ist der Folgesong
expect(p.naechsterIndex(), 1);
// Am Ende (letzter Song): wieder von vorn
p.setWarteschlange([_song(1, 'A'), _song(2, 'B'), _song(3, 'C')], startIndex: 2);
expect(p.naechsterIndex(), 0);
});
});
group('Shuffle (Zufallsmodus)', () {
test('spielt alle anderen Songs genau einmal, ohne den aktuellen', () {
final p = PlayerService();
p.setWarteschlange([for (var i = 1; i <= 5; i++) _song(i, 'S$i')]);
p.setZufallsmodus(true);
final folge = <int>[];
int? idx;
while ((idx = p.naechsterIndex()) != null) {
folge.add(idx!);
if (folge.length > 10) break; // Schutz vor Endlosschleife
}
expect(folge.length, 4); // 5 Songs aktueller
expect(folge.toSet(), {1, 2, 3, 4}); // keine Wiederholung, kein aktueller
});
test('shuffle + playlist: mischt nach Durchlauf neu weiter', () {
final p = PlayerService();
p.setWarteschlange([for (var i = 1; i <= 5; i++) _song(i, 'S$i')]);
p.setZufallsmodus(true);
p.setWiederholmodus(Wiederholmodus.playlist);
final folge = <int>[];
for (var i = 0; i < 12; i++) {
final idx = p.naechsterIndex();
expect(idx, isNotNull);
folge.add(idx!);
}
// Nie der aktuelle Song (Index 0), alle anderen kommen vor
expect(folge.every((i) => i >= 1 && i <= 4), isTrue);
expect(folge.toSet(), {1, 2, 3, 4});
});
test('shuffle mit einzelner Warteschlange: playlist wiederholt den Song', () {
final p = PlayerService();
p.setWarteschlange([_song(1, 'A')]);
p.setZufallsmodus(true);
p.setWiederholmodus(Wiederholmodus.playlist);
expect(p.naechsterIndex(), 0);
expect(p.naechsterIndex(), 0);
});
test('shuffle schließt neuen aktuellen Index nach Umsortieren aus', () {
final p = PlayerService();
p.setWarteschlange([for (var i = 1; i <= 5; i++) _song(i, 'S$i')]);
p.setZufallsmodus(true);
p.verschiebeInWarteschlange(0, 2); // aktueller Song wandert auf Index 2
expect(p.aktuellerIndex, 2);
final folge = <int>[];
int? idx;
while ((idx = p.naechsterIndex()) != null) {
folge.add(idx!);
if (folge.length > 10) break;
}
expect(folge.toSet(), {0, 1, 3, 4}); // Index 2 (aktuell) nie dabei
});
});
group('Sleep-Timer', () {
test('setSleepTimer aktiviert + Restzeit vorhanden', () {
final p = PlayerService();
p.setSleepTimer(const Duration(minutes: 30));
expect(p.sleepTimerAktiv, isTrue);
expect(p.sleepRestzeit, isNotNull);
expect(p.sleepRestzeit!.inMinutes, greaterThanOrEqualTo(29));
});
test('cancelSleepTimer deaktiviert', () {
final p = PlayerService();
p.setSleepTimer(const Duration(minutes: 15));
p.cancelSleepTimer();
expect(p.sleepTimerAktiv, isFalse);
expect(p.sleepRestzeit, isNull);
});
test('setSleepTimer ersetzt einen laufenden Timer', () {
final p = PlayerService();
p.setSleepTimer(const Duration(minutes: 15));
p.setSleepTimer(const Duration(minutes: 60));
expect(p.sleepTimerAktiv, isTrue);
expect(p.sleepRestzeit!.inMinutes, greaterThanOrEqualTo(59));
});
});
}