tests and mocks for e2e testing (#78)
* tests and mocks for e2e testing * formatting * fix analyze errors
This commit is contained in:
+17
-10
@@ -3,6 +3,7 @@ import 'dart:async';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
|
||||
import 'package:livekit_client/src/support/websocket.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../constants.dart';
|
||||
@@ -28,11 +29,10 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
|
||||
static const _lossyDCLabel = '_lossy';
|
||||
static const _reliableDCLabel = '_reliable';
|
||||
|
||||
// Reference to the Room
|
||||
final Room room;
|
||||
|
||||
final SignalClient signalClient;
|
||||
|
||||
final PeerConnectionCreate _peerConnectionCreate;
|
||||
|
||||
@internal
|
||||
PCTransport? publisher;
|
||||
|
||||
@@ -60,6 +60,7 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
|
||||
// remember url and token for reconnect
|
||||
String? url;
|
||||
String? token;
|
||||
ConnectOptions? connectOptions;
|
||||
|
||||
bool _subscriberPrimary = false;
|
||||
|
||||
@@ -71,9 +72,11 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
|
||||
final delays = CancelableDelayManager();
|
||||
|
||||
Engine({
|
||||
required this.room,
|
||||
SignalClient? signalClient,
|
||||
}) : signalClient = signalClient ?? SignalClient() {
|
||||
PeerConnectionCreate? peerConnectionCreate,
|
||||
}) : signalClient = signalClient ?? SignalClient(LiveKitWebSocket.connect),
|
||||
_peerConnectionCreate =
|
||||
peerConnectionCreate ?? rtc.createPeerConnection {
|
||||
if (kDebugMode) {
|
||||
// log all EngineEvents
|
||||
events.listen((event) => logger.fine('[EngineEvent] $objectId ${event}'));
|
||||
@@ -92,9 +95,11 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
|
||||
Future<void> connect(
|
||||
String url,
|
||||
String token,
|
||||
ConnectOptions? connectOptions,
|
||||
) async {
|
||||
this.url = url;
|
||||
this.token = token;
|
||||
this.connectOptions = connectOptions ?? const ConnectOptions();
|
||||
|
||||
_updateConnectionState(ConnectionState.connecting);
|
||||
|
||||
@@ -103,7 +108,7 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
|
||||
await signalClient.connect(
|
||||
url,
|
||||
token,
|
||||
connectOptions: room.connectOptions,
|
||||
connectOptions: this.connectOptions,
|
||||
);
|
||||
|
||||
// wait for join response
|
||||
@@ -272,7 +277,7 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
|
||||
await signalClient.connect(
|
||||
url!,
|
||||
token!,
|
||||
connectOptions: room.connectOptions,
|
||||
connectOptions: connectOptions,
|
||||
reconnect: true,
|
||||
);
|
||||
|
||||
@@ -331,7 +336,7 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
|
||||
|
||||
// RTCConfiguration? config;
|
||||
// use server-provided iceServers if not provided by user
|
||||
final connectOptions = room.connectOptions ?? const ConnectOptions();
|
||||
final connectOptions = this.connectOptions ?? const ConnectOptions();
|
||||
final serverIceServers =
|
||||
_serverProvidedIceServers.map((e) => e.toSDKType()).toList();
|
||||
|
||||
@@ -342,8 +347,10 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
|
||||
.copyWith(iceServers: serverIceServers);
|
||||
}
|
||||
|
||||
publisher = await PCTransport.create(rtcConfiguration);
|
||||
subscriber = await PCTransport.create(rtcConfiguration);
|
||||
publisher =
|
||||
await PCTransport.create(_peerConnectionCreate, rtcConfiguration);
|
||||
subscriber =
|
||||
await PCTransport.create(_peerConnectionCreate, rtcConfiguration);
|
||||
|
||||
publisher?.pc.onIceCandidate = (rtc.RTCIceCandidate candidate) {
|
||||
logger.fine('publisher onIceCandidate');
|
||||
|
||||
+7
-12
@@ -62,16 +62,14 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
|
||||
UnmodifiableListView<Participant> get activeSpeakers =>
|
||||
UnmodifiableListView<Participant>(_activeSpeakers);
|
||||
|
||||
late final engine = Engine(room: this);
|
||||
final Engine engine;
|
||||
|
||||
// suppport for multiple event listeners
|
||||
late final _engineListener = engine.createListener();
|
||||
late final EventsListener<EngineEvent> _engineListener;
|
||||
|
||||
Room({
|
||||
this.connectOptions,
|
||||
this.roomOptions,
|
||||
}) {
|
||||
//
|
||||
Room({this.connectOptions, this.roomOptions, Engine? engine})
|
||||
: engine = engine ?? Engine() {
|
||||
_engineListener = this.engine.createListener();
|
||||
_setUpListeners();
|
||||
|
||||
// Any event emitted will trigger ChangeNotifier
|
||||
@@ -88,7 +86,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
|
||||
// dispose all listeners for RTCEngine
|
||||
await _engineListener.dispose();
|
||||
// dispose the engine
|
||||
await engine.dispose();
|
||||
await this.engine.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -102,10 +100,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
|
||||
this.connectOptions = connectOptions ?? this.connectOptions;
|
||||
this.roomOptions = roomOptions ?? this.roomOptions;
|
||||
|
||||
return engine.connect(
|
||||
url,
|
||||
token,
|
||||
);
|
||||
return engine.connect(url, token, this.connectOptions);
|
||||
}
|
||||
|
||||
void _setUpListeners() => _engineListener
|
||||
|
||||
@@ -22,10 +22,11 @@ class SignalClient extends Disposable with EventsEmittable<SignalEvent> {
|
||||
// Connection state of the socket conection.
|
||||
ConnectionState _connectionState = ConnectionState.disconnected;
|
||||
|
||||
final WebSocketConnector _wsConnector;
|
||||
LiveKitWebSocket? _ws;
|
||||
|
||||
@internal
|
||||
SignalClient() {
|
||||
SignalClient(WebSocketConnector wsConnector) : _wsConnector = wsConnector {
|
||||
events.listen((event) {
|
||||
logger.fine('[SignalEvent] $event');
|
||||
});
|
||||
@@ -59,7 +60,7 @@ class SignalClient extends Disposable with EventsEmittable<SignalEvent> {
|
||||
// Clean up existing socket
|
||||
await _cleanUp();
|
||||
// Attempt to connect
|
||||
_ws = await LiveKitWebSocket.connect(
|
||||
_ws = await _wsConnector(
|
||||
rtcUri,
|
||||
WebSocketEventHandlers(
|
||||
onData: _onSocketData,
|
||||
@@ -174,6 +175,9 @@ class SignalClient extends Disposable with EventsEmittable<SignalEvent> {
|
||||
events.emit(
|
||||
SignalSpeakersChangedEvent(speakers: msg.speakersChanged.speakers));
|
||||
break;
|
||||
case lk_rtc.SignalResponse_Message.roomUpdate:
|
||||
// TODO
|
||||
break;
|
||||
case lk_rtc.SignalResponse_Message.connectionQuality:
|
||||
events.emit(SignalConnectionQualityUpdateEvent(
|
||||
updates: msg.connectionQuality.updates,
|
||||
@@ -209,8 +213,9 @@ class SignalClient extends Disposable with EventsEmittable<SignalEvent> {
|
||||
case lk_rtc.SignalResponse_Message.refreshToken:
|
||||
events.emit(SignalTokenUpdatedEvent(token: msg.refreshToken));
|
||||
break;
|
||||
default:
|
||||
logger.warning('skipping unsupported signal message');
|
||||
case lk_rtc.SignalResponse_Message.notSet:
|
||||
logger.info('signal message not set');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ import '../types.dart';
|
||||
import '../utils.dart';
|
||||
|
||||
typedef PCTransportOnOffer = void Function(rtc.RTCSessionDescription offer);
|
||||
typedef PeerConnectionCreate = Future<rtc.RTCPeerConnection> Function(
|
||||
Map<String, dynamic> configuration,
|
||||
[Map<String, dynamic> constraints]);
|
||||
|
||||
/// a wrapper around PeerConnection
|
||||
class PCTransport extends Disposable {
|
||||
@@ -55,10 +58,11 @@ class PCTransport extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
static Future<PCTransport> create([RTCConfiguration? rtcConfig]) async {
|
||||
static Future<PCTransport> create(PeerConnectionCreate peerConnectionCreate,
|
||||
[RTCConfiguration? rtcConfig]) async {
|
||||
rtcConfig ??= const RTCConfiguration();
|
||||
logger.fine('[PCTransport] creating ${rtcConfig.toMap()}');
|
||||
final _ = await rtc.createPeerConnection(rtcConfig.toMap());
|
||||
final _ = await peerConnectionCreate(rtcConfig.toMap());
|
||||
return PCTransport._(_);
|
||||
}
|
||||
|
||||
@@ -159,5 +163,6 @@ class PCTransport extends Disposable {
|
||||
} catch (_) {
|
||||
logger.warning('pc.getRemoteDescription failed with error: $_');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +305,7 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
|
||||
return await publishVideoTrack(track);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Control who can subscribe to LocalParticipant's published tracks.
|
||||
|
||||
@@ -65,6 +65,7 @@ class RemoteParticipant extends Participant<RemoteTrackPublication> {
|
||||
RemoteTrackPublication? getTrackPublication(String sid) {
|
||||
final pub = trackPublications[sid];
|
||||
if (pub is RemoteTrackPublication) return pub;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// for internal use
|
||||
|
||||
@@ -36,5 +36,6 @@ class Native {
|
||||
} catch (error) {
|
||||
logger.warning('appleOSVersionString did throw error: ${error}');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'platform/io.dart' if (dart.library.html) 'platform/web.dart';
|
||||
|
||||
// Returns the current platform which works for both web and devices.
|
||||
PlatformType lkPlatform() => lkPlatformImplementation();
|
||||
|
||||
bool lkPlatformIs(PlatformType type) => lkPlatform() == type;
|
||||
|
||||
@internal
|
||||
bool lkPlatformIsTest() => Platform.environment.containsKey('FLUTTER_TEST');
|
||||
|
||||
enum PlatformType {
|
||||
web,
|
||||
windows,
|
||||
|
||||
@@ -23,6 +23,7 @@ class WebSocketEventHandlers {
|
||||
final WebSocketOnData? onData;
|
||||
final WebSocketOnError? onError;
|
||||
final WebSocketOnDispose? onDispose;
|
||||
|
||||
const WebSocketEventHandlers({
|
||||
this.onData,
|
||||
this.onError,
|
||||
@@ -30,12 +31,13 @@ class WebSocketEventHandlers {
|
||||
});
|
||||
}
|
||||
|
||||
typedef WebSocketConnector = Future<LiveKitWebSocket> Function(Uri uri,
|
||||
[WebSocketEventHandlers? options]);
|
||||
|
||||
abstract class LiveKitWebSocket extends Disposable {
|
||||
void send(List<int> data);
|
||||
|
||||
static Future<LiveKitWebSocket> connect(
|
||||
Uri uri, [
|
||||
WebSocketEventHandlers? options,
|
||||
]) =>
|
||||
static Future<LiveKitWebSocket> connect(Uri uri,
|
||||
[WebSocketEventHandlers? options]) =>
|
||||
lkWebSocketConnect(uri, options);
|
||||
}
|
||||
|
||||
@@ -65,6 +65,11 @@ class Utils {
|
||||
static final _deviceInfoPlugin = DeviceInfoPlugin();
|
||||
|
||||
static Future<lk_models.ClientInfo?> _clientInfo() async {
|
||||
if (lkPlatformIsTest()) {
|
||||
return lk_models.ClientInfo(
|
||||
os: 'test',
|
||||
);
|
||||
}
|
||||
switch (lkPlatform()) {
|
||||
case PlatformType.web:
|
||||
return lk_models.ClientInfo(
|
||||
@@ -123,6 +128,7 @@ class Utils {
|
||||
default:
|
||||
// case PlatformType.fuchsia:
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@internal
|
||||
|
||||
Reference in New Issue
Block a user