ExoPlayer assigns a new audio session id after transient audio-focus interruptions (navigation prompts, radar warnings), leaving the AndroidEqualizer attached to the dead session so playback resumed without equalization until the next station switch. The session-id listener now detects genuine rotations through a dedicated guard and re-activates the equalizer with the current preset, gated on EQ availability to stay clear of player teardown/rebuild.
100 lines
3.8 KiB
Dart
100 lines
3.8 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:pluriwave/servicios/servicio_audio.dart';
|
|
|
|
/// EQ audio-focus re-apply — pure decision predicate truth table.
|
|
///
|
|
/// Covers spec "EQ Audio-Focus Re-Apply Specification": whenever
|
|
/// `androidAudioSessionIdStream` emits a new native session id mid-playback
|
|
/// (audio-focus ducking by another app), the equalizer must be re-attached
|
|
/// and the current preset's gains re-pushed, gated on EQ availability so it
|
|
/// never races `_recrearPlayer()`'s teardown/rebuild.
|
|
///
|
|
/// [PluriWaveAudioHandler] cannot be instantiated in unit tests — its
|
|
/// constructor builds a real `just_audio.AudioPlayer` that requires platform
|
|
/// MethodChannels (confirmed by `servicio_audio_source_switch_test.dart`, "We
|
|
/// cannot instantiate the handler in unit tests"). The only unit-testable
|
|
/// surface is the pure, side-effect-free static predicate
|
|
/// `debeReaplicarEcualizador`, extracted per the design's "Interfaces /
|
|
/// Contracts" section. Listener wiring and the actual native re-attach are
|
|
/// covered by manual on-device QA (spec's Testability Matrix), not here.
|
|
void main() {
|
|
group('debeReaplicarEcualizador (EQ audio-focus re-apply predicate)', () {
|
|
test('rotation while playing returns true', () {
|
|
// Spec: Requirement "Session Id Rotation Triggers EQ Re-Apply" /
|
|
// Scenario "Session id rotates while playing".
|
|
expect(
|
|
PluriWaveAudioHandler.debeReaplicarEcualizador(
|
|
sessionId: 2,
|
|
ultimaSessionIdEq: 1,
|
|
eqDisponible: true,
|
|
),
|
|
isTrue,
|
|
reason: 'a genuine id rotation with EQ available must re-apply',
|
|
);
|
|
});
|
|
|
|
test('same id re-emitted returns false', () {
|
|
// Spec: Scenario "Same id re-emitted produces no redundant re-apply".
|
|
expect(
|
|
PluriWaveAudioHandler.debeReaplicarEcualizador(
|
|
sessionId: 1,
|
|
ultimaSessionIdEq: 1,
|
|
eqDisponible: true,
|
|
),
|
|
isFalse,
|
|
reason: 'a duplicate emission of the already-processed id is a no-op',
|
|
);
|
|
});
|
|
|
|
test('first activation / matching guard returns false', () {
|
|
// Spec: Scenario "First legitimate activation is not double-applied".
|
|
// Same shape as the duplicate-id case above, kept as a distinct named
|
|
// case per design's testing table to document intent: this represents
|
|
// the id emitted right after a station switch already set the guard
|
|
// (via _activarEcualizador() at the station-switch call site), not
|
|
// just an arbitrary repeated emission.
|
|
expect(
|
|
PluriWaveAudioHandler.debeReaplicarEcualizador(
|
|
sessionId: 1,
|
|
ultimaSessionIdEq: 1,
|
|
eqDisponible: true,
|
|
),
|
|
isFalse,
|
|
reason:
|
|
'the first post-station-switch emission must not double-apply '
|
|
'on top of the station-switch path\'s own _activarEcualizador()',
|
|
);
|
|
});
|
|
|
|
test('null id returns false', () {
|
|
// Spec: Requirement "Session Id Rotation Triggers EQ Re-Apply"
|
|
// (non-null precondition).
|
|
expect(
|
|
PluriWaveAudioHandler.debeReaplicarEcualizador(
|
|
sessionId: null,
|
|
ultimaSessionIdEq: 1,
|
|
eqDisponible: true,
|
|
),
|
|
isFalse,
|
|
reason: 'a null session id must never trigger a re-apply',
|
|
);
|
|
});
|
|
|
|
test('teardown gate returns false', () {
|
|
// Spec: Requirement "Re-Apply Is Gated On EQ Availability" / Scenario
|
|
// "Rotation during player teardown is safely skipped".
|
|
expect(
|
|
PluriWaveAudioHandler.debeReaplicarEcualizador(
|
|
sessionId: 9,
|
|
ultimaSessionIdEq: 1,
|
|
eqDisponible: false,
|
|
),
|
|
isFalse,
|
|
reason:
|
|
'while _eqDisponible is false (mid _recrearPlayer teardown), '
|
|
're-apply must be skipped entirely to avoid racing the rebuild',
|
|
);
|
|
});
|
|
});
|
|
}
|