From 0d14ceffc94f72893c96bb4cd624a274e1fa4b90 Mon Sep 17 00:00:00 2001 From: damencho Date: Fri, 20 Dec 2024 16:28:25 -0600 Subject: [PATCH] feat(tests): Adds codec selection tests. --- tests/helpers/Participant.ts | 5 +- tests/helpers/participants.ts | 23 +++-- tests/helpers/types.ts | 7 ++ tests/specs/3way/codecSelection.spec.ts | 121 ++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 13 deletions(-) create mode 100644 tests/specs/3way/codecSelection.spec.ts diff --git a/tests/helpers/Participant.ts b/tests/helpers/Participant.ts index 3cc6ce6f7aec..6ea306351048 100644 --- a/tests/helpers/Participant.ts +++ b/tests/helpers/Participant.ts @@ -122,7 +122,10 @@ export class Participant { async joinConference(ctx: IContext, options: IJoinOptions = {}): Promise { const config = { room: ctx.roomName, - configOverwrite: this.config, + configOverwrite: { + ...this.config, + ...options.configOverwrite || {} + }, interfaceConfigOverwrite: { SHOW_CHROME_EXTENSION_BANNER: false } diff --git a/tests/helpers/participants.ts b/tests/helpers/participants.ts index 57e47de0cc5f..3d30217bbe02 100644 --- a/tests/helpers/participants.ts +++ b/tests/helpers/participants.ts @@ -28,26 +28,25 @@ export async function ensureOneParticipant(ctx: IContext, options?: IJoinOptions * Ensure that there are three participants. * * @param {Object} ctx - The context. + * @param {IJoinOptions} options - The options to use when joining the participant. * @returns {Promise} */ -export async function ensureThreeParticipants(ctx: IContext): Promise { - await joinTheModeratorAsP1(ctx); - - const p2 = new Participant('participant2'); - const p3 = new Participant('participant3'); - - ctx.p2 = p2; - ctx.p3 = p3; +export async function ensureThreeParticipants(ctx: IContext, options?: IJoinOptions): Promise { + await joinTheModeratorAsP1(ctx, options); // these need to be all, so we get the error when one fails await Promise.all([ - p2.joinConference(ctx), - p3.joinConference(ctx) + _joinParticipant('participant2', ctx.p2, p => { + ctx.p2 = p; + }, options), + _joinParticipant('participant3', ctx.p3, p => { + ctx.p3 = p; + }, options) ]); await Promise.all([ - p2.waitForRemoteStreams(2), - p3.waitForRemoteStreams(2) + ctx.p2.waitForRemoteStreams(2), + ctx.p3.waitForRemoteStreams(2) ]); } diff --git a/tests/helpers/types.ts b/tests/helpers/types.ts index e03e82ea1a10..c86b2bc474a5 100644 --- a/tests/helpers/types.ts +++ b/tests/helpers/types.ts @@ -1,3 +1,5 @@ +import { IConfig } from '../../react/features/base/config/configType'; + import type { Participant } from './Participant'; import WebhookProxy from './WebhookProxy'; @@ -17,6 +19,11 @@ export type IContext = { export type IJoinOptions = { + /** + * Config overwrites to use. + */ + configOverwrite?: IConfig; + /** * Whether to skip setting display name. */ diff --git a/tests/specs/3way/codecSelection.spec.ts b/tests/specs/3way/codecSelection.spec.ts new file mode 100644 index 000000000000..c81f6419ca64 --- /dev/null +++ b/tests/specs/3way/codecSelection.spec.ts @@ -0,0 +1,121 @@ +import { ensureOneParticipant, ensureThreeParticipants, ensureTwoParticipants } from '../../helpers/participants'; + +describe('Codec selection - ', () => { + it('asymmetric codecs', async () => { + await ensureOneParticipant(ctx, { + configOverwrite: { + videoQuality: { + codecPreferenceOrder: [ 'VP9', 'VP8', 'AV1' ] + } + } + }); + + await ensureTwoParticipants(ctx, { + configOverwrite: { + videoQuality: { + codecPreferenceOrder: [ 'VP8', 'VP9', 'AV1' ] + } + } + }); + const { p1, p2 } = ctx; + + // Check if media is playing on both endpoints. + expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true); + expect(await p2.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true); + + // Check if p1 is sending VP9 and p2 is sending VP8 as per their codec preferences. + // Except on Firefox because it doesn't support VP9 encode. + if (p1.driver.isFirefox) { + expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true); + } else { + expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9())).toBe(true); + } + + expect(await p2.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true); + }); + + it('asymmetric codecs with AV1', async () => { + await ensureThreeParticipants(ctx, { + configOverwrite: { + videoQuality: { + codecPreferenceOrder: [ 'AV1', 'VP9', 'VP8' ] + } + } + }); + const { p1, p2, p3 } = ctx; + + // Check if media is playing on p3. + expect(await p3.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true); + + // Check if p1 is encoding in VP9, p2 in VP8 and p3 in AV1 as per their codec preferences. + // Except on Firefox because it doesn't support AV1/VP9 encode and AV1 decode. + if (p1.driver.isFirefox) { + expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true); + } else { + expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9())).toBe(true); + } + + expect(await p2.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true); + + // If there is a Firefox ep in the call, all other eps will switch to VP9. + if (p1.driver.isFirefox) { + expect(await p3.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9())).toBe(true); + } else { + expect(await p3.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingAv1())).toBe(true); + } + }); + + it('codec switch over', async () => { + await Promise.all([ ctx.p1.hangup(), ctx.p2.hangup(), ctx.p3.hangup() ]); + + await ensureTwoParticipants(ctx, { + configOverwrite: { + videoQuality: { + codecPreferenceOrder: [ 'VP9', 'VP8', 'AV1' ] + } + } + }); + const { p1, p2 } = ctx; + + // Disable this test on Firefox because it doesn't support VP9 encode. + if (p1.driver.isFirefox) { + return; + } + + // Check if p1 and p2 are encoding in VP9 which is the default codec. + expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9())).toBe(true); + expect(await p2.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9())).toBe(true); + + await ensureThreeParticipants(ctx, { + configOverwrite: { + videoQuality: { + codecPreferenceOrder: [ 'VP8' ] + } + } + }); + const { p3 } = ctx; + + // Check if all three participants are encoding in VP8 now. + expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true); + expect(await p2.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true); + expect(await p3.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true); + + await p3.hangup(); + + // Check of p1 and p2 have switched to VP9. + await p1.driver.waitUntil( + async () => await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9()), + { + timeout: 10000, + timeoutMsg: 'p1 did not switch back to VP9' + } + ); + await p2.driver.waitUntil( + async () => await p2.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9()), + { + timeout: 10000, + timeoutMsg: 'p1 did not switch back to VP9' + } + ); + }); +});