feat: add tests for calls

This commit is contained in:
td 2023-12-19 21:55:30 +05:30
parent e1d4af80ae
commit b6d5ce02c4
No known key found for this signature in database
GPG Key ID: 62A30523D4D6CE28
7 changed files with 408 additions and 76 deletions

View File

@ -461,6 +461,7 @@ class CallSession {
Logs().d(
'[glare] new call $callId needs to be canceled because the older one ${prevCall.callId} has a smaller lex');
await hangup();
voip.currentCID = prevCall.callId;
return;
} else {
Logs().d(
@ -1690,8 +1691,6 @@ class CallSession {
String? txid,
CallCapabilities? capabilities,
SDPStreamMetadata? metadata}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
final content = {
'call_id': callId,
'party_id': party_id,
@ -1723,8 +1722,6 @@ class CallSession {
Future<String?> sendSelectCallAnswer(
Room room, String callId, String party_id, String selected_party_id,
{String version = voipProtoVersion, String? txid}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
final content = {
'call_id': callId,
'party_id': party_id,
@ -1748,8 +1745,6 @@ class CallSession {
Future<String?> sendCallReject(
Room room, String callId, String party_id, String? reason,
{String version = voipProtoVersion, String? txid}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
final content = {
'call_id': callId,
'party_id': party_id,
@ -1778,7 +1773,6 @@ class CallSession {
String? txid,
CallCapabilities? capabilities,
SDPStreamMetadata? metadata}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
final content = {
'call_id': callId,
'party_id': party_id,
@ -1825,7 +1819,6 @@ class CallSession {
String version = voipProtoVersion,
String? txid,
}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
final content = {
'call_id': callId,
'party_id': party_id,
@ -1854,7 +1847,6 @@ class CallSession {
String? txid,
CallCapabilities? capabilities,
SDPStreamMetadata? metadata}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
final content = {
'call_id': callId,
'party_id': party_id,
@ -1879,8 +1871,6 @@ class CallSession {
Future<String?> sendHangupCall(
Room room, String callId, String party_id, String? hangupCause,
{String version = voipProtoVersion, String? txid}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
final content = {
'call_id': callId,
'party_id': party_id,
@ -1913,7 +1903,6 @@ class CallSession {
Future<String?> sendSDPStreamMetadataChanged(
Room room, String callId, String party_id, SDPStreamMetadata metadata,
{String version = voipProtoVersion, String? txid}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
final content = {
'call_id': callId,
'party_id': party_id,
@ -1938,7 +1927,6 @@ class CallSession {
Future<String?> sendCallReplaces(
Room room, String callId, String party_id, CallReplaces callReplaces,
{String version = voipProtoVersion, String? txid}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
final content = {
'call_id': callId,
'party_id': party_id,
@ -1963,7 +1951,6 @@ class CallSession {
Future<String?> sendAssertedIdentity(Room room, String callId,
String party_id, AssertedIdentity assertedIdentity,
{String version = voipProtoVersion, String? txid}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
final content = {
'call_id': callId,
'party_id': party_id,
@ -1985,7 +1972,7 @@ class CallSession {
Map<String, dynamic> content, {
String? txid,
}) async {
txid ??= client.generateUniqueTransactionId();
txid ??= VoIP.customTxid ?? client.generateUniqueTransactionId();
final mustEncrypt = room.encrypted && client.encryptionEnabled;
// opponentDeviceId is only set for a few events during group calls,

View File

@ -30,6 +30,9 @@ abstract class WebRTCDelegate {
}
class VoIP {
// used only for internal tests, all txids for call events will be overwritten to this
static String? customTxid;
TurnServerCredentials? _turnServerCredentials;
Map<String, CallSession> calls = <String, CallSession>{};
Map<String, GroupCall> groupCalls = <String, GroupCall>{};

View File

@ -1,4 +1,5 @@
import 'package:test/test.dart';
import 'package:webrtc_interface/webrtc_interface.dart';
import 'package:matrix/matrix.dart';
import 'fake_client.dart';
@ -7,24 +8,16 @@ import 'webrtc_stub.dart';
void main() {
late Client matrix;
late Room room;
group('Call Tests', () {
late VoIP voip;
group('Call tests', () {
Logs().level = Level.info;
test('Login', () async {
setUp(() async {
matrix = await getClient();
});
voip = VoIP(matrix, MockWebRTCDelegate());
VoIP.customTxid = '1234';
final id = '!calls:example.com';
test('Create from json', () async {
final id = '!localpart:server.abc';
final membership = Membership.join;
room = Room(
client: matrix,
id: id,
membership: membership,
prev_batch: '',
);
room = matrix.getRoomById(id)!;
});
test('Test call methods', () async {
@ -54,10 +47,9 @@ void main() {
room, '1234', '4567', SDPStreamMetadata({}),
txid: '1234');
});
test('Test call lifetime', () async {
final voip = VoIP(matrix, MockWebRTCDelegate());
test('Call lifetime and age', () async {
expect(voip.currentCID, null);
// persist normal room messages
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
@ -77,12 +69,11 @@ void main() {
)
]))
})));
await Future.delayed(Duration(seconds: 3));
await Future.delayed(Duration(seconds: 2));
// confirm that no call got created after 3 seconds, which is
// expected in this case because the originTs was old asf
expect(voip.currentCID, null);
// persist normal room messages
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
@ -103,11 +94,13 @@ void main() {
)
]))
})));
await Future.delayed(Duration(seconds: 3));
await Future.delayed(Duration(seconds: 2));
// confirm that no call got created after 3 seconds, which is
// expected in this case because age was older than lifetime
expect(voip.currentCID, null);
// persist normal room messages
});
test('Call connection and hanging up', () async {
expect(voip.currentCID, null);
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
@ -118,11 +111,43 @@ void main() {
content: {
'lifetime': 60000,
'call_id': 'originTsValidCall',
'party_id': 'DPCIPPBGPO',
'party_id': 'GHTYAJCE_caller',
'version': '1',
'offer': {'type': 'offer', 'sdp': 'sdp'}
},
senderId: '@alice:testing.com',
eventId: 'newevent',
eventId: 'callerInviteEvent',
originServerTs: DateTime.now(),
)
]))
})));
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(events: [
MatrixEvent(
type: 'm.call.candidates',
content: {
'call_id': 'originTsValidCall',
'party_id': 'GHTYAJCE_caller',
'version': '1',
'candidates': [
{
'candidate': 'candidate:01UDP2122252543uwu50184typhost',
'sdpMid': '0',
'sdpMLineIndex': 0
},
{
'candidate':
'candidate:31TCP2105524479uwu9typhosttcptypeactive',
'sdpMid': '0',
'sdpMLineIndex': 0
}
],
},
senderId: '@alice:testing.com',
eventId: 'callerCallCandidatesEvent',
originServerTs: DateTime.now(),
)
]))
@ -134,8 +159,291 @@ void main() {
}
expect(voip.currentCID, 'originTsValidCall');
final call = voip.calls[voip.currentCID]!;
expect(call.state, CallState.kRinging);
await call.answer(txid: '1234');
call.pc!.onIceGatheringState!
.call(RTCIceGatheringState.RTCIceGatheringStateComplete);
// we send them manually anyway because our stub sends empty list of
// candidates
await call.sendCallCandidates(
room,
'originTsValidCall',
'GHTYAJCE',
[
{
'candidate': 'candidate:0 1 UDP 2122252543 uwu 50184 typ host',
'sdpMid': '0',
'sdpMLineIndex': 0
},
{
'candidate':
'candidate:3 1 TCP 2105524479 uwu 9 typ host tcptype active',
'sdpMid': '0',
'sdpMLineIndex': 0
}
],
txid: '1234');
expect(call.state, CallState.kConnecting);
// caller sends select answer
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(events: [
MatrixEvent(
type: 'm.call.select_answer',
content: {
'call_id': 'originTsValidCall',
'party_id': 'GHTYAJCE_caller',
'version': '1',
'lifetime': 10000,
'selected_party_id': 'GHTYAJCE'
},
senderId: '@alice:testing.com',
eventId: 'callerSelectAnswerEvent',
originServerTs: DateTime.now(),
)
]))
})));
call.pc!.onIceConnectionState!
.call(RTCIceConnectionState.RTCIceConnectionStateChecking);
call.pc!.onIceConnectionState!
.call(RTCIceConnectionState.RTCIceConnectionStateConnected);
// just to make sure there are no errors after running functions
// that are supposed to run once iceConnectionState is connected
await Future.delayed(Duration(seconds: 2));
expect(call.state, CallState.kConnected);
await call.hangup();
expect(call.state, CallState.kEnded);
expect(voip.currentCID, null);
});
test('Call answered elsewhere', () async {
expect(voip.currentCID, null);
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(events: [
MatrixEvent(
type: 'm.call.invite',
content: {
'lifetime': 60000,
'call_id': 'answer_elseWhere',
'party_id': 'GHTYAJCE_caller',
'version': '1',
'offer': {'type': 'offer', 'sdp': 'sdp'}
},
senderId: '@alice:testing.com',
eventId: 'callerInviteEvent',
originServerTs: DateTime.now(),
)
]))
})));
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(events: [
MatrixEvent(
type: 'm.call.candidates',
content: {
'call_id': 'answer_elseWhere',
'party_id': 'GHTYAJCE_caller',
'version': '1',
'candidates': [
{
'candidate': 'candidate:01UDP2122252543uwu50184typhost',
'sdpMid': '0',
'sdpMLineIndex': 0
},
{
'candidate':
'candidate:31TCP2105524479uwu9typhosttcptypeactive',
'sdpMid': '0',
'sdpMLineIndex': 0
}
],
},
senderId: '@alice:testing.com',
eventId: 'callerCallCandidatesEvent',
originServerTs: DateTime.now(),
)
]))
})));
while (voip.currentCID != 'answer_elseWhere') {
// call invite looks valid, call should be created now :D
await Future.delayed(Duration(milliseconds: 50));
Logs().d('Waiting for currentCID to update');
}
expect(voip.currentCID, 'answer_elseWhere');
final call = voip.calls[voip.currentCID]!;
expect(call.state, CallState.kRinging);
// caller sends select answer
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(events: [
MatrixEvent(
type: 'm.call.select_answer',
content: {
'call_id': 'answer_elseWhere',
'party_id': 'GHTYAJCE_caller',
'version': '1',
'lifetime': 10000,
'selected_party_id':
'not_us' // selected some other device for answer
},
senderId: '@alice:testing.com',
eventId: 'callerSelectAnswerEvent',
originServerTs: DateTime.now(),
)
]))
})));
// wait for select answer to end the call
await Future.delayed(Duration(seconds: 2));
// call ended because answered elsewhere
expect(call.state, CallState.kEnded);
expect(voip.currentCID, null);
});
test('Reject incoming call', () async {
expect(voip.currentCID, null);
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(events: [
MatrixEvent(
type: 'm.call.invite',
content: {
'lifetime': 60000,
'call_id': 'reject_call',
'party_id': 'GHTYAJCE_caller',
'version': '1',
'offer': {'type': 'offer', 'sdp': 'sdp'}
},
senderId: '@alice:testing.com',
eventId: 'callerInviteEvent',
originServerTs: DateTime.now(),
)
]))
})));
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(events: [
MatrixEvent(
type: 'm.call.candidates',
content: {
'call_id': 'reject_call',
'party_id': 'GHTYAJCE_caller',
'version': '1',
'candidates': [
{
'candidate': 'candidate:01UDP2122252543uwu50184typhost',
'sdpMid': '0',
'sdpMLineIndex': 0
},
{
'candidate':
'candidate:31TCP2105524479uwu9typhosttcptypeactive',
'sdpMid': '0',
'sdpMLineIndex': 0
}
],
},
senderId: '@alice:testing.com',
eventId: 'callerCallCandidatesEvent',
originServerTs: DateTime.now(),
)
]))
})));
while (voip.currentCID != 'reject_call') {
// call invite looks valid, call should be created now :D
await Future.delayed(Duration(milliseconds: 50));
Logs().d('Waiting for currentCID to update');
}
expect(voip.currentCID, 'reject_call');
final call = voip.calls[voip.currentCID]!;
expect(call.state, CallState.kRinging);
await call.reject();
// call ended because answered elsewhere
expect(call.state, CallState.kEnded);
expect(voip.currentCID, null);
});
test('Glare after invite was sent', () async {
expect(voip.currentCID, null);
final firstCall = await voip.inviteToCall(room.id, CallType.kVoice);
await firstCall.pc!.onRenegotiationNeeded!.call();
expect(firstCall.state, CallState.kInviteSent);
// KABOOM YOU JUST GLARED
await Future.delayed(Duration(seconds: 3));
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(events: [
MatrixEvent(
type: 'm.call.invite',
content: {
'lifetime': 60000,
'call_id': 'zzzz_glare_2nd_call',
'party_id': 'GHTYAJCE_caller',
'version': '1',
'offer': {'type': 'offer', 'sdp': 'sdp'}
},
senderId: '@alice:testing.com',
eventId: 'callerInviteEvent',
originServerTs: DateTime.now(),
)
]))
})));
await Future.delayed(Duration(seconds: 3));
expect(voip.currentCID, firstCall.callId);
await firstCall.hangup();
});
test('Glare before invite was sent', () async {
expect(voip.currentCID, null);
final firstCall = await voip.inviteToCall(room.id, CallType.kVoice);
expect(firstCall.state, CallState.kCreateOffer);
// KABOOM YOU JUST GLARED, but this tiem you were still preparing your call
// so just cancel that instead
await Future.delayed(Duration(seconds: 3));
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(events: [
MatrixEvent(
type: 'm.call.invite',
content: {
'lifetime': 60000,
'call_id': 'zzzz_glare_2nd_call',
'party_id': 'GHTYAJCE_caller',
'version': '1',
'offer': {'type': 'offer', 'sdp': 'sdp'}
},
senderId: '@alice:testing.com',
eventId: 'callerInviteEvent',
originServerTs: DateTime.now(),
)
]))
})));
await Future.delayed(Duration(seconds: 3));
expect(voip.currentCID, 'zzzz_glare_2nd_call');
});
});
}

View File

@ -121,23 +121,23 @@ void main() {
expect(matrix.accountData.length, 10);
expect(matrix.getDirectChatFromUserId('@bob:example.com'),
'!726s6s6q:example.com');
expect(matrix.rooms[1].directChatMatrixID, '@bob:example.com');
expect(matrix.rooms[2].directChatMatrixID, '@bob:example.com');
expect(matrix.directChats, matrix.accountData['m.direct']?.content);
// ignore: deprecated_member_use_from_same_package
expect(matrix.presences.length, 1);
expect(matrix.rooms[1].ephemerals.length, 2);
expect(matrix.rooms[1].typingUsers.length, 1);
expect(matrix.rooms[1].typingUsers[0].id, '@alice:example.com');
expect(matrix.rooms[1].roomAccountData.length, 3);
expect(matrix.rooms[1].encrypted, true);
expect(matrix.rooms[1].encryptionAlgorithm,
expect(matrix.rooms[2].ephemerals.length, 2);
expect(matrix.rooms[2].typingUsers.length, 1);
expect(matrix.rooms[2].typingUsers[0].id, '@alice:example.com');
expect(matrix.rooms[2].roomAccountData.length, 3);
expect(matrix.rooms[2].encrypted, true);
expect(matrix.rooms[2].encryptionAlgorithm,
Client.supportedGroupEncryptionAlgorithms.first);
expect(
matrix.rooms[1].receiptState.global.otherUsers['@alice:example.com']
matrix.rooms[2].receiptState.global.otherUsers['@alice:example.com']
?.ts,
1436451550453);
expect(
matrix.rooms[1].receiptState.global.otherUsers['@alice:example.com']
matrix.rooms[2].receiptState.global.otherUsers['@alice:example.com']
?.eventId,
'\$7365636s6r6432:example.com');
@ -145,8 +145,8 @@ void main() {
.singleWhere((room) => room.membership == Membership.invite);
expect(inviteRoom.name, 'My Room Name');
expect(inviteRoom.states[EventTypes.RoomMember]?.length, 1);
expect(matrix.rooms.length, 2);
expect(matrix.rooms[1].canonicalAlias,
expect(matrix.rooms.length, 3);
expect(matrix.rooms[2].canonicalAlias,
"#famedlyContactDiscovery:${matrix.userID!.split(":")[1]}");
// ignore: deprecated_member_use_from_same_package
expect(matrix.presences['@alice:example.com']?.presence,
@ -209,7 +209,7 @@ void main() {
final eventUpdateList = await eventUpdateListFuture;
expect(eventUpdateList.length, 18);
expect(eventUpdateList.length, 20);
expect(eventUpdateList[0].content['type'], 'm.room.member');
expect(eventUpdateList[0].roomID, '!726s6s6q:example.com');
@ -256,13 +256,13 @@ void main() {
expect(eventUpdateList[10].roomID, '!726s6s6q:example.com');
expect(eventUpdateList[10].type, EventUpdateType.accountData);
expect(eventUpdateList[11].content['type'], 'm.room.name');
expect(eventUpdateList[11].roomID, '!696r7674:example.com');
expect(eventUpdateList[11].type, EventUpdateType.inviteState);
expect(eventUpdateList[11].content['type'], 'm.room.member');
expect(eventUpdateList[11].roomID, '!calls:example.com');
expect(eventUpdateList[11].type, EventUpdateType.state);
expect(eventUpdateList[12].content['type'], 'm.room.member');
expect(eventUpdateList[12].roomID, '!696r7674:example.com');
expect(eventUpdateList[12].type, EventUpdateType.inviteState);
expect(eventUpdateList[12].roomID, '!calls:example.com');
expect(eventUpdateList[12].type, EventUpdateType.state);
await matrix.onToDeviceEvent.close();
@ -909,7 +909,7 @@ void main() {
await Future.delayed(Duration(milliseconds: 500));
expect(client1.isLogged(), true);
expect(client1.rooms.length, 2);
expect(client1.rooms.length, 3);
final client2 = Client(
'testclient',
@ -926,7 +926,7 @@ void main() {
expect(client2.homeserver, client1.homeserver);
expect(client2.deviceID, client1.deviceID);
expect(client2.deviceName, client1.deviceName);
expect(client2.rooms.length, 2);
expect(client2.rooms.length, 3);
if (client2.encryptionEnabled) {
expect(client2.encryption?.fingerprintKey,
client1.encryption?.fingerprintKey);
@ -1111,7 +1111,7 @@ void main() {
final client = await getClient();
await Future.delayed(Duration(milliseconds: 50));
expect(client.rooms.length, 2,
expect(client.rooms.length, 3,
reason:
'Count of invited+joined before loadArchive() rooms does not match');
expect(client.archivedRooms.length, 0,
@ -1120,7 +1120,7 @@ void main() {
await client.loadArchive();
expect(client.rooms.length, 2,
expect(client.rooms.length, 3,
reason: 'Count of invited+joined rooms does not match');
expect(client.archivedRooms.length, 2,
reason: 'Count of archived rooms does not match');

View File

@ -572,7 +572,37 @@ class FakeMatrixApi extends BaseClient {
}
]
}
}
},
'!calls:example.com': {
'state': {
'events': [
{
'sender': '@test:fakeServer.notExisting',
'type': 'm.room.member',
'state_key': '@test:fakeServer.notExisting',
'content': {
'membership': 'join',
'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF',
'displayname': 'Test User',
},
'origin_server_ts': 1417731086795,
'event_id': 'calls_1:example.com'
},
{
'sender': '@alice:example.com',
'type': 'm.room.member',
'state_key': '@alice:example.com',
'content': {
'membership': 'join',
'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF',
'displayname': 'Alice Margatroid',
},
'origin_server_ts': 1417731086795,
'event_id': 'calls_2:example.com'
},
]
},
},
},
'invite': {
'!696r7674:example.com': {
@ -2430,29 +2460,29 @@ class FakeMatrixApi extends BaseClient {
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!localpart%3Aserver.abc/state/m.room.guest_access':
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!localpart%3Aserver.abc/send/m.call.invite/1234':
'/client/v3/rooms/!calls%3Aexample.com/send/m.call.invite/1234':
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!localpart%3Aserver.abc/send/m.call.answer/1234':
'/client/v3/rooms/!calls%3Aexample.com/send/m.call.answer/1234':
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!localpart%3Aserver.abc/send/m.call.select_answer/1234':
'/client/v3/rooms/!calls%3Aexample.com/send/m.call.select_answer/1234':
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!localpart%3Aserver.abc/send/m.call.reject/1234':
'/client/v3/rooms/!calls%3Aexample.com/send/m.call.reject/1234':
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!localpart%3Aserver.abc/send/m.call.negotiate/1234':
'/client/v3/rooms/!calls%3Aexample.com/send/m.call.negotiate/1234':
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!localpart%3Aserver.abc/send/m.call.candidates/1234':
'/client/v3/rooms/!calls%3Aexample.com/send/m.call.candidates/1234':
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!localpart%3Aserver.abc/send/m.call.hangup/1234':
'/client/v3/rooms/!calls%3Aexample.com/send/m.call.hangup/1234':
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!localpart%3Aserver.abc/send/m.call.replaces/1234':
'/client/v3/rooms/!calls%3Aexample.com/send/m.call.replaces/1234':
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!localpart%3Aserver.abc/send/m.call.asserted_identity/1234':
'/client/v3/rooms/!calls%3Aexample.com/send/m.call.asserted_identity/1234':
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!localpart%3Aserver.abc/send/m.call.sdp_stream_metadata_changed/1234':
'/client/v3/rooms/!calls%3Aexample.com/send/m.call.sdp_stream_metadata_changed/1234':
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!localpart%3Aserver.abc/send/org.matrix.call.sdp_stream_metadata_changed/1234':
'/client/v3/rooms/!calls%3Aexample.com/send/org.matrix.call.sdp_stream_metadata_changed/1234':
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!localpart%3Aserver.abc/send/org.matrix.call.asserted_identity/1234':
'/client/v3/rooms/!calls%3Aexample.com/send/org.matrix.call.asserted_identity/1234':
(var req) => {'event_id': '1234'},
'/client/v3/rooms/!1234%3Aexample.com/redact/1143273582443PhrSn%3Aexample.org/1234':
(var req) => {'event_id': '1234'},

View File

@ -62,7 +62,7 @@ void main() {
final archive = await client.loadArchiveWithTimeline();
expect(archive.length, 2);
expect(client.rooms.length, 2);
expect(client.rooms.length, 3);
expect(archive[0].room.id, '!5345234234:example.com');
expect(archive[0].room.membership, Membership.leave);
expect(archive[0].room.name, 'The room name');

View File

@ -150,7 +150,9 @@ class MockRTCPeerConnection implements RTCPeerConnection {
}
@override
RTCIceGatheringState? get iceGatheringState => throw UnimplementedError();
// value doesn't matter we do onIceConnectionState.call manually
RTCIceGatheringState? get iceGatheringState =>
RTCIceGatheringState.RTCIceGatheringStateComplete;
@override
Future<RTCIceGatheringState?> getIceGatheringState() async {
@ -158,7 +160,9 @@ class MockRTCPeerConnection implements RTCPeerConnection {
}
@override
RTCIceConnectionState? get iceConnectionState => throw UnimplementedError();
// value doesn't matter we do onIceConnectionState.call manually
RTCIceConnectionState? get iceConnectionState =>
RTCIceConnectionState.RTCIceConnectionStateNew;
@override
Future<RTCIceConnectionState?> getIceConnectionState() async {