import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; import 'package:pluriwave/estado/estado_alarmas.dart'; import 'package:pluriwave/modelos/alarma_musical.dart'; import 'package:pluriwave/servicios/servicio_alarmas.dart'; import 'package:pluriwave/servicios/servicio_alarmas_android.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); setUp(() { SharedPreferences.setMockInitialValues({}); }); test('posponer persiste snooze y refrescarProgramacion no lo pisa', () async { var ahora = DateTime(2026, 5, 25, 7, 31); final android = FakePuertoAlarmasAndroid(); final estado = EstadoAlarmas( servicio: ServicioAlarmas(reloj: () => ahora), android: android, iniciarAutomaticamente: false, ); addTearDown(estado.dispose); addTearDown(android.dispose); await estado.guardarAlarma( const AlarmaMusical( id: 'a1', nombre: 'Diaria', hora: 7, minuto: 30, tipoProgramacion: TipoProgramacionAlarma.diaria, diasSemana: [], ), ); final alarma = estado.alarmas.single; await estado.posponerAlarma(alarma, 5); expect(estado.alarmas.single.snoozeHasta, DateTime(2026, 5, 25, 7, 36)); expect(android.programadas.last.proximaProgramable, DateTime(2026, 5, 25, 7, 36)); ahora = DateTime(2026, 5, 25, 7, 32); await estado.refrescarProgramacion(); expect(estado.alarmas.single.snoozeHasta, DateTime(2026, 5, 25, 7, 36)); expect(android.programadas.last.proximaProgramable, DateTime(2026, 5, 25, 7, 36)); }); test('posponer desde preaviso mueve esta ejecucion desde la hora original', () async { final android = FakePuertoAlarmasAndroid(); final estado = EstadoAlarmas( servicio: ServicioAlarmas( reloj: () => DateTime(2026, 5, 25, 7), ), android: android, iniciarAutomaticamente: false, ); addTearDown(estado.dispose); addTearDown(android.dispose); await estado.guardarAlarma( AlarmaMusical( id: 'pre1', nombre: 'Preaviso', hora: 7, minuto: 30, tipoProgramacion: TipoProgramacionAlarma.diaria, diasSemana: const [], proximaEjecucion: DateTime(2026, 5, 25, 7, 30), ), ); final alarma = estado.alarmas.single; await estado.posponerProximaDesdePreaviso( alarma, 10, DateTime(2026, 5, 25, 7, 30), ); expect(estado.alarmas.single.snoozeHasta, DateTime(2026, 5, 25, 7, 40)); expect(estado.alarmas.single.snoozeOrigen, DateTime(2026, 5, 25, 7, 30)); expect(android.programadas.last.proximaProgramable, DateTime(2026, 5, 25, 7, 40)); }); test('finalizar diaria calcula siguiente dia y limpia snooze', () async { final android = FakePuertoAlarmasAndroid(); final estado = EstadoAlarmas( servicio: ServicioAlarmas( reloj: () => DateTime(2026, 5, 25, 7, 31), ), android: android, iniciarAutomaticamente: false, ); addTearDown(estado.dispose); addTearDown(android.dispose); await estado.guardarAlarma( AlarmaMusical( id: 'a2', nombre: 'Diaria', hora: 7, minuto: 30, tipoProgramacion: TipoProgramacionAlarma.diaria, diasSemana: const [], proximaEjecucion: DateTime(2026, 5, 25, 7, 30), snoozeHasta: DateTime(2026, 5, 25, 7, 36), snoozeOrigen: DateTime(2026, 5, 25, 7, 30), ), ); await estado.finalizarEjecucion('a2'); expect(estado.alarmas.single.snoozeHasta, isNull); expect(estado.alarmas.single.proximaEjecucion, DateTime(2026, 5, 26, 7, 30)); }); test('finalizar unica la desactiva y queda sin proxima ejecucion', () async { final android = FakePuertoAlarmasAndroid(); final estado = EstadoAlarmas( servicio: ServicioAlarmas( reloj: () => DateTime(2026, 5, 25, 7, 31), ), android: android, iniciarAutomaticamente: false, ); addTearDown(estado.dispose); addTearDown(android.dispose); await estado.guardarAlarma( AlarmaMusical( id: 'a3', nombre: 'Unica', hora: 7, minuto: 30, tipoProgramacion: TipoProgramacionAlarma.unica, diasSemana: const [], fechaUnica: DateTime(2026, 5, 25), proximaEjecucion: DateTime(2026, 5, 25, 7, 30), ), ); await estado.finalizarEjecucion('a3'); expect(estado.alarmas.single.activa, isFalse); expect(estado.alarmas.single.proximaEjecucion, isNull); }); } class FakePuertoAlarmasAndroid implements PuertoAlarmasAndroid { final programadas = []; final canceladas = []; final detenidas = []; final ocultadas = []; final _eventos = StreamController.broadcast(); @override Stream get eventosAlarma => _eventos.stream; @override Future programar(AlarmaMusical alarma) async { programadas.add(alarma); } @override Future cancelar(String alarmaId) async { canceladas.add(alarmaId); } @override Future detenerSonidoNativo(String alarmaId) async { detenidas.add(alarmaId); } @override Future ocultarNotificacionAlarma(String alarmaId) async { ocultadas.add(alarmaId); } @override Future confirmarAudioFlutter(String alarmaId) async { detenidas.add(alarmaId); } @override Future diagnostico() async => const DiagnosticoAlarmasAndroid( puedeProgramarExactas: true, notificacionesPermitidas: true, puedeUsarPantallaCompleta: true, ignoraOptimizacionBateria: true, alarmasNativasPendientes: 0, fabricante: 'test', versionSdk: 35, ); @override Future obtenerEventoInicial() async => null; @override Future solicitarPermisoAlarmasExactas() async => true; @override Future solicitarPermisoNotificaciones() async => true; @override Future solicitarPermisoPantallaCompleta() async => true; Future dispose() => _eventos.close(); }