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.
67 lines
2.1 KiB
Dart
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');
|
|
});
|
|
});
|
|
}
|