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', ); }); }); }