Completes the bridge the native side already exposed. AlarmScheduler and PluriWaveAlarmService record a pre-notice that could not be armed, a refused foreground-service start, and a per-alarm reschedule that failed after a reboot -- but nothing read them, so all three still ended at logcat. EstadoAlarmas now drains them at startup and turns each into a per-alarm exception, which the card already knows how to mark. An alarm that never reached the OS stops looking identical to one that did. The read is deliberately tolerant: a failure to read is logged and swallowed, never surfaced as an alarm error, so a diagnostics gap cannot masquerade as a scheduling problem.
107 lines
3.3 KiB
Dart
107 lines
3.3 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:pluriwave/estado/estado_alarmas.dart';
|
|
import 'package:pluriwave/modelos/alarma_musical.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import '../helpers/fakes_alarmas.dart';
|
|
|
|
/// The native scheduler records three failures entirely on its own, outside
|
|
/// any Dart call: a pre-notice that could not be armed, a refused
|
|
/// foreground-service start when the alarm should have rung, and a per-alarm
|
|
/// reschedule that failed after a reboot.
|
|
///
|
|
/// Before this wiring existed, all three logged to logcat and stopped there.
|
|
/// The alarm stayed switched on in the list, drawn exactly as if scheduling
|
|
/// had succeeded, and simply never fired — the reported "as if there were no
|
|
/// alarm at all". These tests pin the drain path that makes them visible.
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
late FakePuertoAlarmasAndroid android;
|
|
|
|
setUp(() {
|
|
SharedPreferences.setMockInitialValues({});
|
|
android = FakePuertoAlarmasAndroid();
|
|
});
|
|
|
|
EstadoAlarmas crearEstado() =>
|
|
EstadoAlarmas(android: android, iniciarAutomaticamente: false);
|
|
|
|
test('a native pre-notice failure becomes a per-alarm exception', () async {
|
|
android.fallosNativos = [
|
|
{'alarmaId': 'a1', 'tipo': ExcepcionAlarma.tipoFalloPreaviso},
|
|
];
|
|
final estado = crearEstado();
|
|
addTearDown(estado.dispose);
|
|
|
|
await estado.cargarFallosNativos();
|
|
|
|
final fallo = estado.ultimaExcepcionPara('a1');
|
|
expect(fallo, isNotNull);
|
|
expect(fallo!.tipo, ExcepcionAlarma.tipoFalloPreaviso);
|
|
});
|
|
|
|
test(
|
|
'a refused foreground-service start becomes a per-alarm exception',
|
|
() async {
|
|
android.fallosNativos = [
|
|
{'alarmaId': 'a2', 'tipo': ExcepcionAlarma.tipoFalloServicioSonido},
|
|
];
|
|
final estado = crearEstado();
|
|
addTearDown(estado.dispose);
|
|
|
|
await estado.cargarFallosNativos();
|
|
|
|
expect(
|
|
estado.ultimaExcepcionPara('a2')?.tipo,
|
|
ExcepcionAlarma.tipoFalloServicioSonido,
|
|
);
|
|
},
|
|
);
|
|
|
|
test('only the alarm whose reschedule failed is marked', () async {
|
|
android.fallosNativos = [
|
|
{
|
|
'alarmaId': 'a3',
|
|
'tipo': ExcepcionAlarma.tipoFalloReprogramacionArranque,
|
|
},
|
|
];
|
|
final estado = crearEstado();
|
|
addTearDown(estado.dispose);
|
|
|
|
await estado.cargarFallosNativos();
|
|
|
|
expect(estado.ultimaExcepcionPara('a3'), isNotNull);
|
|
expect(
|
|
estado.ultimaExcepcionPara('a4'),
|
|
isNull,
|
|
reason: 'a sibling alarm that scheduled fine must stay unmarked',
|
|
);
|
|
});
|
|
|
|
test('malformed native entries are skipped without throwing', () async {
|
|
android.fallosNativos = [
|
|
{'tipo': ExcepcionAlarma.tipoFalloPreaviso}, // no alarmaId
|
|
{'alarmaId': 'a5'}, // no tipo
|
|
];
|
|
final estado = crearEstado();
|
|
addTearDown(estado.dispose);
|
|
|
|
await estado.cargarFallosNativos();
|
|
|
|
expect(estado.ultimaExcepcionPara('a5'), isNull);
|
|
});
|
|
|
|
test('a failing native read never surfaces as an alarm error', () async {
|
|
// A diagnostics gap must not look like a scheduling problem: an older
|
|
// native build simply has no such channel method.
|
|
android.fallaLecturaFallosNativos = true;
|
|
final estado = crearEstado();
|
|
addTearDown(estado.dispose);
|
|
|
|
await estado.cargarFallosNativos();
|
|
|
|
expect(estado.error, isNull);
|
|
});
|
|
}
|