Skip to content

Commit 6fc9827

Browse files
authored
Add tests for ice candidate sending (#2674)
1 parent f52c5eb commit 6fc9827

File tree

1 file changed

+103
-25
lines changed

1 file changed

+103
-25
lines changed

spec/unit/webrtc/call.spec.ts

Lines changed: 103 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import {
3737
MockRTCRtpSender,
3838
} from "../../test-utils/webrtc";
3939
import { CallFeed } from "../../../src/webrtc/callFeed";
40-
import { Callback, EventType, IContent, MatrixEvent, Room } from "../../../src";
40+
import { Callback, EventType, IContent, ISendEventResponse, MatrixEvent, Room } from "../../../src";
4141

4242
const FAKE_ROOM_ID = "!foo:bar";
4343
const CALL_LIFETIME = 60000;
@@ -99,7 +99,7 @@ describe('Call', function() {
9999
let prevDocument: Document;
100100
let prevWindow: Window & typeof globalThis;
101101
// We retain a reference to this in the correct Mock type
102-
let mockSendEvent: jest.Mock<void, [string, string, IContent, string, Callback<any>]>;
102+
let mockSendEvent: jest.Mock<Promise<ISendEventResponse>, [string, string, IContent, string, Callback<any>]>;
103103

104104
beforeEach(function() {
105105
prevNavigator = global.navigator;
@@ -888,6 +888,8 @@ describe('Call', function() {
888888
});
889889

890890
describe("answering calls", () => {
891+
const realSetTimeout = setTimeout;
892+
891893
beforeEach(async () => {
892894
await fakeIncomingCall(client, call, "1");
893895
});
@@ -898,11 +900,14 @@ describe('Call', function() {
898900
for (let tries = 0; tries < maxTries; ++tries) {
899901
if (tries) {
900902
await new Promise(resolve => {
901-
setTimeout(resolve, 100);
903+
realSetTimeout(resolve, 100);
902904
});
903905
}
906+
// We might not always be in fake timer mode, but it's
907+
// fine to run this if not, so we just call it anyway.
908+
jest.runOnlyPendingTimers();
904909
try {
905-
expect(client.client.sendEvent).toHaveBeenCalledWith(...args);
910+
expect(mockSendEvent).toHaveBeenCalledWith(...args);
906911
return;
907912
} catch (e) {
908913
if (tries == maxTries - 1) {
@@ -926,35 +931,107 @@ describe('Call', function() {
926931
);
927932
});
928933

929-
it("sends ICE candidates as separate events if they arrive after the answer", async () => {
934+
describe("ICE candidate sending", () => {
935+
let mockPeerConn;
930936
const fakeCandidateString = "here is a fake candidate!";
931-
932-
await call.answer();
933-
await untilEventSent(
934-
FAKE_ROOM_ID,
935-
EventType.CallAnswer,
936-
expect.objectContaining({}),
937-
);
938-
939-
const mockPeerConn = call.peerConn as unknown as MockRTCPeerConnection;
940-
mockPeerConn.iceCandidateListener!({
937+
const fakeCandidateEvent = {
941938
candidate: {
942939
candidate: fakeCandidateString,
943940
sdpMLineIndex: 0,
944941
sdpMid: '0',
945942
toJSON: jest.fn().mockReturnValue(fakeCandidateString),
946943
},
947-
} as unknown as RTCPeerConnectionIceEvent);
944+
} as unknown as RTCPeerConnectionIceEvent;
945+
946+
beforeEach(async () => {
947+
await call.answer();
948+
await untilEventSent(
949+
FAKE_ROOM_ID,
950+
EventType.CallAnswer,
951+
expect.objectContaining({}),
952+
);
953+
mockPeerConn = call.peerConn as unknown as MockRTCPeerConnection;
954+
});
948955

949-
await untilEventSent(
950-
FAKE_ROOM_ID,
951-
EventType.CallCandidates,
952-
expect.objectContaining({
953-
candidates: [
954-
fakeCandidateString,
955-
],
956-
}),
957-
);
956+
afterEach(() => {
957+
jest.useRealTimers();
958+
});
959+
960+
it("sends ICE candidates as separate events if they arrive after the answer", async () => {
961+
mockPeerConn!.iceCandidateListener!(fakeCandidateEvent);
962+
963+
await untilEventSent(
964+
FAKE_ROOM_ID,
965+
EventType.CallCandidates,
966+
expect.objectContaining({
967+
candidates: [
968+
fakeCandidateString,
969+
],
970+
}),
971+
);
972+
});
973+
974+
it("retries sending ICE candidates", async () => {
975+
jest.useFakeTimers();
976+
977+
mockSendEvent.mockRejectedValueOnce(new Error("Fake error"));
978+
979+
mockPeerConn!.iceCandidateListener!(fakeCandidateEvent);
980+
981+
await untilEventSent(
982+
FAKE_ROOM_ID,
983+
EventType.CallCandidates,
984+
expect.objectContaining({
985+
candidates: [
986+
fakeCandidateString,
987+
],
988+
}),
989+
);
990+
991+
mockSendEvent.mockClear();
992+
993+
await untilEventSent(
994+
FAKE_ROOM_ID,
995+
EventType.CallCandidates,
996+
expect.objectContaining({
997+
candidates: [
998+
fakeCandidateString,
999+
],
1000+
}),
1001+
);
1002+
});
1003+
1004+
it("gives up on call after 5 attempts at sending ICE candidates", async () => {
1005+
jest.useFakeTimers();
1006+
1007+
mockSendEvent.mockImplementation((roomId: string, eventType: string) => {
1008+
if (eventType === EventType.CallCandidates) {
1009+
return Promise.reject(new Error());
1010+
} else {
1011+
return Promise.resolve({ event_id: "foo" });
1012+
}
1013+
});
1014+
1015+
mockPeerConn!.iceCandidateListener!(fakeCandidateEvent);
1016+
1017+
while (!call.callHasEnded()) {
1018+
jest.runOnlyPendingTimers();
1019+
await untilEventSent(
1020+
FAKE_ROOM_ID,
1021+
EventType.CallCandidates,
1022+
expect.objectContaining({
1023+
candidates: [
1024+
fakeCandidateString,
1025+
],
1026+
}),
1027+
);
1028+
if (!call.callHasEnded) {
1029+
mockSendEvent.mockReset();
1030+
}
1031+
}
1032+
1033+
expect(call.callHasEnded()).toEqual(true);
1034+
});
9581035
});
9591036
});
9601037

@@ -1006,6 +1083,7 @@ describe('Call', function() {
10061083
const sendNegotiatePromise = new Promise<void>(resolve => {
10071084
mockSendEvent.mockImplementationOnce(() => {
10081085
resolve();
1086+
return Promise.resolve({ event_id: "foo" });
10091087
});
10101088
});
10111089

0 commit comments

Comments
 (0)