Skip to content

Commit

Permalink
fix: ignore calls with age older than lifetime
Browse files Browse the repository at this point in the history
feat: add barebones WebRTCDelegateMock
  • Loading branch information
td-famedly committed Dec 15, 2023
1 parent 520dfdb commit 6db1cf3
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 29 deletions.
16 changes: 16 additions & 0 deletions lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2142,6 +2142,22 @@ class Client extends MatrixApi {
return false;
});
}

final age = callEvent.unsigned?.tryGet<int>('age') ??
(DateTime.now().millisecondsSinceEpoch -
callEvent.originServerTs.millisecondsSinceEpoch);

callEvents.removeWhere((element) {
if (callEvent.type == EventTypes.CallInvite &&
age >
(callEvent.content.tryGet<int>('lifetime') ??
CallTimeouts.callInviteLifetime.inMilliseconds)) {
Logs().v(
'Ommiting invite event ${callEvent.eventId} as age was older than lifetime');
return true;
}
return false;
});
}
}
}
Expand Down
134 changes: 134 additions & 0 deletions test/calls_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import 'package:test/test.dart';

import 'package:matrix/matrix.dart';
import 'fake_client.dart';
import 'webrtc_stub.dart';

void main() {
late Client matrix;
late Room room;

group('Call Tests', () {
Logs().level = Level.info;

test('Login', () async {
matrix = await getClient();
});

test('Create from json', () async {
final id = '!localpart:server.abc';
final membership = Membership.join;

room = Room(
client: matrix,
id: id,
membership: membership,
prev_batch: '',
);
});

test('Test call methods', () async {
final call = CallSession(CallOptions()..room = room);
await call.sendInviteToCall(room, '1234', 1234, '4567', '7890', 'sdp',
txid: '1234');
await call.sendAnswerCall(room, '1234', 'sdp', '4567', txid: '1234');
await call.sendCallCandidates(room, '1234', '4567', [], txid: '1234');
await call.sendSelectCallAnswer(room, '1234', '4567', '6789',
txid: '1234');
await call.sendCallReject(room, '1234', '4567', 'busy', txid: '1234');
await call.sendCallNegotiate(room, '1234', 1234, '4567', 'sdp',
txid: '1234');
await call.sendHangupCall(room, '1234', '4567', 'user_hangup',
txid: '1234');
await call.sendAssertedIdentity(
room,
'1234',
'4567',
AssertedIdentity()
..displayName = 'name'
..id = 'some_id',
txid: '1234');
await call.sendCallReplaces(room, '1234', '4567', CallReplaces(),
txid: '1234');
await call.sendSDPStreamMetadataChanged(
room, '1234', '4567', SDPStreamMetadata({}),
txid: '1234');
});
test('Test call lifetime', () async {
final voip = VoIP(matrix, MockWebRTCDelegate());
expect(voip.currentCID, null);
// persist normal room messages
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': '1702472924955oq1uQbNAfU7wAaEA',
'party_id': 'DPCIPPBGPO',
'offer': {'type': 'offer', 'sdp': 'sdp'}
},
senderId: '@alice:testing.com',
eventId: 'newevent',
originServerTs: DateTime.utc(1969),
)
]))
})));
await Future.delayed(Duration(seconds: 3));
expect(voip.currentCID, null);

// persist normal room messages
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(events: [
MatrixEvent(
unsigned: {'age': 60001},
type: 'm.call.invite',
content: {
'lifetime': 60000,
'call_id': 'unsignedTsInvalidCall',
'party_id': 'DPCIPPBGPO',
'offer': {'type': 'offer', 'sdp': 'sdp'}
},
senderId: '@alice:testing.com',
eventId: 'newevent',
originServerTs: DateTime.now(),
)
]))
})));
await Future.delayed(Duration(seconds: 3));

expect(voip.currentCID, null);
// persist normal room messages
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': 'originTsValidCall',
'party_id': 'DPCIPPBGPO',
'offer': {'type': 'offer', 'sdp': 'sdp'}
},
senderId: '@alice:testing.com',
eventId: 'newevent',
originServerTs: DateTime.now(),
)
]))
})));
await Future.delayed(Duration(seconds: 3));

Logs().i(
'ignore initWithInvite failed errors here which are due to incomplete webrtc delegate implementations');
expect(voip.currentCID, 'originTsValidCall');
});
});
}
29 changes: 0 additions & 29 deletions test/room_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1052,35 +1052,6 @@ void main() {
expect(room.pushRuleState, PushRuleState.dontNotify);
});

test('Test call methods', () async {
final call = CallSession(CallOptions()..room = room);
await call.sendInviteToCall(room, '1234', 1234, '4567', '7890', 'sdp',
txid: '1234');
await call.sendAnswerCall(room, '1234', 'sdp', '4567', txid: '1234');
await call.sendCallCandidates(room, '1234', '4567', [], txid: '1234');
await call.sendSelectCallAnswer(room, '1234', 1234, '4567', '6789',
txid: '1234');
await call.sendCallReject(room, '1234', 1234, '4567', 'busy',
txid: '1234');
await call.sendCallNegotiate(room, '1234', 1234, '4567', 'sdp',
txid: '1234');
await call.sendHangupCall(room, '1234', '4567', 'user_hangup',
txid: '1234');
await call.sendAssertedIdentity(
room,
'1234',
'4567',
AssertedIdentity()
..displayName = 'name'
..id = 'some_id',
txid: '1234');
await call.sendCallReplaces(room, '1234', '4567', CallReplaces(),
txid: '1234');
await call.sendSDPStreamMetadataChanged(
room, '1234', '4567', SDPStreamMetadata({}),
txid: '1234');
});

test('enableEncryption', () async {
await room.enableEncryption();
});
Expand Down
110 changes: 110 additions & 0 deletions test/webrtc_stub.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import 'package:webrtc_interface/webrtc_interface.dart';

import 'package:matrix/matrix.dart';

class MockWebRTCDelegate implements WebRTCDelegate {
@override
// TODO: implement canHandleNewCall
bool get canHandleNewCall => true;

@override
Future<RTCPeerConnection> createPeerConnection(
Map<String, dynamic> configuration, [
Map<String, dynamic> constraints = const {},
]) =>
throw UnimplementedError();

@override
VideoRenderer createRenderer() {
// TODO: implement createRenderer
throw UnimplementedError();
}

@override
Future<void> handleCallEnded(CallSession session) {
// TODO: implement handleCallEnded
throw UnimplementedError();
}

@override
Future<void> handleGroupCallEnded(GroupCall groupCall) {
// TODO: implement handleGroupCallEnded
throw UnimplementedError();
}

@override
Future<void> handleMissedCall(CallSession session) {
// TODO: implement handleMissedCall
throw UnimplementedError();
}

@override
Future<void> handleNewCall(CallSession session) async {
Logs().i('handleNewCall called in MockWebRTCDelegate');
}

@override
Future<void> handleNewGroupCall(GroupCall groupCall) async {
Logs().i('handleNewGroupCall called in MockWebRTCDelegate');
}

@override
// TODO: implement isWeb
bool get isWeb => false;

@override
// TODO: implement mediaDevices
MediaDevices get mediaDevices => MockMediaDevices();

@override
Future<void> playRingtone() async {
Logs().i('playRingtone called in MockWebRTCDelegate');
}

@override
Future<void> stopRingtone() {
// TODO: implement stopRingtone
throw UnimplementedError();
}
}

class MockMediaDevices implements MediaDevices {
@override
Function(dynamic event)? ondevicechange;

@override
Future<List<MediaDeviceInfo>> enumerateDevices() {
// TODO: implement enumerateDevices
throw UnimplementedError();
}

@override
Future<MediaStream> getDisplayMedia(Map<String, dynamic> mediaConstraints) {
// TODO: implement getDisplayMedia
throw UnimplementedError();
}

@override
Future<List> getSources() {
// TODO: implement getSources
throw UnimplementedError();
}

@override
MediaTrackSupportedConstraints getSupportedConstraints() {
// TODO: implement getSupportedConstraints
throw UnimplementedError();
}

@override
Future<MediaStream> getUserMedia(Map<String, dynamic> mediaConstraints) {
// TODO: implement getUserMedia
throw UnimplementedError();
}

@override
Future<MediaDeviceInfo> selectAudioOutput([AudioOutputOptions? options]) {
// TODO: implement selectAudioOutput
throw UnimplementedError();
}
}

0 comments on commit 6db1cf3

Please sign in to comment.