- Added state management for startup retry and custom station handling in `EstadoRadio`. - Created tasks for implementing strict TDD with RED tests for HTTP failure retries and EQ persistence. - Developed verification report to ensure compliance with TDD practices. - Introduced fake services for testing, including `FakeServicioAudio`, `FakeServicioFavoritos`, and `FakeServicioRadio`. - Implemented widget tests for `PantallaInicio` and `PantallaFavoritos` to validate UI behavior with custom stations. - Enhanced `ServicioRadio` to support host rotation and retry logic for API calls. - Established a new configuration file to enforce project constraints and testing rules.
214 lines
6.1 KiB
Dart
214 lines
6.1 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:pluriwave/modelos/emisora.dart';
|
|
import 'package:pluriwave/modelos/preset_ecualizador.dart';
|
|
import 'package:pluriwave/servicios/servicio_audio.dart';
|
|
import 'package:pluriwave/servicios/servicio_ecualizador.dart';
|
|
import 'package:pluriwave/servicios/servicio_favoritos.dart';
|
|
import 'package:pluriwave/servicios/servicio_radio.dart';
|
|
|
|
class FakeServicioAudio extends ServicioAudio {
|
|
FakeServicioAudio({this.ecualizadorActivo = true}) {
|
|
_estadoController.add(EstadoReproduccion.detenido);
|
|
}
|
|
|
|
final bool ecualizadorActivo;
|
|
final _estadoController = StreamController<EstadoReproduccion>.broadcast();
|
|
final List<PresetEcualizador> presetsAplicados = [];
|
|
final List<Emisora> emisorasReproducidas = [];
|
|
Emisora? _emisoraActual;
|
|
|
|
@override
|
|
Emisora? get emisoraActual => _emisoraActual;
|
|
|
|
@override
|
|
bool get ecualizadorDisponible => ecualizadorActivo;
|
|
|
|
@override
|
|
Stream<EstadoReproduccion> get estadoStream => _estadoController.stream;
|
|
|
|
@override
|
|
Future<void> reproducir(Emisora emisora) async {
|
|
_emisoraActual = emisora;
|
|
emisorasReproducidas.add(emisora);
|
|
_estadoController.add(EstadoReproduccion.reproduciendo);
|
|
}
|
|
|
|
@override
|
|
Future<void> detener() async {
|
|
_emisoraActual = null;
|
|
_estadoController.add(EstadoReproduccion.detenido);
|
|
}
|
|
|
|
@override
|
|
Future<void> aplicarPreset(PresetEcualizador preset) async {
|
|
presetsAplicados.add(preset);
|
|
}
|
|
|
|
@override
|
|
Future<void> setBanda(int index, double db) async {}
|
|
|
|
@override
|
|
Future<void> dispose() async {
|
|
await _estadoController.close();
|
|
}
|
|
}
|
|
|
|
class FakeServicioFavoritos extends ServicioFavoritos {
|
|
final Map<String, Emisora> _favoritos = {};
|
|
int toggleCalls = 0;
|
|
|
|
@override
|
|
Future<List<Emisora>> obtenerTodos() async => _favoritos.values.toList();
|
|
|
|
@override
|
|
Future<void> agregar(Emisora emisora) async {
|
|
_favoritos[emisora.uuid] = emisora;
|
|
}
|
|
|
|
@override
|
|
Future<void> eliminar(String uuid) async {
|
|
_favoritos.remove(uuid);
|
|
}
|
|
|
|
@override
|
|
Future<bool> esFavorito(String uuid) async => _favoritos.containsKey(uuid);
|
|
|
|
@override
|
|
Future<bool> toggleFavorito(Emisora emisora) async {
|
|
toggleCalls += 1;
|
|
if (_favoritos.containsKey(emisora.uuid)) {
|
|
_favoritos.remove(emisora.uuid);
|
|
return false;
|
|
}
|
|
_favoritos[emisora.uuid] = emisora;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class FakeServicioRadio extends ServicioRadio {
|
|
FakeServicioRadio({
|
|
List<Emisora>? populares,
|
|
List<Emisora>? tendencias,
|
|
List<Emisora>? busqueda,
|
|
List<List<Emisora>>? popularesPorLlamada,
|
|
List<List<Emisora>>? tendenciasPorLlamada,
|
|
List<Object>? erroresPopularesPorLlamada,
|
|
List<Object>? erroresTendenciasPorLlamada,
|
|
}) : _populares = populares ?? [],
|
|
_tendencias = tendencias ?? [],
|
|
_busqueda = busqueda ?? [],
|
|
_popularesPorLlamada = popularesPorLlamada ?? const [],
|
|
_tendenciasPorLlamada = tendenciasPorLlamada ?? const [],
|
|
_erroresPopularesPorLlamada = erroresPopularesPorLlamada ?? const [],
|
|
_erroresTendenciasPorLlamada = erroresTendenciasPorLlamada ?? const [];
|
|
|
|
final List<Emisora> _populares;
|
|
final List<Emisora> _tendencias;
|
|
final List<Emisora> _busqueda;
|
|
final List<List<Emisora>> _popularesPorLlamada;
|
|
final List<List<Emisora>> _tendenciasPorLlamada;
|
|
final List<Object> _erroresPopularesPorLlamada;
|
|
final List<Object> _erroresTendenciasPorLlamada;
|
|
|
|
int obtenerPopularesCalls = 0;
|
|
int obtenerTendenciasCalls = 0;
|
|
int registrarClickCalls = 0;
|
|
String? ultimoUuidClick;
|
|
|
|
Exception _normalizarError(Object error) =>
|
|
error is Exception ? error : Exception(error.toString());
|
|
|
|
@override
|
|
Future<List<Emisora>> obtenerPopulares({int limit = 30}) async {
|
|
final llamada = obtenerPopularesCalls++;
|
|
if (llamada < _erroresPopularesPorLlamada.length) {
|
|
throw _normalizarError(_erroresPopularesPorLlamada[llamada]);
|
|
}
|
|
final data = llamada < _popularesPorLlamada.length
|
|
? _popularesPorLlamada[llamada]
|
|
: _populares;
|
|
return data.take(limit).toList();
|
|
}
|
|
|
|
@override
|
|
Future<List<Emisora>> obtenerTendencias({int limit = 20}) async {
|
|
final llamada = obtenerTendenciasCalls++;
|
|
if (llamada < _erroresTendenciasPorLlamada.length) {
|
|
throw _normalizarError(_erroresTendenciasPorLlamada[llamada]);
|
|
}
|
|
final data = llamada < _tendenciasPorLlamada.length
|
|
? _tendenciasPorLlamada[llamada]
|
|
: _tendencias;
|
|
return data.take(limit).toList();
|
|
}
|
|
|
|
@override
|
|
Future<List<Emisora>> buscar({
|
|
String? nombre,
|
|
String? pais,
|
|
String? idioma,
|
|
String? tag,
|
|
int limit = 30,
|
|
}) async {
|
|
return _busqueda.take(limit).toList();
|
|
}
|
|
|
|
@override
|
|
Future<void> registrarClick(String uuid) async {
|
|
registrarClickCalls += 1;
|
|
ultimoUuidClick = uuid;
|
|
}
|
|
}
|
|
|
|
class FakeServicioEcualizador extends ServicioEcualizador {
|
|
FakeServicioEcualizador({
|
|
PresetEcualizador? principal,
|
|
Map<String, PresetEcualizador>? porEmisora,
|
|
}) : _config = ConfiguracionEcualizador(
|
|
principal: principal ?? PresetEcualizador.flat,
|
|
porEmisora: porEmisora ?? {},
|
|
);
|
|
|
|
ConfiguracionEcualizador _config;
|
|
|
|
@override
|
|
Future<ConfiguracionEcualizador> cargar() async => _config;
|
|
|
|
@override
|
|
Future<void> guardarPrincipal(PresetEcualizador preset) async {
|
|
_config = ConfiguracionEcualizador(
|
|
principal: preset,
|
|
porEmisora: _config.porEmisora,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<void> guardarPorEmisora(String uuid, PresetEcualizador preset) async {
|
|
final mapa = Map<String, PresetEcualizador>.from(_config.porEmisora);
|
|
mapa[uuid] = preset;
|
|
_config = ConfiguracionEcualizador(
|
|
principal: _config.principal,
|
|
porEmisora: mapa,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<void> eliminarPorEmisora(String uuid) async {
|
|
final mapa = Map<String, PresetEcualizador>.from(_config.porEmisora);
|
|
mapa.remove(uuid);
|
|
_config = ConfiguracionEcualizador(
|
|
principal: _config.principal,
|
|
porEmisora: mapa,
|
|
);
|
|
}
|
|
}
|
|
|
|
Emisora emisoraDemo({
|
|
required String uuid,
|
|
required String nombre,
|
|
String url = 'https://stream.demo/radio',
|
|
}) {
|
|
return Emisora(uuid: uuid, nombre: nombre, url: url);
|
|
}
|