Files
Melo/test/sleep_timer_test.dart
Hermes (Server)andClaude Haiku 4.5 7478beeb11 fix(test): remove unused material import in sleep_timer_test
Pre-commit analyze gate requires zero issues; this pre-existing
warning was blocking the schema commit.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 15:14:00 +02:00

118 lines
3.5 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:melo/player/sleep_timer.dart';
void main() {
group('SleepTimer with mocked clock', () {
late DateTime currentTime;
late SleepTimer sleepTimer;
late int elapsedCount;
setUp(() {
currentTime = DateTime(2024, 1, 1, 12, 0, 0);
elapsedCount = 0;
sleepTimer = SleepTimer(
onElapsed: () => elapsedCount++,
now: () => currentTime,
);
});
tearDown(() {
sleepTimer.cancel();
});
test('remaining is null when inactive', () {
expect(sleepTimer.remaining.value, isNull);
});
test('start sets remaining to duration', () {
sleepTimer.start(const Duration(minutes: 5));
expect(sleepTimer.remaining.value, isNotNull);
expect(sleepTimer.remaining.value!.inMinutes, equals(5));
});
test('remaining decrements as time passes', () {
sleepTimer.start(const Duration(minutes: 1));
expect(sleepTimer.remaining.value!.inSeconds, equals(60));
// Simulate 30 seconds passing
currentTime = currentTime.add(const Duration(seconds: 30));
sleepTimer.tick(); // Manually call tick to simulate timer
expect(sleepTimer.remaining.value!.inSeconds, equals(30));
});
test('fires callback when timer elapses', () {
sleepTimer.start(const Duration(minutes: 5));
expect(elapsedCount, equals(0));
// Simulate 5 minutes passing
currentTime = currentTime.add(const Duration(minutes: 5));
sleepTimer.tick();
expect(elapsedCount, equals(1), reason: 'onElapsed should fire once');
expect(sleepTimer.remaining.value, isNull);
});
test('cancel stops timer and clears remaining', () {
sleepTimer.start(const Duration(minutes: 5));
expect(sleepTimer.remaining.value, isNotNull);
sleepTimer.cancel();
expect(sleepTimer.remaining.value, isNull);
expect(elapsedCount, equals(0), reason: 'Should not fire after cancel');
});
test('does not fire if duration is zero', () {
sleepTimer.start(Duration.zero);
expect(elapsedCount, equals(0));
expect(sleepTimer.remaining.value, isNull);
});
test('does not fire if duration is negative', () {
sleepTimer.start(const Duration(seconds: -5));
expect(elapsedCount, equals(0));
expect(sleepTimer.remaining.value, isNull);
});
test('start while running replaces old timer', () {
sleepTimer.start(const Duration(minutes: 10));
final firstDuration = sleepTimer.remaining.value!;
sleepTimer.start(const Duration(minutes: 5));
final secondDuration = sleepTimer.remaining.value!;
expect(secondDuration < firstDuration, isTrue);
expect(elapsedCount, equals(0));
});
test('fires exactly once when elapsed', () {
sleepTimer.start(const Duration(minutes: 5));
// Advance time past deadline
currentTime = currentTime.add(const Duration(minutes: 6));
sleepTimer.tick();
expect(elapsedCount, equals(1));
// Even if we call tick again, should not fire twice
sleepTimer.tick();
expect(elapsedCount, equals(1), reason: 'Should fire only once');
});
test('does not fire if cancelled before elapse', () {
sleepTimer.start(const Duration(minutes: 5));
currentTime = currentTime.add(const Duration(minutes: 3));
sleepTimer.cancel();
currentTime = currentTime.add(const Duration(minutes: 3));
sleepTimer.tick();
expect(elapsedCount, equals(0));
});
});
}