079e19f0ee
- Integrate audio_session (new servicio_audio_session.dart): incoming calls pause the radio and resume on end, headphone unplug pauses without auto-resume, permanent focus loss never auto-resumes, duck lowers volume - Add play-intent flag to ServicioAudio so interruption handling and future reconnect logic can distinguish user pause from system-driven stops - Eliminate read-modify-write race in ServicioAlarmas with an in-memory cache and single-writer queue across all mutations; recalcularTodas persists only when state actually changed - Convert ServicioAlarmasAndroid static StreamController/handler to injectable instance fields, restoring test isolation - Inject a single cached SharedPreferences from main.dart across services and state (removes 23 inline getInstance() calls) - Move configurarLocalizaciones out of MiniReproductor.build() (was running on every rebuild during playback) - Bound the alarm fire-dedup set (cap 200 entries, 24h pruning) - 12 new tests (89 total green), flutter analyze clean
83 lines
2.3 KiB
Dart
83 lines
2.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:pluriwave/servicios/servicio_alarmas.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import '../helpers/fakes_alarmas.dart';
|
|
|
|
/// S3-R6: `_ejecucionesEmitidas` must be bounded — stale entries (>24 h)
|
|
/// pruned and total size capped.
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
setUp(() {
|
|
SharedPreferences.setMockInitialValues({});
|
|
});
|
|
|
|
EstadoAlarmas crearEstado(FakePuertoAlarmasAndroid android) {
|
|
final estado = EstadoAlarmas(
|
|
servicio: ServicioAlarmas(),
|
|
android: android,
|
|
iniciarAutomaticamente: false,
|
|
);
|
|
addTearDown(estado.dispose);
|
|
addTearDown(android.dispose);
|
|
return estado;
|
|
}
|
|
|
|
const base = AlarmaMusical(
|
|
id: 'a1',
|
|
nombre: 'Diaria',
|
|
hora: 7,
|
|
minuto: 0,
|
|
tipoProgramacion: TipoProgramacionAlarma.diaria,
|
|
diasSemana: [],
|
|
);
|
|
|
|
test('poda las entradas con mas de 24 horas (S3-R6-A)', () {
|
|
final estado = crearEstado(FakePuertoAlarmasAndroid());
|
|
final ahora = DateTime.now();
|
|
|
|
for (var i = 0; i < 100; i++) {
|
|
estado.marcarEjecucionGestionada(
|
|
base.copyWith(
|
|
proximaEjecucion: ahora.subtract(Duration(hours: 25, minutes: i)),
|
|
),
|
|
);
|
|
}
|
|
estado.marcarEjecucionGestionada(
|
|
base.copyWith(
|
|
id: 'fresca',
|
|
proximaEjecucion: ahora.add(const Duration(minutes: 5)),
|
|
),
|
|
);
|
|
|
|
expect(
|
|
estado.ejecucionesEmitidasLength,
|
|
lessThanOrEqualTo(EstadoAlarmas.maxEjecucionesEmitidas),
|
|
);
|
|
expect(
|
|
estado.ejecucionesEmitidasLength,
|
|
1,
|
|
reason: 'solo la entrada fresca sobrevive a la poda por antiguedad',
|
|
);
|
|
});
|
|
|
|
test('limita el total de entradas al tope configurado', () {
|
|
final estado = crearEstado(FakePuertoAlarmasAndroid());
|
|
final ahora = DateTime.now();
|
|
|
|
for (var i = 0; i < EstadoAlarmas.maxEjecucionesEmitidas + 50; i++) {
|
|
estado.marcarEjecucionGestionada(
|
|
base.copyWith(proximaEjecucion: ahora.add(Duration(minutes: i))),
|
|
);
|
|
}
|
|
|
|
expect(
|
|
estado.ejecucionesEmitidasLength,
|
|
lessThanOrEqualTo(EstadoAlarmas.maxEjecucionesEmitidas),
|
|
);
|
|
});
|
|
}
|