Bisher landete Musik nur zufaellig auf dem Geraet: was man abspielte, blieb nebenbei liegen. Gezielt "dieses Album fuer die Zugfahrt" gab es nicht. Und dieser Zwischenspeicher wuchs unbegrenzt — es gab nur "alles loeschen". Zwei getrennte Ablagen. Bewusste Downloads liegen NICHT im Cache-Ordner: den darf Android bei Speichernot jederzeit selbst leeren, und genau das darf einem mitgenommenen Album nicht passieren. Sie liegen deshalb neben der Datenbank im Support-Ordner. Die Grenze gilt nur fuer den Zufalls-Cache; Downloads sind unbegrenzt und werden nie verdraengt. - Download-Pfeil je Album und Kuenstler, mit Fortschritt und Abbruch - Rueckfrage ab 30 Titeln samt Groessenschaetzung - Obergrenze wahlbar (aus / 512 MB / 1 / 2 / 4 / 8 GB), Standard 2 GB; am laengsten nicht Gehoertes weicht zuerst - Liste "Heruntergeladen" mit Titel, Kuenstler, Groesse, einzeln oder alle entfernbar (Schema 10) - Wiedergabe nimmt erst den Download, dann den Zwischenspeicher Aus dem Code-Review nachgebessert, vier ernste Punkte: "aus" (0 MB) schaltete das Aufraeumen ab statt das Zwischenspeichern und liess den Cache unbegrenzt wachsen; wer sich erst waehrend der Sitzung am Server anmeldete, konnte gar nichts laden; die Verdraengung konnte Dateien loeschen, die die laufende Warteschlange schon als Quelle eingetragen hatte (Abbruch mitten im Album, ohne Rueckfall aufs Streamen); und ein heruntergeladener Titel wurde beim Abspielen ein zweites Mal in den Cache geholt. Dazu sechs kleinere und der "zuletzt gehoert"-Stempel, der beim Aufbau der Warteschlange statt beim Abspielen gesetzt wurde. Nebenbei aufgeraeumt, weil die Dateien ohnehin offen waren: keine Emoji als Knoepfe mehr, kein Colors.redAccent und kein grey.shade mehr im ganzen Projekt, und die Einstellungen heissen nicht mehr "Settings". 433 Tests gruen (vorher 411), flutter analyze ohne Befund, Release-APK gebaut. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xAHJTJM6UUqmjUgk1PUEd
117 lines
3.5 KiB
Dart
117 lines
3.5 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:melo/services/cache_manager.dart';
|
||
|
||
CacheEintrag _e(String pfad, int groesse, int geaendertMs) =>
|
||
CacheEintrag(pfad: pfad, groesse: groesse, geaendertMs: geaendertMs);
|
||
|
||
/// Der Zwischenspeicher wuchs bisher unbegrenzt — es gab nur „alles löschen".
|
||
/// Diese Regel entscheidet, was wegmuss: am längsten nicht gehört zuerst.
|
||
void main() {
|
||
group('zuVerdraengen', () {
|
||
test('unter der Grenze fällt nichts weg', () {
|
||
final weg = zuVerdraengen([_e('a', 100, 1), _e('b', 100, 2)], 500);
|
||
|
||
expect(weg, isEmpty);
|
||
});
|
||
|
||
test('genau auf der Grenze fällt nichts weg', () {
|
||
final weg = zuVerdraengen([_e('a', 200, 1), _e('b', 300, 2)], 500);
|
||
|
||
expect(weg, isEmpty);
|
||
});
|
||
|
||
test('der älteste Eintrag geht zuerst', () {
|
||
// 3 × 200 = 600 bei Grenze 500: einer muss weg, der mit dem
|
||
// kleinsten Zeitstempel.
|
||
final weg = zuVerdraengen(
|
||
[_e('neu', 200, 30), _e('alt', 200, 10), _e('mittel', 200, 20)],
|
||
500,
|
||
);
|
||
|
||
expect(weg, ['alt']);
|
||
});
|
||
|
||
test('es fällt so viel weg, bis die Grenze eingehalten ist', () {
|
||
// 4 × 200 = 800 bei Grenze 300: zwei reichen nicht, drei müssen weg.
|
||
final weg = zuVerdraengen(
|
||
[_e('a', 200, 1), _e('b', 200, 2), _e('c', 200, 3), _e('d', 200, 4)],
|
||
300,
|
||
);
|
||
|
||
expect(weg, ['a', 'b', 'c']);
|
||
});
|
||
|
||
test('Grenze 0 räumt alles weg', () {
|
||
final weg = zuVerdraengen([_e('a', 1, 1), _e('b', 1, 2)], 0);
|
||
|
||
expect(weg, ['a', 'b']);
|
||
});
|
||
|
||
test('eine negative Grenze wird wie 0 behandelt', () {
|
||
expect(zuVerdraengen([_e('a', 1, 1)], -5), ['a']);
|
||
});
|
||
|
||
test('bei gleichem Zeitstempel entscheidet der Pfad — nie zufällig', () {
|
||
final weg = zuVerdraengen([_e('b', 200, 7), _e('a', 200, 7)], 200);
|
||
|
||
expect(weg, ['a']);
|
||
});
|
||
|
||
test('ein leerer Zwischenspeicher ergibt nichts zu tun', () {
|
||
expect(zuVerdraengen(const [], 100), isEmpty);
|
||
});
|
||
|
||
test('lässt die Ausgangsliste unverändert', () {
|
||
final eintraege = [_e('b', 200, 2), _e('a', 200, 1)];
|
||
|
||
zuVerdraengen(eintraege, 100);
|
||
|
||
expect(eintraege.map((e) => e.pfad), ['b', 'a']);
|
||
});
|
||
});
|
||
|
||
group('geschützte Dateien', () {
|
||
test('eine benutzte Datei bleibt liegen, auch wenn sie die älteste ist', () {
|
||
// Der schwerste Fall: die Warteschlange hat den Pfad schon fest als
|
||
// Quelle eingetragen. Wird die Datei gelöscht, bricht die Wiedergabe
|
||
// mitten im Album ab — ohne Rückfall aufs Streamen.
|
||
final weg = zuVerdraengen(
|
||
[_e('laeuft', 200, 1), _e('alt', 200, 2), _e('neu', 200, 3)],
|
||
200,
|
||
geschuetzt: {'laeuft'},
|
||
);
|
||
|
||
expect(weg, isNot(contains('laeuft')));
|
||
expect(weg, ['alt', 'neu']);
|
||
});
|
||
|
||
test('lieber über der Grenze bleiben als die Wiedergabe abwürgen', () {
|
||
// Alles geschützt: dann wird eben nichts frei. Die Grenze greift beim
|
||
// nächsten Mal wieder.
|
||
final weg = zuVerdraengen(
|
||
[_e('a', 500, 1), _e('b', 500, 2)],
|
||
100,
|
||
geschuetzt: {'a', 'b'},
|
||
);
|
||
|
||
expect(weg, isEmpty);
|
||
});
|
||
|
||
test('ohne Schutzliste verhält es sich wie zuvor', () {
|
||
final weg = zuVerdraengen([_e('a', 200, 1), _e('b', 200, 2)], 200);
|
||
|
||
expect(weg, ['a']);
|
||
});
|
||
|
||
test('ein geschützter Pfad, den es gar nicht gibt, stört nicht', () {
|
||
final weg = zuVerdraengen(
|
||
[_e('a', 200, 1), _e('b', 200, 2)],
|
||
200,
|
||
geschuetzt: {'gibtesnicht'},
|
||
);
|
||
|
||
expect(weg, ['a']);
|
||
});
|
||
});
|
||
}
|