Files
pluriwave/test/servicios/arranque_audio_test.dart
T
FreeTLab d0abe32eef
Build & Deploy PluriWave / Análisis de código (push) Successful in 27s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 2m23s
fix(audio): survive audio_service init hang on Android Auto cold start
AudioService.init has no internal timeout and an unhandled
onConnectionSuspended case in its MediaBrowser self-bind; under bind
contention with the car's connection it can hang forever, so runApp
never ran (black car screen, white phone UI until process kill).

Race init against an 8s timeout without ever re-calling it: on timeout
run a bootstrap app that waits on the same future, wires the handler
exactly once when it resolves, reports errors via FlutterError, and
swaps to the real app. Auto browse sources now register before the
init await since they take no handler dependency.
2026-07-25 13:43:40 +02:00

67 lines
2.1 KiB
Dart

import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:pluriwave/servicios/arranque_audio.dart';
/// S8-android-auto-cold-start: covers [esperarArranqueAudio]'s timeout race
/// in isolation, without touching the real `audio_service` plugin (Design
/// "Testability" — the init future is injected).
void main() {
group('esperarArranqueAudio', () {
test(
'handler resuelto antes del timeout devuelve ArranqueAudioListo',
() async {
final resultado = await esperarArranqueAudio<String>(
Future.value('handler-ok'),
timeout: const Duration(milliseconds: 200),
);
expect(resultado, isA<ArranqueAudioListo<String>>());
expect((resultado as ArranqueAudioListo<String>).handler, 'handler-ok');
},
);
test('handler colgado mas alla del timeout devuelve ArranqueAudioPendiente '
'con el MISMO future original', () async {
final completer = Completer<String>();
final resultado = await esperarArranqueAudio<String>(
completer.future,
timeout: const Duration(milliseconds: 20),
);
expect(resultado, isA<ArranqueAudioPendiente<String>>());
expect(
identical(
(resultado as ArranqueAudioPendiente<String>).handlerFuturo,
completer.future,
),
isTrue,
reason:
'init nunca debe llamarse dos veces — el future pendiente '
'debe ser exactamente el mismo objeto',
);
// El completer nunca se completó — liberarlo evita un future
// colgado tras el test.
completer.complete('handler-tardio');
});
test('finalizacion tardia tras el timeout resuelve el future original con '
'el handler real', () async {
final completer = Completer<String>();
final resultado = await esperarArranqueAudio<String>(
completer.future,
timeout: const Duration(milliseconds: 20),
);
final pendiente = resultado as ArranqueAudioPendiente<String>;
completer.complete('handler-tardio');
final handler = await pendiente.handlerFuturo;
expect(handler, 'handler-tardio');
});
});
}