Files
pluriwave/test/servicios/servicio_audio_salto_fuente_test.dart
T
FreeTLab d754e28ddf fix(audio): keep local skips local, and stop a failed station blanking Auto
Three reported suspicions. Two confirmed by reading, one not.

1. CONFIRMED, self-inflicted. Playing a song from the phone and pressing
NEXT jumped to a radio station.

3398d02 taught skipToNext/skipToPrevious to fall back to station skipping
when there is no local queue, so the car's buttons would not be dead for
radio. But queue-less does not mean radio: tapping ONE track goes through
reproducirPistaLocal, which never builds a queue -- only folder playback
sets _colaLocal. That is exactly why the report said "at least the first
time".

emisoraActual cannot tell them apart either: _cambiarFuente fills it in for
every source, so a local MP3 arrives as an Emisora whose url is its
content:// document URI. The media id's scheme is the real discriminator,
the same test that already keeps the recorder off local files. A local
track now skips nowhere, which is the correct behaviour for a single item.

2. CONFIRMED mechanism. A failed station made the app disappear from the
Android Auto pane.

The error path published STATE_ERROR and then cleared everything:
`emisoraActual = null; mediaItem.add(null)`. That leaves the session in an
error state with no metadata at all, and Auto drops a session with nothing
to show -- reported as "if a station fails it seems to crash, and going to
1/3 it fails".

Both are kept now. Nothing outside servicio_audio.dart consumes mediaItem
(verified), so the phone is unaffected, and the car gains two things: the
screen can still name the station that failed instead of going blank, and
previous/next stay usable, so a driver can skip out of a dead station
instead of being stranded -- _saltarEmisora needs emisoraActual to know
where it is in the list. The error state itself is unchanged.

3. NOT CONFIRMED. A local track occasionally jumping to another one mid-play.

An advance requires a genuine `completed` from just_audio, so either the
player reports the end early -- plausible for a content:// SAF source,
whose duration is not always exact -- or something else moved the track.
Reading the code cannot separate those, so nothing was changed on a guess.
The advance now logs the decision with the processing state, position and
duration that caused it, so the next occurrence arrives with its reason
attached.

Tests: 1192 -> 1195.
2026-08-07 17:17:48 +02:00

53 lines
2.2 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
/// Reported: playing a song stored on the phone and pressing NEXT jumped to a
/// radio station — "at least the first time".
///
/// Self-inflicted, by 3398d02. That commit taught `skipToNext`/`skipToPrevious`
/// to fall back to station-to-station skipping when there is no local queue,
/// so the car's transport buttons would not be dead for radio. But a
/// queue-less state does NOT mean "radio is playing": tapping ONE local track
/// goes through `reproducirPistaLocal`, which never builds a queue. Only
/// folder playback sets `_colaLocal` — which is exactly why the report said
/// "at least the first time".
///
/// `emisoraActual` cannot tell them apart either: `_cambiarFuente` fills it in
/// for every source, so a local MP3 arrives as an `Emisora` whose `url` is its
/// `content://` document URI. The media id's SCHEME is the real
/// discriminator, and this asserts on that rule — the same one
/// `esEmisoraGrabable` uses to keep the recorder off local files.
///
/// `PluriWaveAudioHandler` needs platform MethodChannels and cannot be built
/// in a unit test, so this pins the predicate rather than the private getter.
void main() {
bool esRadio(String? id) {
if (id == null) return false;
final esquema = Uri.tryParse(id)?.scheme.toLowerCase();
return esquema == 'http' || esquema == 'https';
}
test('un stream de radio SÍ permite saltar de emisora', () {
expect(esRadio('http://stream.example.com/live'), isTrue);
expect(esRadio('https://stream.example.com/live'), isTrue);
});
test('una pista local NO: es el caso exacto del reporte', () {
expect(
esRadio(
'content://com.android.externalstorage.documents/tree/'
'primary%3AMusic/document/primary%3AMusic%2FNew%20Limit%20-%20Smile.mp3',
),
isFalse,
reason:
'con una pista suelta no hay cola, y antes de esto el boton '
'siguiente se iba a una emisora de radio',
);
expect(esRadio('file:///storage/emulated/0/musica/a.mp3'), isFalse);
});
test('sin nada sonando tampoco se salta', () {
expect(esRadio(null), isFalse);
expect(esRadio(''), isFalse);
});
}