Skip to content

Commit 3b01892

Browse files
committed
test2
1 parent 15a836c commit 3b01892

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed

test/core/room_e2e_test.dart

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@
1515
@Timeout(Duration(seconds: 5))
1616
library;
1717

18+
import 'dart:typed_data';
19+
1820
import 'package:flutter_test/flutter_test.dart';
21+
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
22+
import 'package:logging/logging.dart';
1923

2024
import 'package:livekit_client/livekit_client.dart';
25+
import 'package:livekit_client/src/internal/events.dart';
2126
import '../mock/e2e_container.dart';
2227
import '../mock/test_data.dart';
2328
import '../mock/websocket_mock.dart';
@@ -28,6 +33,12 @@ void main() {
2833
late Room room;
2934
late MockWebSocketConnector ws;
3035
setUp(() async {
36+
// configure logs for debugging
37+
Logger.root.level = Level.FINEST;
38+
Logger.root.onRecord.listen((record) {
39+
print('[${record.level.name}]: ${record.message}');
40+
});
41+
3142
container = E2EContainer();
3243
room = container.room;
3344
ws = container.wsConnector;
@@ -167,5 +178,144 @@ void main() {
167178
emits(predicate<RoomDisconnectedEvent>((event) => event.reason == DisconnectReason.unknown)));
168179
ws.onData(leaveResponse.writeToBuffer());
169180
});
181+
182+
test('tracks arriving before participant metadata are handled once metadata arrives', () async {
183+
final fakeStream = _FakeMediaStream('${remoteParticipantData.sid}|remote_stream');
184+
final fakeTrack = _FakeMediaStreamTrack(
185+
id: remoteAudioTrack.sid,
186+
kind: 'audio',
187+
);
188+
189+
var subscriptionException = false;
190+
room.events.on<TrackSubscriptionExceptionEvent>((event) {
191+
subscriptionException = true;
192+
});
193+
194+
// Emit onTrack before participant update arrives.
195+
container.engine.events.emit(EngineTrackAddedEvent(
196+
track: fakeTrack,
197+
stream: fakeStream,
198+
receiver: null,
199+
));
200+
201+
// Now deliver participant metadata.
202+
ws.onData(participantJoinResponse.writeToBuffer());
203+
204+
// Track should eventually subscribe once metadata is available.
205+
final trackSubscribed = await room.events.waitFor<TrackSubscribedEvent>(
206+
duration: const Duration(seconds: 1),
207+
);
208+
209+
expect(subscriptionException, isFalse, reason: 'Track subscription should not fail when metadata arrives later');
210+
expect(trackSubscribed.participant.sid, remoteParticipantData.sid);
211+
expect(trackSubscribed.publication.track, isNotNull);
212+
});
170213
});
171214
}
215+
216+
class _FakeMediaStream extends rtc.MediaStream {
217+
final List<rtc.MediaStreamTrack> _tracks = [];
218+
219+
_FakeMediaStream(String id) : super(id, 'fake-owner');
220+
221+
@override
222+
bool? get active => true;
223+
224+
@override
225+
Future<void> addTrack(rtc.MediaStreamTrack track, {bool addToNative = true}) async {
226+
_tracks.add(track);
227+
}
228+
229+
@override
230+
Future<rtc.MediaStream> clone() async => _FakeMediaStream('${id}_clone');
231+
232+
@override
233+
List<rtc.MediaStreamTrack> getAudioTracks() => _tracks.where((t) => t.kind == 'audio').toList();
234+
235+
@override
236+
Future<void> getMediaTracks() async {}
237+
238+
@override
239+
List<rtc.MediaStreamTrack> getTracks() => List<rtc.MediaStreamTrack>.from(_tracks);
240+
241+
@override
242+
List<rtc.MediaStreamTrack> getVideoTracks() => _tracks.where((t) => t.kind == 'video').toList();
243+
244+
@override
245+
Future<void> removeTrack(rtc.MediaStreamTrack track, {bool removeFromNative = true}) async {
246+
_tracks.remove(track);
247+
}
248+
}
249+
250+
class _FakeMediaStreamTrack implements rtc.MediaStreamTrack {
251+
@override
252+
rtc.StreamTrackCallback? onEnded;
253+
254+
@override
255+
rtc.StreamTrackCallback? onMute;
256+
257+
@override
258+
rtc.StreamTrackCallback? onUnMute;
259+
260+
@override
261+
bool enabled;
262+
263+
@override
264+
final String id;
265+
266+
@override
267+
final String kind;
268+
269+
@override
270+
String? get label => '$kind-track';
271+
272+
@override
273+
bool? get muted => false;
274+
275+
_FakeMediaStreamTrack({
276+
required this.id,
277+
required this.kind,
278+
this.enabled = true,
279+
});
280+
281+
@override
282+
Future<void> applyConstraints([Map<String, dynamic>? constraints]) async {}
283+
284+
@override
285+
Future<rtc.MediaStreamTrack> clone() async => _FakeMediaStreamTrack(id: id, kind: kind, enabled: enabled);
286+
287+
@override
288+
Future<void> dispose() async {}
289+
290+
@override
291+
Future<void> adaptRes(int width, int height) async {}
292+
293+
@override
294+
Map<String, dynamic> getConstraints() => const {};
295+
296+
@override
297+
Map<String, dynamic> getSettings() => const {};
298+
299+
@override
300+
Future<void> stop() async {}
301+
302+
@override
303+
void enableSpeakerphone(bool enable) {}
304+
305+
@override
306+
Future<ByteBuffer> captureFrame() {
307+
throw UnimplementedError();
308+
}
309+
310+
@override
311+
Future<bool> hasTorch() async => false;
312+
313+
@override
314+
Future<void> setTorch(bool torch) async {}
315+
316+
@override
317+
Future<bool> switchCamera() async => false;
318+
319+
@override
320+
String toString() => 'FakeMediaStreamTrack(id: $id, kind: $kind, enabled: $enabled)';
321+
}

0 commit comments

Comments
 (0)