fix(eq): keep valid presets when stored maps are partially corrupt
Convert the 4 EQ persistence readers (device presets, matrix presets, device names, per-station presets) to per-entry tolerant parsing via the shared persistencia_tolerante helper, so one corrupt entry no longer discards every sibling preset. The principal-preset reader gains diagnostic logging on its existing fallback path. No degraded flag or quarantine here (unlike alarms/stations) since EQ writes are explicit-only and presets are trivially re-creatable.
This commit is contained in:
@@ -1,8 +1,16 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pluriwave/modelos/preset_ecualizador.dart';
|
||||
import 'package:pluriwave/servicios/servicio_ecualizador.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
Map<String, dynamic> _presetJson(String nombre) => {
|
||||
'nombre': nombre,
|
||||
'bandas': [1.0, 2.0, 3.0, 4.0, 5.0],
|
||||
};
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
@@ -406,4 +414,160 @@ void main() {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// persistence-resilience Phase C: per-entry tolerant reads for the 4 EQ
|
||||
// map/single-value readers + degraded-read logging. NO flag/quarantine
|
||||
// here (D6, intentional asymmetry vs. Alarms/Stations) — EQ presets are
|
||||
// explicit-only writes and trivially re-creatable.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
group(
|
||||
'ServicioEcualizador — lectura tolerante y diagnostico (persistence-resilience)',
|
||||
() {
|
||||
late DebugPrintCallback debugPrintOriginal;
|
||||
late List<String> logs;
|
||||
|
||||
setUp(() {
|
||||
debugPrintOriginal = debugPrint;
|
||||
logs = [];
|
||||
debugPrint = (String? message, {int? wrapWidth}) {
|
||||
if (message != null) logs.add(message);
|
||||
};
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
debugPrint = debugPrintOriginal;
|
||||
});
|
||||
|
||||
test(
|
||||
'C2 presetsMatriz: sobrevive parcial con 2 entradas validas + 1 valor '
|
||||
'corrupto, claves identicas al original',
|
||||
() async {
|
||||
final raw = jsonEncode({
|
||||
'station-1:device-a': _presetJson('Rock'),
|
||||
'station-2:device-b': _presetJson('Pop'),
|
||||
'station-3:device-c': {'nombre': 'Malo', 'bandas': 'no-es-lista'},
|
||||
});
|
||||
SharedPreferences.setMockInitialValues({
|
||||
'eq_presets_matriz_v1': raw,
|
||||
});
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final servicio = ServicioEcualizador(prefs: prefs);
|
||||
|
||||
final config = await servicio.cargar();
|
||||
|
||||
expect(config.presetsMatriz.keys.toSet(), {
|
||||
'station-1:device-a',
|
||||
'station-2:device-b',
|
||||
});
|
||||
expect(config.presetsMatriz['station-1:device-a']!.nombre, 'Rock');
|
||||
expect(config.presetsMatriz['station-2:device-b']!.nombre, 'Pop');
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'C3 presetsMatriz: clave sobreviviente con deviceId estilo MAC '
|
||||
'(varios dos puntos) queda intacta pese a un vecino corrupto',
|
||||
() async {
|
||||
const claveMac = 'station-X:bt_a2dp:AA:BB:CC:DD:EE:FF';
|
||||
final raw = jsonEncode({
|
||||
claveMac: _presetJson('Jazz'),
|
||||
'station-Y:wired_headset': _presetJson('Rock'),
|
||||
'station-Z:corrupt': {'nombre': 'Malo', 'bandas': 'no-es-lista'},
|
||||
});
|
||||
SharedPreferences.setMockInitialValues({
|
||||
'eq_presets_matriz_v1': raw,
|
||||
});
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final servicio = ServicioEcualizador(prefs: prefs);
|
||||
|
||||
final config = await servicio.cargar();
|
||||
|
||||
expect(config.presetsMatriz.keys.toSet(), {
|
||||
claveMac,
|
||||
'station-Y:wired_headset',
|
||||
});
|
||||
expect(config.presetsMatriz[claveMac]!.nombre, 'Jazz');
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'C4 presetsDispositivo totalmente corrupto: cargar() queda vacio, '
|
||||
'guardarPresetDispositivo sigue escribiendo (sin bandera en EQ)',
|
||||
() async {
|
||||
SharedPreferences.setMockInitialValues({
|
||||
'eq_preset_por_dispositivo_v1': '{bad',
|
||||
});
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final servicio = ServicioEcualizador(prefs: prefs);
|
||||
|
||||
final config = await servicio.cargar();
|
||||
expect(config.presetsDispositivo, isEmpty);
|
||||
|
||||
const deviceId = 'bt_a2dp:AA:BB:CC:DD:EE:FF';
|
||||
await servicio.guardarPresetDispositivo(
|
||||
deviceId,
|
||||
PresetEcualizador.rock,
|
||||
);
|
||||
final tras = await servicio.cargar();
|
||||
|
||||
expect(
|
||||
tras.presetsDispositivo[deviceId],
|
||||
equals(PresetEcualizador.rock),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'C5 preset principal totalmente corrupto: cargar() retorna flat y '
|
||||
'loguea la lectura degradada',
|
||||
() async {
|
||||
SharedPreferences.setMockInitialValues({
|
||||
'eq_preset_principal_v1': '{bad',
|
||||
});
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final servicio = ServicioEcualizador(prefs: prefs);
|
||||
|
||||
final config = await servicio.cargar();
|
||||
|
||||
expect(config.principal, equals(PresetEcualizador.flat));
|
||||
expect(
|
||||
logs.any((l) => l.startsWith('[PluriWave][persistencia]')),
|
||||
isTrue,
|
||||
reason:
|
||||
'una lectura degradada del preset principal debe quedar '
|
||||
'registrada (antes silenciosa)',
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'C6 nombresDispositivos: sobrevive parcial con 2 nombres validos + 1 '
|
||||
'valor corrupto',
|
||||
() async {
|
||||
final raw = jsonEncode({
|
||||
'bt_a2dp:AA:BB': 'Living Room BT',
|
||||
'wired_headset': 'Office Headset',
|
||||
'builtin_speaker': 12345, // not a String -> must be skipped
|
||||
});
|
||||
SharedPreferences.setMockInitialValues({
|
||||
'eq_nombres_dispositivos_v1': raw,
|
||||
});
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final servicio = ServicioEcualizador(prefs: prefs);
|
||||
|
||||
final config = await servicio.cargar();
|
||||
|
||||
expect(
|
||||
config.nombresDispositivos,
|
||||
equals({
|
||||
'bt_a2dp:AA:BB': 'Living Room BT',
|
||||
'wired_headset': 'Office Headset',
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user