1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-07-31 15:24:23 +03:00

Add tests for ice candidate sending (#2674)

This commit is contained in:
David Baker
2022-09-16 09:26:37 +01:00
committed by GitHub
parent f52c5eb667
commit 6fc9827b10

View File

@ -37,7 +37,7 @@ import {
MockRTCRtpSender, MockRTCRtpSender,
} from "../../test-utils/webrtc"; } from "../../test-utils/webrtc";
import { CallFeed } from "../../../src/webrtc/callFeed"; import { CallFeed } from "../../../src/webrtc/callFeed";
import { Callback, EventType, IContent, MatrixEvent, Room } from "../../../src"; import { Callback, EventType, IContent, ISendEventResponse, MatrixEvent, Room } from "../../../src";
const FAKE_ROOM_ID = "!foo:bar"; const FAKE_ROOM_ID = "!foo:bar";
const CALL_LIFETIME = 60000; const CALL_LIFETIME = 60000;
@ -99,7 +99,7 @@ describe('Call', function() {
let prevDocument: Document; let prevDocument: Document;
let prevWindow: Window & typeof globalThis; let prevWindow: Window & typeof globalThis;
// We retain a reference to this in the correct Mock type // We retain a reference to this in the correct Mock type
let mockSendEvent: jest.Mock<void, [string, string, IContent, string, Callback<any>]>; let mockSendEvent: jest.Mock<Promise<ISendEventResponse>, [string, string, IContent, string, Callback<any>]>;
beforeEach(function() { beforeEach(function() {
prevNavigator = global.navigator; prevNavigator = global.navigator;
@ -888,6 +888,8 @@ describe('Call', function() {
}); });
describe("answering calls", () => { describe("answering calls", () => {
const realSetTimeout = setTimeout;
beforeEach(async () => { beforeEach(async () => {
await fakeIncomingCall(client, call, "1"); await fakeIncomingCall(client, call, "1");
}); });
@ -898,11 +900,14 @@ describe('Call', function() {
for (let tries = 0; tries < maxTries; ++tries) { for (let tries = 0; tries < maxTries; ++tries) {
if (tries) { if (tries) {
await new Promise(resolve => { await new Promise(resolve => {
setTimeout(resolve, 100); realSetTimeout(resolve, 100);
}); });
} }
// We might not always be in fake timer mode, but it's
// fine to run this if not, so we just call it anyway.
jest.runOnlyPendingTimers();
try { try {
expect(client.client.sendEvent).toHaveBeenCalledWith(...args); expect(mockSendEvent).toHaveBeenCalledWith(...args);
return; return;
} catch (e) { } catch (e) {
if (tries == maxTries - 1) { if (tries == maxTries - 1) {
@ -926,35 +931,107 @@ describe('Call', function() {
); );
}); });
it("sends ICE candidates as separate events if they arrive after the answer", async () => { describe("ICE candidate sending", () => {
let mockPeerConn;
const fakeCandidateString = "here is a fake candidate!"; const fakeCandidateString = "here is a fake candidate!";
const fakeCandidateEvent = {
await call.answer();
await untilEventSent(
FAKE_ROOM_ID,
EventType.CallAnswer,
expect.objectContaining({}),
);
const mockPeerConn = call.peerConn as unknown as MockRTCPeerConnection;
mockPeerConn.iceCandidateListener!({
candidate: { candidate: {
candidate: fakeCandidateString, candidate: fakeCandidateString,
sdpMLineIndex: 0, sdpMLineIndex: 0,
sdpMid: '0', sdpMid: '0',
toJSON: jest.fn().mockReturnValue(fakeCandidateString), toJSON: jest.fn().mockReturnValue(fakeCandidateString),
}, },
} as unknown as RTCPeerConnectionIceEvent); } as unknown as RTCPeerConnectionIceEvent;
await untilEventSent( beforeEach(async () => {
FAKE_ROOM_ID, await call.answer();
EventType.CallCandidates, await untilEventSent(
expect.objectContaining({ FAKE_ROOM_ID,
candidates: [ EventType.CallAnswer,
fakeCandidateString, expect.objectContaining({}),
], );
}), mockPeerConn = call.peerConn as unknown as MockRTCPeerConnection;
); });
afterEach(() => {
jest.useRealTimers();
});
it("sends ICE candidates as separate events if they arrive after the answer", async () => {
mockPeerConn!.iceCandidateListener!(fakeCandidateEvent);
await untilEventSent(
FAKE_ROOM_ID,
EventType.CallCandidates,
expect.objectContaining({
candidates: [
fakeCandidateString,
],
}),
);
});
it("retries sending ICE candidates", async () => {
jest.useFakeTimers();
mockSendEvent.mockRejectedValueOnce(new Error("Fake error"));
mockPeerConn!.iceCandidateListener!(fakeCandidateEvent);
await untilEventSent(
FAKE_ROOM_ID,
EventType.CallCandidates,
expect.objectContaining({
candidates: [
fakeCandidateString,
],
}),
);
mockSendEvent.mockClear();
await untilEventSent(
FAKE_ROOM_ID,
EventType.CallCandidates,
expect.objectContaining({
candidates: [
fakeCandidateString,
],
}),
);
});
it("gives up on call after 5 attempts at sending ICE candidates", async () => {
jest.useFakeTimers();
mockSendEvent.mockImplementation((roomId: string, eventType: string) => {
if (eventType === EventType.CallCandidates) {
return Promise.reject(new Error());
} else {
return Promise.resolve({ event_id: "foo" });
}
});
mockPeerConn!.iceCandidateListener!(fakeCandidateEvent);
while (!call.callHasEnded()) {
jest.runOnlyPendingTimers();
await untilEventSent(
FAKE_ROOM_ID,
EventType.CallCandidates,
expect.objectContaining({
candidates: [
fakeCandidateString,
],
}),
);
if (!call.callHasEnded) {
mockSendEvent.mockReset();
}
}
expect(call.callHasEnded()).toEqual(true);
});
}); });
}); });
@ -1006,6 +1083,7 @@ describe('Call', function() {
const sendNegotiatePromise = new Promise<void>(resolve => { const sendNegotiatePromise = new Promise<void>(resolve => {
mockSendEvent.mockImplementationOnce(() => { mockSendEvent.mockImplementationOnce(() => {
resolve(); resolve();
return Promise.resolve({ event_id: "foo" });
}); });
}); });