Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
b16172a
feat: Add PlayingStateUpdate event
Gustl22 Jun 6, 2026
1ac9cd6
format
Gustl22 Jun 6, 2026
cdc8948
windows
Gustl22 Jun 8, 2026
bc60226
attempt Linux
Gustl22 Jun 8, 2026
5f66543
WIP Android old
Gustl22 Jun 8, 2026
277d49b
WIP darwin
Gustl22 Jun 9, 2026
375e358
more android
Gustl22 Jun 10, 2026
b27b1cf
Option to set player state without setting desired state
Gustl22 Jun 11, 2026
20faa06
playing state update darwin
Gustl22 Jun 16, 2026
0017904
nsobject
Gustl22 Jun 19, 2026
a5e8a05
more explanation
Gustl22 Jun 19, 2026
9bf947f
darwin fix
Gustl22 Jun 27, 2026
b50dcf8
remove unused test
Gustl22 Jun 27, 2026
8cfa0c4
interruption
Gustl22 Jun 27, 2026
83cc08e
simplify test
Gustl22 Jun 28, 2026
1fa1f77
rework completion of playing state
Gustl22 Jun 28, 2026
be47b7e
lock
Gustl22 Jun 28, 2026
be6c577
await
Gustl22 Jun 28, 2026
9b927ef
fix
Gustl22 Jul 5, 2026
2977c6c
fix mock
Gustl22 Jul 5, 2026
d3ab33d
more forgiving stream test
Gustl22 Jul 5, 2026
6b8fc29
replace rate with timeControlStatus
Gustl22 Jul 5, 2026
885fb72
add timeout to test
Gustl22 Jul 5, 2026
43f849a
change test
Gustl22 Jul 5, 2026
37b7efa
change test
Gustl22 Jul 5, 2026
ec1416a
att tear down dispose
Gustl22 Jul 5, 2026
c6bbb8d
await instead of tear down
Gustl22 Jul 5, 2026
45db0c5
fix some test
Gustl22 Jul 5, 2026
dbe529f
format
Gustl22 Jul 5, 2026
8cd792c
format + fix
Gustl22 Jul 7, 2026
6aa08f0
try linux fixes
Gustl22 Jul 7, 2026
00abc5a
reactivate linux exemptions
Gustl22 Jul 7, 2026
d1ffb72
try pump and settle
Gustl22 Jul 8, 2026
072cf28
add timeouts
Gustl22 Jul 9, 2026
51dd401
try with testWidgets timeout only
Gustl22 Jul 9, 2026
0fed17c
upgrade xcodes version
Gustl22 Jul 9, 2026
f9e48f2
set integrationtest defaultTestTimeout binding
Gustl22 Jul 9, 2026
aed3dfb
improve timeouts
Gustl22 Jul 10, 2026
c873d82
use gh like xcodes
Gustl22 Jul 10, 2026
b7b8e7e
upgrade xcodes
Gustl22 Jul 10, 2026
39ebd0c
fix release on linux
Gustl22 Jul 19, 2026
51efe34
move releaseMode logic to Dart
Gustl22 Jul 19, 2026
f90b70b
fix test
Gustl22 Jul 19, 2026
bd02eff
format
Gustl22 Jul 19, 2026
88f2c72
some improvement
Gustl22 Jul 19, 2026
6da6cd6
format
Gustl22 Jul 19, 2026
6d651ab
xcodes was updated
Gustl22 Jul 19, 2026
96a4c66
slightly more timeout
Gustl22 Jul 19, 2026
3a4292e
fixes
Gustl22 Jul 20, 2026
e2ead6e
await position update
Gustl22 Jul 20, 2026
bc76ef5
more android
Gustl22 Jul 20, 2026
4c13f75
triggered comment
Gustl22 Jul 22, 2026
d8760cf
comment therefore
Gustl22 Jul 22, 2026
432e30b
comment be
Gustl22 Jul 22, 2026
dc25b4c
remove playerStatusObservation
Gustl22 Jul 22, 2026
ac66407
isPlaying equality check
Gustl22 Jul 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 109 additions & 70 deletions packages/audioplayers/example/integration_test/lib_test.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
@Timeout(Duration(minutes: 5))
library;

import 'package:audioplayers/audioplayers.dart';
import 'package:audioplayers_example/tabs/sources.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

import 'lib/lib_source_test_data.dart';
import 'lib/lib_test_utils.dart';
import 'platform_features.dart';
import 'test_utils.dart';

void main() async {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
Expand Down Expand Up @@ -135,36 +130,43 @@ void main() async {
interval: const Duration(milliseconds: 100),
);
}
final futurePositions = player.onPositionChanged.toList();
final positionsStream = player.onPositionChanged;

expectLater(positionsStream, neverEmits(null));
Future futureExpectations;
if (td.isLiveStream) {
// TODO(gustl22): Live streams may have zero or null as initial
// position. This should be consistent across all platforms.
futureExpectations = expectLater(
positionsStream,
emitsThrough(greaterThan(Duration.zero)),
);
} else {
futureExpectations = expectLater(
positionsStream,
emitsInOrder([
Duration.zero,
emitsThrough(greaterThan(Duration.zero)),
emitsThrough(Duration.zero),
]),
);
}

await player.setReleaseMode(ReleaseMode.stop);
await player.setSource(td.source);
await player.resume();
await tester.pumpGlobalFrames(const Duration(seconds: 5));

if (!td.isLiveStream && td.duration! < const Duration(seconds: 2)) {
expect(player.state, PlayerState.completed);
} else {
if (td.isLiveStream || td.duration! > const Duration(seconds: 10)) {
expect(player.state, PlayerState.playing);
} else {
// Don't know for sure, if has yet completed or is still playing
}
await player.stop();
expect(player.state, PlayerState.stopped);
}
expect(
player.state,
anyOf([PlayerState.completed, PlayerState.playing]),
);
await player.stop();
expect(player.state, PlayerState.stopped);

await futureExpectations;

await player.dispose();
final positions = await futurePositions;
printOnFailure('Positions: $positions');
expect(positions, isNot(contains(null)));
expect(positions, contains(greaterThan(Duration.zero)));
if (td.isLiveStream) {
// TODO(gustl22): Live streams may have zero or null as initial
// position. This should be consistent across all platforms.
} else {
expect(positions.first, Duration.zero);
expect(positions.last, Duration.zero);
}
},
timeout: const Timeout(Duration(seconds: 30)),
skip:
Expand Down Expand Up @@ -200,32 +202,14 @@ void main() async {
(i) async => players[i].play(audioTestDataList[i].source),
),
);
final playerStates = List<PlayerState?>.generate(
audioTestDataList.length,
(index) => null,
);
await tester.waitFor(
() async {
// TODO(gustl22): Improve detection of started players via player
// state.
final unplayed = playerStates
.mapIndexed(
(index, element) => element != null ? null : index,
)
.nonNulls;
for (final i in unplayed) {
final player = players[i];
if (player.state == PlayerState.completed ||
player.state == PlayerState.disposed) {
playerStates[i] = player.state;
} else if (((await player.getCurrentPosition()) ??
Duration.zero) >
Duration.zero) {
playerStates[i] = PlayerState.playing;
}
}
expect(playerStates, everyElement(isNotNull));
},
final playerStates = players.map((player) => player.state);
expect(
playerStates,
everyElement(
(playerState) =>
playerState != PlayerState.stopped &&
playerState != PlayerState.paused,
),
);
await Future.wait<void>(iterator.map((i) => players[i].stop()));
await Future.wait(players.map((p) => p.dispose()));
Expand All @@ -244,22 +228,12 @@ void main() async {
final player = AudioPlayer();

for (final td in audioTestDataList) {
player.play(td.source);
// TODO(gustl22): Improve detection of started players via player
// state.
PlayerState? playerState;
await tester.waitFor(
() async {
if (player.state == PlayerState.completed ||
player.state == PlayerState.disposed) {
playerState = player.state;
} else if (((await player.getCurrentPosition()) ??
Duration.zero) >
Duration.zero) {
playerState = PlayerState.playing;
}
expect(playerState, isNotNull);
},
await player.play(td.source);
expect(
player.state,
(playerState) =>
playerState != PlayerState.stopped &&
playerState != PlayerState.paused,
);
await player.stop();
}
Expand Down Expand Up @@ -415,6 +389,71 @@ void main() async {
await player.dispose();
});

group('Call twice:', () {
testWidgets('SetSource', (WidgetTester tester) async {
final player = AudioPlayer();
await player.setSource(mp3Url1TestData.source);
expect(player.state, PlayerState.stopped);
await player.setSource(mp3Url1TestData.source);
expect(player.state, PlayerState.stopped);
await player.dispose();
expect(player.state, PlayerState.disposed);
});
testWidgets('Play', (WidgetTester tester) async {
final player = AudioPlayer();
await player.play(mp3Url1TestData.source);
expect(player.state, PlayerState.playing);
await player.play(mp3Url1TestData.source);
expect(player.state, PlayerState.playing);
await player.dispose();
expect(player.state, PlayerState.disposed);
});
testWidgets('Pause', (WidgetTester tester) async {
final player = AudioPlayer();
await player.play(mp3Url1TestData.source);
expect(player.state, PlayerState.playing);
await player.pause();
expect(player.state, PlayerState.paused);
await player.pause();
expect(player.state, PlayerState.paused);
await player.dispose();
expect(player.state, PlayerState.disposed);
});
testWidgets('Resume', (WidgetTester tester) async {
final player = AudioPlayer();
await player.setSource(mp3Url1TestData.source);
expect(player.state, PlayerState.stopped);
await player.resume();
expect(player.state, PlayerState.playing);
await player.resume();
expect(player.state, PlayerState.playing);
await player.dispose();
expect(player.state, PlayerState.disposed);
});
testWidgets('Stop', (WidgetTester tester) async {
final player = AudioPlayer();
await player.play(mp3Url1TestData.source);
expect(player.state, PlayerState.playing);
await player.stop();
expect(player.state, PlayerState.stopped);
await player.stop();
expect(player.state, PlayerState.stopped);
await player.dispose();
expect(player.state, PlayerState.disposed);
});
testWidgets('Release', (WidgetTester tester) async {
final player = AudioPlayer();
await player.play(mp3Url1TestData.source);
expect(player.state, PlayerState.playing);
await player.release();
expect(player.state, PlayerState.stopped);
await player.release();
expect(player.state, PlayerState.stopped);
await player.dispose();
expect(player.state, PlayerState.disposed);
});
});

group(
'Android only:',
() {
Expand Down
22 changes: 2 additions & 20 deletions packages/audioplayers/example/integration_test/platform_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:pub_semver/pub_semver.dart';

import 'lib/lib_source_test_data.dart';
import 'platform_features.dart';
Expand All @@ -20,15 +19,10 @@ final isAndroid = !kIsWeb && defaultTargetPlatform == TargetPlatform.android;

bool canDetermineDuration(SourceTestData td) {
// TODO(gustl22): cannot determine duration for VBR on Linux
// FIXME(gustl22): duration event is not emitted for short duration
// WAV on Linux (only platform tests, may be a race condition).
if (td.duration == null) {
return true;
}
if (isLinux) {
return !(td.isVBR || td.duration! < const Duration(seconds: 5));
}
return true;
return !isLinux || !td.isVBR;
}

void main() async {
Expand Down Expand Up @@ -69,16 +63,6 @@ void main() async {
testData: invalidAssetTestData,
),
);

// TODO(gustl22): remove when min supported Flutter version is 3.41.0
if (isLinux &&
FlutterVersion.version != null &&
Version.parse(FlutterVersion.version!) < Version.parse('3.41.0')) {
// Flutter throws a second failure event for invalid files on Linux.
// If not caught, it would be randomly thrown in the following tests.
final nextEvent = platform.getEventStream(playerId).first;
await tester.expectSettingSourceFailure(future: nextEvent);
}
},
);

Expand Down Expand Up @@ -147,9 +131,7 @@ void main() async {
),
);
},
// FIXME(gustl22): determines wrong initial position for m3u8 on Linux
skip: !canDetermineDuration(td) ||
isLinux && td.source == m3u8UrlTestData.source,
skip: !canDetermineDuration(td),
);
}

Expand Down
1 change: 0 additions & 1 deletion packages/audioplayers/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ dev_dependencies:
sdk: flutter
integration_test:
sdk: flutter
pub_semver: ^2.2.0
pubspec_parse: ^1.5.0

flutter:
Expand Down
Loading
Loading